Summary
MapAttributes.latLongToPixels positions markers incorrectly on the world Mercator map (SMapWorld.instructionsMercator). Markers in the northern hemisphere are pushed far above the map and end up off-screen, so in practice only southern-hemisphere markers land anywhere near their real location.
Cause
The world map is a standard spherical Mercator projection, where the vertical position of a latitude is non-linear:
But latLongToPixels computes y with an ad-hoc secant-based "scaleFactor" instead (and the code itself carries a TODO admitting it is inaccurate):
double scaleFactor = secant(Angle(deg: amountAboveBaseLine));
scaleFactor = 1 - scaleFactor;
scaleFactor = scaleFactor + 1; // = 2 - sec(Δφ)
double relativeY =
((topLeft.latitude - position.latitude) / mapHeightInGeoUnits) * scaleFactor;
As latitude increases, sec(Δφ) grows quickly, so scaleFactor becomes small and then negative, which throws northern points well above y = 0 (off the top of the map). The longitude → x mapping is fine; only the latitude → y mapping is broken.
There are also two leftover print(scaleFactor); statements in this method that spam stdout on every layout pass.
Reproduction
Render SimpleMap(instructions: SMapWorld.instructionsMercator, markers: [...]) with a marker at, say, London (51.5, -0.13) and one at Sydney (-33.9, 151.2). Sydney lands roughly right; London is placed above the visible map.
A note on the map bounds
The map metadata declares "g": {"a": "-180.0,90.0", "b": "180.0,-60.0"}. Those latitude bounds can't be literal for a Mercator projection — 90° maps to infinity. Calibrating the proper Mercator formula against the package's own country paths, the actual rendered vertical extent of this SVG is about 83.6°N at the top and -55.2°S at the bottom (mean error ~3px on the 651px-tall map). It would help a lot if the published bounds matched the real projection extent.
Suggested fix
Use the real Mercator transform for y, normalized between the map's true top/bottom latitudes, and drop the prints:
double _mercatorY(double latDeg) =>
log(tan(pi / 4 + (latDeg * pi / 180) / 2));
Size latLongToPixels(LatLong position) {
final double relativeX =
(position.longitude - topLeft.longitude) /
(bottomRight.longitude - topLeft.longitude);
final double top = _mercatorY(topLeft.latitude);
final double bottom = _mercatorY(bottomRight.latitude);
final double relativeY =
(top - _mercatorY(position.latitude)) / (top - bottom);
return Size(relativeX * mapWidth, relativeY * mapHeight);
}
(For this to be exact, topLeft.latitude / bottomRight.latitude in the world map metadata need to reflect the real ~83.6 / -55.2 extent rather than 90 / -60.)
Thanks for the package!
Summary
MapAttributes.latLongToPixelspositions markers incorrectly on the world Mercator map (SMapWorld.instructionsMercator). Markers in the northern hemisphere are pushed far above the map and end up off-screen, so in practice only southern-hemisphere markers land anywhere near their real location.Cause
The world map is a standard spherical Mercator projection, where the vertical position of a latitude is non-linear:
But
latLongToPixelscomputesywith an ad-hocsecant-based "scaleFactor" instead (and the code itself carries aTODOadmitting it is inaccurate):As latitude increases,
sec(Δφ)grows quickly, soscaleFactorbecomes small and then negative, which throws northern points well abovey = 0(off the top of the map). The longitude → x mapping is fine; only the latitude → y mapping is broken.There are also two leftover
print(scaleFactor);statements in this method that spam stdout on every layout pass.Reproduction
Render
SimpleMap(instructions: SMapWorld.instructionsMercator, markers: [...])with a marker at, say, London (51.5, -0.13) and one at Sydney (-33.9, 151.2). Sydney lands roughly right; London is placed above the visible map.A note on the map bounds
The map metadata declares
"g": {"a": "-180.0,90.0", "b": "180.0,-60.0"}. Those latitude bounds can't be literal for a Mercator projection — 90° maps to infinity. Calibrating the proper Mercator formula against the package's own country paths, the actual rendered vertical extent of this SVG is about 83.6°N at the top and -55.2°S at the bottom (mean error ~3px on the 651px-tall map). It would help a lot if the published bounds matched the real projection extent.Suggested fix
Use the real Mercator transform for
y, normalized between the map's true top/bottom latitudes, and drop theprints:(For this to be exact,
topLeft.latitude/bottomRight.latitudein the world map metadata need to reflect the real ~83.6 / -55.2 extent rather than 90 / -60.)Thanks for the package!