Skip to content

Add UTM zone projections (EPSG:32601–32660, 32701–32760) - #241

Closed
trasch wants to merge 1 commit into
registry-driven-wktfrom
add-utm-zones
Closed

trasch wants to merge 1 commit into
registry-driven-wktfrom
add-utm-zones

Conversation

@trasch

@trasch trasch commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Phase 5 of making the projection system generic — the UTM family. Stacks on #240 (phase 4: registry-driven WKT matching); merge the chain in order. The API decision from the plan discussion was option 1: 120 dedicated enum cases.

Math

Standard Snyder transverse Mercator on the WGS84 ellipsoid (k₀ = 0.9996, 500_000 m false easting, per-hemisphere false northing). Verified independently before porting:

  • Meridian arc validated against numeric (Simpson) integration of the meridian radius — divergence < 0.5 mm
  • Round trips over 252 points spanning every zone and latitudes to ±89°: worst error 1.06e-08° (~1 m)
  • Hemisphere checks: equal eastings, exact 10_000_000 m northing offset
  • The existing UTM→4326 parser (Coordinate3D.init?(easting:northing:zone:hemisphere:)) already matched the verified math; the new definition adds the long-missing 4326→UTM direction

Changes

  • UtmDefinition.swift: UtmHemisphere + UtmDefinition — both directions of the projection, per-zone central meridian, EPSG sector extents ([100_000–900_000, 0–10_000_000]), world box = zone sector, no WKT patterns (per-zone .prj matching would need zone-number parsing — left out of scope)
  • Projection: 120 new cases; init?(srid:) accepts the contiguous SRID ranges without enumerating cases; wraparoundExtent is nil (zone coordinates never wrap); description defaults to the EPSG: label so it scales with SRID count
  • ProjectionRegistry: definitions resolve through an eagerly built dictionary (SRID ranges can't drive exhaustive switches); registration coverage is asserted by tests at runtime and via assertionFailure in debug

Testing (+13)

  • SRID→zone/hemisphere mapping for all 120 SRIDs, registry-coverage sweep, per-zone central-meridian checks
  • Known forward values: zone-edge equator eastings (±166_021.443), scaled meridian arcs, both hemispheres
  • Hemisphere symmetry, round trips, boundingBox/contains/distance, antimeridian no-ops in date-line zones 1/60, normalize/clamp, WKB round trips, random generation in the zone sector

Full suite: 2463 tests in 177 suites, zero warnings.

Phase 5 of making the projection system generic.

Math: standard Snyder transverse Mercator formulas on the WGS84
ellipsoid, verified independently in Python before porting - the
meridian arc against Simpson integration of the meridian radius
(<0.5mm divergence) and round trips over 252 points covering all
zones and latitudes to 89 degrees (max error 1.06e-08 degrees).

- UtmDefinition.swift: UtmHemisphere and UtmDefinition implementing
  both directions of the projection with a per-zone central
  meridian, the 0.9996 scale factor, the 500_000 false easting and
  per-hemisphere false northing. Sector extents follow the EPSG
  convention (easting 100_000-900_000, northing 0-10_000_000);
  the world bounding box is the zone sector and no WKT pattern is
  registered (UTM .prj files would need per-zone parsing).
- Projection gains all 120 zone cases (northern 32601-32660,
  southern 32701-32760) and init?(srid:) accepts the contiguous
  SRID ranges. wraparoundExtent is nil (zone coordinates never
  wrap), kind is planar; description now defaults to the EPSG
  label so it scales with the SRID count.
- ProjectionRegistry resolves definitions via an eagerly built
  dictionary; registration coverage is asserted at runtime by the
  new UtmDefinitionTests sweep over all UTM SRIDs.

Tests: SRID-to-zone/hemisphere mapping for all 120 SRIDs, registry
coverage sweep, central meridian math per zone, known forward
values (zone-edge equator eastings, scaled meridian arcs, both
hemispheres), hemisphere symmetry (equal eastings, exact 10_000_000
northing offset), round trips, boundingBox/contains/distance,
antimeridian no-ops in date-line zones, normalize/clamp, WKB round
trips and random generation within the zone sector.
@trasch

trasch commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #250, which consolidates the full projection-genericity chain into a single reviewable PR. All commits are unchanged in #250.

@trasch trasch closed this Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant