[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