[Java-collab] CRS axis issues (Was: Introduction and a few
suggestions on recent topics)
Martin Desruisseaux
martin.desruisseaux at geomatys.fr
Thu Aug 20 19:38:57 EDT 2009
Hello all
I have read the discussion about Point.getX() / Point.getY() methods. In case it
may be of interest to some, below is an approach which I think adresses the
concerns expressed in the recent emails in a way which is both type safe and
efficient.
The discussion below uses GeoAPI/GeoTools/Geotoolkit terminology.
1) I agree with Markus that getX() and getY() methods in a general-purpose
DirectPosition interface is a risk of confusion. GeoAPI defines only
getOrdinate(int). There is not even get0() or get1() methods. Such
methods however could be defined in sub-interfaces (see point below).
2) It is perfectly legitimate from an object-programming point of view
to create sub-interfaces of DirectPosition with additional constraint.
GeoTools/Geotk for example defines a DirectPosition2D which impose the
new constraint that the CRS must be two-dimensional. The get0() and
get1() methods mentioned by Markus could be in this kind of interface.
3) We could imagine a new sub-interfaces, said DirectPositionXYZ, which
impose the new constraint that the CRS must have its first axis
orientated toward east, the second axis orientated toward north, and
the third axis (if any) orientated toward up. Attempts to assign an
other kind of CRS to a DirectPositionXYZ throws IllegalArgumentException
at construction time. This DirectPositionXYZ interface would have the
getX(), getY(), getZ() methods.
4) The most efficient way to handle the axis order issue in a
GeoTools/Geotk-like implementation is to impose the desired order right
at the CRS creation time. Rather than having DirectPosition accepting
arbitrary axis order and have its getX()/getY() methods perform analysis
on the CRS axes, it is better to let the referencing module provides the
MathTransform from the arbitrary CRS to the "XYZ-ordered" CRS.
Advantages:
* This avoid duplication of code since the referencing module
already performs analysis of axes when infering MathTransform.
* A MathTransform performing axes swapping is an AffineTransform.
AffineTransforms can be efficiently concatenated with other kind
of conversions like unit conversions or "scaling", "false easting",
"false northing" after a map projection. Consequently in many cases
the cost of performing axis swapping is absolutly null, because
we just updated the coefficients of an affine transform that
would have been applied anyway.
5) The MathTransform interface defines the following method:
DirectPosition transform(DirectPosition source, DirectPosition target);
where target is an optionnaly pre-allocated object (this is exactly
the same approach than java.awt.geom.AffineTransform, but lets forget
that for now). For now lets assume that "target" is null, which means
than MathTransform will return a newly allocated object. In the
particular case where the target CRS is one having XYZ axis order,
MathTransform could return a DirectPositionXYZ object.
Advantage:
* User can know if he is allowed to use the getX(), getY() methods
by performing an "if (p instanceof DirectPositionXYZ)" check.
* The check proposed by Mathias (where getX() throw an exception
if the point doesn't have a legal CRS) is replaced by a cast to
DirectPositionXYZ, which throw a ClassCastException if we are
not allowed to. The advantage is that this check is performed
only once for the DirectPosition as a whole, instead than for
each individual ordinate.
In summary this approach is:
* Type safe, since the getX() / getY() methods are defined only
in an sub-interface where the meaning of those methods is
garanteed to be the expected one. This allow API who want
a XYZ position to enforce this restriction in their method
signature.
* Highly performant, since 1) in a majority of cases the axis
swapping has been concatenated in an affine transform
which already existed anyway, and 2) the check for validity
of getX() / getY() method call is performed only once for
the DirectPosition as a whole (and is not performed any more
once we have casted to DirectPositionXYZ), instead than every
time a get[X|Y] method is invoked.
Martin
More information about the Java-collab
mailing list