65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 MapWinGIS 和 C# 构建桌面 GIS 应用程序 - 第三部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (28投票s)

2009 年 11 月 22 日

CPOL

13分钟阅读

viewsIcon

170949

downloadIcon

11708

使用 MapWinGIS 显示和操作栅格数据。

引言

在前几课中,我们学习了如何使用 MapWInGIS.ocx 显示和管理矢量数据,并将这些功能嵌入到使用 C# 开发的 Windows 应用程序中。在本课中,我将教你如何以相同的方式管理栅格数据。本课的读者目标是了解如何将栅格图层添加到地图控件,如何操作栅格符号系统,如何获取栅格信息,以及如何将光标定位到栅格位置。一如既往,我的工具包包含 MapWinGIS.ocx,它是一个 ActiveX 控件,可以用于任何支持 ActiveX 的语言(例如 C#、VB、Microsoft Access、Microsoft PowerPoint 等)。MapWinGIS.ocx 是开源的,你可以根据 MPL 许可证免费下载。本文中的所有代码示例和屏幕截图均基于 Microsoft Visual C# 2008 Professional Edition,但读者可以使用 Microsoft Visual C# Express Edition 获得相同的结果。

GIS 中的栅格数据

栅格数据是 GIS 中一种非常常见且有用的数据模型。任何栅格都可以表示为由列和行组成的数组。一行与一列相交,形成一个称为栅格单元的矩形区域。在最常见的栅格形式中,列的宽度等于行的高度,因此栅格单元是正方形的。栅格单元通常代表研究区域上的一个方形区域。每个单元都有两个标识符(X, Y),表示单元位置的坐标。第一个标识符/坐标 X 表示穿过该单元的列。第二个标识符/坐标 Y 表示穿过该单元的行。在通用栅格格式(如 JPEG 和 GIFF)中,X, Y 直接表示穿过该单元的列数和行数,但在 GIS 栅格数据格式中,这些坐标与世界坐标系统相关联,以描述单元在现实世界中的地理位置。

raster_array.gif

栅格单元通常使用数字值来表示,这些数字值可以是离散的(整数类型)或连续的(实数类型)。这些数字值用于表示地理要素。例如,数字值可以是 1 来描述某个要素的存在,0 来描述某个要素的缺失。下图展示了如何使用前面的两个值(1 和 0)来表示一个地理要素,其中蓝色方块表示标记为 1 的单元,白色方块表示标记为 0 的单元。

raster1.gif

GIS 栅格数据中的离散数字值用于模拟土地利用和行政区划等离散现象,而连续数字值通常用于表示地形高程和土壤酸度等连续现象。GIS 专业人员知道许多获取栅格数据的方法。他们可以直接使用不同的工具进行生成,例如自动扫描和矢量数据栅格化。他们可以获取航空照片和遥感数据作为栅格数据。

示例应用

本课的示例应用程序演示了使用 MapWinGIS.ocx 进行 GIS 栅格数据操作的许多功能。在接下来的几节中,我将详细讨论这些功能,而忽略前几课中已经讨论过的功能。

准备,开始!

首先,你需要启动一个新的 Windows 应用程序。给你的项目起一个名字。我给我的项目命名为“GISSampleApplication3”。在“解决方案资源管理器”窗口中,选择项目节点,右键单击它,然后从上下文菜单中选择“添加引用”。在将出现的“添加引用”窗口中使用“COM”选项卡,在列表中找到 MapWinGIS.ocx 并添加它。现在,你的项目就拥有了 MapWinGIS.ocx 的所有功能,但仍然需要添加 MapWinGIS.ocx 地图控件提供的可视化功能。要将此控件添加到你的工具箱,请选择工具箱中的“常规”选项卡,右键单击它,从上下文菜单中选择“选择项”,从“选择工具箱项”窗口中选择“COM 组件”选项卡,找到“地图”控件,勾选它,然后按“确定”。现在,“地图”控件工具将显示在你的工具箱中。以上步骤已在第一课中进行了详细的说明和图示。

给我看点东西

本课的示例应用程序是一个简单的单窗体应用程序。该窗体有一个名为 toolStrip1ToolStrip,其中包含五个按钮:toolCursortoolZoomExtenttoolZoomIntoolZoomOuttoolPan。这些按钮的代码已在第一课中讨论过。该窗体底部还有一个名为 StatusStrip1docked 的状态栏。状态栏包含一个名为 ToolStripStatusLabel1ToolStripStatusLabel。该窗体有一个名为 splitContainer1SplitContainer,它垂直分割成两个面板:左侧为 splitContainer1.panel1,右侧为 splitContainer1.panel2。左侧面板包含一个名为 tbnInfo 的按钮。右侧面板包含名为 axMap1MapWinGIS.ocx 地图控件组件,该控件已停靠以完全填充右侧面板。

让我们写一些代码

现在是时候进行一些实际工作了。我们将讨论实现本课目标所需的代码及其含义。在 Form1 类声明的开始大括号后,添加以下代码:

#region PublicDeclaration
//(1) Create an integer variable to store the raster grid layer handler
public int intHandler;
//(2) Create an instance for the grid raster 
public MapWinGIS.Grid myGrid = new MapWinGIS.Grid();
//(3) Create a new grid header object
public MapWinGIS.GridHeader myHeader = new MapWinGIS.GridHeader();
//(4) Create an image instance to store and display the raster
public MapWinGIS.Image myImage = new MapWinGIS.Image();
#endregion

这一组类型声明显示了在 MapWinGIS 中操作栅格网格数据所需的四种主要类型。第一种类型是图层句柄,存储在整数变量(intHandler)中。图层句柄是用于操作图层的唯一值。第二种类型是 MapWinGIS.Grid。这是 MapWinGIS 用于存储栅格网格的特定类型。第三种类型是 MapWinGIS.GridHeader,这是 MapWinGIS 提供的另一个用于操作栅格头部的类型。第四种类型是 MapWinGIS.Image。它是由 MapWinGIS 提供的一种类型,用于存储栅格的图像副本,可用于在地图控件中可视化栅格网格数据。所有之前的 MapWinGIS 类型都是引用类型,需要使用 new 关键字来初始化。

现在我们有了操作栅格所需的所有实例。本课使用的栅格是埃及西北沙漠地区的研究区域的示例数字高程模型 DEM,该模型是通过对从现场收集和从地形图获得的标高点进行插值生成的。该栅格以 GeoTIFF 格式存储。GeoTIFF 格式是一种标准的 TIFF 格式,并包含有关数据空间域的额外信息。将以下代码添加到 Form1_Load 事件中:

#region LoadTheData
//(1) Open the grid
myGrid.Open(@"D:\SampleData\dem.tif",
        MapWinGIS.GridDataType.DoubleDataType,
        false,MapWinGIS.GridFileType.GeoTiff,
        null);
//(2) Get the header of the raster
myHeader = myGrid.Header;

//Symobolize the raster grid
//(3) Create a raster grid color scheme
MapWinGIS.GridColorScheme myScheme = 
        new MapWinGIS.GridColorScheme();            
//(4)Assign the no data color to lack
myScheme.NoDataColor =
        System.Convert.ToUInt32( 
        System.Drawing.ColorTranslator.ToOle( //
        System.Drawing.Color.FromArgb(0, 0, 0)));
//(5) Set the attributes of the color scheme
myScheme.UsePredefined(
        (double)myGrid.Minimum,
        (double)myGrid.Maximum,
        MapWinGIS.PredefinedColorScheme.DeadSea );

//Convert the grid to an image
//(6) Create a Utili instance
MapWinGIS.Utils myUtil = new MapWinGIS.Utils();
//(7) and use it to convert the grid to the image
myImage = myUtil.GridToImage(myGrid, myScheme, null);

//(8) Finally add the layer to the map
intHandler = axMap1.AddLayer(myImage, true);
#endregion

显示数据的第一步是将栅格数据从文件加载到网格实例(myGrid)中。Open 方法用于将数据加载到网格实例中。Open 方法需要五个参数:

  • 第一个参数是包含栅格数据的文件的完整路径。
  • 第二个参数是网格数据类型,在 MapWinGIS 中表示为枚举类型 MapWinGIS.GridDataType,它包含六个元素。这些元素包括四个已定义元素(ShortDataType 用于短整型数据类型像素,LongDataType 用于长整型数据类型像素,FloatDataType 用于单精度数据类型像素,DoubleDataType 用于双精度数据类型像素)和两个未定义元素(UnknownDataType 用于未知数据类型,InvalidDataType 用于无效数据类型)。
  • 第三个参数是一个 bool 值,表示栅格是应该从 RAM 读取(true)还是从文件读取(false)。
  • 第四个参数是 GridFileType。MapWinGIS 提供了一个枚举来描述 MapWinGIS 本身支持的网格文件类型。支持的网格文件类型有:ASCII 网格文件格式(MapWinGIS.GridFileType.Ascii)、逐行带状格式(MapWinGIS.GridFileType.Bil)、二进制网格文件格式(MapWinGIS.GridFileType.Binary)、数字高程模型文件格式(MapWinGIS.GridFileType.DTed)、ERMapper 压缩小波文件格式(MapWinGIS.GridFileType.Ecw)、ESRI 网格文件格式(MapWinGIS.GridFileType.Esri)、ArcView 二进制栅格文件格式(MapWinGIS.GridFileType.Flt)、GeoTIFF 栅格文件格式(MapWinGIS.GridFileType.GeoTiff)、MrSID 栅格文件格式(MapWinGIS.GridFileType.MrSid)、PCI PAux 栅格文件格式(MapWinGIS.GridFileType.PAux)、PCI Geomatics 数据库文件格式(用于栅格数据)(MapWinGIS.GridFileType.PCIDsk)以及美国地质调查局 USGS 空间数据传输标准栅格数据格式(MapWinGIS.GridFileType.Sdts)。
  • 除了这些类型,用户还可以分配两个不同的枚举:MapWinGIS.GridFileType.InvalidGridFileType,可用于处理具有间隙或错误的数据,以及 MapWinGIS.GridFileType.UseExtension,它允许用户打开不同的常见栅格格式。

  • Open 方法所需的最后一个参数是回调。在大多数常见情况下,此参数分配为 null

第二步是将网格头部分配给头部实例(myHeader)。网格头部包含有关栅格网格的基本信息,如其宽度、高度、像素分辨率等。完成第二步后,网格及其头部将被加载到计算机内存中,因此该过程需要一段时间。步骤 3、4 和 5 的目标是准备显示网格所需的符号系统。第三步是创建栅格网格颜色方案。栅格网格颜色方案在 MapWinGIS 中由 MapWinGIS.GridColorScheme 类表示。在步骤 3 中,创建并初始化了这个类的实例(myScheme)。

在第四步中,我们将使用 MapWinGIS.GridColorScheme 实例(myScheme)的 NoDataColor 属性设置一个颜色来表示无数据像素。分配的颜色是黑色。第五步是使用实例 mySchemeUsePredefined 方法为 myScheme 设置分配预定义的网格颜色方案设置。此方法需要三个组件:最小像素值、最大像素值以及预定义的网格颜色方案。最小和最大像素值可以使用网格实例属性 MinimumMaximum 轻松获取。MapWinGIS 中以枚举类型引入的预定义网格颜色方案包含八个元素:

  • MapWinGIS.PredefinedColorScheme.DeadSea
  • MapWinGIS.PredefinedColorScheme.Desert
  • MapWinGIS.PredefinedColorScheme.FallLeaves
  • MapWinGIS.PredefinedColorScheme.Glaciers
  • MapWinGIS.PredefinedColorScheme.Highway1
  • MapWinGIS.PredefinedColorScheme.Meadow
  • MapWinGIS.PredefinedColorScheme.SummerMountains
  • MapWinGIS.PredefinedColorScheme.ValleyFires

现在,该展示了。让我们添加显示网格所需的代码。要显示网格,必须将其转换为图像。将网格转换为图像需要 MapWinGIS.Utils 类的实例。在步骤 7 中,创建并初始化了这个实例(myUtil)。在步骤 8 中,使用 MapWinGIS.UtilsmyUtil)提供的 GridToImage 方法,通过定义的网格颜色方案(第二个参数 myScheme)将网格(第一个参数 myGrid)转换为图像。GridToImage 方法需要的第三个参数是回调。现在,使用 AddLayer 方法将图像(myImage)添加到地图控件(axMap1)中。不要忘记为导航按钮分配必要的代码。这些按钮的代码可在第一课中找到。现在,按下键盘上的 F5 键开始调试应用程序,如果你一切都做得对,你将看到以下屏幕:

01.jpg

关于我的网格的一些信息

每个栅格网格都有一组基本信息 - 在某些技术文献中称为元数据 - 这些信息对于理解有关网格和网格所表示的地理信息的许多事实至关重要。最常见和最重要的信息包括测量单位(例如,米)下的像素尺寸、网格的宽度和高度(以像素为单位)以及左下角坐标。这些信息有助于程序员了解和计算许多有用的信息,例如网格的宽度和高度(以测量单位为单位)(宽度(或高度)以测量单位为单位 = 宽度(或高度)以像素为单位 * 像素边长)、网格的面积(宽度(像素)* 高度(像素)* 像素边长 * 像素边长)。所有这些基本数据都可以在 GridHeader 实例(myHeader)中找到。将以下代码添加到 btnInfo_Click 事件中:

//(1) Get the width and height of the 
//grid in pixels
int width = myHeader.NumberCols;
int hight = myHeader.NumberRows;
string sizeInPixels = 
  String.Format("Grid sizs - in pixels - is ({0},{1})\n", width, hight);      

//(2) Get the width and height of the pixel
double deltaX = myHeader.dX;
double deltaY = myHeader.dY;
string pixelDimensions = 
  String.Format("Pixel dimensions are ({0},{1})\n", deltaX, deltaY);

//(3) Get the lower left corner coordiantes
double xLlLocation = myHeader.XllCenter;
double yLlLocation = myHeader.YllCenter;
string lowerLeftCorner = String.Format("Lower Left corner coordinates ({0},{1})\n", 
                                       xLlLocation ,yLlLocation );
    
//(4) Show the message
string infoMessage = sizeInPixels + pixelDimensions + lowerLeftCorner;
MessageBox.Show(infoMessage, "Raster Information", 
                MessageBoxButtons.OK, MessageBoxIcon.Information);

在第一组语句中,使用了 GridHeader 实例(myHeader)的 NumberColsNumberRows 属性,并将恢复的值存储在整数数据变量 widthheight 中。这些属性分别恢复列数和行数。在第二组中,使用了 GridHeader 实例(myHeader)的 dXdY 属性,并将恢复的值存储在整数数据变量 deltaXdeltaY 中。在第三组中,使用了 GridHeader 实例(myHeader)的 xllCenteryllCenter 属性,并将恢复的值存储在整数数据变量 xLlLocationxLlLocation 中。这两个变量表示左下角像素中心的坐标。第四组也是最后一组代码将构建一个字符串来表示之前的信息,并将其可视化显示给用户。按下键盘上的 F5 键。你将看到如下输出:

02.jpg

告诉我,我在哪里???

GIS 提供的一些最重要的功能是“它在哪里”和“它是什么”。对于栅格数据,这些信息可以使用地图控件实例的 PixelToProj 方法和网格实例的 get_Value 方法来引入。在设计视图中转到 Form1,然后选择 axMap1 控件。按 F4 获取 axMap1 的“属性”窗口。在 axMap1 的“属性”窗口中找到 SendMouseMove 属性并将其设置为 true。此操作将激活 axMap1_MouseMoveEvent。现在,将此事件 axMap1_MouseMoveEvent 添加到 axMap1 的事件列表中,然后双击它在代码视图中打开。为该事件添加以下代码:

//(1) Get the location of the cursor
// form the MapControl and transform it to
//the projection location
double X =0;
double Y =0;
axMap1.PixelToProj(e.x, e.y, ref X, ref Y);
//(2) Get the value of pixel at specified point
double pixelVal = (double) myGrid.get_Value(e.x, e.y);
//(3) Print the result to the status toolbar.
string pointLocation = String.Format("Cursor location ({0},{1}) " + 
                       "and the pixel value is {2}",X,Y, pixelVal  );
toolStripStatusLabel1.Text = pointLocation;

第一组代码旨在识别光标在 axMap1 控件上的位置。在此代码中,我们将声明两个 double 类型变量 XY,并将初始值(0)赋给两者。使用 axMap1 实例的 PixelToProj 方法,它需要四个参数:前两个参数是光标在 axMap1 上的位置,由 e.xe.y 表示,以及用于存储转换值的变量。这些变量 XY 应该使用 ref 关键字传递。使用此方法后,XY 的值将以投影测量单位存储光标在 axMap1 上的位置。第二组代码旨在识别光标所在像素的值。在此代码中,我们将声明一个 double 类型变量 pixelVal,并将 Grid 实例(myGrid)的 get_Value 方法返回的值赋给它。get_Value 方法需要两个参数来表示光标的位置。这些参数用 e.xe.y 表示。现在,按下 F5,尝试将光标移动到地图上方,看看状态栏如何直接更改以显示光标的位置以及位于该位置的像素的值。

结论

MapWinGIS 提供了一种易于使用的简单方法来显示和操作栅格数据,分为两个层次的操作。第一层处理网格,以便程序员可以返回诸如网格宽度和高度或左下角坐标之类的信息。第二层在像素级别处理网格数据,程序员可以使用其位置设置或返回像素的值。使用相同的工具箱,程序员可以显示和操作图像,这为 GIS 程序员增加了巨大的功能,并支持他们包含处理多光谱图像和航空照片等复杂栅格格式的应用程序。

历史

  • 第一版:2009 年 11 月 22 日,星期日。
© . All rights reserved.