Overcoming 32-bit limitations, when using a System.Drawing.Graphics surface translated by millions of units

Viewed 128

Context:

In .NET WinForms I built a map control so I can plot geometry and render satellite images. As in any 2D projection, the geographic coordinates are mapped to 2D surface. But as you zoom IN, you notice that the resulting 2D coordinates get to the order of Millions of Units as you move to higher latitude and longitude values.


Problem:

The way you can pan/zoom at large pictures such as in a map, is translating the drawing surface using a transformation matrix. The GDI+ uses float for internal calculations (as far you can tell by the .NET System.Drawing namespace). The issue is that when your float grows to millions, you lose your decimal places precision, and things starts to get weird. Even though the screen works in a discrete integral type (multiples of 1 pixel) rendering operations and transformation still need to use float math.


Code:

public void Render(Graphics gfx, ICamera camera, Polygon polygon) {
    var points = polygon.Points();
    if (!(FillBrush is null)) {
        gfx.FillPolygon(FillBrush, points);
    }
    if (!(OutlinePen is null)) {
        gfx.DrawPolygon(OutlinePen, points);
    }
}

As seen below, the map show a filled polygon followed by an outline. Notice that the weird result for the dashed outline, while the fill seems correct. However both operation took the same Point[] as input.

Projected Mercator map using GDI+ in Windows

So the only sensible reasoning is that the GDI+ calculations with floating point will get rounding/truncation errors.

This is evident when I look closer into the values of each coordinate just before they are converted to integers to feed the drawing methods:

Value of 2D Coordinates

You can already see that the values are either .0 or .5 with the current magnitude of values in the order of few millions. And as you zoom IN even more, the errors will probably get worse.

I had a similar issue when rendering the tiles into place. They also got rounding errors and they would jump 1 or 2 pixels out of alignment sometimes. The way I solved that was to overlap them a little bit.

Question and ideas:

How can I workaround that GDI+ limitation of losing precision when the graphical surface is translated by millions of units?

I suppose that the main issue is the Matrix.Translate() call which offsets the visible region by millions of pixels. If I could somehow avoid to translate the surface by that much... but doesn't seem possible to avoid that.

Even though I could easily re-write the Matrix class to work with double types, the GDI+ still operates in 32-bit float.


1 Answers

Managing geodedic coordinates requires double-precision math, otherwise you'll loose precision with (projected) high coordinate magnitude values.

I wonder which map projection you use. Normally, projection models requires a projection center, which it is the projection plane origin (0, 0). Setting the projection center to the coordinate of the current map view make projected coordinates values lower in their magnitude (since they are near to the projection center).

Change the projection center each time the map is recentered, and you'll need double precision only at lowest zoom levels.

Related