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

MapWinGIS Shape 文件坐标转换和文件合并

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2012 年 9 月 3 日

CPOL

3分钟阅读

viewsIcon

32329

downloadIcon

863

一些示例代码,用于在最低级别将多个 Shapefile 合并为一个。

引言

MapWinGIS 是一个非常强大的库,但文档有些缺乏,尤其是在操作 shape 文件的示例代码方面。我有一些 shape 文件,它们由使用 OSGB1936 坐标(英国地形测量网格格式)的地图片段组成,我想将它们合并到一个 Shapefile 中,并转换为 WGS384(纬度和经度)坐标系。

在弄清楚如何做到这一点后,我想我应该分享这些代码,因为它涵盖了在最低级别构建 Shape 文件的所有基本原理,即组合点和字段以创建单个形状,然后将形状组合成 Shape 文件。

背景  

如果您不熟悉 MapWinGIS,请访问他们的网页:http://www.mapwindow.org/

作为快速总结,Shape 文件的结构如下:

每个 Shape 文件由多个 Shape 实体组成:MapWinGIS.Shape 和一组字段:MapWinGIS.Field。

这些字段是与每个形状关联的参数,它们的数量和名称是用户定义的:例如,索引、名称、建筑物高度等。Shapefile 中的所有形状都具有相同的字段集。

每个形状由多个点组成:MapWinGIS.Point,用于定义形状的坐标。有几种不同类型的形状,最常见的是多边形。此外,点可以分组为多个部分。部分是通过将信息添加到形状来定义的,这些信息是形状的每个部分的起点索引。

每个点都有多个与其关联的参数:坐标 (x, y, z) 和测量值 (M)。

每个形状还具有为 Shape 文件定义的每个字段的值。

使用代码

该代码是用 VB.NET 编写的,我省略的唯一函数是坐标转换函数:Convert_OSGB36_to_WGS384_HF,但如果您恰好想在 OSGB1936 和 WGS284 之间进行转换,您可以在 Hannah Fry 的网站上找到 Python 中的方法:http://hannahfry.co.uk/2012/02/01/converting-latitude-and-longitude-to-british-national-grid/

主函数 Main 接收文件名(包括完整路径)的集合和输出 shape 文件名(作为字符串)。它处理集合中的每个 Shape 文件,并尝试将所有 Shape 文件的内容合并到单个输出 Shape 文件中。

以下代码片段涵盖了该方法的一些基本部分。

  • sfIn 是源 Shape 文件,正在添加到输出 Shape 文件。
  • sfOut 是输出 Shape 文件。

创建/复制字段

为了创建一个新字段,您需要指定字段的名称、类型和宽度。

'Now we need to copy across the Field Table entries
Newfield = New MapWinGIS.Field
Newfield.Name = sfIn.Field(FieldIndex).Name
Newfield.Type = sfIn.Field(FieldIndex).Type
Newfield.Width = sfIn.Field(FieldIndex).Width
 
'Try and add the new field to the Output Shape file
If Not sfOut.EditInsertField(Newfield, FieldIndex) Then
    Debug.Print("Failed to add new Field. Error was " & sfOut.ErrorMsg(sfOut.LastErrorCode))
    Stop
End If  

创建一个新形状

要创建一个新形状,您只需要指定类型,例如多边形。合并 shape 文件时,我们只需从源 shape 文件 sfIn 中提取类型。

'In order to clone the existing shape, we need to know it's type eg Polygon etc
ShapeType = sfIn.Shape(ShapeIndex).ShapeType
 
'Attempt to create a new Shape
NewShape = New MapWinGIS.Shape
If Not NewShape.Create(ShapeType) Then
    Debug.Print("New shape creation failed, aborting import of " & _
            sfIn.Filename & " at ShapeIndex = " & ShapeIndex)
    Exit Sub
End If

为了转换坐标,每个点被逐个复制过来,并在过程中进行转换:

'Each shape has a set of points, so we need to copy these across one by one
For PointIndex = 0 To sfIn.Shape(ShapeIndex).numPoints - 1

  'Get the point attributes - Cordinates x,y,z and Measured Data Value 'M'
  X = sfIn.Shape(ShapeIndex).Point(PointIndex).x
  Y = sfIn.Shape(ShapeIndex).Point(PointIndex).y
  Z = sfIn.Shape(ShapeIndex).Point(PointIndex).Z
  M = sfIn.Shape(ShapeIndex).Point(PointIndex).M

  'Convert to Lat and Lon
  Call Convert_OSGB36_to_WGS384_HF(Lat, Lon, Y, X)

  'Create a new point
  NewPoint = New MapWinGIS.Point

  'Populate the with attributes from the old point
  NewPoint.x = Lon   'transformed OSGB X (Eastings) to WGS384 Longitude
  NewPoint.y = Lat   'transformed OSGB Y (Northings) to WGS384 Latitude
  NewPoint.Z = Z     'left alone
  NewPoint.M = M     'left alone

  'Add the point to the New shape
  If Not NewShape.InsertPoint(NewPoint, PointIndex) Then
    Debug.Print("Point add Failed, aborting import of " & _
        sfIn.Filename & " at ShapeIndex = " & ShapeIndex)
    Exit Sub
  End If
 
Next PointIndex

复制形状的各个部分

每个形状可以有一个或多个部分。一个部分只是点的一个子集。点之间的关系决定了它们的显示方式,顺时针部分被填充,逆时针部分是空的。

'We need to copy across the Part Indices
Dim FirstPointInPartIndex As Integer
For PartIndex = 0 To sfIn.Shape(ShapeIndex).NumParts - 1
 
  FirstPointInPartIndex = sfIn.Shape(ShapeIndex).Part(PartIndex)  'get the existing index
  NewShape.InsertPart(FirstPointInPartIndex, PartIndex)           'insert it, to create a new part

Next

复制字段值

这证明有点复杂,因为并非所有源 Shape 文件都具有完全相同的字段列,所以我不得不按名称匹配字段,即根据字段名称查找正确的列。

'Now we need to copy across the Field Values for the shape
Dim OutFieldIndex As Integer
Dim AddedField As Boolean
For FieldIndex = 0 To sfIn.NumFields - 1
 
  'Try and match Field Names
  InFieldName = sfIn.Field(FieldIndex).Name

  'reset search flag for each heading
  AddedField = False

  'Look for Field Name in the Output file
  For OutFieldIndex = 0 To sfOut.NumFields - 1

      'Do the Field headings match?
      If sfOut.Field(OutFieldIndex).Name = InFieldName Then

          'Try and add the entry to the new Shapefile, now at index:  sfOut.NumShapes - 1
          If sfOut.EditCellValue(OutFieldIndex, sfOut.NumShapes - 1, _
                   sfIn.CellValue(FieldIndex, ShapeIndex)) Then
              AddedField = True
          Else
              Debug.Print("Failed to add cell value from import of " & _
                    sfIn.Filename & " at ShapeIndex = " & ShapeIndex)
          End If

     End If

  Next OutFieldIndex

   'Warn if we failed
   If Not AddedField Then
       Debug.Print("Failed to find Filed Heading " & InFieldName & _
             " in output file, skipping entry for ShapeIndex = " & ShapeIndex)
   End If
 
Next FieldIndex

兴趣点 

从 MapWinGIS 开始时,最大的难题是知道做事情的顺序或序列,例如,您必须先使用以下命令启用编辑才能编辑形状:

'Switch the Shape file into Editing Mode
ShapeFile.StartEditingShapes()
 
'Switch the Shapefile attribute table into editing mode
ShapeFile.StartEditingTable()

我发现非常有用的一件事是始终在每次调用 DLL 后获取错误消息,因为这有助于我弄清楚我做错了什么,例如:

Debug.print ShapeFile.ErrorMsg(ShapeFile.LastErrorCode))

历史

  • 1.0 版,写于 2012 年 9 月 3 日。
© . All rights reserved.