Fix Oblique Mercator natural-origin offset to use the central-line az… · locationtech/proj4j@e79dbaf · GitHub
Skip to content

Commit e79dbaf

Browse files
Fix Oblique Mercator natural-origin offset to use the central-line azimuth (#130)
The natural-origin (uc) offset was computed from the rectified bearing (Gamma) via cosrot, but Snyder's Hotine Oblique Mercator defines it with the central-line azimuth (alpha): u_c = (A / B) * atan(sqrt(D^2 - 1) / cos(alpha_c)) When Gamma equals alpha the two agree, which is why this went unnoticed. For definitions where the rectified bearing differs from the azimuth -- notably the ESRI "Local" / mine-grid convention of an explicit +gamma=0 with a non-zero azimuth -- the projection centre was displaced along the central line (e.g. ~2.5 km), so the false easting/northing no longer landed on the centre. Use cos(alpha) for the offset. This is a no-op when Gamma == alpha and matches PROJ output when Gamma != alpha. Adds a regression test (Proj4VariousTest#testObliqueMercatorExplicitGamma) covering +gamma=0 with a non-zero azimuth, with reference coordinates computed by PROJ. Signed-off-by: Sergei Malyshko <sergei.malyshko@navvis.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 463bad3 commit e79dbaf

3 files changed

Lines changed: 21 additions & 1 deletion

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions

‎core/src/main/java/org/locationtech/proj4j/proj/ObliqueMercatorProjection.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,12 @@ else if (con > Math.PI)
158158
sinrot = Math.sin(Gamma);
159159
cosrot = Math.cos(Gamma);
160160

161+
// The natural-origin offset uses the azimuth of the central line (alpha), following Snyder's
162+
// Hotine Oblique Mercator: u_c = (A / B) * atan(sqrt(D^2 - 1) / cos(alpha_c)). Using the
163+
// rectified bearing (Gamma) here misplaces the projection centre whenever Gamma != alpha,
164+
// e.g. for definitions with an explicit "+gamma=0" and a non-zero azimuth.
161165
u_0 = no_uoff ? 0. :
162-
Math.abs(al * Math.atan(Math.sqrt(d * d - 1.) / cosrot) / bl);
166+
Math.abs(al * Math.atan(Math.sqrt(d * d - 1.) / Math.cos(alpha)) / bl);
163167
if (projectionLatitude < 0.)
164168
u_0 = - u_0;
165169
}

‎core/src/test/java/org/locationtech/proj4j/Proj4VariousTest.java‎

Lines changed: 13 additions & 0 deletions

0 commit comments

Comments
 (0)