FA-70091 / Map projection transforms / Open access
Sinusoidal equal-area inverse: longitude wrap · case 01
Pixels near the outline on shifted central meridians report longitudes beyond 180.
ROOT CAUSE
After adding the central meridian the longitude is not wrapped.
VERIFIED REPAIR
At the longitude wrap step restore `lon = ((lon0 + math.degrees(dl) + 180.0) % 360.0) - 180.0`, leaving the rest of the model unchanged.
Unsuccessful approach: Wrapping into [0, 360) returns western longitudes as large positive values.
Case contract
Input [x, y, lon0] metres on a sphere R = 6371007. lat = y/R radians; if |lat| > pi/2 return None. If cos(lat) < 1e-12 (a pole) return [lon0, +/-90]. Otherwise dl = x/(R*cos(lat)); if |dl| > pi the point is outside the map outline and None is returned. lon = lon0 + deg(dl) wrapped to [-180, 180). Return [lon, lat] degrees rounded to 6 decimals.
Why this case matters
MODIS-style sinusoidal grids are inverted to label pixels; outside-outline pixels must be recognised as fill.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
px, py, lon0 = x
R = 6371007.0
lat_r = py / R
if abs(lat_r) > math.pi / 2:
return None
c = math.cos(lat_r)
if c < 1e-12:
return [round(lon0, 6), round(math.degrees(lat_r), 6)]
dl = px / (R * c)
if abs(dl) > math.pi:
return None
lon = lon0 + math.degrees(dl)
return [round(lon, 6), round(math.degrees(lat_r), 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-6456860.5, 6184557.0, 100.0], [-2.830805, 55.618996]), ('control #1', [-4076169.5, -114936.6, -60.0], [-96.663799, -1.033649]), ('control #2', [7301979.2, 2452334.2, -60.0], [10.852625, 22.054347]), ('control #3', [-869092.1, -5986427.5, 170.0], [156.75451, -53.837177]), ('control #4', [-3900713.2, 4055798.9, 100.0], [56.374778, 36.474636]), ('control #5', [-18585422.9, -1353.5, -60.0], [132.857456, -0.012172]), ('control #6', [-775892.3, 7364756.8, 0.0], [-17.313602, 66.232776]), ('control #7', [4713150.3, 7011518.5, 0.0], [93.543502, 63.056031])], [('control #1', [-4076169.5, -114936.6, -60.0], [-96.663799, -1.033649]), ('control #4', [-3900713.2, 4055798.9, 100.0], [56.374778, 36.474636]), ('control #5', [-18585422.9, -1353.5, -60.0], [132.857456, -0.012172]), ('control #6', [-775892.3, 7364756.8, 0.0], [-17.313602, 66.232776]), ('control #7', [4713150.3, 7011518.5, 0.0], [93.543502, 63.056031]), ('control #8', [-5244799.8, -3910343.5, -60.0], [-117.698632, -35.166525]), ('control #9', [13447778.9, -3010314.0, 0.0], [135.820143, -27.072374]), ('regression #20', [5000000.0, 1000000.0, 150.0], [-164.474317, 8.993206])], [('control #6', [-775892.3, 7364756.8, 0.0], [-17.313602, 66.232776]), ('control #7', [4713150.3, 7011518.5, 0.0], [93.543502, 63.056031]), ('control #8', [-5244799.8, -3910343.5, -60.0], [-117.698632, -35.166525]), ('control #9', [13447778.9, -3010314.0, 0.0], [135.820143, -27.072374]), ('control #10', [-2612435.3, 3809472.3, 100.0], [71.573824, 34.25937]), ('control #11', [-517878.4, 4527351.0, 170.0], [163.855358, 40.715401]), ('control #12', [455295.0, 8282288.4, -60.0], [-44.693339, 74.484327]), ('regression #21', [-5000000.0, -1000000.0, -150.0], [164.474317, -8.993206])], [('control #5', [-18585422.9, -1353.5, -60.0], [132.857456, -0.012172]), ('control #8', [-5244799.8, -3910343.5, -60.0], [-117.698632, -35.166525]), ('control #11', [-517878.4, 4527351.0, 170.0], [163.855358, 40.715401]), ('control #12', [455295.0, 8282288.4, -60.0], [-44.693339, 74.484327]), ('control #13', [-3490805.5, 8855899.7, 170.0], [-4.619905, 79.642932]), ('boundary #14', [0.0, 10007554.393584574, 20.0], [20.0, 90.0]), ('boundary #15', [1000.0, -10007554.393584574, -45.0], [-45.0, -90.0]), ('regression #16', [9000000.0, 8000000.0, 0.0], None)], [('control #12', [455295.0, 8282288.4, -60.0], [-44.693339, 74.484327]), ('control #13', [-3490805.5, 8855899.7, 170.0], [-4.619905, 79.642932]), ('boundary #14', [0.0, 10007554.393584574, 20.0], [20.0, 90.0]), ('boundary #15', [1000.0, -10007554.393584574, -45.0], [-45.0, -90.0]), ('regression #16', [9000000.0, 8000000.0, 0.0], None), ('regression #17', [15000000.0, 6000000.0, 10.0], None), ('boundary #18', [0.0, 10100000.0, 0.0], None), ('regression #20', [5000000.0, 1000000.0, 150.0], [-164.474317, 8.993206])]]
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 | [-2.830805, 55.618996] | [-2.830805, 55.618996] | Passed |
| control #1 | [-96.663799, -1.033649] | [-96.663799, -1.033649] | Passed |
| control #2 | [10.852625, 22.054347] | [10.852625, 22.054347] | Passed |
| control #3 | [156.75451, -53.837177] | [156.75451, -53.837177] | Passed |
| control #4 | [56.374778, 36.474636] | [56.374778, 36.474636] | Passed |
| control #5 | [-227.142544, -0.012172] | [132.857456, -0.012172] | Failed |
| control #6 | [-17.313602, 66.232776] | [-17.313602, 66.232776] | Passed |
| control #7 | [93.543502, 63.056031] | [93.543502, 63.056031] | Passed |
SHA-256 / fc5e3069d701a2952642083ee9b01860c1c6faee4883cfb7c0fc0170422a5521
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
px, py, lon0 = x
R = 6371007.0
lat_r = py / R
if abs(lat_r) > math.pi / 2:
return None
c = math.cos(lat_r)
if c < 1e-12:
return [round(lon0, 6), round(math.degrees(lat_r), 6)]
dl = px / (R * c)
if abs(dl) > math.pi:
return None
lon = (lon0 + math.degrees(dl)) % 360.0
return [round(lon, 6), round(math.degrees(lat_r), 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-6456860.5, 6184557.0, 100.0], [-2.830805, 55.618996]), ('control #1', [-4076169.5, -114936.6, -60.0], [-96.663799, -1.033649]), ('control #2', [7301979.2, 2452334.2, -60.0], [10.852625, 22.054347]), ('control #3', [-869092.1, -5986427.5, 170.0], [156.75451, -53.837177]), ('control #4', [-3900713.2, 4055798.9, 100.0], [56.374778, 36.474636]), ('control #5', [-18585422.9, -1353.5, -60.0], [132.857456, -0.012172]), ('control #6', [-775892.3, 7364756.8, 0.0], [-17.313602, 66.232776]), ('control #7', [4713150.3, 7011518.5, 0.0], [93.543502, 63.056031])], [('control #1', [-4076169.5, -114936.6, -60.0], [-96.663799, -1.033649]), ('control #4', [-3900713.2, 4055798.9, 100.0], [56.374778, 36.474636]), ('control #5', [-18585422.9, -1353.5, -60.0], [132.857456, -0.012172]), ('control #6', [-775892.3, 7364756.8, 0.0], [-17.313602, 66.232776]), ('control #7', [4713150.3, 7011518.5, 0.0], [93.543502, 63.056031]), ('control #8', [-5244799.8, -3910343.5, -60.0], [-117.698632, -35.166525]), ('control #9', [13447778.9, -3010314.0, 0.0], [135.820143, -27.072374]), ('regression #20', [5000000.0, 1000000.0, 150.0], [-164.474317, 8.993206])], [('control #6', [-775892.3, 7364756.8, 0.0], [-17.313602, 66.232776]), ('control #7', [4713150.3, 7011518.5, 0.0], [93.543502, 63.056031]), ('control #8', [-5244799.8, -3910343.5, -60.0], [-117.698632, -35.166525]), ('control #9', [13447778.9, -3010314.0, 0.0], [135.820143, -27.072374]), ('control #10', [-2612435.3, 3809472.3, 100.0], [71.573824, 34.25937]), ('control #11', [-517878.4, 4527351.0, 170.0], [163.855358, 40.715401]), ('control #12', [455295.0, 8282288.4, -60.0], [-44.693339, 74.484327]), ('regression #21', [-5000000.0, -1000000.0, -150.0], [164.474317, -8.993206])], [('control #5', [-18585422.9, -1353.5, -60.0], [132.857456, -0.012172]), ('control #8', [-5244799.8, -3910343.5, -60.0], [-117.698632, -35.166525]), ('control #11', [-517878.4, 4527351.0, 170.0], [163.855358, 40.715401]), ('control #12', [455295.0, 8282288.4, -60.0], [-44.693339, 74.484327]), ('control #13', [-3490805.5, 8855899.7, 170.0], [-4.619905, 79.642932]), ('boundary #14', [0.0, 10007554.393584574, 20.0], [20.0, 90.0]), ('boundary #15', [1000.0, -10007554.393584574, -45.0], [-45.0, -90.0]), ('regression #16', [9000000.0, 8000000.0, 0.0], None)], [('control #12', [455295.0, 8282288.4, -60.0], [-44.693339, 74.484327]), ('control #13', [-3490805.5, 8855899.7, 170.0], [-4.619905, 79.642932]), ('boundary #14', [0.0, 10007554.393584574, 20.0], [20.0, 90.0]), ('boundary #15', [1000.0, -10007554.393584574, -45.0], [-45.0, -90.0]), ('regression #16', [9000000.0, 8000000.0, 0.0], None), ('regression #17', [15000000.0, 6000000.0, 10.0], None), ('boundary #18', [0.0, 10100000.0, 0.0], None), ('regression #20', [5000000.0, 1000000.0, 150.0], [-164.474317, 8.993206])]]
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 | [357.169195, 55.618996] | [-2.830805, 55.618996] | Failed |
| control #1 | [263.336201, -1.033649] | [-96.663799, -1.033649] | Failed |
| control #2 | [10.852625, 22.054347] | [10.852625, 22.054347] | Passed |
| control #3 | [156.75451, -53.837177] | [156.75451, -53.837177] | Passed |
| control #4 | [56.374778, 36.474636] | [56.374778, 36.474636] | Passed |
| control #5 | [132.857456, -0.012172] | [132.857456, -0.012172] | Passed |
| control #6 | [342.686398, 66.232776] | [-17.313602, 66.232776] | Failed |
| control #7 | [93.543502, 63.056031] | [93.543502, 63.056031] | Passed |
SHA-256 / ddf69bd81545b6d6e85a5c84d5a7f6b6d306cd3051ee44bd803d76cbd67e5a07
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
px, py, lon0 = x
R = 6371007.0
lat_r = py / R
if abs(lat_r) > math.pi / 2:
return None
c = math.cos(lat_r)
if c < 1e-12:
return [round(lon0, 6), round(math.degrees(lat_r), 6)]
dl = px / (R * c)
if abs(dl) > math.pi:
return None
lon = ((lon0 + math.degrees(dl) + 180.0) % 360.0) - 180.0
return [round(lon, 6), round(math.degrees(lat_r), 6)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-6456860.5, 6184557.0, 100.0], [-2.830805, 55.618996]), ('control #1', [-4076169.5, -114936.6, -60.0], [-96.663799, -1.033649]), ('control #2', [7301979.2, 2452334.2, -60.0], [10.852625, 22.054347]), ('control #3', [-869092.1, -5986427.5, 170.0], [156.75451, -53.837177]), ('control #4', [-3900713.2, 4055798.9, 100.0], [56.374778, 36.474636]), ('control #5', [-18585422.9, -1353.5, -60.0], [132.857456, -0.012172]), ('control #6', [-775892.3, 7364756.8, 0.0], [-17.313602, 66.232776]), ('control #7', [4713150.3, 7011518.5, 0.0], [93.543502, 63.056031])], [('control #1', [-4076169.5, -114936.6, -60.0], [-96.663799, -1.033649]), ('control #4', [-3900713.2, 4055798.9, 100.0], [56.374778, 36.474636]), ('control #5', [-18585422.9, -1353.5, -60.0], [132.857456, -0.012172]), ('control #6', [-775892.3, 7364756.8, 0.0], [-17.313602, 66.232776]), ('control #7', [4713150.3, 7011518.5, 0.0], [93.543502, 63.056031]), ('control #8', [-5244799.8, -3910343.5, -60.0], [-117.698632, -35.166525]), ('control #9', [13447778.9, -3010314.0, 0.0], [135.820143, -27.072374]), ('regression #20', [5000000.0, 1000000.0, 150.0], [-164.474317, 8.993206])], [('control #6', [-775892.3, 7364756.8, 0.0], [-17.313602, 66.232776]), ('control #7', [4713150.3, 7011518.5, 0.0], [93.543502, 63.056031]), ('control #8', [-5244799.8, -3910343.5, -60.0], [-117.698632, -35.166525]), ('control #9', [13447778.9, -3010314.0, 0.0], [135.820143, -27.072374]), ('control #10', [-2612435.3, 3809472.3, 100.0], [71.573824, 34.25937]), ('control #11', [-517878.4, 4527351.0, 170.0], [163.855358, 40.715401]), ('control #12', [455295.0, 8282288.4, -60.0], [-44.693339, 74.484327]), ('regression #21', [-5000000.0, -1000000.0, -150.0], [164.474317, -8.993206])], [('control #5', [-18585422.9, -1353.5, -60.0], [132.857456, -0.012172]), ('control #8', [-5244799.8, -3910343.5, -60.0], [-117.698632, -35.166525]), ('control #11', [-517878.4, 4527351.0, 170.0], [163.855358, 40.715401]), ('control #12', [455295.0, 8282288.4, -60.0], [-44.693339, 74.484327]), ('control #13', [-3490805.5, 8855899.7, 170.0], [-4.619905, 79.642932]), ('boundary #14', [0.0, 10007554.393584574, 20.0], [20.0, 90.0]), ('boundary #15', [1000.0, -10007554.393584574, -45.0], [-45.0, -90.0]), ('regression #16', [9000000.0, 8000000.0, 0.0], None)], [('control #12', [455295.0, 8282288.4, -60.0], [-44.693339, 74.484327]), ('control #13', [-3490805.5, 8855899.7, 170.0], [-4.619905, 79.642932]), ('boundary #14', [0.0, 10007554.393584574, 20.0], [20.0, 90.0]), ('boundary #15', [1000.0, -10007554.393584574, -45.0], [-45.0, -90.0]), ('regression #16', [9000000.0, 8000000.0, 0.0], None), ('regression #17', [15000000.0, 6000000.0, 10.0], None), ('boundary #18', [0.0, 10100000.0, 0.0], None), ('regression #20', [5000000.0, 1000000.0, 150.0], [-164.474317, 8.993206])]]
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 | [-2.830805, 55.618996] | [-2.830805, 55.618996] | Passed |
| control #1 | [-96.663799, -1.033649] | [-96.663799, -1.033649] | Passed |
| control #2 | [10.852625, 22.054347] | [10.852625, 22.054347] | Passed |
| control #3 | [156.75451, -53.837177] | [156.75451, -53.837177] | Passed |
| control #4 | [56.374778, 36.474636] | [56.374778, 36.474636] | Passed |
| control #5 | [132.857456, -0.012172] | [132.857456, -0.012172] | Passed |
| control #6 | [-17.313602, 66.232776] | [-17.313602, 66.232776] | Passed |
| control #7 | [93.543502, 63.056031] | [93.543502, 63.056031] | Passed |
SHA-256 / 6e159c2bc7ff20796a112f71956f1f9ac33d6fb93b6ba4da8695a3a626187262
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.325725+00:00.
Case digest / 757d5b531906681ff6b1e009f638d93632ac2bf25aad130aff42e07cd30f3fc4