Lesson 3: General Programming Practices

Overview

This lesson covers some general topics of interest to developers working with the GeoCalc tool kit.  These topics include:

 

Memory Management

With the introduction of the GeoCalc 6.3 software library, several new mechanisms were added to handle basic memory management.  In previous versions standard “new” and "delete" operations were the only available technique for allocating and freeing the memory associated with any given GeoCalc object.  The new global Disposal class can now be used to assist with these operations. By using this class, certain issues associated with memory management are completely bypassed. Each GeoCalc object type has an associated Create method used for allocating memory. When you no longer need to use an object that resides on the heap, you call the static Disposal::Dispose method, and it will free the memory associated with that object.

Let's look at an example of how to use the Dispose and object Create methods.  We will create some objects on the heap, and then free the memory associated with them:

DataSource * dataSource = DataSource::CreateDataSource();

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

GeodeticCoordSys * cs = dataSource->GetGeodeticCoordSys(BMG_T("BMG"), BMG_T("WGS84_coordinate_system"));

BmgChar * wktString = dataSource->ExportCoordSysToString(*cs);

Disposal::Dispose(cs);

Disposal::Dispose(wktString);

Disposal::Dispose(dataSource);

The Dispose method should be used with the following objects:

In addition to the Dispose() methods, there is also a DisposeVector() call that can be used when dealing with std::List<> objects that are returned from GeoCalc Class methods.

Strings

GeoCalc uses a Blue Marble specific character type called a BmgChar. By using this method, it is possible to avoid issues that crop up with the use of the wchar_t character type. For your convenience, a conversion macro has been added that will convert a standard char* to a BmgChar*. Whether your application is built to use the wchar_t as a native type, or not, you can use this macro for handling all of your GeoCalc specific string needs.

BmgChar* a WideString = BMG_T("This is a wide string");  // use the "BMG_T" macro.

GeoCalc provides the header file TConvert.h, which defines two objects used to make copies of char*s and BmgChar*s and to convert between these two types. The _tochar object is used to produce a char* that is a copy of an existing char* or BmgChar*. The _towchar object is used to produce a BmgChar* that is a copy of an existing char* or BmgChar*. For example:

char * aString = "This is a string";

_towchar localWideCopy(aString);

BmgChar * aWideString = localCopy; // Only good until localWideCopy goes out of scope.

_tochar localCopy(aWideString);

char * anotherString = localCopy; // Only good until localCopy goes out of scope.

 

Identifiers

All Serializable objects that are stored in the GeoCalc DataSource must have at least one identifier. Many objects have multiple identifiers. An identifier combined with an object type, is enough information to uniquely identify any object in the DataSource. An identifier consists of a pair of strings referred to as the issuer and the code. For example, there is a ProjectedCoordSys defined in geodata.xml that has an identifier with the issuer = "BMG" and code = "UTM-19N". This object is uniquely identified in the DataSource by this information:

Object Type:

ProjectedCoordSys

Issuer:

BMG

Code:

UTM-19N

The issuer of an identifier is the source that provided the definition for the object. Many objects in the supplied datasource files have an identifier where the issuer is equal to "BMG", which indicates that Blue Marble Geographics has provided the definition. Many other objects also have an identifier where the issuer is equal to "EPSG", which indicates that the definition came from the OGP’s EPSG database. All objects in the GeoCalc DataSource must have an identifier where the issuer is equal to "GC". It is recommended that you avoid altering the GC code of an object once it has been put into the datasource, as this value is used as a primary key. For this reason, the GeoCalc dialogs do not allow the GC code of an object to be altered.

The code of an identifier is a value that distinguishes an object from other objects of the same type defined by the same issuer.

The identifiers for a Serializable object are stored in the IdentifierCollection that can be accessed through the Serializable::get_Identifiers method. The IdentifierCollection returned by this method provides the functionality to get the identifiers and also to add, remove, and change the identifiers.

 

Exception Handling

When an unexpected or problematic condition occurs within GeoCalc during runtime, a GeoCalcException will be thrown.  When an exception is thrown by some method, the exception will travel down the call stack until it is either caught by a try-catch statement or it reaches the bottom of the call stack.  If an exception reaches the bottom of the call stack without being caught, it will cause the application to crash.  Therefore, it is a good practice to catch all exceptions.

The methods on the GeoCalcException object will provide some information about why the exception was thrown.  The most useful of these is the get_ErrorCode method, which returns a member of the GeoCalcException::Code enumeration.  This value will indicate the general nature of the condition that caused the exception to be thrown.  The get_FullMessage method can then be used to get a string that gives a verbose description of the exception.

For example, the DataSource::GetProjectedCoordSys method will throw a GeoCalcException if the specified identifier does not exist in the DataSource.  Here is an example of how to catch this exception:

DataSource * dataSource = 0;

dataSource = DataSource::CreateDataSource();

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

ProjectedCoordSys * pcs = 0;

try

{

pcs = dataSource->GetProjectedCoordSys(BMG_T("NOT_A_VALID_ISSUER"), BMG_T("NOT_A_VALID_CODE"));

}

catch(GeoCalcException & ex)

{

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

{

const BmgChar * desc = ex.get_FullMessage();

MessageBox(0, _tochar(desc).c_str(), "GeoCalcException", 0);

}

}

if(pcs) Disposal::Dispose(pcs);

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