一、地图是怎么出来的?首先说一下地图是怎么出来的。也许你觉得这很扯淡,但其实很多人都不知道如何下手。我觉得需要先给你一个思想准备:地图是用画图语句画出来的!
要从底部绘制地图,可以使用绘图功能。英寸NET,可以画点、线、面、标准、网格等。在窗口中使用图形类的方法,并将它们组合起来形成一幅地图(平铺绘制方法除外)。
的图画。NET不在本文解释。如果你不熟悉,建议你先看看这个资料。
二、坐标转换——地图绘制的关键。NET提供了大量的绘图方法,基本上都是以图形函数的形式提供,包括各种几何形状、图像、字符的绘制。灵活运用这些方法,你就能画出漂亮的画。假设你熟悉。NET绘图,所以要解决的问题只有一个:我会画地图,但是地图元素通常是地理坐标(经纬度)。我应该在地图上的什么地方画它们?这就需要涉及到坐标变换的问题。
不管如何实现,我们首先需要这样一个函数:
///《summary》
///纬度和经度被转换成屏幕坐标
///《/summary》
///《param name=“xy”》经纬度《/param》
///《returns》屏幕坐标《/returns》
公共点WorldToScreen(点F xy)
再一个,有时候需要根据屏幕上一个点的位置计算出它的经纬度,比如鼠标指针需要显示的经纬度,所以需要一个函数:
///《summary》
///屏幕坐标转换为经纬度。
///《/summary》
///《param name=“xy”》屏幕坐标《/param》
///《returns》经纬度《/returns》
公共点f屏幕到世界(xy点)
有了这两个函数,就可以把经纬度表示的地理坐标转换成屏幕坐标,然后在屏幕上进行绘制。
为了完成坐标转换,需要用到地图参数的几个变量:地图缩放因子、地图中心点经纬度、地图大小。有关地图参数,请参考本文:
http://hi . Baidu . com/geochenyj/blog/item/6b 5c 5 c 1294057557 f 819 b 835 . html
另外,你需要对地图进行缩放和平移,本质上也是对地图参数的操作。比如放大是地图缩放倍数的操作,平移是地图中心点的操作。我们也用Coordinator类的方法写这些操作。投影变换也是坐标变换的一部分,Coordinator类也增加了投影方法,后面会讲到。
将上述两个坐标转换函数和三个地图参数封装到一个类协调器中。的类别如下:
三、绘图用坐标转换类Coordinator,可以用经纬度数据绘图。比如你得到一个省的行政边界的经纬度坐标数据,你可以把经纬度数据转换成屏幕坐标,然后用图形的方法画出来。图形对象从何而来?您可以从Image对象创建它,也可以从控件的Paint事件获取它。总之有了坐标,发挥你的想象力,自己画出来。
在气象数据的分析中,除了画点、线、面、字、格之外,还需要画一些特殊的符号,如风、天气现象、云等。这些符号可以通过图片、天气字体、符号库来实现。图片模式简单多彩,但是缩放效果不好。字库模式需要安装字库,程序部署比较麻烦;用符号库模式写代码比较麻烦。FreeMicaps的天气现象符号采用符号库方法。参见:http://blog.csdn.net/HZGJF/archive/2009/05/27/4220508.aspx.
风符号和云量符号由计算坐标绘制。
为了使用方便,FreeMicaps将符号绘制函数封装成三个符号类,以静态的方式提供。
的图画。NET是GDI的封装,包括点、线、面等各种图形元素的封装,图形图像的绘制,坐标旋转,各种反走样和平滑功能,非常强大(当然效率不太高),用它可以画出漂亮的图形。
按照OGC标准,GIS系统需要先对地图元素进行抽象和封装,但在FreeMicaps中,这种方法经过反复考虑后被放弃了,一是因为工作量大,二是因为我不能保证能封装好,可能会给插件开发带来麻烦。还不如把绘图权完全留给图层,让大家自由发挥。
四、图层为了使绘图过程易于管理,可以将绘图过程分成组。比如一张地图的绘制,可以分为几个过程:绘制世界地图、绘制中国地图、绘制河流、填写地名。每一幅画都像是画在一块玻璃上,叠加起来就成了图图。这里,每个绘制过程称为一个层。地图分层后,可以添加或删除图层,可以单独隐藏每个图层,还可以设置属性。更重要的是,通过使用面向对象技术,每一层都可以作为一个对象来管理。详见:http://blog.csdn.net/HZGJF/archive/2008/10/03/3014558.aspx。
抽象一个图层,要有一个图层绘制方法(Render),一个图层标题(LayerName),一个用来表示DataSource的字符串(datasource),一个用来表示绘制样式设置的LayerStyle,以及一些辅助的方法属性,最后形成下面的抽象图层类(CustomLayer),各种图层都是从这个类继承而来的:
在FreeMicaps中,每个数据对应一个图层类。为了方便图层类的编写,使用了设计模式下的模板方法来定义绘制过程。主程序调用图层的Render()方法时,会自动判断数据是否已经读取,并根据需要读取数据绘图。
对于一种类型的数据,您需要通过从CustomLayer继承来创建一个新的图层类。所有类型的数据图层的工作方式完全相同,但在数据读取和绘制方面有所不同。因此,在编写新的图层类时,只需要两个抽象方法DoLoad()和DoRender(),就可以读取数据和绘制图层代码。FreeMicaps使用字符串作为数据源标识,通用GIS系统对数据源进行抽象。我也尝试过这样做,但是代码太复杂,增加了层开发的难度,最终增加了插件开发的难度,所以放弃了。
如前所述,一张地图有多个图层,需要将图层放入一个列表中,在绘制地图时遍历图层,调用每个图层的Render()方法绘制一张完整的地图。对于图层列表,你会马上想到使用List类,但是图层的绘制需要有序。比如卫星云图上叠加了地名,需要先画出卫星云图,再填入地名,否则云图会覆盖地名,于是你在LayerStyle中放了一个ZOrder属性来控制图层的顺序。但是,由于链表的排序方式本身就是一种“不稳定排序”,也就是说,当两层的ZOrder相等时,它们的顺序是不确定的。为了避免这个问题,FreeMicaps从CollectionBase继承了一个LayerList类,实现了对层的管理,并实现了IXmlSerializable接口来完成层序列化的功能。此外,还添加了添加层和删除层事件。LayerList类如下所示:
在FreeMicaps中,每个数据对应一个图层类。为了方便图层类的编写,使用了设计模式下的模板方法来定义绘制过程。主程序调用图层的Render()方法时,会自动判断数据是否已经读取,并根据需要读取数据绘图。
对于一种类型的数据,您需要通过从CustomLayer继承来创建一个新的图层类。所有类型的数据图层的工作方式完全相同,但在数据读取和绘制方面有所不同。因此,在编写新的图层类时,只需要两个抽象方法DoLoad()和DoRender(),就可以读取数据和绘制图层代码。FreeMicaps使用字符串作为数据源标识,通用GIS系统对数据源进行抽象。我也尝试过这样做,但是代码太复杂,增加了层开发的难度,最终增加了插件开发的难度,所以放弃了。
如前所述,一张地图有多个图层,需要将图层放入一个列表中,在绘制地图时遍历图层,调用每个图层的Render()方法绘制一张完整的地图。对于图层列表,你会马上想到使用List类,但是图层的绘制需要有序。比如卫星云图上叠加了地名,需要先画出卫星云图,再填入地名,否则云图会覆盖地名,于是你在LayerStyle中放了一个ZOrder属性来控制图层的顺序。但是,由于链表的排序方式本身就是一种“不稳定排序”,也就是说,当两层的ZOrder相等时,它们的顺序是不确定的。为了避免这个问题,FreeMicaps从CollectionBase继承了一个LayerList类,实现了对层的管理,并实现了IXmlSerializable接口来完成层序列化的功能。此外,还添加了添加层和删除层事件。
五、打包的地图带有坐标变换类、图层类和图层列表类,可以用它们来制作一张具有缩放平移、图层管理等功能的地图,但是为了更方便的操作地图,需要将这些类进行组合打包。创建一个新的类WeatherMap,并添加Coordinator和LayerList实例作为其属性。为了更符合大家的操作习惯,将Coordinator类的实例作为私有成员,在WeatherMap类中加入地图坐标变换等方法。也就是说,在地图坐标转换中,应该调用WeatherMap类的方法,而不是访问Coordinator。类图如下:
返回抽象图层类CustomLayer,它有一个成员地图,即WeatherMap对象,在向图层列表添加图层时会自动分配该对象。编写CustomLayer的子类时,可以调用它进行坐标变换和地图操作。
为了使地图在绘制复杂图形的过程中不假死,并在绘制过程中随时中断绘制,比如快速缩放和平移地图来终止之前的绘制过程,直接绘制最后一次,地图绘制使用了多线程,但是多线程增加了代码编写的难度,尤其是多线程对UI的操作,对程序流程造成了一定的混乱,影响了程序结构。好在不会给图层代码造成困难。
六、重新打包-在UI上添加地图绘制的核心代码。为了让代码编写更简单,我们需要重新打包WeatherMap类(MapView类)并添加UI部分,也就是给地图添加一个带接口的shell,在上面实现缩放、拖动等地图操作。
MapView从PictureBox类继承并构建WeatherMap类的实例。在MapView的Refresh()方法中,调用WeatherMap。Render()来绘制地图。
为了操作地图,FreeMicaps定义了一个IMapTool接口,其中包含了鼠标和键盘操作方法。IMapTool接口的一个成员内置在MapView类中,MapView的鼠标键盘操作将由IMapTool接口的一个实例接管。在实现IMapTool接口的类中,可以对地图进行各种操作,比如平移和缩放。这个对象可以随时替换,实现不同的地图操作。在FreeMicaps中,我们已经完成了一个实现IMapTool接口的ZoomTool类。该类是默认的地图缩放和转换工具。IMapTool接口类图如下:
另外,在MapView中引入了CurrentLayer的概念,用来表示当前操作的图层,然后用它来实现图层元素拾取、图层工具栏等功能。
MapView类图如下:
七、概览图的类关系图如下:
地图绘制部分的活动地图如下:
以上已经介绍了FreeMicaps地图设计框架,相信大家对设计思路也有了一定的了解。该框架不仅适用于天气图分析软件,也适用于一般的GIS系统。本文只介绍FreeMicaps的地图框架,不涉及具体的地图数据读取和绘制,将在下一篇文章中介绍。