MO开发小技巧(三)-- 出图
这一篇讲如何用MO出图
一、先看一下我完成的程序的界面:
图一、按国家标准设置的面状地类层的符号化效果
图二、按广东省国土资源厅标准完成的土地利用现状图出图界面
可以看到,出图界面有一点模仿ArcGis的意思。
二、出图实现在基本思路
1、难题。
MO对出图的支持有三个函数:PrintMap、OutputMap和OutputMap2。三个函数的原型是:
PrintMap docName, outputFile, landscape
OutputMap hDC
OutputMap2 hDC, x, y, width, height, [drawFlags]
其中PrintMap的功能非常弱,只能地图可见部分直接输出到打印机上。OutputMap2很明显是OutputMap的加强版本,可以将地图显示的内容输出到指定的hdc的指定矩形区域中。
虽然如此,但这离我们现实的出图要求还相差很远。体现在以下几点:
(1)实际出图一般要求按指定比例尺出图,故存在一个比例尺的控制的问题;
(2)实际出图往往带有整饰性的内容,而且不同的出图类型,整饰的内容也不同。
(3)实际出图往往并不是简单对当前地图控件的拷贝。例如当前地图的长宽比例与出图要求可能并不相符;每出一次图就要调整一次显示内容也是不可接受的。
鉴于此,需要对出图模块在架构上进行较好的设计,以应付多变的出图要求。
2、设计思路
为了满足各种出图要求,原则上要求可以进行打印定制,即通过参数设置来定义不同的打印类型,从而实现不同的出图效果。
但这些参数有哪些呢?我将它们分为以下几类:
>图层控制类:需要哪些图层数据,以各个图层的显示效果;
>地图注记类:需要哪些种类的注记,以及各种注记的字体;
>地图布局及装饰类:如出图大小,地图以外的文字,图例,指北针等。
有了这些分类之后,就可以定义各个分类所需的参数了,然后将其形成模板,就可以自定义各种打印类型了。
三、出图模块编写碰到的问题
1、面状填充出图时绘图仪设备的特殊性
对于显示器和普通打印机,用图片进行面状填充均可正常进行。但对于绘图仪设备,用同样的方式进行面状填充,会发现符号非常的小,这是由于分辨率的差别的缘故。故在填充前,可以先判断设备是否是绘图仪,如果不是,则正常处理;如果是,则进行相应的缩放,缩放的比例就是绘图仪的分辨率与屏幕分辨率之比,缩放的放法是使用StretchBlt函数即可。经过这样的处理,面状填充就可以达到显示与出图完全一致的效果。
判断一个设备是否是绘图仪:GetDeviceCaps(hdc, TECHNOLOGY) = DT_PLOTTER
2、出图线粗的问题
MO的OuputMap2方法有一个特性(还是Bug?),就是将其输出到打印机上时,其线条的粗细比屏幕显示要细得多。所以出图为了得到较粗的线,要将线状图层的线粗设置得特别大,看起来极不美观。
究其原因,又是由于打印机分辨率比屏幕分辨屏要高的缘故。我想到了一个简单的办法来解决这个问题,就是在输出到打印机前,将所有的线宽进行加粗,输出完毕后立即改回来,这样就保证了显示与出图线宽的一致。
3、宗地图界址点过滤的问题
按照广东省国土资源厅的出图标准,出宗地图时,要显示宗地的界址点编号及界址线长度等信息。对于国有宗地,其图形往往很复杂,界址点个数动辄达到三四千个。这个多个界址点要全部显示出来,整个图会变成乱糟糟的一团。为了改善出图效果,必须对宗地界址点进行过滤,过滤的原则就是保留“关键点”,舍弃无关紧要的点。由于关键点这个标准比较主观,所以程序必须可接受人工干预。当然关键点也不是毫无标准,我们可以通过曲线拟合的算法来找出这些关键点。算法如下:
> 首先找到一个起始点,这个起始点按标准为西北角点;
>然后在多边形所有的点集合中,找到距这个点最远的另一个点,现在得到了两个关键点;这两个点将多边形分成了两段弧;
>分别处理这两段弧,处理办法是在弧上找到距离弦最远的一点,如果该距离大于某一预设值,就加入到关键点中。这个点与弧的两个端点又分别构成了一段弧,用递归处法处理它;
这样就得到了一个关键点的集合。然后再取得界址线长,这中间算法要精心处理。
4、注记位置的问题。
注记是出图中非常重要的内容。注记内容一般是各个图层中的某些属性字段,所以处理注记最简单的办法就是通过LabelRenderer对象来进行显示这些属性的内容。但这样做有一个致命的弱点:就是不能随便移动注记位置,图层标注的位置默认位于图形中心点的位置,要移动注记就要移动图形,因为要出图而移动图形是不可能的。我的办法是出图前将所需要的注记输出到一个Shape文件中(范围之外的注记不要生成),通过Text字段保存注记内容,然后用LabelRenerer来进行显示。这样做在一定程序上影响了速度,但从根本上解决了注记显示位置的问题。
5、其它要注意的问题
出图是一件很复杂的工作,编写程序的过程中学会碰到其它各种各样的问题,下面再简单列举一下:
>多边形中心点不在多边形范围内的问题
>注记的字体大小及缩放问题
>中小比例尺分幅不是矩形的问题
.......
以上问题本人已经比较妥善解决,请朋友们自己思考方法。如有兴趣,可以用邮件进行交流。shyhs@21cn.com
四、打印及符号化组件一览
为了重用方便,我将所有的出图功能封装到几个COM组件中,下面列出了我设计的出图模块所包含的组件及其功能。
组件文件名 | 组件功能 | 开发工具 |
mohelper.dll | 这是符号化用到的一个组件,来自于MO所带的CustomSym例子中。其功能是将一系列的点集合进行分解组合,形成自定义线对象,方便进行处理。 | |
GrphList.ocx | 这是一个自定义控件,用来绘制任意图形列表。用于实现符号化面板,符号选择等与图形列表相关的功能。能容纳十万条以上的记录,显示时速度不受影响,与数十条记录无异。 | VB |
Theme2.dll | 这是符号化核心组件,封装了符号化的实现,界面以及符号库的编辑等功能 | VB+MO |
MapPrint2.ocx | 这是一个用来设计和保存打印布局模板的控件。 | VC++ |
Hw_Vb.dll | 这是一个用Dephi开发的通用函数库,主要提供了符号化用需要用到的一些高级函数。 | Delphi |
HwPrint.dll | 这是打印功能的核心模块,封装了打印配置定义及调用,出图界面等功能。 | VB+MO |