Lesson 7: Performing a Two-Dimensional Coordinate Transformation

Overview

Coordinate transformation is what GeoCalc was built to do.  This lesson and the next lesson provide a description of precisely how to convert coordinates from one coordinate system to another.  This lesson covers the most common kind of coordinate transformation performed by GeoCalc - one in which no vertical data is considered.  

Since all of the definitions we will be using reside in the DataSource, we will start by creating and loading a DataSource.  For more information about the DataSource object, see Lesson 4.

[VB6]

Dim gcDataSource As New DataSource

gcDataSource.LoadFile ("c:\bmg\GeoCalcCOM\data\geocalc.xml")

 

[Delphi 7]

var

  gcDataSource : DataSource;

begin

  gcDataSource := CoDataSource.Create;

  gcDataSource.LoadFile('c:\bmg\GeoCalcCOM\data\geocalc.xml');

 

Creating a CoordTransform

The CoordTransform object facilitates the transformation of points from one coordinate system to another.  It requires two ICoordSys objects, one of which corresponds to the SourceCoordSys property, and the other of which corresponds to the TargetCoordSys property.  The SourceCoordSys represents the coordinate system from which points will be transformed, and the TargetCoordSys represents the coordinate system to which points will be transformed.  If you think of the CoordTransform as a mathematical function that acts upon ICoordPoints, then the SourceCoordSys is the domain of the function and the TargetCoordSys is the codomain of the function.

So the first step in creating our CoordTransform will be to get a couple ICoordSys objects.  It does not matter what type the ICoordSys objects are, or if they use the same HorizontalDatum; the CoordTransform can facilitate a transformation between any two ICoordSys objects.  To make things interesting, let's use two ICoordSys objects that are different types, one ProjectedCoordSys and one GeodeticCoordSys.  Let's also select ICoordSys objects that use different HorizontalDatums, so that we will need to perform some DatumShifts.  For this example, we will perform a transformation from the US State Plane 27 Maine East ProjectedCoordSys and the WGS84 GeodeticCoordSys.  First, we will need to retrieve these objects from the DataSource:

[VB6]

Dim source As ICoordSys

Set source = gcDataSource.GetProjectedCoordSys("BMG", "ME-27E")

Dim target As ICoordSys

Set target = gcDataSource.GetGeodeticCoordSys("BMG", "WGS84_coordinate_system")

 

[Delphi 7]

var

  source : ICoordSys;

  target : ICoordSys;

begin

  source := gcDataSource.GetProjectedCoordSys('BMG', 'ME-27E');

  target := gcDataSource.GetGeodeticCoordSys('BMG', 'WGS84_coordinate_system');

Now that we have these objects, we can use them to construct a new CoordTransform:

[VB6]

Dim ct As New CoordTransform

ct.SourceCoordSys = source

ct.TargetCoordSys = target

 

[Delphi 7]

var

  ct : CoordTransform;

begin

  ct := CoCoordTransform.Create;

  ct.SourceCoordSys := source;

  ct.TargetCoordSys := target;

 

Selecting DatumShifts

We now have a CoordTransform that we will use to convert between two different coordinate systems.  However, we are not quite ready to transform any points yet.  Since our source and target coordinate systems use different HorizontalDatums, we will need to perform one or more DatumShifts.  Given any two HorizontalDatums, there are probably multiple DatumShift combinations that will shift between the HorizontalDatums.  The CoordTransform object will not make any assumptions about how you wish to perform a DatumShift; you must explicitly specify all of the DatumShifts.

The CoordTransform::DatumShifts property holds a DatumShiftCollection that holds all of the DatumShifts used by the CoordTransform.  In many cases, a single DatumShift will be sufficient for a CoordTransform.  However, the GeoCalc data source does not contain each type of DatumShift for each pair of HorizontalDatums.  In some cases, it is necessary to use multiple DatumShifts to shift from one HorizontalDatum to another.

There are multiple ways to find the DatumShifts needed for a CoordTransform, and Lesson 6 describes these methods.  For the purpose of this lesson, we will just retrieve a single DatumShift from the DataSource and put it into our CoordTransform::DatumShifts collection:

[VB6]

Dim ds As DatumShift

Set ds = gcDataSource.GetDatumShift("BMG", "NAD27-EAST US_to_WGS84")

Set ds = ct.DatumShifts.InsertBack(ds)

 

[Delphi 7]

var

  ds : DatumShift;

begin

  ds := gcDataSource.GetDatumShift('BMG', 'NAD27-EAST US_to_WGS84');

  ds := ct.DatumShifts.InsertBack(ds);

It is also possible to intentionally not use a DatumShift by not populating the DatumShifts property.  Users should be aware that some coordinate shift may occur when shifting between ProjectedCoordSys' even though no DatumShift is being used.  This can be occur if the source and target HorizontalDatums use different Ellipsoids.

 

Transforming a Point

Our CoordTransform is now ready to transform a point.  In order to do this, we will first need some ICoordPoints and some coordinate values.  We can get the ICoordPoints we need by cloning the PointStyle objects on our source and target ICoordSys objects:

[VB6]

Dim inPt As ICoordPoint

Dim outPt As ICoordPoint

Dim temp As ICloneable

Set temp = ct.SourceCoordSys.PointStyle

Set inPt = temp.Clone

Set temp = ct.TargetCoordSys.PointStyle

Set outPt = temp.Clone

 

[Delphi 7]

var

  inPt : ICoordPoint;

  outPt : ICoordPoint;

begin

  (ct.SourceCoordSys.PointStyle as ICloneable).Clone(IInterface(inPt));

  (ct.TargetCoordSys.PointStyle as ICloneable).Clone(IInterface(outPt));

Now inPt is an instance of a ProjectedPoint, and outPt is an instance of a GeodeticPoint.  Our inPt is the point that we will transform, and outPt is the point that will hold the result of the transformation.  Let's set the North coordinate to 1200 US feet, and let's set the East coordinate to -250 US feet.  This can be accomplished with this line of code:

[VB6]

inPt.SetInUnits -250, 1200

 

[Delphi 7]

inPt.SetInUnits(-250, 1200, 0);

To transform inPt, we need to call the ConvertAhead method:

[VB6]

ct.ConvertAhead inPt, outPt

 

[Delphi 7]

ct.ConvertAhead(inPt, outPt);

Assuming that ConvertAhead succeeded in transforming our point, outPt will now hold the result.  We can get the coordinate values for outPt like this:

[VB6]

Dim lon As Double

Dim lat As Double

Dim height As Double

outPt.GetInUnits lon, lat, height

 

[Delphi 7]

var

  lon : double;

  lat : double;

  height : double;

begin

  outPt.GetInUnits(lon, lat, height);

The coordinate values for ourPt are lat = 43.82089062 and lon = -70.39491858.  Please note that deviating from the steps outlined in this lesson may cause different output values than are shown here.  Specifically, if you used a different method for selecting DatumShifts and selected some different DatumShifts, then the output values would not match those shown above.

 

Transforming a Collection of Points

In many cases, it is convenient to transform many points at once, and GeoCalc provides this functionality.  The CoordTransform::ConvertListAhead method will transform all of the ICoordPoints in a CoordPointCollection.  In order to use this method, we must first construct two CoordPointCollections: one to hold our input points and one to hold the output points.

[VB6]

Dim inCPC As New CoordPointCollection

Dim outCPC As New CoordPointCollection

inCPC.PointStyle = ct.SourceCoordSys.PointStyle

outCPC.PointStyle = ct.TargetCoordSys.PointStyle

 

[Delphi 7]

var

  inCPC : CoordPointCollection;

  outCPC : CoordPointCollection;

begin

  inCPC := CoCoordPointCollection.Create;

  outCPC := CoCoordPointCollection.Create;

  inCPC.PointStyle := ct.SourceCoordSys.PointStyle;

  outCPC.PointStyle := ct.TargetCoordSys.PointStyle;

Next, we put some points that we wish to convert into the input CoordPointCollection:

[VB6]

Dim inPt As ICoordPoint

Dim outPt As ICoordPoint

Dim temp As ICloneable

Set temp = ct.SourceCoordSys.PointStyle

Set inPt = temp.Clone

Set temp = ct.TargetCoordSys.PointStyle

Set outPt = temp.Clone

inPt.SetInUnits -250, 1200

inCPC.AddPoint inPt

inPt.SetInUnits 0, 0

inCPC.AddPoint inPt

 

[Delphi 7]

var

  inPt : ICoordPoint;

  outPt : ICoordPoint;

begin

  (ct.SourceCoordSys.PointStyle as ICloneable).Clone(IInterface(inPt));

  (ct.TargetCoordSys.PointStyle as ICloneable).Clone(IInterface(outPt));

  inPt.SetInUnits(-250, 1200, 0);

  inCPC.AddPoint(inPt);

  inPt.SetInUnits(0, 0, 0);

  inCPC.AddPoint(inPt);

Finally, we call the ConvertListAhead method to send our points through the CoordTransform:

[VB6]

ct.ConvertListAhead inCPC, outCPC

 

[Delphi 7]

ct.ConvertListAhead(inCPC, outCPC);

If you have set up everything in the same way as described in this lesson, then both of these points will be successfully converted.