2

I have a geometry object of type (com.vividsolutions.jts.geom.Geometry). it is currently in latitude, longitude form and I'd like to flip the coordinates so that its longitude latitude so that I can have it in GeoJSON format for mongodb.

My constraints that I am seeing are: a) the input that I would like to flip coordinates for is a Geometry object. b) The Geometry object will be either a Polygon type or Multipolygon. c) I would like to flip the coordinates before the type cast to Polygon/multipolygon

I have tried geo.reverse() but it does not work.

As well, I have tried using: CRSAuthorityFactory factory = CRS.getAuthorityFactory(true); CoordinateReferenceSystem crs = factory.createCoordinateReferenceSystem("EPSG:4326");

And another option and I did not see it work.

Thanks!

aruuuuu
  • 1,605
  • 2
  • 22
  • 32

5 Answers5

11

You can use a CoordinateFilter to invert all x and y values in a given geometry.

private static class InvertCoordinateFilter implements CoordinateFilter {
    public void filter(Coordinate coord) {
        double oldX = coord.x;
        coord.x = coord.y;
        coord.y = oldX;
    }
}

Then apply the filter thusly:

// Invert Coordinates since GeoJSON likes them that way
myGeometryObj.apply(new InvertCoordinateFilter());
spt5007
  • 178
  • 1
  • 7
  • 3
    Excellent and elegant solution! This should be the accepted answer. – polaretto Jul 27 '15 at 10:42
  • Great solution, although as the [javadoc](https://locationtech.github.io/jts/javadoc/org/locationtech/jts/geom/CoordinateFilter.html) say, there is no guarantee that coordinates will be modified: the `CoordinateFilter` is usualyl meant for other purposes. They suggest to use the [**`CoordinateSequenceFilter`**](https://locationtech.github.io/jts/javadoc/org/locationtech/jts/geom/CoordinateSequenceFilter.html)for changes on coordinates. – Campa Nov 30 '18 at 08:36
3

The solution is that the Geometry.getCoordinates() gives a Coordinate[] array which is live. Therefore, I could use the following:

Where myGeometryObject is a Geometry object:

Coordinate[] original = myGeometryobject.getCoordinates();
for(int i = 0; i < original.length; i++){
    Double swapValue = original[i].x;
    original[i].x = original[i].y;
    original[i].y = swapValue;
}

The changes to the Coordinate objects will affect the underlying Geometry permanently.

pokkanome
  • 347
  • 1
  • 9
aruuuuu
  • 1,605
  • 2
  • 22
  • 32
2

As important addendum to the existing answers: You should always call yourGeometry.geometryChanged() after you changed a Geometry's geometry!

See http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/geom/Geometry.html#getCoordinates%28%29 and http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/geom/Geometry.html#geometryChanged%28%29

bugmenot123
  • 1,069
  • 1
  • 18
  • 33
  • 1
    Good point, but by using the [_CoordinateSequenceFilter_](https://locationtech.github.io/jts/javadoc/org/locationtech/jts/geom/CoordinateSequenceFilter.html) the `geometryChanged()` methods gets automatically called as soon as you declare that you have changed the coordinates. – Campa Nov 30 '18 at 09:10
1

One potential solution to this is extending the class to provide an additional function that either outputs the data you need in some convenient way:

public Coordinate[] getReversedCoordinates(){

  Coordinate[] original = this.getCoordinates();
  Coordinate[] ret = new Coordinate[original.length];

  for(int i =0; i<original.length; i++){
      ret[i] = new Coordinate( original[i].x , original[i].y );
  }

  return ret;

}

Alternately you could alter the interpretation of the data. It's a little harder for me to give you a code snippet for that as I'm not sure how you're using the information specifically.

EDIT:

Once you have the reversed coordinates,you can create a duplicate Geometry of type linear ring. A means of doing this is to use your factory to use your geometry factory:

GeometryFactory gf = //However this was instantiated;
Coordinate[] reversedCoordinates = getReversedCoordinates();
gf.createLinearRing(reversedCoordinates);

Happy coding and leave a comment if you have any questions!

Matt
  • 5,404
  • 3
  • 27
  • 39
  • hi! I tried the above code and have a further question, do you think this will work for the Polygon and Multipolygon shapes? It seems that GeometryFactory gf = new GeometryFactory() and then gf.toGeometry(...) does not have an input for the Coordinate[] value returned by the proposed method. – aruuuuu Dec 23 '14 at 16:16
  • Can you clarify how it is that your program will be interacting with GeoJSON? Does it just pass a polygon object? If so, you may want to just create a duplicate polygon with the coordinates reversed. – Matt Dec 23 '14 at 16:25
  • Yes a duplicate polygon with coordinates reversed would be great, but I am unable to create this duplicate polygon. I am starting out with a WKT, parsing it as a Geometry and then converting to geojson successfully, except the coordinates are not in the correct order, geojson needs to be long,lat for mongodb – aruuuuu Dec 23 '14 at 16:28
  • I edited my answer with what I think is a potential solution. Let me know how that works – Matt Dec 23 '14 at 16:49
  • sure thing! Let us know if it worked and if it did, can you hi the green check next to my answer? ;) – Matt Dec 23 '14 at 16:57
1

As the JTS javadocs suggest, it is best to use a CoordinateSequenceFilter to change the coordinates of a geometry in place.

/**
 * Swaps the XY coordinates of a geometry.
 */
public void swapCoordinates(Geometry g) {
    g.apply(new CoordinateSequenceFilter() {

        @Override
        public void filter(CoordinateSequence seq, int i) {
            double oldX = seq.getCoordinate(i).x;
            seq.getCoordinate(i).x = seq.getCoordinate(i).y;
            seq.getCoordinate(i).y = oldX;
        }

        @Override
        public boolean isGeometryChanged() {
            return true;
        }

        @Override
        public boolean isDone() {
            return false;
        }
    });
}

NOTE: there are nasty cases where Coordinate object you get access to is a copy of what is internally stored (eg. see PackedCoordinateSequence). In that case, as suggested in the javadocs, you must use the provided setters, that is setX() setY().


There are also even nastier cases where there is simply no way to change the coordinates in place, eg. when fetching a geometry from the DB with PostGIS where this Geolatte PackedPositionSequence sequence implementation just won't let you modify the coordinates (afaik).

Campa
  • 4,267
  • 3
  • 37
  • 42
  • Thank to your answer I've managed that that for all geom types I've done transformation by setting new values using seq.setOrdinate(i, 0, _newXValue_); and same for y but using ordinate index 1 – Marko Djurovic Nov 30 '18 at 12:28
  • Thanks @MarkoDjurovic, yes that is a correct way for sure. Still a `PackedPositionSequence` throws `UnsupportedOperationException` on _setOrdinate()_: it's just unmutable I believe. I solved it by just making my own new geometry out of such unmutable coordinates, then work on it. – Campa Nov 30 '18 at 14:17