SharpMap介绍及源码分析

本文发表于《3sNews新闻周刊》第一期,有删改,请勿转载。

SharpMap是一个基于.net 2.0使用C#开发的Map渲染类库,可以渲染各类GIS数据(目前支持ESRI Shape和PostGIS格式),可应用于桌面和Web程序。

其网址为:http://sharpmap.iter.dk/

SharpMap的发布许可(License)为GNU General Public License,开发者为Morten Nielsen(http://www.iter.dk/)。目前的稳定版本为0.8(9.0beta已发布),代码行数近10000行,实现了以下功能:

支持的数据格式:
      PostGreSQL/PostGIS,ESRI Shapefile
      支持WMS layers
      支持ECW 和 JPEG2000 栅格数据格式
Windows Forms 控件,可以移动和缩放
通过HttpHandler支持ASP.net程序
点、线、多边形、多点、多线和多多边形等几何类型
几何集合(GeometryCollections)等OpenGIS Simple Features Specification
可通过Data Providers(增加数据类型支持)、Layer Types(增加层类型)和Geometry Types等扩展
图形使用GDI+渲染,支持anti-aliased等
专题图

可以看出,SharpMap目前可以算是一个实现了最基本功能的GIS系统,但一些很重要的功能,例如投影,比例尺,空间分析,图形的属性信息,查询检索等等,还没有或者还在开发中。一个好消息是,作者在SharpMap的网站写到:Diego Guidi(NetTopologySuite的开发者)已经创建了一个SharpMap和NTS之间的一个连接,这样,就可以在SharpMap中使用NTS的空间变换、缓冲区等功能。

笔者之所以在这里分析ShrapMap,出于以下原因:

      SharpMap足够小(小于10000行),且具备了一个GIS软件的基本功能,容易下手;
      基于.net和C#开发;
      开放源码(不开放就没有办法分析,废话);
      SharpMap还在开发中,可以通过跟踪其源码学习提高。

一项技术或者一个工具,知其然和知其所以然,对于应用的深度和熟练程度还是具有很大的影响,特别是程序开发。分析SharpMap,不一定是要使用SharpMap,是希望通过分析SharpMap,可以了解一个GIS系统的纵剖面,从而可以更好的进行GIS的应用和开发。

源码结构

以下是SharpMap在VS 2005下的Class视图和Solution视图,可以看出SharpMap由SharpMap和其他14个次级名称空间组成,其中SharpMap名称空间下的Map类为这个系统的核心所在。

SharpMap介绍及源码分析

Map类,位于SharpMap命名空间下,通过创建Map对象的实例来生成地图。Map对象由包含Layer对象组成Layers集合,通过GetMap方法来Render地图。

Converts名称空间,提供数据转换服务。

Forms名称空间,包含MapImage控件,一个简单的User Control(用户控件),封装了Map类,用于Windows Form编程。

Geometries名称空间,包括了SharpMap要使用到的各种几何类及其接口类,例如点、线、面等类。

是SharpMap的基础之一,所有几何对象都继承自Geometry这个抽象类,其中定义了几何对象应该具备的公共操作,例如大小、ID、外接矩阵、几何运算等等。
 
Layers名称空间,包括了ILayer接口,Layer集合类等,代表地图的图层。
 
Layer是一个抽象类,实现了ILayer接口,Layer目前有3个子类,分别是VectorLayer、LabelLayer和WmsLayer,分别代3种不同数据类型的图层。

Providers名称空间,包括了IProvider接口和Shape文件、PostGIS数据的读取实现。该名称空间为SharpMap提供数据读(写)支持,通过面向接口的设计,可以比较容易的增加各类数据格式。

Rendering名称空间,目前包括矢量渲染器类和几个专题图渲染器类,该类可以将几何对象根据其Style设置渲染为一个System.Drawing.Graphics对象。

Styles名称空间,该名称空间主要提供了图层的样式设置类,例如线样式、点样式、填充样式等.
 
Utilities名称空间包括Algorithms类(目前仅实现了一个方法);Providers类,是Provider的一个Helper,应用了反射机制;Surrogates主要用于系统的Pen和Brush的序列化;Transform提供了从图片坐标到地理坐标的互相变换,也即桌面GIS的二次开发中经常使用的屏幕坐标和地理坐标的转换,主要用于地图的渲染、交互操作等。

Utilities.SpatialIndexing用于对象的空间索引,我们后面还会继续介绍,Web名称空间实现了HttpHandler和Caching类,用于网络环境。

运行机制

通过剖析其名称空间,我们对SharpMap源码的结构和组成有了大概的了解,下来我们通过上面的应用实例来剖析其运行机制。

SharpMap.Map myMap = new SharpMap.Map(picMap.Size);

这句代码创建了一个新的Map的对象,Map对象包括了中点、大小、缩放比例、图层等字段,Layers集合对象包括了地图的各个图层,可以通过Layers.Add方法增加新的图层,通过GetLayerByName方法返回某个图层。缩放等方法是通过Center和Zoom属性来控制的。当地图图层修改和地图渲染后会触发相应的事件。

接着就需要新建不同的Layer对象,设置其属性和数据源以及样式。例如:

SharpMap.Layers.VectorLayer myLayer = new SharpMap.Layers.VectorLayer("My layer");
string ConnStr = "Server=127.0.0.1;Port=5432;User Id=postgres;Password=password;Database=myGisDb;";
//创建图层的数据源
myLayer.DataSource = new SharpMap.Providers.PostGIS(ConnStr, "myTable", "the_geom", 32632);

不同的Providers对象,例如PostGIS或者Shape对象负责打开相应的空间数据集,读取数据,返回部分或者全部的空间对象。

接着就需要设置Layer的样式,例如填充、线形等属性。目前,Style对象只是简单的封装了System.Drawing.Pen、System.Drawing.Brush、System.Drawing.Bitmap等对象。并增加图层到Map的Layers集合:

myMap.Layers.Add(myLayer);

我们可以通过设置Map的Center、Zoom、Size等属性来进行Map操作,例如缩放、平移等操作,例如封装Map对象为一个User Control控件,主要的动作就是操作Map的各个属性。

最后,我们就可以通过GetMap对象返回一个System.Drawing.Image对象,代表目前的地图,用于显示或输出:

System.Drawing.Image imgMap = myMap.GetMap(); //Renders the map

GetMap是整个SharpMap的核心之一,我们来一步步剖析其流程。

System.Drawing.Image img  =   new  System.Drawing.Bitmap();
System.Drawing.Graphics g 
=  System.Drawing.Graphics.FromImage(img);
g.Clear(
this .BackColor);
foreach  (SharpMap.ILayer layer  in   this .Layers)
{    
if(layer.Enabled && layer.MaxVisible>=this.Zoom &&
    layer.MinVisible
<this.Zoom)
        layer.Render(g,
this);
}

if  (MapRendered  !=   null ) MapRendered(g);  // Fire render event
g.Dispose();
return  img;

以上代码就是GetMap的全部代码,首先创建一个System.Drawing.Image对象,然后为其创建一个System.Drawing.Graphics对象,通过这个对象来渲染地图到这个Image对象,最后返回这个对象。

其核心在于循环所有图层,如果其在显示范围之内,则调用Layer对象的Render方法,渲染这个图层。

抽象类Layer中的Render方法为:

if(LayerRendered!=null) LayerRendered(this, g);

VectorLayer子类的Render方法为:

g.SmoothingMode  =   this .SmoothingMode;
 
List
< SharpMap.Geometries.Geometry >  features  =   this .DataSource.GetFeaturesInView(map.Envelope);
// Linestring outlines is drawn by drawing the layer once with a thicker line
// before drawing the "inline" on top.
if  ( this .Style.EnableOutline)
{
    
foreach (SharpMap.Geometries.Geometry feature in features)
    
{
        
//首先绘制所有图形对象的线轮廓
        
    }

}

 
// double i = 0;
foreach  (Geometries.Geometry feature  in  features)
{
    
switch (feature.GeometryType)
    
{
        
//根据不同的图形绘制其
        
    }

}

base .Render(g,map);

这段代码首先根据地图的显示范围,获取范围内的所有对象:GetFeaturesInView,然后逐次绘制不同这些对象。绘制对象时,调用了SharpMap.Rendering对象的不同方法。在SharpMap.Rendering对象中,不同几何对象的绘制方法最终调用了.net的System.Drawing中的GDI+的绘制方法,完成地图的绘制。

需要说明的是不同的数据源的GetFeaturesInView方法实现方法是不同的,目前版本的Shape数据源的Provider是使用了空间索引算法,PostGIS数据源则通过PostGIS的空间索引接口来获取需要的数据。

系统效率的好坏,基本上就在于这里,一个是如何获取视图内的空间对象,关键在于空间索引;一个是渲染机制,例如使用DirectX加快渲染。

这样,我们就基本完成了对SharpMap的剖析。SharpMap是一个刚刚启动不久的项目,开发者到目前为止只有一人,所以,实现的功能有限,有些设计也不是很成熟,例如缺乏空间分析和检索功能,但完全可以作为一个非常好的教学系统来使用。

从作者Morten Nielsen了解到,该项目是从2005年夏天开始的,目的是:

It was a good case for learning some of the new .NET 2.0 features and for getting "under the hood" of many of the GIS-related algorithms needed, like spatial indexing, spatial translations and topology rules.
通过项目,可以学习.net 2.0的一些新的特性,而且可以了解GIS相关的一些底层算法,例如空间索引、空间变换、拓扑规则等。

从中我们是否可以看到对技术的一种不同的态度,少一些空谈,多一些务实,这是笔者个人的感受。


后续文章:
SharpMap深度分析:地图渲染、坐标和比例尺
SharpMap深度分析:地图数据Provider

你可能感兴趣的:(源码分析)