You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@sis.apache.org by Thierry Danard <th...@int.com> on 2019/10/22 23:10:33 UTC

Order of axis attributes in WKT string changes results

Hello,

I noticed a surprising behavior, I do not know whether this is a bug, or
whether it's expected: I found that when the order of the AXIS attributes
is changed, the conversion returns different results

I have the unit test below performing a sample conversion, and getting the
right result
(longitude=-99.20531579390813
latitude=29.225237498621965)

      private DefaultGeographicBoundingBox
getExtentFromAreaOfUse(CoordinateReferenceSystem fromCrs,
CoordinateReferenceSystem toCrs,
            double minX, double minY, double maxX, double maxY) throws
Exception {
        CoordinateOperation operation = CRS.findOperation(fromCrs, toCrs,
null);
        MathTransform mt = operation.getMathTransform();
        DirectPosition topLeftPosition = new DirectPosition2D(maxY, minX);
        DirectPosition topRightPosition = new DirectPosition2D(maxY, maxX);
        DirectPosition bottomRightPosition = new DirectPosition2D(minY,
maxX);
        DirectPosition bottomLeftPosition = new DirectPosition2D(minY,
minX);
        DirectPosition topLeftLatLongPosition =
mt.transform(topLeftPosition, topLeftPosition);
        DirectPosition topRightLatLongPosition =
mt.transform(topRightPosition, topRightPosition);
        DirectPosition bottomLeftLatLongPosition =
mt.transform(bottomLeftPosition, bottomLeftPosition);
        DirectPosition bottomRightLatLongPosition =
mt.transform(bottomRightPosition, bottomRightPosition);
        double westBoundLongitude =
Math.min(topLeftLatLongPosition.getCoordinate()[1],
bottomLeftLatLongPosition.getCoordinate()[1]);
        double eastBoundLongitude =
Math.max(topLeftLatLongPosition.getCoordinate()[1],
bottomLeftLatLongPosition.getCoordinate()[1]);
        double southBoundLatitude =
Math.min(topRightLatLongPosition.getCoordinate()[0],
bottomRightLatLongPosition.getCoordinate()[0]);
        double northBoundLatitude =
Math.max(topRightLatLongPosition.getCoordinate()[0],
bottomRightLatLongPosition.getCoordinate()[0]);
        return new DefaultGeographicBoundingBox(westBoundLongitude,
eastBoundLongitude, southBoundLatitude, northBoundLatitude);
    }

    @Test
    public void testWKTGitHub() throws FactoryException, Exception {
        String wkt = "PROJCS[\"NAD27 / Texas South Central\","
                + "GEOGCS[\"NAD27\","
                + "DATUM[\"North American Datum 1927\","
                + "SPHEROID[\"Clarke 1866\", 6378206.4, 294.97869821]],"
                + "UNIT[\"degree\", 0.0174532925199433],"
                + "AXIS[\"Lat\", NORTH],"
                + "AXIS[\"Long\", EAST]],"
                + "PROJECTION[\"Lambert_Conformal_Conic_2SP\"],"
                + "PARAMETER[\"latitude_of_origin\", 27.833333333333332],"
                + "PARAMETER[\"central_meridian\", -99.0],"
                + "PARAMETER[\"standard_parallel_1\", 28.383333333333333],"
                + "PARAMETER[\"standard_parallel_2\", 30.28333333333333],"
                + "PARAMETER[\"false_easting\", 2000000.0],"
                + "PARAMETER[\"false_northing\", 0.0],"
                + "UNIT[\"US survey foot\", 0.30480060960121924],"
                + "AXIS[\"X\", EAST],"
                + "AXIS[\"Y\", NORTH]]";
        CoordinateReferenceSystem fromCrs = CRS.fromWKT(wkt);
        CoordinateReferenceSystem toCrs = CRS.forCode("EPSG:4326");
        double x = 1934615.424;
        double y = 506052.281;
        CoordinateOperation operation = CRS.findOperation(fromCrs, toCrs,
getExtentFromAreaOfUse(fromCrs, toCrs, x, y, x, y));
        DirectPosition position = new DirectPosition2D(x, y);
        double[] coordinate =
operation.getMathTransform().transform(position, position).getCoordinate();
        System.out.println("longitude=" + coordinate[1]);
        System.out.println("latitude=" + coordinate[0]);
    }

If I change the WKT from

                  + "AXIS[\"X\", EAST],"
                + "AXIS[\"Y\", NORTH]]";

to

                 + "AXIS[\"Y\",NORTH],"
                + "AXIS[\"X\",EAST]]";

I get a different, incorrect result:
(longitude=-103.86819946668828
latitude=33.06508313602342)

Is this an expected behavior (the WKT string does not meet the specs?), or
is it a defect ?

-Thierry

Re: Order of axis attributes in WKT string changes results

Posted by Martin Desruisseaux <ma...@geomatys.com>.
Hello Thierry

Le 24/10/2019 à 23:40, Thierry Danard a écrit :

> After 
> using AbstractCRS.castOrCopy(...).forConvention(AxesConvention.DISPLAY_ORIENTED), 
> I was able to get consistent results
>
Thanks for letting us know. Alternatively, it is also possible to use 
WKTFormat [1] which provides some control on the parsing. In particular, 
the following line:

    format.setConvention(Convention.WKT1_IGNORE_AXES);

instructs the parser to ignore the AXIS declarations in WKT, in which 
case the default is EAST, NORTH axis directions in degrees or metres. 
This setting also has another effect on unit of measurements which may 
also affect the results when using some old Coordinate Reference System 
with grads angular units instead than degrees. The javadoc [2] gives 
some more details. Those different conventions are provided for 
compatibility with different software who had different interpretations 
of WKT 1 specification.

     Martin

[1] http://sis.apache.org/apidocs/org/apache/sis/io/wkt/WKTFormat.html
[2] http://sis.apache.org/apidocs/org/apache/sis/io/wkt/Convention.html#WKT1_IGNORE_AXES


Re: Order of axis attributes in WKT string changes results

Posted by Thierry Danard <th...@int.com>.
Thank you

After
using AbstractCRS.castOrCopy(...).forConvention(AxesConvention.DISPLAY_ORIENTED),
I was able to get consistent results

Here is the updated unit test

    @Test
    public void testWKT throws FactoryException, Exception {
        String wkt = "PROJCS[\"NAD27 / Texas South Central\","
                + "GEOGCS[\"NAD27\","
                + "DATUM[\"North American Datum 1927\","
                + "SPHEROID[\"Clarke 1866\", 6378206.4, 294.97869821]],"
                + "UNIT[\"degree\",0.0174532925199433],"
                + "AXIS[\"Lat\",NORTH],"
                + "AXIS[\"Long\",EAST]],"
                + "PROJECTION[\"Lambert_Conformal_Conic_2SP\"],"
                + "PARAMETER[\"latitude_of_origin\",27.83333333333333],"
                + "PARAMETER[\"central_meridian\",-99.0],"
                + "PARAMETER[\"standard_parallel_1\",28.383333333333],"
                + "PARAMETER[\"standard_parallel_2\",30.283333333333],"
                + "PARAMETER[\"false_easting\",2000000],"
                + "PARAMETER[\"false_northing\",0],"
                + "UNIT[\"US survey foot\",0.304800609601219],"
                + "AXIS[\"Y\",NORTH],"
                + "AXIS[\"X\",EAST]]";
        CoordinateReferenceSystem fromCrs = CRS.fromWKT(wkt);
        CoordinateReferenceSystem toCrs = CRS.forCode("EPSG:4326");
        CoordinateReferenceSystem displayOrientedFromCrs =
AbstractCRS.castOrCopy(fromCrs).forConvention(AxesConvention.DISPLAY_ORIENTED);
        CoordinateReferenceSystem displayOrientedToCrs =
AbstractCRS.castOrCopy(toCrs).forConvention(AxesConvention.DISPLAY_ORIENTED);
        double x = 1934615.424;
        double y = 506052.281;
        CoordinateOperation operation =
CRS.findOperation(displayOrientedFromCrs, displayOrientedToCrs,
ExtentUtil.getExtentFromAreaOfUse(displayOrientedFromCrs, x, y, x, y));
        DirectPosition position = new DirectPosition2D(x, y);
        double[] coordinate =
operation.getMathTransform().transform(position, position).getCoordinate();
        Assert.assertEquals(coordinate[0], -99.205, 0.001);
        Assert.assertEquals(coordinate[1], 29.225, 0.001);
    }

    public static DefaultGeographicBoundingBox
getExtentFromAreaOfUse(CoordinateReferenceSystem displayOrientedFromCrs,
            double minX, double minY, double maxX, double maxY) throws
Exception {
        CoordinateReferenceSystem toCrs = CRS.forCode("EPSG:4326");
        AbstractCRS displayOrientedToCrs =
AbstractCRS.castOrCopy(toCrs).forConvention(AxesConvention.DISPLAY_ORIENTED);
// longitude should be first
        CoordinateOperation operation =
CRS.findOperation(displayOrientedFromCrs, displayOrientedToCrs, null);
        MathTransform mt = operation.getMathTransform();
        DirectPosition topLeftPosition = new DirectPosition2D(minX, maxY);
        DirectPosition topRightPosition = new DirectPosition2D(maxX, maxY);
        DirectPosition bottomRightPosition = new DirectPosition2D(maxX,
minY);
        DirectPosition bottomLeftPosition = new DirectPosition2D(minX,
minY);
        DirectPosition topLeftLatLongPosition =
mt.transform(topLeftPosition, topLeftPosition);
        DirectPosition topRightLatLongPosition =
mt.transform(topRightPosition, topRightPosition);
        DirectPosition bottomLeftLatLongPosition =
mt.transform(bottomLeftPosition, bottomLeftPosition);
        DirectPosition bottomRightLatLongPosition =
mt.transform(bottomRightPosition, bottomRightPosition);
        double westBoundLongitude =
Math.min(topLeftLatLongPosition.getCoordinate()[0],
bottomLeftLatLongPosition.getCoordinate()[0]);
        double eastBoundLongitude =
Math.max(topLeftLatLongPosition.getCoordinate()[0],
bottomLeftLatLongPosition.getCoordinate()[0]);
        double southBoundLatitude =
Math.min(topRightLatLongPosition.getCoordinate()[1],
bottomRightLatLongPosition.getCoordinate()[1]);
        double northBoundLatitude =
Math.max(topRightLatLongPosition.getCoordinate()[1],
bottomRightLatLongPosition.getCoordinate()[1]);
        return new DefaultGeographicBoundingBox(westBoundLongitude,
eastBoundLongitude, southBoundLatitude, northBoundLatitude);
    }



On Wed, Oct 23, 2019 at 3:02 AM Martin Desruisseaux <
martin.desruisseaux@geomatys.com> wrote:

> Hello Thierry
>
> Le 23/10/2019 à 01:10, Thierry Danard a écrit :
>
> I noticed a surprising behavior, I do not know whether this is a bug, or
> whether it's expected: I found that when the order of the AXIS attributes
> is changed, the conversion returns different results (...snip...) If I
> change the WKT from "AXIS["X",EAST], AXIS["Y",NORTH]]" to "AXIS["Y",NORTH],
> AXIS["X",EAST]]" I get a different, incorrect result.
>
> This is expected. The AXIS attributes that are swapped in the test are
> the axes of the source CRS ("fromCRS"). Those axes define the expected
> order of coordinates given to the "transform" methods. If the AXIS
> attributes of the source CRS are swapped, then the input coordinate values
> need to be swapped to. In the following part of the test:
>
> double y = 1934615.424;
> double x = 506052.281;
>
> Just rename "y" as "x" and "x" as "y", and we are back on the expected
> results.
>
>     Regards,
>
>         Martin
>
>
>

Re: Order of axis attributes in WKT string changes results

Posted by Martin Desruisseaux <ma...@geomatys.com>.
Le 23/10/2019 à 10:02, Martin Desruisseaux a écrit :

> Just rename "y" as "x" and "x" as "y", and we are back on the expected 
> results.
>
Just to clarify, swapping the values in the call to "transform" would be 
conceptually more appropriate than renaming those variables; the 
renaming was just a trick for going quickly.

As a side note, I noticed that the getExtentFromAreaOfUse method 
transforms a rectangle. Just for information, SIS provides also an 
Envelopes class with static methods doing this task, taking in account 
the curvature caused by the projection:

http://sis.apache.org/apidocs/org/apache/sis/geometry/Envelopes.html

     Martin



Re: Order of axis attributes in WKT string changes results

Posted by Martin Desruisseaux <ma...@geomatys.com>.
Hello Thierry

Le 23/10/2019 à 01:10, Thierry Danard a écrit :

> I noticed a surprising behavior, I do not know whether this is a bug, 
> or whether it's expected: I found that when the order of the AXIS 
> attributes is changed, the conversion returns different results 
> (...snip...) If I change the WKT from "AXIS["X",EAST], 
> AXIS["Y",NORTH]]" to "AXIS["Y",NORTH], AXIS["X",EAST]]" I get a 
> different, incorrect result.

This is expected. The AXIS attributes that are swapped in the test are 
the axes of the source CRS ("fromCRS"). Those axes define the expected 
order of coordinates given to the "transform" methods. If the AXIS 
attributes of the source CRS are swapped, then the input coordinate 
values need to be swapped to. In the following part of the test:

    double y = 1934615.424;
    double x = 506052.281;

Just rename "y" as "x" and "x" as "y", and we are back on the expected 
results.

     Regards,

         Martin