地图绘图输出使用Map控件提供的属性和方法,学习本节前请先熟悉Map控件,重点掌握它的绘图属性和方法。
2.8.1坐标系
1 Map控件窗口坐标系
Map控件窗口使用直角坐标系,以控件窗口的左上角为原点,向右是X轴,向下是Y轴。坐标系默认的度量单位是缇(twip),缇是固定长度单位,1缇等于1/1440英寸(inch)。Map 控件的鼠标事件函数使用这一坐标系。
在VB中,Form.ScaleMode属性设置窗体的坐标度量单位,在VB 6.0版中,可以设置7种度量单位。ScaleMode默认值指定窗体的度量单位是缇。窗体上的所有控件在默认情下使用Form.ScaleMode属性指定的度量单位,因此Map控件窗口的默认度量单位是缇。本书样例程序中使用的坐标长度单位是缇,本节中特别指定的程序例外。
Map控件窗口使用Form的坐标度量单位,因此可以用Form.ScaleMode设置Map控件窗口的坐标度量单位,然而设置的度量单位对Map控件本身的函数和方法不起作用,如果不使用Windows API函数,不应更改Form.ScaeMode的默认值,以免引起错误。
Map控件窗口坐标系由开发语言的编译器定义,同一种语言编译器若来自不同的厂家,窗口坐标系可能有差异。不同的语言定义的窗口坐标系也有差异。
2 Map控件窗口数据坐标系
Map.CoordinateSystem属性指定的坐标系,坐标系的定义方法与图层数据坐标系相同。
在程序中若没有用语句给Map.CoordinateSystem赋值,则它的值是Nothing。此时Map控件窗口数据坐标系使用图层数据坐标系。
3 图层数据坐标系
文件中的地图数据使用笛卡儿直角坐标系。坐标系及其坐标单位在投影元文件中描述。地图数据是点、线、面等图形要数的(X,Y)坐标值。
(1) 矢量地图数据的坐标单位
若地图已经投影,则地图数据坐标使用长度单位,我国最常用的长度单位是米,小比例尺地图常用用千米。
若地图未经投影,则地图数据坐标单位使用十进制度,X坐标表示经度,Y坐标表示纬度。坐标(123.5,43.2)表示经度是123.5度、纬度是43.2度的一点。地图不进行投影,用地理坐标记录地图数据是地理信息系统中数据最优表示方案。我国国家基本比例尺地形图数据库采用这一方案。
(2) 栅格地图数据的坐标单位
用栅格像元在地面对应矩形的长和宽表示坐标单位,像元所在的列号为X坐标,行号为Y坐标。栅格数据坐标系与设备坐标系一致。多数情况下栅格像元是正方形,也可以是矩形。
4 设备坐标系
输出设备具有自己的坐标系,用行、列表示坐标,用像素(pixel)作为坐标度量单位。像素是输出设备在介质上能描绘的最小图形,无论是图象、图形还是文字,任何输出都转换成像素在设备上输出。在打印机或绘图仪的说明书上常使用dot,就是指像素,用每英寸点数(dot per inch也称dpi )表示分辨率。设备上的点以像素(dot)尺寸为度量单位,以点所在的列为X坐标,以点所在的行为Y坐标。如此定义的坐标系称为设备坐标系,设备坐标系的原点在左上角,向右是X轴方向,向下是Y轴方向。不难看出,如果坐标长度单位用Pixel, Map控件坐标就是设备坐标系。因此设备坐标系是控件坐标系的一种特例。
多数设备的像素是正方形,也有是长方形的。
有些设备绘图的最小像素尺寸可以通过软件或硬件设置。打印机、绘图仪、显示器是常见的输出设备,可以根据说明书设置分辨率。有些设备的分辨率是固定的,不能进行设置。
设备坐标系在对设备直接编程控制时使用,在Windows操作系统下,可以调用设备驱动程序控制设备,设备驱动程序及Windows API函数使用设备坐标系。
VB的预定义对象实例Printer和Screen提供了TwipsPerPixelX与TwipsPerPixelY属性,可用于像素与坐标单位之间的转换。
5 绘图显示过程中的坐标变换
Map1窗口显示地图的坐标变换过程如下:
If Map1.CoordinateSystem = Nothing
将图层坐标数据变换成Map1窗口坐标系数据
用Map1窗口坐标系数据绘图
Else
将图层坐标数据变换成Map1控件数据坐标系数据
将Map1控件数据坐标系数据变换成Map1窗口坐标系数据
用Map1窗口坐标系数据绘图
End If
用Map1窗口坐标系数据绘图经过两步完成:
将窗口坐标系数据变换成设备坐标系数据
用设备坐标系数据调用Windows的API函数绘图
用打印机绘图的坐标变换过程也是如此。
6 Map.Extent属性及Map事件函数中使用的坐标系
Map1.Extent属性中的坐标数据使用地图数据坐标系。
Map1.Extent属性各分量名称 |
注释 |
Map1.Extent.Width |
Map1窗口的宽度 |
Map1.Extent.Height |
Map1窗口的高度 |
Map1.Extent.Top |
Map1窗口的顶部边线Y坐标 |
Map1.Extent.Left |
Map1窗口的左部边线X坐标 |
Map1.Extent属性的各分量
Map1事件函数中使用Map控件坐标系,如
Map1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Map1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
等函数中的X、Y表示鼠标当前位置在Map1控件窗口坐标系下的坐标,坐标单位是缇。参见MapExtent目录中的程序运行结果。
例1 计算Map1 窗口中的一点在4种坐标系下的坐标(完整程序参见样例目录Coordinate)
Map1.CoordinateSystem = Map1.Layers.Item(0).CoordinateSystem.GeoCoordSys
Private Sub Map1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim DevX, DevY As Single
Label1.Caption = "Map 控件窗口坐标系 X = " & X & " Y = " & Y
'计算Map控件设备坐标系坐标:
DevX = Form1.ScaleX(X, Form1.ScaleMode, vbPixels)
DevY = Form1.ScaleY(Y, Form1.ScaleMode, vbPixels)
Label2.Caption = "Map 控件设备坐标系 X = " & DevX & " Y = " & DevY
'计算Map控件数据坐标系坐标:
Dim aPoint As New MapObjects2.Point
Dim TwipsX, TwipsY As Long
If Form1.ScaleMode <> vbTwips Then
TwipsX = ScaleX(X, Form1.ScaleMode, vbTwips)
TwipsY = ScaleY(Y, Form1.ScaleMode, vbTwips)
Else
TwipsX = X
TwipsY = Y
End If
Set aPoint = Map1.ToMapPoint(TwipsX, TwipsY)
Label3.Caption = "Map 控件数据坐标系 X = " & aPoint.X & " Y = " & aPoint.Y
'计算Map1图层坐标系坐标:
Dim thePrj As MapObjects2.ProjCoordSys
Dim bPoint As MapObjects2.Point
Set thePrj = Map1.Layers.Item(0).CoordinateSystem
Set bPoint = thePrj.Transform(Map1.CoordinateSystem, aPoint)
Label4.Caption = "Map 控件图层坐标系 X = " & bPoint.X & " Y = " & bPoint.Y
End Sub
习题
1 运行例题1程序,观察Map1窗口中点的坐标值变化规律,总结出4种坐标系的定义。
2 交互式改变Map1.CoordinateSystem及Form1. ScaleMode的值,设计程序实现例题1的功能。
2.8.2 Map控件的绘图属性和方法
1 hWnd 属性
hWnd As OLE_HANDLE
OLE_HANDLE是long数据类型的别名 , 因此hWnd的数据类型是long型。
Windows操作系统在屏幕上绘图时,并不需要使用屏幕整个显示区域,而只向屏幕的一个矩形区域发送数据,这种区域称为窗口。桌面上运行的每一个应用程序都有一个窗口,Windows操作系统为了管理窗口,给每个窗口赋予一个唯一编号,称为窗口句柄(Handle)。对窗口进行操作时使用窗口句柄。在Windows许多API(Application Programming Interface,应用程序编程接口)函数中,经常使用窗口句柄,且用专用的名称hWnd表示,在其它场合,也常用hWnd表示窗口句柄,hWnd已经成了窗口句柄的专用名称。API提供了丰富的窗口操作函数,利用Map.hWnd属性可以增加地图窗口的显示内容,扩充Map控件的功能。在Visaul Basic的Objects Browser(对象浏览器)中,定位到Map.hWd按F1键可以看到样例程序。
2 输出图像或图形文件
在Map控件中绘制的地图可以形成栅格结构图像或矢量结构图形保存到文件中,也可保存到剪贴板中,供其它软件使用。有四个方法实现这项功能。ExportMap、ExportMap1、ExportMap2三个方法作用相同。
(1) Sub ExportMap(exportType As ExportMapConstants, outputFile As String, scaleFactor As Double)
将Map1窗口中可见范围的地图输出到BMP或EMF格式的文件中,也可保存在剪贴板中。输出数据格式和数据存放位置由exportType 参数指定,含义如下表。
exportType 值 |
数据格式 |
存放位置 |
moExportEMF 0 |
EMF |
文件中 |
moExportBMP 1 |
BMP |
文件中 |
moExportClipboardEMF 2 |
EMF |
剪贴板 |
moExportClipboardBMP 3 |
BMP |
剪贴板 |
exportType参数值表
outputFile参数指定输出文件名称。
scaleFactor参数指定像元个数放大系数,X,Y两个方向使用同一系数。例如,Map1窗口中每行有110个像元,scaleFactor的值是3,则输出的图像每行有330个像元。
(2) Sub ExportMap2(exportType As ExportMapConstants, outputFile As String, scaleFactor As Double, [useSourceDepth])
在ExprotMap的接口参数中增加useSourceDepth参数形成ExportMap2。当Map控件窗口中含有影像图层时使用ExportMap2方法。
useSourceDepth参数是Boolean 型,缺省值是False,。指定在输出的图像中是否采用影像图层的调色板(Pallete)。若useSourceDepth取值为True,则输出影像采用原图像层的调色板,即目标影像与原影像的位层面数相同,影像色彩保真。
(3) Sub ExportMap3(formatType As ExportMapConstants, formatData, [scaleFactor As Double = 1], [scaleSymbology As ExportSymbologyScaleConstants = moLineSymbolsNotScaled], [exportDepth As ExportDepthConstants = moExport8BitDepth], [palette As ExportPaletteConstants = moDefaultPalette])
这个方法在ExportMap的接口参数中增加三个参数,可为输出影像进行更多参数设置,含义如下:
1) ExportSymbology指定输出影像中的符号是否按scaleFactor因子进行比例拉伸,取值的含义如下表。
ExportSymbology 参数值 |
含义 |
moNoSymbologyScaled 0 |
符号尺寸不变 |
moLineSymbolsNotScaled 1 |
线的宽度不变,符号的其它尺寸乘以scaleFactor |
moAllSymbologyScaled 2 |
符号尺寸乘以scaleFactor |
ExportSymbologyScaleConstants枚举常量值表
2) exportDepth 参数指定输出影像的位平面数,取值的含义如下表。
exportDepth 参数值 |
输出影像的位平面数 |
moExport8BitDepth 0 |
8 bit , palette参数有效 |
moExportSourceDepth 1 |
24 bit ,若Map1中有24 bit图像。否则8 bit |
moExport24BitDepth 2 |
24 bit |
ExportDepthConstants枚举常量值表
3) palette 当输出8bit位图时,指定输出位图的调色板,取值含义如下表所示。
palette 参数值 |
输出位图采用的调色板 |
moDefaultPalette 0 |
Windows操作系统的缺省调色板 |
moHalfTonePalette 1 |
与Map1最接近的半色调调色板 |
moWebSafePalette 2 |
与Map1最接近的网络上用的调色板 |
moGraysWithSystemColorsPalette 3 |
8 bit颜色映射成灰度值调色板, |
ExportPaletteConstant枚举常量值表
(4) Sub ExportMapToJpeg(outputFile As String, [percentQuality As Long = 85], [isProgressive As Boolean = False], [scaleFactor As Double = 1], [scaleSymbology As ExportSymbologyScaleConstants = moLineSymbolsNotScaled])
这个方法输出JPEG格式图像,有两个参数前文未加说明:
1) percentQuality 取值0到100之间,指定输出图像的质量。值越大,质量越高,但图像文件也越大。
2) isProgressive 指定输出的图像是否使用改进的JPEG格式。
3 PrintMap方法
Sub PrintMap(docName As String, outputFile As String, landscapeOrientation As Boolean)
将Map1窗口中的地图输出到打印机,也可存为一个打印文件。打印时自动将地图尺寸调整到打印纸的大小。
1) docName 打印文档名称,可以任意命名,打印时在打印机的打印任务队列中可以看到您给的名字。
2) outputFile 若将打印结果保存到文件中,可在此指定文件名,否则用空字符串。
3) landscape 指定地图在纸张上的打印方向。值为True表示纵向打印,否则横向打印。
4 将地图复印到hDC标识的设备上。
(1) Sub OutputMap(hDC As OLE_HANDLE)
将地图窗口中显示的全部内容复印到hDC标识的设备上。
hDC long型参数(OLE_HANDLE 是long型的别称),用于指定输出设备。hDC是Windows操作系统API函数参数的专用名词之一,称为设备描述表句柄(Handle of device context),本质上是标识设备参数表及其驱动程序的编号,Windows操作系统中设备输入输出API函数用hDC作为参数。
在Visual Basic中, Form窗体、Picture图片框,Printer实例等有hDC属性。
例1复制地图到控件。将Map1窗口中的全部显示内容复印到图片控件Picture1的窗口中(参见OutMap目录中的OutMap程序窗口上的按钮 复制地图到控件 对应的代码)。
Private Sub Command1_Click() '将Map1中的地图复印到Picture1控件窗口中
Map1.OutputMap Picture1.hDC
End Sub
OutputMap方法也可以将地图窗口中的内容硬拷贝输出到打印机。
例2 打印地图。 将Map1窗口中的地图输出到打印机。
Private Sub Command4_Click() '打印地图
Printer.Print '初始化打印机
Map1.OutputMap Printer.hDC '打印地图
Printer.EndDoc '结束打印
End Sub
(2) Sub OutputMap2(hDC As OLE_HANDLE, X As Long, Y As Long, Width As Long, Height As Long, [DrawFlags])
将地图窗口中显示的全部内容复印到hDC标识的设备上。
X 设备坐标系下目标设备绘图矩形区左上角的 X坐标
Y 设备坐标系下目标设备绘图矩形区左上角的 Y坐标
width 设备坐标系下目标设备绘图矩形区的宽度
height 设备坐标系下目标设备绘图矩形区的高度
drawFlags 一个数值型常数,指定如何绘图,含义如下表:
drawFlags 的值 |
说明 |
moNoBackground 1 |
不复印背景矩形边框 |
moClipToExtent 2 |
只复印Map1.Extent以内的内容 |
drawFlags参数取值
例3 打印组合地图。将Map1中的地图复印到打印纸右上四分之一区域及右下四分之一区域,组合成一张地图。
Private Sub Command5_Click() '打印组合地图
Dim pixHeight As Long
Dim PixWidth As Long
Printer.Print ‘初始化打印页
' 将页面尺寸转换成像元数.
pixHeight = Printer.ScaleHeight / Printer.TwipsPerPixelY
PixWidth = Printer.ScaleWidth / Printer.TwipsPerPixelX
' 打印地图
Map1.OutputMap2 Printer.hDC, PixWidth / 2, 0, PixWidth / 2, pixHeight / 2
Map1.OutputMap2 Printer.hDC, PixWidth / 2, pixHeight / 2, PixWidth / 2, pixHeight / 2
Printer.EndDoc ‘打印文档结束,输出当前打印页,结束打印
End Sub
OutputMap与OutputMap2必需参数hDC,如果不知道hDC,但知道窗口的hWnd,可以用API函数获取hDC。下段程序获得当前FORM窗体的hDC:
Private Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long ‘API函数
Private Sub Command1_Click()
Dim GethDC As Long
GethDC = GetDC(Me.hWnd) ‘ 用API函数获取窗口的hDC
End Sub
5 CopyMap方法
Sub CopyMap(scaleFactor As Double)
将Map1窗口中地图复制到剪贴板上,用EMF(enhanced metafile format,Windows增强元文件格式)和WMF(standard metafile format,Windows标准元文件格式)格式表示。
scaleFactor 地图输出缩放因子。
样例参见OutMap目录中程序的复制地图到剪贴板 命令按钮对应的代码。
习题
1 编写程序实现例1、例2、例3的功能
2 用Map1.CopyMap方法将地图复制到剪贴板,用你熟悉的图象处理软件提取剪贴板中的地图。
2.8.3 地图绘图输出
1 窗体和控件上的绘图输出
Map控件中的地图可以输出到窗体,也可以输出到控件。窗体和控件都是Windows操作系统的窗口,Map控件底层的函数绘图是在窗口上实现的,因此两者绘输出编程原理相同。Map.OutputMap或Map.OutputMap2方法可以实现这两项功能。
例1复制地图到另一个窗体。将Form1窗体上Map1中的地图复制到窗体ShowMap上(参见OutMap2目录中的程序)。
1) 在命令按钮复制地图到另一个窗体 中显示ShowMap窗体
Private Sub Command2_Click()
ShowMap.Show
End Sub
2) 在ShowMap窗体的Paint事件中复制地图
Private Sub Form_Paint()
Form1.Map1.OutputMap Me.hDC
End Sub
例2 地图组合。 将Map1中的地图分别复制到Picture1图片框上的右半部上下两个区域(见OutMap2目录中程序的地图组合 命令按钮对应的代码)。
Private Sub Command3_Click()
With Me.Picture1
Map1.OutputMap2 .hDC, .ScaleWidth / 2, 0, .ScaleWidth / 2, .ScaleHeight / 2
Map1.OutputMap2 .hDC, .ScaleWidth /2, .ScaleHeight /2, .ScaleWidth/2, .ScaleHeight /2
End With
End Sub
Map1.OutputMap2方法中的坐标参数使用设备坐标系,应将Me.Picture1.ScaleMode属性值设置成Pixel,这样,Picture1的控件坐标系与设备坐标系一致。
2 打印机绘图输出
Map.PrintMap或Map.OutputMap方法可将Map窗口中的地图复印到打印机,输出图范围是整个地图窗口,并调整地图的输出比例尺,尽可能获得较大的输出地图。
Map.PrintMap方法一个语句就可以打印一页地图,不必使用打印机初始化及打印文档结束语句。
Map.OutputMap2方法可以指定输出地图的尺寸及其在纸张上的位置,可以输出组合地图。
3 比例尺计算
(1)比例尺的定义
若两地物在地面上的实际距离是L,图面上的距离是M,比值B
B = L/M
则注记
1:B
称为地图的比例尺。
(2) 计算B值
在MapObjects2中,地图输出函数Map1.OutputMap Map1.OutPutMap2 Map1.PrintMap Map1.CopyMap输出地图的范围是Map1窗口中显示的地图矩形区域,可用矩形区在地面上的尺寸和打印尺寸计算B值。
1) 确定L的值 MappObjects2中,若投影元数据完整,Map1.Extent中的数据记录的是地图窗口矩形在地面上的实际尺寸,据此可以获得L值,若地图数据是经过投影的,从Map1,CoordinateSystem.Unit.Type可以确定确定L值的长度单位,若地图数据未经投影,则L值的长度单位是经纬度,这时需经投影计算获得地面矩形的实际尺寸。
2) 确定M的值 Printer.ScaleWidth Printer.ScaleHeight分别是打印纸着墨区的宽度和高度,以缇(twips)为度量单位。Map1.OutputMap Map1.PrintMap Map1.CopyMap三个函数以尽可能大的尺寸打印地图窗口矩形区中的地图,根据地图窗口的长宽比与打印纸张着墨区长宽比的大小可以确定M的值。
Map1.OutputMap2打印地图时,可以根据它的接口参数Width 或Height 确定M的值。
例3 计算打印比例尺。Map1窗口中地图是经过投影了的,计算Map1.OutputMap Map1.PrintMap Map1.CopyMap三个方法打印地图的比例尺(见OutMap2目录中程序的计算地图比例尺 命令按钮对应的代码)。
Private Sub Command7_Click() '计算比例尺
Dim MapUnitToTwipsFactor As Double
Dim M As Double, L As Double, B As Double
MapUnitToTwipsFactor = -1
If Map1.CoordinateSystem.IsProjected Then '地图是经过投影的
Select Case Map1.CoordinateSystem.Unit.Type
Case moUnit_Meter '地图数据长度单位是米
MapUnitToTwipsFactor = 1440# * 100 / 2.54 '米变成Twips的乘数因子
Case moUnit_Kilometer '地图数据长度单位是公里
MapUnitToTwipsFactor = 1440# * 100 / 2.54 * 1000 '公里变成Twips的乘数因子
' Case 是其它长度单位时 .......
End Select
If MapUnitToTwipsFactor > 0# Then
' Map1.OutputMap Printer.hDC 打印地图
If Map1.Extent.Width/Map1.Extent.Height > Printer.ScaleWidth / Printer.ScaleHeight Then
L = Map1.Extent.Width * MapUnitToTwipsFactor
M = Printer.ScaleWidth
Else
L = Map1.Extent.Height * MapUnitToTwipsFactor
M = Printer.ScaleHeight
End If
B = L / M
MsgBox "地图打印的比例尺是 1 : " & B
End If
End If
End Sub