SharpMap深度分析:地图渲染、坐标和比例尺

上篇对SharpMap的分析文章里,一个重点就是地图的渲染流程和机制,这里就不专门介绍这个问题了,只是就坐标的一些细节问题分析一下。

地图都有一个单位(Unit)、比例尺(Zoom)的概念,还有投影的问题。对于Unit,一般使用Km、m或者经纬度来表示。一幅地图,在其所有数据的Unit和投影都一致的情况下,在绘制这些对象到地图时,就要根据比例尺进行坐标转换;同时,在进行地图的缩放、移动、拾取等操作的时候,鼠标的坐标是桌面的坐标系统,也要转换到地图坐标系统(一般称为World Coordinates System,简称WCS)。

首先来看比例(Zoom)在Map类里的定义:

private double _Zoom;

public double Zoom
{
    
get return _Zoom; }
    
set {
        
if (value < _MinimumZoom)
            _Zoom 
= _MinimumZoom;
        
else if (value > _MaximumZoom)
            _Zoom 
= _MaximumZoom;
        
else
            _Zoom 
= value;
        
if (MapViewOnChange != null)
            MapViewOnChange();
    }

}


这个Zoom表示使用地图Unit表示的地图宽度。例如地图单位是Km,那么如果目前地图的宽度是500Km,Zoom就是500。这个和Mapinfo中Zoom的概念是一致的。

那么在渲染的时候,就要对所有对象进行坐标转换,转换为要渲染的图片的坐标系统,然后调用GDI+进行渲染。

对于对象的渲染,定义在Layer的名称空间里,在VectorLayer类的Render方法里,根据Geometry对象的层次依次遍历各个对象,然后调用Rendering名称空间的VectorRenderer的各个方法来渲染不同的点、线、面等对象。

在渲染具体对象时,我们看到这些方法都调用了一个TransformToImage的方法,而这个方法定义在不同的Geometry名称空间的不同类里,目的是由空间对象经过坐标变换后返回一个.net的绘图对象。

我们把这个流程整理如下:

Map对象GetMap方法→GetMap方法遍历其Layer,调用Layer的Render方法→各个Layer开始渲染自己,对于栅格和WMS层,返回范围内的图片即可,主要是VectorLayer的渲染→VectorLayer调用自己DataSource Provider的GetFeaturesInView方法,返回范围内的对象到一个列表→依次遍历列表的各个对象,调用Rendering名称空间的VectorRenderer的各个方法来渲染不同的点、线、面等对象→渲染这些对象前,调用几何对象的TransformToImage方法,返回一个.net的绘图对象→GDI+根据Style渲染

在最后一步,各个对象调用的TransformToImage方法其实是逐次转换这个对象的各个点。而点的坐标转换定义在Utilities.Transform下,有2个方法:

public static System.Drawing.PointF WorldtoMap(SharpMap.Geometries.Point p, SharpMap.Map map)

public static SharpMap.Geometries.Point MapToWorld(System.Drawing.PointF p, SharpMap.Map map)
分别转换WCS坐标到Image坐标和转换Image坐标到WCS坐标。

这是转换代码:

System.Drawing.PointF result = new System.Drawing.Point();
double Height = (map.Zoom * map.Size.Height) / map.Size.Width;
double left = map.Center.X - map.Zoom/2;
double top = map.Center.Y + Height/2;
double pxSize = map.Zoom / map.Size.Width;
result.X 
= (float)Math.Round(((p.X - left) / pxSize), 0);
result.Y 
= (float)Math.Round(((top - p.Y) / pxSize), 0);
return result;

left和top表示当前地图的左上角坐标,Height是高度,需要通过Zoom和Height来换算一下,也许写作map.Zoom * (map.Size.Height / map.Size.Width)更好理解一点。pxSize相当于在最终的图片上的一个单位相当于WCS的多少单位,这样,(p.X - left) / pxSize就是横坐标,纵坐标由于图片y轴相反,因此是(top - p.Y) / pxSize。有过Dos或者Windows图形编程经验的人对于这样的代码应该是非常熟悉。

这段代码的计算left、height、top、pxSize这些参数的语句其实应该在Map每次更改Zoom时计算比较好,因为这个函数会被调用非常多次(每个点都要转换坐标),不过这些都是优化的话了,可以放在系统稳定以后。

不同的地图单位和投影下的地图渲染操作

SharpMap目前还没有Unit的问题,在Map和Layer里也没有定义Unit,投影在新版0.90的beta里有部分代码。象ArcGIS和Mapinfo都支持动态投影,也就是对Map定义一个Unit和投影,对不同的Layer定义一个投影和Unit,他可以自动的转换这些Layer的Unit到地图,然后叠加显示。

这样的话,在显示(渲染)时,就需要对所有对象都要进行投影和尺度变换。虽然似乎在打开数据的时候进行转换,但是由于对于空间数据库,一次打开所有数据已经越来越不可能,而且对数据作分析的时候,如果数据打开时转换了数据的Unit,那么分析结果也会出现问题。因此,这类实现应该是在地图渲染时进行投影和变换。

posted on 2006-03-06 17:10  马维峰  阅读(11273)  评论(7编辑  收藏  举报