FAILURE MAP
← Case archive

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.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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