javageotoolswgs84

geotools reversing lat/lon for WGS when transforming between projections


I'm having issues with Geotools reversing the latitude and longitude, using JTS.transform(). It's been driving me mad now for days.

I have different data sets defined in different projections. The goal is that I can map any coordinate in a given projection WKT to a (lon, lat) in WGS84. Here's my util that does the conversion:

public class TransformUtil {
    private static CoordinateReferenceSystem wgs;
    static {
        wgs = DefaultGeographicCRS.WGS84;
    }

    private static double[] convert(String wkt, double x, double y) throws TransformException {
        CoordinateReferenceSystem sourceCRS = CRS.parseWKT(wkt);
        MathTransform transform = CRS.findMathTransform(sourceCRS, WGS, true);
        Point point = geometryFactory.createPoint( new Coordinate( x, y ) );
        Geometry targetGeometry = JTS.transform( point , transform);
        Coordinate coord = targetGeometry.getCoordinate();
        return new double[] {coord.x, coord.y};
    }
}

and here are my unit tests:

public class TestTransformUtil {
    private double lon = 5;
    private double lat = 50;
    private double margin = 1E-5d;

    @Test
    public void testWgs() {
        String wkt = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]";
        double[] res = TransformUtil.toLonLat(lon, lat, wkt);
        Assert.assertEquals("Longitude", lon, res[0], margin);
        Assert.assertEquals("Latitude", lat, res[1], margin);
    }

    @Test
    public void testLaea() {
        double x= 3962799.45096;
        double y = 2999718.85316;
        String wkt = "PROJCS[\"ETRS89-extended / LAEA Europe\",GEOGCS[\"ETRS89\",DATUM[\"European_Terrestrial_Reference_System_1989\",SPHEROID[\"GRS 1980\",6378137,298.257222101004,AUTHORITY[\"EPSG\",\"7019\"]],AUTHORITY[\"EPSG\",\"6258\"]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4258\"]],PROJECTION[\"Lambert_Azimuthal_Equal_Area\"],PARAMETER[\"latitude_of_center\",52],PARAMETER[\"longitude_of_center\",10],PARAMETER[\"false_easting\",4321000],PARAMETER[\"false_northing\",3210000],UNIT[\"metre\",1],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH]]";
        double[] res = TransformUtil.toLonLat(x, y, wkt);
        Assert.assertEquals("Longitude", lon, res[0], margin);
        Assert.assertEquals("Latitude", lat, res[1], margin);
    }
}

What happens is really confusing. For the WGS84 to WGS84 projection testWgs(), the transform action reverses the coordinates. Coordinate.x becomes the latitude, Coordinate.y is the longitude, thus the assert fails:

java.lang.AssertionError: Longitude expected:<5.0> but was:<50.0>

The testLaea() passes without problems.

I found this tip in the geotools docs: https://docs.geotools.org/stable/userguide/tutorial/geometry/geometrycrs.html#workarounds that says that you should use

private static CoordinateReferenceSystem wgs;
static {
    try {
        CRSAuthorityFactory factory = CRS.getAuthorityFactory(true);
        wgs = factory.createCoordinateReferenceSystem("urn:ogc:def:crs:EPSG:6.6:4326");
    } catch (FactoryException e) {
        e.printStackTrace();
    }
}

When I run the tests now, testWgs() passes, but now the coordinates are reversed for testLaea()!!

The final thing I tried is to use

static {
    System.setProperty("org.geotools.referencing.forceXY", "true");
    //wgs = ...
}

which does nothing at all. Can anybody explain what's happing here, and does anyone know how I can force the outcome of the transform always to have x=longitude and y=latitude? Thanks!


Solution

  • If you look at your WKT definition of WGS84, it clearly states AXIS[\"Latitude\",NORTH], AXIS[\"Longitude\",EAST] so you are specifying that you want Lat,Lon not Lon,Lat.

    If however, you had used DefaultGeographicCRS.WGS84.toWKT() you would have had a string that said AXIS["Geodetic longitude", EAST], AXIS["Geodetic latitude", NORTH]] which would be the order you expected.

    So the only way to be sure is to check the axis order of the input (and output) coordinate reference system before you create the points in your convert method.

      private static double[] convert(String wkt, double x, double y) throws TransformException, FactoryException {
        CoordinateReferenceSystem sourceCRS = CRS.parseWKT(wkt);
        MathTransform transform = CRS.findMathTransform(sourceCRS, WGS, true);
        Point point;
        if (CRS.getAxisOrder(sourceCRS).equals(AxisOrder.EAST_NORTH)) {
          point = geometryFactory.createPoint(new Coordinate(x, y));
        } else {
          point = geometryFactory.createPoint(new Coordinate(y, x));
        }
        Geometry targetGeometry = JTS.transform(point, transform);
        Coordinate coord = targetGeometry.getCoordinate();
        double[] ret;
        if (CRS.getAxisOrder(WGS).equals(AxisOrder.EAST_NORTH)) {
          ret = new double[] { coord.x, coord.y };
        } else {
          ret = new double[] { coord.y, coord.x };
        }
        return ret;
      }