Options > Maps > Map Projections > Creating a New Projection > Step 2: Defining a New Projection
Step 2: Defining a New Projection
This step explains how to add parameters to a projection, and how to write the Input/Output functions to support these additional parameters. It also shows how to create specific error codes.
The complete code of this projection is in the files proj_step2.h and proj_step2.cpp.
Defining a New Parameter
The Mercator projection does not maintain distances. With the Mercator projection, the scale factor changes with the latitude: the further a point is from the equator, the larger the scale factor. However, it is possible to specify the latitude of the true scale, that is, the latitude around which the distances between the projected points are maintained.
In our example, we will introduce this new parameter and manage its persistence.
Defining a New Error Code
As we saw in the previous step with the Mercator projection, the forward projection of a point can cause an error if the point is too close to a pole. Instead of returning the generic IlvProjection::ToleranceConditionError(), we will create a specific error code that will be returned in this case.
Declaring the New Class
The second version of the Mercator class is declared in the file proj_step2.h.
We have added:
*a method to set the latitude of the true scale,
*a method to get the latitude of the true scale,
*a static method PolarZoneError to get the specific error code of the Mercator projection,
*a virtual write method that overrides the default IlvProjection::write method,
*a private static method InitClass that will be used to allocate the new error code,
*a private field to store the latitude of the true scale, and
*a private static field to store the error code.
class Mercator : public IlvProjection
{
public:
Mercator();
 
void setLatitudeOfTrueScale(IlvDouble latitudeOfTrueScale)
{_latitudeOfTrueScale = latitudeOfTrueScale;}
 
IlvDouble getLatitudeOfTrueScale() const
{return _latitudeOfTrueScale;}
 
static IlvMapsError PolarZoneError() {return _polarZoneError;}
 
virtual void write(IlvOutputFile&) const;
protected:
virtual IlvMapsError sForward(IlvCoordinate &) const;
virtual IlvMapsError sInverse(IlvCoordinate &) const;
virtual IlvMapsError eForward(IlvCoordinate &) const;
virtual IlvMapsError eInverse(IlvCoordinate &) const;
private:
static void InitClass();
private:
IlvDouble _latitudeOfTrueScale;
static IlvMapsError _polarZoneError;
IlvMapsDeclareProjectionIO(Mercator);
};
Defining The Projection
The projection is defined in the file proj_step2.cpp.
To define the class, use the IlvMapsDefineProjectionIO macro. This macro must be used instead of the IlvMapsDefineBasicProjectionIO macro for projections that have to save additional parameters.
In this step, the initialization statement of the macro is set to the static private function Mercator::InitClass(), which will initialize the error code. It is possible to call a static private function of the Mercator class at this place because the IlvMapsDefineProjectionIO macro generates the initialization statement in a scope that has been declared as friend of the Mercator class by the IlvMapsDeclareProjectionIO macro.
IlvMapsDefineProjectionIO(Mercator,
IlvProjection,
"My Mercator Implementation",
new Mercator(),
Mercator::InitClass());
Initializing the Error Code
The new error code is allocated by the InitClass method, which is automatically called during the static initialization phase.
void
Mercator::InitClass()
{
_polarZoneError =
IlvMaps::CreateError("&MercatorPolarZoneError");
}
Using the New Parameter and the New Error Code
The latitude of the true scale and the new error code are used in the forward and inverse functions. The following is an example of the way they are used in the projection functions.
IlvMapsError
Mercator::sForward(IlvCoordinate& ll) const
{
if (fabs(fabs(ll.y()) - IlvMaps::Pi() / 2.) <= 1e-10)
// Returning the specific error code.
return PolarZoneError();
 
IlvDouble k = cos(_latitudeOfTrueScale);
ll.setY(k * log(tan(IlvMaps::Pi() / 4. + 0.5 * ll.y())));
ll.setX(k * ll.x());
return IlvMaps::NoError();
}
Writing the Input/Output Functions for the New Parameter
To provide complete IO support for the new parameter, you must implement a write method to save this additional parameter. This write method must call the write method of its superclass before writing any data.
void Mercator::write(IlvOutputFile &file) const
{
IlvProjection::write(file);
file.getStream() << _latitudeOfTrueScale << IlvSpc();
}
You must also implement a read constructor. This read constructor calls the read constructor of its superclass and then reads the latitude of the true scale.
Mercator::Mercator(IlvInputFile& file)
:IlvProjection(file)
{
file.getStream() >> _latitudeOfTrueScale;
}
Finally, the copy constructor must also be updated to implement the copy of this new parameter.
Mercator::Mercator(const Mercator& source)
:IlvProjection(source),
_latitudeOfTrueScale(source._latitudeOfTrueScale)
{
}
Published date: 05/24/2022
Last modified date: 02/24/2022