FA-70106 / Map projection transforms / Open access
Spherical Lambert conformal conic forward: scale constant exponent · case 01
The whole map is scaled by the cone constant.
ROOT CAUSE
The mapping constant F omits the division by n.
VERIFIED REPAIR
At the scale constant exponent step restore `Fc = math.cos(p1) * t(p1) ** n / n`, leaving the rest of the model unchanged.
Unsuccessful approach: The exponent is written as 1/n instead of n.
Case contract
Input [lon, lat, lon0, lat0, lat1, lat2] degrees, sphere R = 6371000, all latitudes strictly inside (-90, 90) and on the cone side of the equator. t(p) = tan(pi/4 + p/2). n = ln(cos p1/cos p2)/ln(t(p2)/t(p1)), or sin(p1) for a tangent cone (lat1 == lat2). F = cos(p1)*t(p1)**n/n; rho = R*F/t(phi)**n; rho0 = R*F/t(p0)**n; theta = n*dlon with dlon wrapped to [-180, 180). x = rho*sin(theta), y = rho0 - rho*cos(theta), rounded to 2 decimals.
Why this case matters
Aeronautical charts and many national grids use conformal conics; a wrong cone constant distorts whole regions.
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, lon0, lat0, lat1, lat2 = x
R = 6371000.0
p0, p1, p2, phi = [math.radians(v) for v in (lat0, lat1, lat2, lat)]
def t(p):
return math.tan(math.pi / 4 + p / 2)
if abs(lat1 - lat2) < 1e-9:
n = math.sin(p1)
else:
n = math.log(math.cos(p1) / math.cos(p2)) / math.log(t(p2) / t(p1))
Fc = math.cos(p1) * t(p1) ** n
rho = R * Fc / t(phi) ** n
rho0 = R * Fc / t(p0) ** n
dl = math.radians(((lon - lon0 + 180.0) % 360.0) - 180.0)
theta = n * dl
return [round(rho * math.sin(theta), 2), round(rho0 - rho * math.cos(theta), 2)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-89.295, -2.579, -96.0, 23.0, 33.0, 45.0], [944572.37, -3177093.78]), ('control #1', [43.173, 23.809, 10.0, 52.0, 35.0, 65.0], [3477264.95, -2333351.52]), ('control #2', [133.802, -22.769, 132.0, 0.0, -18.0, -36.0], [182997.23, -2612489.01]), ('control #3', [-77.146, 4.744, -60.0, -32.0, -5.0, -42.0], [-2030234.59, 3881165.57]), ('control #4', [-6.229, 62.13, 0.0, 40.0, 40.0, 40.0], [-353408.33, 2543656.82]), ('control #5', [57.493, -17.538, 20.0, -45.0, -45.0, -45.0], [4256679.54, 2162268.41]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #1', [43.173, 23.809, 10.0, 52.0, 35.0, 65.0], [3477264.95, -2333351.52]), ('control #2', [133.802, -22.769, 132.0, 0.0, -18.0, -36.0], [182997.23, -2612489.01]), ('control #5', [57.493, -17.538, 20.0, -45.0, -45.0, -45.0], [4256679.54, 2162268.41]), ('control #6', [108.376, 58.574, 100.0, 30.0, 25.0, 47.0], [519881.13, 3212621.55]), ('control #7', [-134.048, 58.473, -96.0, 23.0, 33.0, 45.0], [-2281076.94, 4475835.48]), ('control #8', [38.766, 26.431, 10.0, 52.0, 35.0, 65.0], [2923972.77, -2242833.53]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #2', [133.802, -22.769, 132.0, 0.0, -18.0, -36.0], [182997.23, -2612489.01]), ('control #3', [-77.146, 4.744, -60.0, -32.0, -5.0, -42.0], [-2030234.59, 3881165.57]), ('control #8', [38.766, 26.431, 10.0, 52.0, 35.0, 65.0], [2923972.77, -2242833.53]), ('control #9', [128.089, -10.995, 132.0, 0.0, -18.0, -36.0], [-438054.92, -1302331.66]), ('control #10', [-72.231, -55.506, -60.0, -32.0, -5.0, -42.0], [-871966.44, -2710910.26]), ('control #11', [-30.764, 26.528, 0.0, 40.0, 40.0, 40.0], [-3080105.21, -974518.51]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #3', [-77.146, 4.744, -60.0, -32.0, -5.0, -42.0], [-2030234.59, 3881165.57]), ('control #4', [-6.229, 62.13, 0.0, 40.0, 40.0, 40.0], [-353408.33, 2543656.82]), ('control #11', [-30.764, 26.528, 0.0, 40.0, 40.0, 40.0], [-3080105.21, -974518.51]), ('control #12', [31.835, 4.636, 20.0, -45.0, -45.0, -45.0], [1831181.26, 6076923.26]), ('control #13', [74.565, 35.028, 100.0, 30.0, 25.0, 47.0], [-2247809.44, 846928.26]), ('control #14', [-102.998, 17.408, -96.0, 23.0, 33.0, 45.0], [-788758.63, -620642.12]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #4', [-6.229, 62.13, 0.0, 40.0, 40.0, 40.0], [-353408.33, 2543656.82]), ('control #5', [57.493, -17.538, 20.0, -45.0, -45.0, -45.0], [4256679.54, 2162268.41]), ('control #14', [-102.998, 17.408, -96.0, 23.0, 33.0, 45.0], [-788758.63, -620642.12]), ('control #15', [-3.245, 2.365, 10.0, 52.0, 35.0, 65.0], [-1927899.63, -5755732.58]), ('control #16', [150.921, -43.877, 132.0, 0.0, -18.0, -36.0], [1562347.67, -5075599.0]), ('control #17', [-99.291, 2.109, -60.0, -32.0, -5.0, -42.0], [-4518513.04, 3061080.05]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])]]
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 | [595531.81, -2003086.77] | [944572.37, -3177093.78] | Failed |
| control #1 | [2695885.33, -1809021.81] | [3477264.95, -2333351.52] | Failed |
| control #2 | [-83424.51, 1190977.75] | [182997.23, -2612489.01] | Failed |
| control #3 | [824289.64, -1575780.74] | [-2030234.59, 3881165.57] | Failed |
| control #4 | [-227166.5, 1635031.09] | [-353408.33, 2543656.82] | Failed |
| control #5 | [-3009926.97, -1528954.65] | [4256679.54, 2162268.41] | Failed |
| boundary #28 | [0.0, 0.0] | [0.0, 0.0] | Passed |
| boundary #29 | [0.0, 0.0] | [0.0, 0.0] | Passed |
SHA-256 / 71057c74bd9a6fe7d88e87160181b946e2b960c99600148205507f9f9fd985d4
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, lon0, lat0, lat1, lat2 = x
R = 6371000.0
p0, p1, p2, phi = [math.radians(v) for v in (lat0, lat1, lat2, lat)]
def t(p):
return math.tan(math.pi / 4 + p / 2)
if abs(lat1 - lat2) < 1e-9:
n = math.sin(p1)
else:
n = math.log(math.cos(p1) / math.cos(p2)) / math.log(t(p2) / t(p1))
Fc = math.cos(p1) * t(p1) ** (1 / n) / n
rho = R * Fc / t(phi) ** n
rho0 = R * Fc / t(p0) ** n
dl = math.radians(((lon - lon0 + 180.0) % 360.0) - 180.0)
theta = n * dl
return [round(rho * math.sin(theta), 2), round(rho0 - rho * math.cos(theta), 2)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-89.295, -2.579, -96.0, 23.0, 33.0, 45.0], [944572.37, -3177093.78]), ('control #1', [43.173, 23.809, 10.0, 52.0, 35.0, 65.0], [3477264.95, -2333351.52]), ('control #2', [133.802, -22.769, 132.0, 0.0, -18.0, -36.0], [182997.23, -2612489.01]), ('control #3', [-77.146, 4.744, -60.0, -32.0, -5.0, -42.0], [-2030234.59, 3881165.57]), ('control #4', [-6.229, 62.13, 0.0, 40.0, 40.0, 40.0], [-353408.33, 2543656.82]), ('control #5', [57.493, -17.538, 20.0, -45.0, -45.0, -45.0], [4256679.54, 2162268.41]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #1', [43.173, 23.809, 10.0, 52.0, 35.0, 65.0], [3477264.95, -2333351.52]), ('control #2', [133.802, -22.769, 132.0, 0.0, -18.0, -36.0], [182997.23, -2612489.01]), ('control #5', [57.493, -17.538, 20.0, -45.0, -45.0, -45.0], [4256679.54, 2162268.41]), ('control #6', [108.376, 58.574, 100.0, 30.0, 25.0, 47.0], [519881.13, 3212621.55]), ('control #7', [-134.048, 58.473, -96.0, 23.0, 33.0, 45.0], [-2281076.94, 4475835.48]), ('control #8', [38.766, 26.431, 10.0, 52.0, 35.0, 65.0], [2923972.77, -2242833.53]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #2', [133.802, -22.769, 132.0, 0.0, -18.0, -36.0], [182997.23, -2612489.01]), ('control #3', [-77.146, 4.744, -60.0, -32.0, -5.0, -42.0], [-2030234.59, 3881165.57]), ('control #8', [38.766, 26.431, 10.0, 52.0, 35.0, 65.0], [2923972.77, -2242833.53]), ('control #9', [128.089, -10.995, 132.0, 0.0, -18.0, -36.0], [-438054.92, -1302331.66]), ('control #10', [-72.231, -55.506, -60.0, -32.0, -5.0, -42.0], [-871966.44, -2710910.26]), ('control #11', [-30.764, 26.528, 0.0, 40.0, 40.0, 40.0], [-3080105.21, -974518.51]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #3', [-77.146, 4.744, -60.0, -32.0, -5.0, -42.0], [-2030234.59, 3881165.57]), ('control #4', [-6.229, 62.13, 0.0, 40.0, 40.0, 40.0], [-353408.33, 2543656.82]), ('control #11', [-30.764, 26.528, 0.0, 40.0, 40.0, 40.0], [-3080105.21, -974518.51]), ('control #12', [31.835, 4.636, 20.0, -45.0, -45.0, -45.0], [1831181.26, 6076923.26]), ('control #13', [74.565, 35.028, 100.0, 30.0, 25.0, 47.0], [-2247809.44, 846928.26]), ('control #14', [-102.998, 17.408, -96.0, 23.0, 33.0, 45.0], [-788758.63, -620642.12]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #4', [-6.229, 62.13, 0.0, 40.0, 40.0, 40.0], [-353408.33, 2543656.82]), ('control #5', [57.493, -17.538, 20.0, -45.0, -45.0, -45.0], [4256679.54, 2162268.41]), ('control #14', [-102.998, 17.408, -96.0, 23.0, 33.0, 45.0], [-788758.63, -620642.12]), ('control #15', [-3.245, 2.365, 10.0, 52.0, 35.0, 65.0], [-1927899.63, -5755732.58]), ('control #16', [150.921, -43.877, 132.0, 0.0, -18.0, -36.0], [1562347.67, -5075599.0]), ('control #17', [-99.291, 2.109, -60.0, -32.0, -5.0, -42.0], [-4518513.04, 3061080.05]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])]]
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 | [1693167.84, -5695014.16] | [944572.37, -3177093.78] | Failed |
| control #1 | [4865477.08, -3264884.47] | [3477264.95, -2333351.52] | Failed |
| control #2 | [318808.24, -4551342.31] | [182997.23, -2612489.01] | Failed |
| control #3 | [-2429989.88, 4645371.08] | [-2030234.59, 3881165.57] | Failed |
| control #4 | [-709181.85, 5104337.11] | [-353408.33, 2543656.82] | Failed |
| control #5 | [7938423.04, 4032486.16] | [4256679.54, 2162268.41] | Failed |
| boundary #28 | [0.0, 0.0] | [0.0, 0.0] | Passed |
| boundary #29 | [0.0, 0.0] | [0.0, 0.0] | Passed |
SHA-256 / 44386598efdb09963e050fb7073b05cdbb2eae7daaf964df300724256857ff39
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, lon0, lat0, lat1, lat2 = x
R = 6371000.0
p0, p1, p2, phi = [math.radians(v) for v in (lat0, lat1, lat2, lat)]
def t(p):
return math.tan(math.pi / 4 + p / 2)
if abs(lat1 - lat2) < 1e-9:
n = math.sin(p1)
else:
n = math.log(math.cos(p1) / math.cos(p2)) / math.log(t(p2) / t(p1))
Fc = math.cos(p1) * t(p1) ** n / n
rho = R * Fc / t(phi) ** n
rho0 = R * Fc / t(p0) ** n
dl = math.radians(((lon - lon0 + 180.0) % 360.0) - 180.0)
theta = n * dl
return [round(rho * math.sin(theta), 2), round(rho0 - rho * math.cos(theta), 2)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-89.295, -2.579, -96.0, 23.0, 33.0, 45.0], [944572.37, -3177093.78]), ('control #1', [43.173, 23.809, 10.0, 52.0, 35.0, 65.0], [3477264.95, -2333351.52]), ('control #2', [133.802, -22.769, 132.0, 0.0, -18.0, -36.0], [182997.23, -2612489.01]), ('control #3', [-77.146, 4.744, -60.0, -32.0, -5.0, -42.0], [-2030234.59, 3881165.57]), ('control #4', [-6.229, 62.13, 0.0, 40.0, 40.0, 40.0], [-353408.33, 2543656.82]), ('control #5', [57.493, -17.538, 20.0, -45.0, -45.0, -45.0], [4256679.54, 2162268.41]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #1', [43.173, 23.809, 10.0, 52.0, 35.0, 65.0], [3477264.95, -2333351.52]), ('control #2', [133.802, -22.769, 132.0, 0.0, -18.0, -36.0], [182997.23, -2612489.01]), ('control #5', [57.493, -17.538, 20.0, -45.0, -45.0, -45.0], [4256679.54, 2162268.41]), ('control #6', [108.376, 58.574, 100.0, 30.0, 25.0, 47.0], [519881.13, 3212621.55]), ('control #7', [-134.048, 58.473, -96.0, 23.0, 33.0, 45.0], [-2281076.94, 4475835.48]), ('control #8', [38.766, 26.431, 10.0, 52.0, 35.0, 65.0], [2923972.77, -2242833.53]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #2', [133.802, -22.769, 132.0, 0.0, -18.0, -36.0], [182997.23, -2612489.01]), ('control #3', [-77.146, 4.744, -60.0, -32.0, -5.0, -42.0], [-2030234.59, 3881165.57]), ('control #8', [38.766, 26.431, 10.0, 52.0, 35.0, 65.0], [2923972.77, -2242833.53]), ('control #9', [128.089, -10.995, 132.0, 0.0, -18.0, -36.0], [-438054.92, -1302331.66]), ('control #10', [-72.231, -55.506, -60.0, -32.0, -5.0, -42.0], [-871966.44, -2710910.26]), ('control #11', [-30.764, 26.528, 0.0, 40.0, 40.0, 40.0], [-3080105.21, -974518.51]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #3', [-77.146, 4.744, -60.0, -32.0, -5.0, -42.0], [-2030234.59, 3881165.57]), ('control #4', [-6.229, 62.13, 0.0, 40.0, 40.0, 40.0], [-353408.33, 2543656.82]), ('control #11', [-30.764, 26.528, 0.0, 40.0, 40.0, 40.0], [-3080105.21, -974518.51]), ('control #12', [31.835, 4.636, 20.0, -45.0, -45.0, -45.0], [1831181.26, 6076923.26]), ('control #13', [74.565, 35.028, 100.0, 30.0, 25.0, 47.0], [-2247809.44, 846928.26]), ('control #14', [-102.998, 17.408, -96.0, 23.0, 33.0, 45.0], [-788758.63, -620642.12]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])], [('control #4', [-6.229, 62.13, 0.0, 40.0, 40.0, 40.0], [-353408.33, 2543656.82]), ('control #5', [57.493, -17.538, 20.0, -45.0, -45.0, -45.0], [4256679.54, 2162268.41]), ('control #14', [-102.998, 17.408, -96.0, 23.0, 33.0, 45.0], [-788758.63, -620642.12]), ('control #15', [-3.245, 2.365, 10.0, 52.0, 35.0, 65.0], [-1927899.63, -5755732.58]), ('control #16', [150.921, -43.877, 132.0, 0.0, -18.0, -36.0], [1562347.67, -5075599.0]), ('control #17', [-99.291, 2.109, -60.0, -32.0, -5.0, -42.0], [-4518513.04, 3061080.05]), ('boundary #28', [-96.0, 23.0, -96.0, 23.0, 33.0, 45.0], [0.0, 0.0]), ('boundary #29', [5.0, 40.0, 5.0, 40.0, 40.0, 40.0], [0.0, 0.0])]]
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 | [944572.37, -3177093.78] | [944572.37, -3177093.78] | Passed |
| control #1 | [3477264.95, -2333351.52] | [3477264.95, -2333351.52] | Passed |
| control #2 | [182997.23, -2612489.01] | [182997.23, -2612489.01] | Passed |
| control #3 | [-2030234.59, 3881165.57] | [-2030234.59, 3881165.57] | Passed |
| control #4 | [-353408.33, 2543656.82] | [-353408.33, 2543656.82] | Passed |
| control #5 | [4256679.54, 2162268.41] | [4256679.54, 2162268.41] | Passed |
| boundary #28 | [0.0, 0.0] | [0.0, 0.0] | Passed |
| boundary #29 | [0.0, 0.0] | [0.0, 0.0] | Passed |
SHA-256 / 51c0c51930998eb0219c55fb93562f1865114f107929a46d779dcd79006d4960
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:17.621667+00:00.
Case digest / f1bfd1af0bd3dfeaa2e6b4fe6014937586af7b7bfc3071f06fd0566df6e7eb4e