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:
Newfield = New MapWinGIS.Field
Newfield.Name = sfIn.Field(FieldIndex).Name
Newfield.Type = sfIn.Field(FieldIndex).Type
Newfield.Width = sfIn.Field(FieldIndex).Width
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.
ShapeType = sfIn.Shape(ShapeIndex).ShapeType
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:
For PointIndex = 0 To sfIn.Shape(ShapeIndex).numPoints - 1
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
Call Convert_OSGB36_to_WGS384_HF(Lat, Lon, Y, X)
NewPoint = New MapWinGIS.Point
NewPoint.x = Lon
NewPoint.y = Lat
NewPoint.Z = Z
NewPoint.M = M
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.
Dim FirstPointInPartIndex As Integer
For PartIndex = 0 To sfIn.Shape(ShapeIndex).NumParts - 1
FirstPointInPartIndex = sfIn.Shape(ShapeIndex).Part(PartIndex)
NewShape.InsertPart(FirstPointInPartIndex, PartIndex)
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.
Dim OutFieldIndex As Integer
Dim AddedField As Boolean
For FieldIndex = 0 To sfIn.NumFields - 1
InFieldName = sfIn.Field(FieldIndex).Name
AddedField = False
For OutFieldIndex = 0 To sfOut.NumFields - 1
If sfOut.Field(OutFieldIndex).Name = InFieldName Then
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
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:
ShapeFile.StartEditingShapes()
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.:
Debug.print ShapeFile.ErrorMsg(ShapeFile.LastErrorCode))
History
- Version 1.0, written 3 September 2012.