本文使用 Sharpmap 0.9。
Sharpmap 作为优秀的基于 vs2005 的 GIS 解决方案,大家也对其了解甚多,我就不
多说了。在实际使用中,特别是 web 控件,作者并没有暴露很多编程方法,本文基于此进
行讨论。
在项目开发中,我们希望在鼠标拖拽时起码要实现几个功能:平移、拉框缩放、拉框选
择。通过更改 Sharpmap.UI.dll 的方式修改,可以实现。
首先,增加鼠标拖拽选择模式属性 MouseMode,并增加 enum,代码形如:
public enum eMouseMode
{
}
Pan,
Zoom,
Select
private eMouseMode _MouseMode;
/**////
/// Set Mouse down,up, move event to pan or zoom mode
///
[Category("Behavior")]
[DefaultValue(0)]
[Description("设置鼠标拖拽方式:即设置 Mousedown,Mousemove,Mouseup 事件
的处理方式。默认值为 Pan。")]
public eMouseMode MouseMode
{
get { return _MouseMode; }
set { _MouseMode = value; }
}
其次,将属性暴露给 JS,让客户端可以调用。
通过修改 GenerateClientScripts 函数,增加 setvarsScript 变量定义。
拉框需要有 html 控件,我这里使用 div。
首先,在 GenerateMapBox 函数中生成控件,使用 Controls.add 方法增加进来。
其次,在 JS 中注册。
通过修改 JS 文件的 SharpMap_Init 函数和 CS 文件的 GenerateClientScripts 函数,
将增加的控件暴露给 JS 使用。
至此我们在前后台均可以使用新增加的控件和方法。
然后,就可以在 SharpMap_MouseDown(Up, Over)函数里面写代码了。
SharpMap 深度分析:地图数据 Provider
Provider 或者 Provider 模式对于很多人应该都不陌生,在 DNN 和 Asp.net 2.0 中都大量
应用了 Provider 模式。目前主流的 GIS 平台的数据提供也应该基本上都是基于 Provider 这
样的模式,大家比较熟悉的应该是 SuperMap 提出的多源空间数据引擎的概念。
在 SharpMap 里,数据 Provider 是这样使用的:
string ConnStr = "Server=127.0.0.1;Port=5432;UserId=postgres;Password=password;D
atabase=myGisDb;";
myLayer.DataSource = new SharpMap.Providers.PostGIS(ConnStr, "myTable", "the_geo
m", 32632);
而实际上,在 Layer 类里,DataSource 定义为:
public SharpMap.Data.Providers.IProvider DataSource
{
}
...
也就是说,这里的 Provider 是针对 IProvider 接口的,这样,对于不同的层,你可以指
定不同的数据源(使用不同的数据 Provider,而支持不同的数据格式)。下面是 Provider
接口的类图,定义了接口的属性和操作:
个人认为,Provider 的核心思想在于面向接口编程,也就是说通过接口定义需要的服务,
至于服务的实现,可以通过具体的方式来实现。就 GIS 数据引擎来说,就是定义对空间数
据需要的操作,例如打开、关闭、读取某个范围内的数据、检索、分析等等这些接口,然后
通过继承这个接口来实现对不同的数据的操作。例如对 Shape 文件的操作和对 PostGIS 文
件的操作是完全不同的,但其接口一致。而在系统内部,对数据的操作,例如放大缩小、变
换、显示,只需要针对接口编程,就可以实现支持不同的数据源。
这里的实现和 DNN 等的不同在于,DNN 是通过配置文件和反射机制,来实现不同的 P
rovider 的更换,而无须更改代码实现,而 SharpMap 或其他 GIS 的类似实现是需要在开发
时指定使用的 Provider。
Provider 的实现还有一个比较重要的问题就是要操作的数据的定义,因为具体的 Provi
der 的实现最终要将数据转换为系统内部的数据类型和结构,然后返回。
对于具体的 Geometry 的结构,基本上是在 OGC 的规范的基础上实现,对于这部分内
容,很多非 GIS 的面向对象的书也喜欢用这个来讲述类、对象、继承等概念,大家都很熟
悉,这里就不多说了。对 OGC 的 Simple Feature 实现比较好的一个.net 类库是 NTS(JT
S 的.net 移植版本),目前正在看他的源码,后面会写一些自己的笔记。SharpMap 的一些
代码,看注释也是在 NTS 的基础上实现的。
SharpMap 的 Provider 没有定义数据的修改、编辑,从理论上讲,完全可以实现任意
数据的读取、修改,但实际上,数据的读取显示一般来说,实现难度不是很大,因为大家都
是点线面及其组合这样的对象;但由于内部结构,拓扑关系,修改就比较困难了。例如 Su
perMap 的产品也只是可以只读读取一些其他格式的数据(如 MicroStation DGN,AutoCA
D 数据),而没有修改功能。
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 名称空间的 VectorRe
nderer 的各个方法来渲染不同的点、线、面等对象。
在渲染具体对象时,我们看到这些方法都调用了一个 TransformToImage 的方法,而这
个方法定义在不同的 Geometry 名称空间的不同类里,目的是由空间对象经过坐标变换后返
回一个.net 的绘图对象。
我们把这个流程整理如下:
Map 对象 GetMap 方法→GetMap 方法遍历其 Layer,调用 Layer 的 Render 方法→各
个 Layer 开始渲染自己,对于栅格和 WMS 层,返回范围内的图片即可,主要是 VectorLay
er 的渲染→VectorLayer 调用自己 DataSource Provider 的 GetFeaturesInView 方法,返回
范围内的对象到一个列表→依次遍历列表的各个对象,调用 Rendering 名称空间的 Vector
Renderer 的各个方法来渲染不同的点、线、面等对象→渲染这些对象前,调用几何对象的
TransformToImage 方法,返回一个.net 的绘图对象→GDI+根据 Style 渲染
在最后一步,各个对象调用的 TransformToImage 方法其实是逐次转换这个对象的各个
点。而点的坐标转换定义在 Utilities.Transform 下,有 2 个方法:
public static System.Drawing.PointF WorldtoMap(SharpMap.Geometries.Point p, SharpMa
p.Map map)
和
public static SharpMap.Geometries.Point MapToWorld(System.Drawing.PointF p, SharpMa
p.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)更好理解一点。px
Size 相当于在最终的图片上的一个单位相当于 WCS 的多少单位,这样,(p.X - left) / px
Size 就是横坐标,纵坐标由于图片 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 的 Un
it 到地图,然后叠加显示。
这样的话,在显示(渲染)时,就需要对所有对象都要进行投影和尺度变换。虽然似乎
在打开数据的时候进行转换,但是由于对于空间数据库,一次打开所有数据已经越来越不可
能,而且对数据作分析的时候,如果数据打开时转换了数据的 Unit,那么分析结果也会出现
问题。因此,这类实现应该是在地图渲染时进行投影和变换。
SharpMap 学习(3)
我写的东西内容浅显,希望能给初学者一些帮助。至于深入研究 sharpmap 和 GIS 技术的
大牛,请不吝赐教,给我们这些菜鸟多一些指导。
今天我们接着来聊 sharpmap 的基本使用技巧,根据 attribute 来填充地图对象的颜色,
让用户更清晰的看到重点的业务对象对应在地图上的表示,以及如何自定义 label 层的显示
内容,字体的大小等。所以,今天的主题主要是自定义:自定义 theme,自定义 label 以及
label 字体。
首先,我们要为地图填充上不同的色彩,让他们看起来五颜六色,容易分辨。比如河流
和湖泊要填成蓝色,草地要填充上绿色,房子要填充上白色,道路要填充上青色等等。怎么
做呢?很简单,先看下代码:
SharpMap.Rendering.Thematics.CustomTheme iTheme = new SharpMap.Rendering.The
matics.CustomTheme(GetMyStyle);
在初始化 Map 的时候,加上上面的一行代码。它定义了一个自定义的 Theme 对象,
这个对象的构造函数需要传入一个我们自己写的方法(委托),这个方法里面具体说明了这
个 theme 是如何定义的,方法代码如下:
private static SharpMap.Styles.VectorStyle GetMyStyle(SharpMap.Data.FeatureData
Row row)
ctorStyle();
yellow
{
}
SharpMap.Styles.VectorStyle style = new SharpMap.Styles.Ve
switch (row["Status"].ToString().ToLower())
{
}
case "available":
//If status is interred,
fill
it with
style.Fill = Brushes.Yellow;
return style;
default:
style.Fill = Brushes.Green;
return style;
这段简单代码大家都看得懂,这个委托定义需要传入一个 FeatureDataRow,它的作用就是
在初始化地图的过程中,每处理一个对象,都要到这个方法中来考察一下 feature 的值是多