Lesson 7: Performing a Two-Dimensional Coordinate Transformation

Overview

The original mission of the GeoCalc SDK was to provide a mechanism for converting and transforming geodetic point data. This lesson and the next lesson provide a description of precisely how to convert coordinates from one coordinate system to another, and transform these same data between two distinct reference frames. This lesson covers the most common kind of coordinate transformation performed by GeoCalc - one in which no vertical data is considered.  

In order to compile the code samples provided in this lesson, it will be necessary to include the "WDataSource.h" header file.  

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.

DataSource * dataSource = 0;

dataSource = DataSource::CreateDataSource();

dataSource->LoadFile(BMG_T("C:\\bmg\\GeoCalcPBW\\data\\geodata.xml"), BMG_T("C:\\bmg\\GeoCalcPBW\\data\\geodata.xvw"), true, BMG_T("C:\\bmg\\GeoCalcPBW\\data\custom.xml"), 0, 0, 0);

 

Creating a ConcatenatedTransform

The ConcatenatedTransform object is the conduit for the transformation of points between different coordinate systems. It requires at least two CoordSys objects, one of which corresponds to the Source Coordinate System, and the other of which corresponds to the Target Coordinate System. The assumption is that all input data points are defined in the Source system, and will be converted to the Target system, but it is also possible to reverse the transformation.

The first step in creating our ConcatenatedTransform will be to get a couple of CoordSys objects. It doesn't matter what types we use, or if they use the same BaseObject; the ConcatenatedTransform can facilitate a transformation between any CoordSys objects. To make things interesting, let's use two CoordSys objects that are different types, one ProjectedCoordSys and one GeodeticCoordSys. Let's also select CoordSys objects that use different BaseObjects, so that we will need to perform a CoordTransform.  For this example, we will perform a transformation from the US State Plane 27 Maine East ProjectedCoordSys to the WGS84 GeodeticCoordSys.  

First, we will need to retrieve these objects from the DataSource:

CoordSys * source = 0;

source = dataSource->GetProjectedCoordSys(BMG_T("BMG"), BMG_T("ME-27E"));

CoordSys * target = 0;

target = dataSource->GetGeodeticCoordSys(BMG_T("BMG"), BMG_T("WGS84_coordinate_system"));

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

ConcatenatedTransform * concatTransform = 0;

concatTransform = ConcatenatedTransform::CreateConcatenatedTransform();

concatTransform->get_Sources().AddBack(*source);

concatTransform->get_Targets().AddBack(*target);

Selecting Transforms

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

In many cases, a single CoordTransform will be sufficient for converting between the source and target reference systems identified in our ConcatenatedTransform. However, the GeoCalc Datasource contains various individual transformations between each pair of BaseObjects. In some cases it may also be necessary to use multiple CoordTransforms to shift from one BaseObject to another. For example, if we are using local coordinate system that only had a transformation to a WGS84 base system, and our target coordinate systems is not using WGS84 as a base. There are multiple ways to find the CoordTransform needed for a ConcatenatedTransform that are more completely discussed in Lesson 6..  

For the purpose of this lesson, we will just retrieve a single CoordTransform from the DataSource.

Serializable * coordTransform = 0;

coordTransform = dataSource->GetCoordinateTransform(BMG_T("BMG"), BMG_T("NAD27-EAST US_to_WGS84"));

concatTransform->get_Transforms().AddBack(*coordTransform);

It is also possible to intentionally not use a CoordTransform by not populating the Transforms collection. Be very careful when providing this as an option to the user, because the end result will be bad data! Users should be aware that some coordinate shifting may occur when transforming data from one CoordSys to another, even when no CoordTransforms are required, if there is a conversion between two different ProjectedCoordSys types.

 

Transforming a Point

To transform, we need to create a PointTransformer.

GEOCALCPBW_NAMESPACE::PointTransformer * pTransformer = concatTransform->GeneratePointTransformer();

Our PointTransformer is now ready to perform coordinate conversions and transformations. In order to do this, we will first need some CoordPoints and some coordinate values. This is accomplished by using the handy properties extended in the class. We can get the CoordPoints we need by cloning the source and target CoordPoint objects on our PointTransformer.

CoordPoint * inPt = 0;

inPt = pTransformer->GetSourceCoordPoint()->CloneCoordPoint();

CoordPoint * outPt = 0;

outPt = pTransformer->GetTargetCoordPoint()->CloneCoordPoint();

Now inPt is based on our Source target system, which  uses a ProjectedPoint, and outPt uses the Target Geodetic coordinate system's GeodeticPoint defintion. 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 361274 US feet, and let's set the East coordinate to 267637 US feet.

inPt->set_InUnits(267637, 361274);

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

try

{

if(! pTransformer->ConvertSourceToTarget(*inPt, *outPt))

{

// ConvertSourceToTarget failed

return false;

}

}

catch(GeoCalcException & ex)

{

if(ex.get_ErrorCode() == GeoCalcException::Code::InPointWrongType)

{

// Then inPt has the wrong ClassType

return false;

}

else if(ex.get_ErrorCode() == GeoCalcException::Code::OutPointWrongType)

{

// Then outPt has the wrong ClassType

return false;

}

else if(ex.get_ErrorCode() == GeoCalcException::Code::InPointOutOfBounds)

{

// Then inPt is not a valid point within the SourceCoordSys

return false;

}

else if(ex.get_ErrorCode() == GeoCalcException::Code::OutPointOutOfBounds)

{

// Then outPt is not a valid point within the TargetCoordSys

return false;

}

}

 

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

double lat, lon;

outPt->get_InUnits(lon, lat);

The coordinate values for outPt are lat = 44.82088949 and lon = -69.39492068, which puts us somewhere in Douglas Pond, Pittsfield, Maine. (Hopefully we're there lazily kayaking on some warm summer day, and not writing documentation for a Coordinate Transformation library.) 

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 CoordTransforms and selected some different CoordTransforms, then the output values would not match those shown above.

 

Clean Up

Finally, we must clean up after ourselves and free the memory that we have allocated in this lesson using the Disposal::Dispose method:

if(concatTransform) Disposal::Dispose(concatTransform);

if(source) Disposal::Dispose(source);

if(target) Disposal::Dispose(target);

if(coordTransform) Disposal::Dispose(coordTransform);

if(pTransformer) Disposal::Dispose(pTransformer);

if(inPt) Disposal::Dispose(inPt);

if(outPt) Disposal::Dispose(outPt);

if(dataSource) Disposal::Dispose(dataSource);