FA-70036 / Map projection transforms / Open access
Spherical transverse mercator in a UTM grid: false easting direction · case 01
The zone is mirrored about its central meridian.
ROOT CAUSE
The easting subtracts the projected x from the false easting.
VERIFIED REPAIR
At the false easting direction step restore `easting = 500000.0 + xm`, leaving the rest of the model unchanged.
Unsuccessful approach: Using abs(x) folds western points onto the eastern half of the zone.
Case contract
Input [lon, lat, zone, hemisphere] with |lat| <= 80 and lon within 4 degrees of the zone central meridian 6*zone-183. On a sphere R = 6371000 with k0 = 0.9996: B = cos(phi)*sin(dl); x = 0.5*k0*R*ln((1+B)/(1-B)); y = k0*R*atan2(tan(phi), cos(dl)). Easting = 500000 + x. Northing = y plus 10000000 when the requested grid hemisphere is "S" (the caller may deliberately project points just north of the equator in a southern grid). Return [easting, northing] rounded to 2 decimals.
Why this case matters
Survey exports and GPS receivers express positions as UTM eastings and northings; a wrong false origin or meridian moves points by hundreds of kilometres.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
lon, lat, zone, hemi = x
R = 6371000.0
k0 = 0.9996
lon0 = zone * 6 - 183
dl = math.radians(lon - lon0)
phi = math.radians(lat)
B = math.cos(phi) * math.sin(dl)
xm = 0.5 * k0 * R * math.log((1 + B) / (1 - B))
ym = k0 * R * math.atan2(math.tan(phi), math.cos(dl))
easting = 500000.0 - xm
northing = ym + (10000000.0 if hemi == 'S' else 0.0)
return [round(easting, 2), round(northing, 2)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-146.456, 65.451, 6, 'S'], [525121.58, 17275016.5]), ('control #1', [-24.305, 48.206, 26, 'S'], [699628.48, 15361620.03]), ('control #2', [-3.252, -34.222, 30, 'N'], [476839.56, -3803819.3]), ('control #3', [140.223, -1.339, 54, 'S'], [413657.04, 9851155.87]), ('control #4', [-99.681, -57.299, 14, 'S'], [459106.63, 3630985.93]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #1', [-24.305, 48.206, 26, 'S'], [699628.48, 15361620.03]), ('control #3', [140.223, -1.339, 54, 'S'], [413657.04, 9851155.87]), ('control #5', [131.956, -7.201, 52, 'S'], [826109.36, 9198550.44]), ('control #6', [-63.683, 39.81, 20, 'S'], [441683.41, 14425121.91]), ('control #7', [-31.602, 39.795, 25, 'S'], [619393.11, 14424164.48]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #2', [-3.252, -34.222, 30, 'N'], [476839.56, -3803819.3]), ('control #6', [-63.683, 39.81, 20, 'S'], [441683.41, 14425121.91]), ('control #8', [87.283, -45.722, 45, 'N'], [521960.41, -5082059.64]), ('control #9', [74.243, 54.044, 43, 'S'], [450595.96, 16007279.04]), ('control #10', [-96.557, 39.544, 14, 'S'], [709406.66, 14398176.63]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #3', [140.223, -1.339, 54, 'S'], [413657.04, 9851155.87]), ('control #9', [74.243, 54.044, 43, 'S'], [450595.96, 16007279.04]), ('control #11', [162.356, -15.269, 58, 'N'], [216405.53, -1698880.3]), ('control #12', [-107.58, 5.652, 13, 'N'], [214531.37, 628855.65]), ('control #13', [100.982, 3.293, 47, 'S'], [719980.03, 10366237.05]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #4', [-99.681, -57.299, 14, 'S'], [459106.63, 3630985.93]), ('control #11', [162.356, -15.269, 58, 'N'], [216405.53, -1698880.3]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('regression #15', [-75.5, 0.2, 18, 'S'], [444424.41, 10022230.94]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('regression #17', [30.0, -0.3, 36, 'N'], [166400.77, -33390.89]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19]), ('control #19', [12.3, -35.0, 33, 'S'], [254136.28, 6106410.11])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| control #0 | [474878.42, 17275016.5] | [525121.58, 17275016.5] | Failed |
| control #1 | [300371.52, 15361620.03] | [699628.48, 15361620.03] | Failed |
| control #2 | [523160.44, -3803819.3] | [476839.56, -3803819.3] | Failed |
| control #3 | [586342.96, 9851155.87] | [413657.04, 9851155.87] | Failed |
| control #4 | [540893.37, 3630985.93] | [459106.63, 3630985.93] | Failed |
| regression #14 | [500000.0, 10055575.22] | [500000.0, 10055575.22] | Passed |
| boundary #16 | [500000.0, 0.0] | [500000.0, 0.0] | Passed |
| boundary #18 | [500000.0, 5001770.19] | [500000.0, 5001770.19] | Passed |
SHA-256 / 160152553d9b77df7d5edc7d798d3ef6f743c4229a94658e05c9f47a40d9a1d7
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
lon, lat, zone, hemi = x
R = 6371000.0
k0 = 0.9996
lon0 = zone * 6 - 183
dl = math.radians(lon - lon0)
phi = math.radians(lat)
B = math.cos(phi) * math.sin(dl)
xm = 0.5 * k0 * R * math.log((1 + B) / (1 - B))
ym = k0 * R * math.atan2(math.tan(phi), math.cos(dl))
easting = 500000.0 + abs(xm)
northing = ym + (10000000.0 if hemi == 'S' else 0.0)
return [round(easting, 2), round(northing, 2)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-146.456, 65.451, 6, 'S'], [525121.58, 17275016.5]), ('control #1', [-24.305, 48.206, 26, 'S'], [699628.48, 15361620.03]), ('control #2', [-3.252, -34.222, 30, 'N'], [476839.56, -3803819.3]), ('control #3', [140.223, -1.339, 54, 'S'], [413657.04, 9851155.87]), ('control #4', [-99.681, -57.299, 14, 'S'], [459106.63, 3630985.93]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #1', [-24.305, 48.206, 26, 'S'], [699628.48, 15361620.03]), ('control #3', [140.223, -1.339, 54, 'S'], [413657.04, 9851155.87]), ('control #5', [131.956, -7.201, 52, 'S'], [826109.36, 9198550.44]), ('control #6', [-63.683, 39.81, 20, 'S'], [441683.41, 14425121.91]), ('control #7', [-31.602, 39.795, 25, 'S'], [619393.11, 14424164.48]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #2', [-3.252, -34.222, 30, 'N'], [476839.56, -3803819.3]), ('control #6', [-63.683, 39.81, 20, 'S'], [441683.41, 14425121.91]), ('control #8', [87.283, -45.722, 45, 'N'], [521960.41, -5082059.64]), ('control #9', [74.243, 54.044, 43, 'S'], [450595.96, 16007279.04]), ('control #10', [-96.557, 39.544, 14, 'S'], [709406.66, 14398176.63]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #3', [140.223, -1.339, 54, 'S'], [413657.04, 9851155.87]), ('control #9', [74.243, 54.044, 43, 'S'], [450595.96, 16007279.04]), ('control #11', [162.356, -15.269, 58, 'N'], [216405.53, -1698880.3]), ('control #12', [-107.58, 5.652, 13, 'N'], [214531.37, 628855.65]), ('control #13', [100.982, 3.293, 47, 'S'], [719980.03, 10366237.05]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #4', [-99.681, -57.299, 14, 'S'], [459106.63, 3630985.93]), ('control #11', [162.356, -15.269, 58, 'N'], [216405.53, -1698880.3]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('regression #15', [-75.5, 0.2, 18, 'S'], [444424.41, 10022230.94]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('regression #17', [30.0, -0.3, 36, 'N'], [166400.77, -33390.89]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19]), ('control #19', [12.3, -35.0, 33, 'S'], [254136.28, 6106410.11])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| control #0 | [525121.58, 17275016.5] | [525121.58, 17275016.5] | Passed |
| control #1 | [699628.48, 15361620.03] | [699628.48, 15361620.03] | Passed |
| control #2 | [523160.44, -3803819.3] | [476839.56, -3803819.3] | Failed |
| control #3 | [586342.96, 9851155.87] | [413657.04, 9851155.87] | Failed |
| control #4 | [540893.37, 3630985.93] | [459106.63, 3630985.93] | Failed |
| regression #14 | [500000.0, 10055575.22] | [500000.0, 10055575.22] | Passed |
| boundary #16 | [500000.0, 0.0] | [500000.0, 0.0] | Passed |
| boundary #18 | [500000.0, 5001770.19] | [500000.0, 5001770.19] | Passed |
SHA-256 / 5b86f3cf0a77b1eb7b62805a3359cac9762e9a43953c8dca9c714c858d6c31a2
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
lon, lat, zone, hemi = x
R = 6371000.0
k0 = 0.9996
lon0 = zone * 6 - 183
dl = math.radians(lon - lon0)
phi = math.radians(lat)
B = math.cos(phi) * math.sin(dl)
xm = 0.5 * k0 * R * math.log((1 + B) / (1 - B))
ym = k0 * R * math.atan2(math.tan(phi), math.cos(dl))
easting = 500000.0 + xm
northing = ym + (10000000.0 if hemi == 'S' else 0.0)
return [round(easting, 2), round(northing, 2)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-146.456, 65.451, 6, 'S'], [525121.58, 17275016.5]), ('control #1', [-24.305, 48.206, 26, 'S'], [699628.48, 15361620.03]), ('control #2', [-3.252, -34.222, 30, 'N'], [476839.56, -3803819.3]), ('control #3', [140.223, -1.339, 54, 'S'], [413657.04, 9851155.87]), ('control #4', [-99.681, -57.299, 14, 'S'], [459106.63, 3630985.93]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #1', [-24.305, 48.206, 26, 'S'], [699628.48, 15361620.03]), ('control #3', [140.223, -1.339, 54, 'S'], [413657.04, 9851155.87]), ('control #5', [131.956, -7.201, 52, 'S'], [826109.36, 9198550.44]), ('control #6', [-63.683, 39.81, 20, 'S'], [441683.41, 14425121.91]), ('control #7', [-31.602, 39.795, 25, 'S'], [619393.11, 14424164.48]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #2', [-3.252, -34.222, 30, 'N'], [476839.56, -3803819.3]), ('control #6', [-63.683, 39.81, 20, 'S'], [441683.41, 14425121.91]), ('control #8', [87.283, -45.722, 45, 'N'], [521960.41, -5082059.64]), ('control #9', [74.243, 54.044, 43, 'S'], [450595.96, 16007279.04]), ('control #10', [-96.557, 39.544, 14, 'S'], [709406.66, 14398176.63]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #3', [140.223, -1.339, 54, 'S'], [413657.04, 9851155.87]), ('control #9', [74.243, 54.044, 43, 'S'], [450595.96, 16007279.04]), ('control #11', [162.356, -15.269, 58, 'N'], [216405.53, -1698880.3]), ('control #12', [-107.58, 5.652, 13, 'N'], [214531.37, 628855.65]), ('control #13', [100.982, 3.293, 47, 'S'], [719980.03, 10366237.05]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19])], [('control #4', [-99.681, -57.299, 14, 'S'], [459106.63, 3630985.93]), ('control #11', [162.356, -15.269, 58, 'N'], [216405.53, -1698880.3]), ('regression #14', [9.0, 0.5, 32, 'S'], [500000.0, 10055575.22]), ('regression #15', [-75.5, 0.2, 18, 'S'], [444424.41, 10022230.94]), ('boundary #16', [15.0, 0.0, 33, 'N'], [500000.0, 0.0]), ('regression #17', [30.0, -0.3, 36, 'N'], [166400.77, -33390.89]), ('boundary #18', [3.0, 45.0, 31, 'N'], [500000.0, 5001770.19]), ('control #19', [12.3, -35.0, 33, 'S'], [254136.28, 6106410.11])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| control #0 | [525121.58, 17275016.5] | [525121.58, 17275016.5] | Passed |
| control #1 | [699628.48, 15361620.03] | [699628.48, 15361620.03] | Passed |
| control #2 | [476839.56, -3803819.3] | [476839.56, -3803819.3] | Passed |
| control #3 | [413657.04, 9851155.87] | [413657.04, 9851155.87] | Passed |
| control #4 | [459106.63, 3630985.93] | [459106.63, 3630985.93] | Passed |
| regression #14 | [500000.0, 10055575.22] | [500000.0, 10055575.22] | Passed |
| boundary #16 | [500000.0, 0.0] | [500000.0, 0.0] | Passed |
| boundary #18 | [500000.0, 5001770.19] | [500000.0, 5001770.19] | Passed |
SHA-256 / 39891577b5e90cd4a6b794b46139b54eab2a01f16567bf94fbbed48f980139b6
Verification & scope
Stipulated deterministic toy contract on a bounded input domain; results are rounded as stated and no conformance with any published standard or library is claimed. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:48:16.993867+00:00.
Case digest / 4e4d1202a840a145fb72e9f0eeb80026935193114b68a820fcfb60e06e7717de