Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

MapWinGIS Shape File Coordinate Conversion and File Merging

5.00/5 (2 votes)
26 Feb 2013CPOL3 min read 32K   835  
Some example code to merge a number of Shapefiles into one at the lowest level.

Introduction

MapWinGIS is an extremely powerful library, but the documentation is somewhat lacking especially when it comes to example code manipulating shape files. I had a number of shape files which consisted of map segments using OSGB1936 coordinates (United Kingdom Ordnance Survey Grid Format), which I wanted to merge into a single Shapefile and convert to WGS384 (Latitude and Longitude) coordinate system. 

Having worked out how to do this, I thought I'd share the code as it covers all the fundamentals for constructing Shape Files at the lowest level i.e., combining Points and Fields to create individual shapes and then combining Shapes into a Shape file.

Background  

If you're not familiar with MapWinGIS, see their webpage here: http://www.mapwindow.org/.

As a quick summary the structure of a Shape File is thus:

Each Shape File consists of a number of Shape entities: MapWinGIS.Shape and a set of Fields: MapWinGIS.Field.

The Fields are the parameters associated with each shape and their number and Names are user defined: e.g., Index, Name, Building Height, etc. All Shapes, in a Shapefile, have the same set of Fields.

Each Shape consists of a number of Points: MapWinGIS.Point which define the coordinates of the Shape. There are several different types of Shapes, the most common being a Polygon. In addition, the Points can be grouped into a number of Parts. Parts are defined by adding information to the Shape on which Point indices are the start point for each Part of the Shape.

Each Point has a number of parameters associated with it: Coordinates (x, y, z) and a Measured Value (M).

Each Shape also has values for each of the Fields defined for the Shape File.

Using the code  

The code is written in VB.NET and the only function I've omitted is the coordinate conversion function: Convert_OSGB36_to_WGS384_HF, but if you happen to want to convert between OSGB1936 and WGS284, you can find the method in Python on Hannah Fry's website here: http://hannahfry.co.uk/2012/02/01/converting-latitude-and-longitude-to-british-national-grid/.

The main function, Main, takes a Collection of file names (including full path) and the Output shape file name, as a string. It processes each Shape file in the Collection and attempts to merge the contents of all the Shape files into a single output Shape File.

The following code snippets cover some of the basic parts of the method.

  • sfIn is the Source Shape file, being added to the Output Shape File.
  • sfOut is the Output Shape file. 

Creating / Copying a Field 

In order to create a new field, you need to specify the Name, Type and Width of the field:

VB.NET
'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  

Creating a new shape 

To create a new shape, all you need to do is specify the type e.g. Polygon. When merging shape files, we just extract the type from the source shape file, sfIn.

VB.NET
'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

In order to convert coordinates, each Point is copied across one by one and transformed in the process: 

VB
'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

Copying the Parts of the Shape across

Each Shape can have one or more Parts. A part is just a subset of Points. How the points relate to each other determines how they are displayed, Clockwise Parts are filled in and Anti-clockwise parts are empty. 

VB.NET
'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

Copying across Field Values

This proved a little more complicated as not all the source Shape Files had the same exact Field columns, so I had to match the fields by name, i.e., look up the correct column based on Field Name.

VB.NET
'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

Points of Interest 

The biggest headache, when starting with MapWinGIS, is knowing the order or sequence in which to do things e.g. you can't edit Shapes until you enable editing with:

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

One thing I found very useful was to always get the error message after each call to the DLL, as that helped me figure out what I was doing wrong e.g.:

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

History

  • Version 1.0, written 3 September 2012.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)