FA-69966 / Map projection transforms / Open access
Web mercator ground resolution and scale denominator: latitude cosine unit · case 01
Resolution oscillates and even turns negative across latitudes.
ROOT CAUSE
The cosine is evaluated on degrees instead of radians.
VERIFIED REPAIR
At the latitude cosine unit step restore `math.cos(math.radians(lat))`, leaving the rest of the model unchanged.
Unsuccessful approach: Dividing by 360 halves the angle, so resolution is overestimated away from the equator.
Case contract
Input [lat, z, tile_size, dpi]. Ground resolution in metres per pixel is 2*pi*6378137*cos(lat)/(tile_size*2**z) with the equatorial circumference computed exactly. The map scale denominator is resolution*dpi/0.0254. Return [resolution rounded to 6 decimals, scale denominator rounded to the nearest integer] computed from the unrounded resolution.
Why this case matters
Scale bars, level-of-detail choice and print exports depend on these values.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
lat, z, tile, dpi = x
circumference = 2 * math.pi * 6378137.0
res = circumference * math.cos(lat) / (tile * 2 ** z)
scale = res * dpi / 0.0254
return [round(res, 6), round(scale)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-0.88, 17, 256, 72], [1.194188, 3385]), ('control #1', [-33.07, 0, 512, 72], [65591.886576, 185929757]), ('control #2', [-22.5, 12, 512, 72], [17.654651, 50045]), ('control #3', [75.86, 10, 256, 300], [37.345917, 441094]), ('control #4', [30.21, 20, 256, 72], [0.129015, 366]), ('control #5', [-75.78, 4, 256, 300], [2403.383393, 28386418]), ('control #6', [-15.49, 9, 512, 72], [147.321225, 417603]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #1', [-33.07, 0, 512, 72], [65591.886576, 185929757]), ('control #2', [-22.5, 12, 512, 72], [17.654651, 50045]), ('control #5', [-75.78, 4, 256, 300], [2403.383393, 28386418]), ('control #6', [-15.49, 9, 512, 72], [147.321225, 417603]), ('control #7', [76.6, 19, 512, 300], [0.034598, 409]), ('control #8', [20.0, 19, 512, 72], [0.140288, 398]), ('control #9', [-14.77, 4, 256, 96], [9460.648974, 35756784]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #2', [-22.5, 12, 512, 72], [17.654651, 50045]), ('control #3', [75.86, 10, 256, 300], [37.345917, 441094]), ('control #8', [20.0, 19, 512, 72], [0.140288, 398]), ('control #9', [-14.77, 4, 256, 96], [9460.648974, 35756784]), ('control #10', [6.58, 15, 256, 300], [4.745845, 56053]), ('control #11', [-18.37, 9, 256, 72], [290.167542, 822522]), ('control #12', [-51.53, 14, 512, 300], [2.97199, 35102]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #3', [75.86, 10, 256, 300], [37.345917, 441094]), ('control #4', [30.21, 20, 256, 72], [0.129015, 366]), ('control #11', [-18.37, 9, 256, 72], [290.167542, 822522]), ('control #12', [-51.53, 14, 512, 300], [2.97199, 35102]), ('control #13', [-69.8, 6, 256, 300], [844.594183, 9975522]), ('control #14', [-23.41, 10, 256, 72], [140.290274, 397673]), ('control #15', [-37.12, 12, 256, 96], [30.474424, 115179]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #4', [30.21, 20, 256, 72], [0.129015, 366]), ('control #5', [-75.78, 4, 256, 300], [2403.383393, 28386418]), ('control #14', [-23.41, 10, 256, 72], [140.290274, 397673]), ('control #15', [-37.12, 12, 256, 96], [30.474424, 115179]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711]), ('boundary #17', [60.0, 1, 512, 96], [19567.879241, 73957339]), ('regression #18', [45.0, 10, 512, 90.7], [54.049141, 193002]), ('boundary #19', [-60.0, 3, 256, 300], [9783.939621, 115558342])]]
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 | [0.760968, 2157] | [1.194188, 3385] | Failed |
| control #1 | [-6510.696404, -18455517] | [65591.886576, 185929757] | Failed |
| control #2 | [-16.688203, -47305] | [17.654651, 50045] | Failed |
| control #3 | [136.862409, 1616485] | [37.345917, 441094] | Failed |
| control #4 | [0.053271, 151] | [0.129015, 366] | Failed |
| control #5 | [9079.538035, 107238638] | [2403.383393, 28386418] | Failed |
| control #6 | [-149.257041, -423091] | [147.321225, 417603] | Failed |
| boundary #16 | [156543.033928, 591658711] | [156543.033928, 591658711] | Passed |
SHA-256 / 6640c38b32ab3f91132375229c98b84fc8eae65b16df305661ecafa6bc6e6e0e
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
lat, z, tile, dpi = x
circumference = 2 * math.pi * 6378137.0
res = circumference * abs(math.cos(lat * math.pi / 360.0)) / (tile * 2 ** z)
scale = res * dpi / 0.0254
return [round(res, 6), round(scale)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-0.88, 17, 256, 72], [1.194188, 3385]), ('control #1', [-33.07, 0, 512, 72], [65591.886576, 185929757]), ('control #2', [-22.5, 12, 512, 72], [17.654651, 50045]), ('control #3', [75.86, 10, 256, 300], [37.345917, 441094]), ('control #4', [30.21, 20, 256, 72], [0.129015, 366]), ('control #5', [-75.78, 4, 256, 300], [2403.383393, 28386418]), ('control #6', [-15.49, 9, 512, 72], [147.321225, 417603]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #1', [-33.07, 0, 512, 72], [65591.886576, 185929757]), ('control #2', [-22.5, 12, 512, 72], [17.654651, 50045]), ('control #5', [-75.78, 4, 256, 300], [2403.383393, 28386418]), ('control #6', [-15.49, 9, 512, 72], [147.321225, 417603]), ('control #7', [76.6, 19, 512, 300], [0.034598, 409]), ('control #8', [20.0, 19, 512, 72], [0.140288, 398]), ('control #9', [-14.77, 4, 256, 96], [9460.648974, 35756784]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #2', [-22.5, 12, 512, 72], [17.654651, 50045]), ('control #3', [75.86, 10, 256, 300], [37.345917, 441094]), ('control #8', [20.0, 19, 512, 72], [0.140288, 398]), ('control #9', [-14.77, 4, 256, 96], [9460.648974, 35756784]), ('control #10', [6.58, 15, 256, 300], [4.745845, 56053]), ('control #11', [-18.37, 9, 256, 72], [290.167542, 822522]), ('control #12', [-51.53, 14, 512, 300], [2.97199, 35102]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #3', [75.86, 10, 256, 300], [37.345917, 441094]), ('control #4', [30.21, 20, 256, 72], [0.129015, 366]), ('control #11', [-18.37, 9, 256, 72], [290.167542, 822522]), ('control #12', [-51.53, 14, 512, 300], [2.97199, 35102]), ('control #13', [-69.8, 6, 256, 300], [844.594183, 9975522]), ('control #14', [-23.41, 10, 256, 72], [140.290274, 397673]), ('control #15', [-37.12, 12, 256, 96], [30.474424, 115179]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #4', [30.21, 20, 256, 72], [0.129015, 366]), ('control #5', [-75.78, 4, 256, 300], [2403.383393, 28386418]), ('control #14', [-23.41, 10, 256, 72], [140.290274, 397673]), ('control #15', [-37.12, 12, 256, 96], [30.474424, 115179]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711]), ('boundary #17', [60.0, 1, 512, 96], [19567.879241, 73957339]), ('regression #18', [45.0, 10, 512, 90.7], [54.049141, 193002]), ('boundary #19', [-60.0, 3, 256, 300], [9783.939621, 115558342])]]
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 | [1.194293, 3385] | [1.194188, 3385] | Failed |
| control #1 | [75034.681417, 212696735] | [65591.886576, 185929757] | Failed |
| control #2 | [18.742078, 53127] | [17.654651, 50045] | Failed |
| control #3 | [120.581298, 1424189] | [37.345917, 441094] | Failed |
| control #4 | [0.144133, 409] | [0.129015, 366] | Failed |
| control #5 | [7721.399889, 91197636] | [2403.383393, 28386418] | Failed |
| control #6 | [151.479488, 429391] | [147.321225, 417603] | Failed |
| boundary #16 | [156543.033928, 591658711] | [156543.033928, 591658711] | Passed |
SHA-256 / b4029e036dbf915da8a300590efccda93388a1cad3c0d631385a5a98836a4959
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
lat, z, tile, dpi = x
circumference = 2 * math.pi * 6378137.0
res = circumference * math.cos(math.radians(lat)) / (tile * 2 ** z)
scale = res * dpi / 0.0254
return [round(res, 6), round(scale)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [-0.88, 17, 256, 72], [1.194188, 3385]), ('control #1', [-33.07, 0, 512, 72], [65591.886576, 185929757]), ('control #2', [-22.5, 12, 512, 72], [17.654651, 50045]), ('control #3', [75.86, 10, 256, 300], [37.345917, 441094]), ('control #4', [30.21, 20, 256, 72], [0.129015, 366]), ('control #5', [-75.78, 4, 256, 300], [2403.383393, 28386418]), ('control #6', [-15.49, 9, 512, 72], [147.321225, 417603]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #1', [-33.07, 0, 512, 72], [65591.886576, 185929757]), ('control #2', [-22.5, 12, 512, 72], [17.654651, 50045]), ('control #5', [-75.78, 4, 256, 300], [2403.383393, 28386418]), ('control #6', [-15.49, 9, 512, 72], [147.321225, 417603]), ('control #7', [76.6, 19, 512, 300], [0.034598, 409]), ('control #8', [20.0, 19, 512, 72], [0.140288, 398]), ('control #9', [-14.77, 4, 256, 96], [9460.648974, 35756784]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #2', [-22.5, 12, 512, 72], [17.654651, 50045]), ('control #3', [75.86, 10, 256, 300], [37.345917, 441094]), ('control #8', [20.0, 19, 512, 72], [0.140288, 398]), ('control #9', [-14.77, 4, 256, 96], [9460.648974, 35756784]), ('control #10', [6.58, 15, 256, 300], [4.745845, 56053]), ('control #11', [-18.37, 9, 256, 72], [290.167542, 822522]), ('control #12', [-51.53, 14, 512, 300], [2.97199, 35102]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #3', [75.86, 10, 256, 300], [37.345917, 441094]), ('control #4', [30.21, 20, 256, 72], [0.129015, 366]), ('control #11', [-18.37, 9, 256, 72], [290.167542, 822522]), ('control #12', [-51.53, 14, 512, 300], [2.97199, 35102]), ('control #13', [-69.8, 6, 256, 300], [844.594183, 9975522]), ('control #14', [-23.41, 10, 256, 72], [140.290274, 397673]), ('control #15', [-37.12, 12, 256, 96], [30.474424, 115179]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711])], [('control #4', [30.21, 20, 256, 72], [0.129015, 366]), ('control #5', [-75.78, 4, 256, 300], [2403.383393, 28386418]), ('control #14', [-23.41, 10, 256, 72], [140.290274, 397673]), ('control #15', [-37.12, 12, 256, 96], [30.474424, 115179]), ('boundary #16', [0.0, 0, 256, 96], [156543.033928, 591658711]), ('boundary #17', [60.0, 1, 512, 96], [19567.879241, 73957339]), ('regression #18', [45.0, 10, 512, 90.7], [54.049141, 193002]), ('boundary #19', [-60.0, 3, 256, 300], [9783.939621, 115558342])]]
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 | [1.194188, 3385] | [1.194188, 3385] | Passed |
| control #1 | [65591.886576, 185929757] | [65591.886576, 185929757] | Passed |
| control #2 | [17.654651, 50045] | [17.654651, 50045] | Passed |
| control #3 | [37.345917, 441094] | [37.345917, 441094] | Passed |
| control #4 | [0.129015, 366] | [0.129015, 366] | Passed |
| control #5 | [2403.383393, 28386418] | [2403.383393, 28386418] | Passed |
| control #6 | [147.321225, 417603] | [147.321225, 417603] | Passed |
| boundary #16 | [156543.033928, 591658711] | [156543.033928, 591658711] | Passed |
SHA-256 / 6a434c73a2a7e3aee929800440234e752e6bf4c033bae24f469b9b1a10ee367f
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.363809+00:00.
Case digest / b07c5ae156d50638805fe0a684a80ef4d06ab8b85ac133f46dcf3e54cb1e0c74