MapWinGIS Shape File Coordinate Conversion and File Merging





5.00/5 (2 votes)
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:
'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.
'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:
'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.
'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.
'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:
'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.:
Debug.print ShapeFile.ErrorMsg(ShapeFile.LastErrorCode))
History
- Version 1.0, written 3 September 2012.