克服 32 位限制,当使用由数百万单位翻译的 System.Drawing.Graphics 表面时

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

上下文:

在 .NET WinForms 中,我构建了一个地图控件,因此我可以绘制几何图形和渲染卫星图像。与任何 2D 投影一样,地理坐标映射到 2D 表面。但是当您放大时,您会注意到当您移动到更高的纬度和经度值时,生成的 2D 坐标达到百万单位的数量级。


问题:

您可以 pan/zoom 处理大型图片(例如地图)的方法是使用变换矩阵平移绘图表面。 GDI+ 使用 float 进行内部计算(根据 .NET System.Drawing 命名空间可以看出)。问题是,当你的 float 增长到数百万时,你会失去小数点的精确度,事情开始变得奇怪起来。即使屏幕以离散整数类型(1 像素的倍数)工作,渲染操作和转换仍然需要使用 float 数学。


代码:

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);
    }
}

如下图所示,地图显示了一个填充的多边形,后跟一个轮廓。请注意虚线轮廓的奇怪结果,而填充似乎是正确的。然而,这两个操作都采用相同的 Point[] 作为输入。

所以唯一合理的推理是 GDI+ 浮点计算会出现 rounding/truncation 错误。

当我仔细观察每个坐标的值,就在它们被转换为整数以提供给绘图方法之前,这一点很明显:

您已经可以看到值是 .0 或 .5,当前值的数量级为数百万。随着您放大得更多,错误可能会变得更糟。

我在将图块渲染到位时遇到了类似的问题。他们也有舍入错误,有时他们会跳出 1 或 2 个像素不对齐。我解决这个问题的方法是让它们重叠一点。

问题和想法:

我如何解决 GDI+ 在转换图形表面时丢失精度的限制 以百万为单位?

我认为主要问题是 Matrix.Translate() 调用使可见区域偏移了数百万像素。如果我能以某种方式避免将表面平移那么多...但似乎无法避免。

即使我可以轻松地重写 Matrix class 以使用 double 类型,GDI+ 仍然在 32 位 float.

中运行

管理大地坐标需要 double-precision 数学,否则您将失去(投影的)高坐标幅度值的精度。

不知道你用的是哪种地图投影。通常,投影模型需要一个投影中心,即投影平面原点(0, 0)。将投影中心设置为当前地图视图的坐标会使投影坐标值的幅度降低(因为它们靠近投影中心)。

每次重新居中地图时都会更改投影中心,并且只有在最低缩放级别时才需要双精度。