FA-69841 / Map projection transforms / Open access
Spherical web mercator forward projection: longitude wrap · case 01
Features east of the antimeridian or given in [180, 540) ranges land off the projected world extent.
ROOT CAUSE
The wrap step only subtracts one turn for out-of-range longitudes, so values below -180 or beyond one extra turn are never normalised and +180 stays at +180 instead of mapping to -180.
VERIFIED REPAIR
At the longitude wrap step restore `lon = ((lon + 180.0) % 360.0) - 180.0`, leaving the rest of the model unchanged.
Unsuccessful approach: math.fmod keeps the sign of the dividend, so longitudes below -180 still come out below -180.
Case contract
Input [lon, lat] in degrees. Longitude is first wrapped into the half-open range [-180, 180); latitude is clamped to +/-85.05112878. Return [x, y] metres on a sphere of radius 6378137 with y growing northward, each rounded to 3 decimals.
Why this case matters
Web map tiles, vector tile encoders and map viewers all start from this forward transform; small mistakes shift features by kilometres or mirror them across the equator.
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 = x
lon = lon if abs(lon) <= 180.0 else lon - 360.0
lat = max(-85.05112878, min(85.05112878, lat))
R = 6378137.0
px = R * math.radians(lon)
py = R * math.log(math.tan(math.pi / 4 + math.radians(lat) / 2))
return [round(px, 3), round(py, 3)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression #0', [190.0, 10.0], [-18924313.435, 1118889.975]), ('regression #1', [-200.0, -20.0], [17811118.527, -2273030.927]), ('boundary #2', [180.0, 0.0], [-20037508.343, -0.0]), ('boundary #3', [-180.0, 45.0], [-20037508.343, 5621521.486]), ('regression #4', [545.0, 5.0], [-19480910.889, 557305.257]), ('boundary #5', [0.0, 89.0], [0.0, 20037508.343]), ('boundary #6', [10.0, -88.0], [1113194.908, -20037508.343]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19])], [('boundary #2', [180.0, 0.0], [-20037508.343, -0.0]), ('regression #4', [545.0, 5.0], [-19480910.889, 557305.257]), ('boundary #5', [0.0, 89.0], [0.0, 20037508.343]), ('boundary #6', [10.0, -88.0], [1113194.908, -20037508.343]), ('boundary #7', [0.0, 85.05112878], [0.0, 20037508.343]), ('control #8', [0.0, 0.0], [0.0, -0.0]), ('regression #9', [-540.0, 30.0], [-20037508.343, 3503549.844]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19])], [('regression #4', [545.0, 5.0], [-19480910.889, 557305.257]), ('boundary #7', [0.0, 85.05112878], [0.0, 20037508.343]), ('control #8', [0.0, 0.0], [0.0, -0.0]), ('regression #9', [-540.0, 30.0], [-20037508.343, 3503549.844]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19]), ('regression #11', [-359.5, -40.0], [55659.745, -4865942.28]), ('boundary #12', [45.0, -86.5], [5009377.086, -20037508.343]), ('boundary #13', [-120.0, -87.25], [-13358338.895, -20037508.343])], [('regression #9', [-540.0, 30.0], [-20037508.343, 3503549.844]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19]), ('regression #11', [-359.5, -40.0], [55659.745, -4865942.28]), ('boundary #12', [45.0, -86.5], [5009377.086, -20037508.343]), ('boundary #13', [-120.0, -87.25], [-13358338.895, -20037508.343]), ('regression #14', [-181.0, 0.5], [19926188.852, 55660.452]), ('control #15', [-17.048, 10.042], [-1897774.679, 1123637.827]), ('control #16', [151.867, -5.771], [16905757.108, -643513.79])], [('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19]), ('regression #11', [-359.5, -40.0], [55659.745, -4865942.28]), ('regression #14', [-181.0, 0.5], [19926188.852, 55660.452]), ('control #15', [-17.048, 10.042], [-1897774.679, 1123637.827]), ('control #16', [151.867, -5.771], [16905757.108, -643513.79]), ('control #17', [2.807, 14.681], [312473.811, 1652463.738]), ('control #18', [-112.892, 2.001], [-12567079.955, 222795.596]), ('control #19', [46.498, 49.22], [5176133.683, 6312273.571])]]
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 |
|---|---|---|---|
| regression #0 | [-18924313.435, 1118889.975] | [-18924313.435, 1118889.975] | Passed |
| regression #1 | [-62338914.844, -2273030.927] | [17811118.527, -2273030.927] | Failed |
| boundary #2 | [20037508.343, -0.0] | [-20037508.343, -0.0] | Failed |
| boundary #3 | [-20037508.343, 5621521.486] | [-20037508.343, 5621521.486] | Passed |
| regression #4 | [20594105.797, 557305.257] | [-19480910.889, 557305.257] | Failed |
| boundary #5 | [0.0, 20037508.343] | [0.0, 20037508.343] | Passed |
| boundary #6 | [1113194.908, -20037508.343] | [1113194.908, -20037508.343] | Passed |
| regression #10 | [-67904889.384, 1402665.19] | [12245143.987, 1402665.19] | Failed |
SHA-256 / 12bc903f10fbf807780a501880be6218ba92853f46b16378030db73b773239f1
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 = x
lon = math.fmod(lon + 180.0, 360.0) - 180.0
lat = max(-85.05112878, min(85.05112878, lat))
R = 6378137.0
px = R * math.radians(lon)
py = R * math.log(math.tan(math.pi / 4 + math.radians(lat) / 2))
return [round(px, 3), round(py, 3)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression #0', [190.0, 10.0], [-18924313.435, 1118889.975]), ('regression #1', [-200.0, -20.0], [17811118.527, -2273030.927]), ('boundary #2', [180.0, 0.0], [-20037508.343, -0.0]), ('boundary #3', [-180.0, 45.0], [-20037508.343, 5621521.486]), ('regression #4', [545.0, 5.0], [-19480910.889, 557305.257]), ('boundary #5', [0.0, 89.0], [0.0, 20037508.343]), ('boundary #6', [10.0, -88.0], [1113194.908, -20037508.343]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19])], [('boundary #2', [180.0, 0.0], [-20037508.343, -0.0]), ('regression #4', [545.0, 5.0], [-19480910.889, 557305.257]), ('boundary #5', [0.0, 89.0], [0.0, 20037508.343]), ('boundary #6', [10.0, -88.0], [1113194.908, -20037508.343]), ('boundary #7', [0.0, 85.05112878], [0.0, 20037508.343]), ('control #8', [0.0, 0.0], [0.0, -0.0]), ('regression #9', [-540.0, 30.0], [-20037508.343, 3503549.844]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19])], [('regression #4', [545.0, 5.0], [-19480910.889, 557305.257]), ('boundary #7', [0.0, 85.05112878], [0.0, 20037508.343]), ('control #8', [0.0, 0.0], [0.0, -0.0]), ('regression #9', [-540.0, 30.0], [-20037508.343, 3503549.844]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19]), ('regression #11', [-359.5, -40.0], [55659.745, -4865942.28]), ('boundary #12', [45.0, -86.5], [5009377.086, -20037508.343]), ('boundary #13', [-120.0, -87.25], [-13358338.895, -20037508.343])], [('regression #9', [-540.0, 30.0], [-20037508.343, 3503549.844]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19]), ('regression #11', [-359.5, -40.0], [55659.745, -4865942.28]), ('boundary #12', [45.0, -86.5], [5009377.086, -20037508.343]), ('boundary #13', [-120.0, -87.25], [-13358338.895, -20037508.343]), ('regression #14', [-181.0, 0.5], [19926188.852, 55660.452]), ('control #15', [-17.048, 10.042], [-1897774.679, 1123637.827]), ('control #16', [151.867, -5.771], [16905757.108, -643513.79])], [('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19]), ('regression #11', [-359.5, -40.0], [55659.745, -4865942.28]), ('regression #14', [-181.0, 0.5], [19926188.852, 55660.452]), ('control #15', [-17.048, 10.042], [-1897774.679, 1123637.827]), ('control #16', [151.867, -5.771], [16905757.108, -643513.79]), ('control #17', [2.807, 14.681], [312473.811, 1652463.738]), ('control #18', [-112.892, 2.001], [-12567079.955, 222795.596]), ('control #19', [46.498, 49.22], [5176133.683, 6312273.571])]]
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 |
|---|---|---|---|
| regression #0 | [-18924313.435, 1118889.975] | [-18924313.435, 1118889.975] | Passed |
| regression #1 | [-22263898.159, -2273030.927] | [17811118.527, -2273030.927] | Failed |
| boundary #2 | [-20037508.343, -0.0] | [-20037508.343, -0.0] | Passed |
| boundary #3 | [-20037508.343, 5621521.486] | [-20037508.343, 5621521.486] | Passed |
| regression #4 | [-19480910.889, 557305.257] | [-19480910.889, 557305.257] | Passed |
| boundary #5 | [0.0, 20037508.343] | [0.0, 20037508.343] | Passed |
| boundary #6 | [1113194.908, -20037508.343] | [1113194.908, -20037508.343] | Passed |
| regression #10 | [-27829872.698, 1402665.19] | [12245143.987, 1402665.19] | Failed |
SHA-256 / d84a71cf9a090a5794f49d9870535fc33a699e53b9a8f8ccb26796033446f8a7
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 = x
lon = ((lon + 180.0) % 360.0) - 180.0
lat = max(-85.05112878, min(85.05112878, lat))
R = 6378137.0
px = R * math.radians(lon)
py = R * math.log(math.tan(math.pi / 4 + math.radians(lat) / 2))
return [round(px, 3), round(py, 3)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression #0', [190.0, 10.0], [-18924313.435, 1118889.975]), ('regression #1', [-200.0, -20.0], [17811118.527, -2273030.927]), ('boundary #2', [180.0, 0.0], [-20037508.343, -0.0]), ('boundary #3', [-180.0, 45.0], [-20037508.343, 5621521.486]), ('regression #4', [545.0, 5.0], [-19480910.889, 557305.257]), ('boundary #5', [0.0, 89.0], [0.0, 20037508.343]), ('boundary #6', [10.0, -88.0], [1113194.908, -20037508.343]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19])], [('boundary #2', [180.0, 0.0], [-20037508.343, -0.0]), ('regression #4', [545.0, 5.0], [-19480910.889, 557305.257]), ('boundary #5', [0.0, 89.0], [0.0, 20037508.343]), ('boundary #6', [10.0, -88.0], [1113194.908, -20037508.343]), ('boundary #7', [0.0, 85.05112878], [0.0, 20037508.343]), ('control #8', [0.0, 0.0], [0.0, -0.0]), ('regression #9', [-540.0, 30.0], [-20037508.343, 3503549.844]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19])], [('regression #4', [545.0, 5.0], [-19480910.889, 557305.257]), ('boundary #7', [0.0, 85.05112878], [0.0, 20037508.343]), ('control #8', [0.0, 0.0], [0.0, -0.0]), ('regression #9', [-540.0, 30.0], [-20037508.343, 3503549.844]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19]), ('regression #11', [-359.5, -40.0], [55659.745, -4865942.28]), ('boundary #12', [45.0, -86.5], [5009377.086, -20037508.343]), ('boundary #13', [-120.0, -87.25], [-13358338.895, -20037508.343])], [('regression #9', [-540.0, 30.0], [-20037508.343, 3503549.844]), ('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19]), ('regression #11', [-359.5, -40.0], [55659.745, -4865942.28]), ('boundary #12', [45.0, -86.5], [5009377.086, -20037508.343]), ('boundary #13', [-120.0, -87.25], [-13358338.895, -20037508.343]), ('regression #14', [-181.0, 0.5], [19926188.852, 55660.452]), ('control #15', [-17.048, 10.042], [-1897774.679, 1123637.827]), ('control #16', [151.867, -5.771], [16905757.108, -643513.79])], [('regression #10', [-250.0, 12.5], [12245143.987, 1402665.19]), ('regression #11', [-359.5, -40.0], [55659.745, -4865942.28]), ('regression #14', [-181.0, 0.5], [19926188.852, 55660.452]), ('control #15', [-17.048, 10.042], [-1897774.679, 1123637.827]), ('control #16', [151.867, -5.771], [16905757.108, -643513.79]), ('control #17', [2.807, 14.681], [312473.811, 1652463.738]), ('control #18', [-112.892, 2.001], [-12567079.955, 222795.596]), ('control #19', [46.498, 49.22], [5176133.683, 6312273.571])]]
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 |
|---|---|---|---|
| regression #0 | [-18924313.435, 1118889.975] | [-18924313.435, 1118889.975] | Passed |
| regression #1 | [17811118.527, -2273030.927] | [17811118.527, -2273030.927] | Passed |
| boundary #2 | [-20037508.343, -0.0] | [-20037508.343, -0.0] | Passed |
| boundary #3 | [-20037508.343, 5621521.486] | [-20037508.343, 5621521.486] | Passed |
| regression #4 | [-19480910.889, 557305.257] | [-19480910.889, 557305.257] | Passed |
| boundary #5 | [0.0, 20037508.343] | [0.0, 20037508.343] | Passed |
| boundary #6 | [1113194.908, -20037508.343] | [1113194.908, -20037508.343] | Passed |
| regression #10 | [12245143.987, 1402665.19] | [12245143.987, 1402665.19] | Passed |
SHA-256 / 3b7025574842ff1df70d59dae61b72c8562f297f43cbbf41923a818220f09d04
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:15.183263+00:00.
Case digest / 246396c07fd430328e3c4788ef30daecd92d46ca17a6be122089e532b8fefd93