Skip to content

Instantly share code, notes, and snippets.

@ianturton
Last active November 6, 2015 11:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ianturton/d163277cf5c7a052e6f6 to your computer and use it in GitHub Desktop.
Save ianturton/d163277cf5c7a052e6f6 to your computer and use it in GitHub Desktop.
Reorder polygons so you can import them into programs which don't understand Exterior + Interior rings.
package spike;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.geotools.data.DataUtilities;
import org.geotools.data.shapefile.shp.JTSUtilities;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.geojson.feature.FeatureJSON;
import org.opengis.feature.simple.SimpleFeature;
import com.vividsolutions.jts.algorithm.RobustCGAlgorithms;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
public class WindingOrder {
public static void main(String[] args) throws IOException {
FeatureJSON fjson = new FeatureJSON();
FileReader reader = new FileReader(new File("/home/ian/code/geotools-cookbook/geotools-cookbook/modules/spike/geojson.json"));
SimpleFeatureCollection collection = (SimpleFeatureCollection) fjson.readFeatureCollection(reader);
WindingOrder winder = new WindingOrder();
List<SimpleFeature> features = winder.windFeatures(collection);
for(SimpleFeature f:features) {
System.out.println(f.getDefaultGeometry());
}
}
/**
* Wind polygons clockwise/anti clockwise for programs that require that sort
* of thing
*/
static GeometryFactory GEOMFAC = new GeometryFactory();
private boolean ccw = false;
public boolean isCcw() {
return ccw;
}
public void setCcw(boolean ccw) {
this.ccw = ccw;
}
public List<SimpleFeature> windFeatures(FeatureCollection<?, SimpleFeature> features) {
ArrayList<SimpleFeature> ret = new ArrayList<>();
FeatureIterator<SimpleFeature> it = features.features();
try {
while (it.hasNext()) {
SimpleFeature f = (SimpleFeature) it.next();
Geometry geom = (Geometry) f.getDefaultGeometry();
System.out.println(geom);
if (geom instanceof Polygon) {
f.setDefaultGeometry(fixPolygon((Polygon) geom));
} else if (geom instanceof MultiPolygon) {
MultiPolygon multi = (MultiPolygon) geom;
int numGeometries = multi.getNumGeometries();
Polygon[] polys = new Polygon[numGeometries];
for (int i = 0; i < numGeometries; i++) {
polys[i] = fixPolygon((Polygon) multi.getGeometryN(i));
}
f.setDefaultGeometry(GEOMFAC.createMultiPolygon(polys));
}
ret.add(f);
}
} finally {
it.close();
}
return ret;
}
/**
* return counterclockwise wound polygons
*
* @param geom
* the polygon
* @return a new counterclockwise polygon
*/
private Polygon fixPolygon(Polygon geom) {
return fixPolygon(geom, !ccw);
}
/**
* Reverse exterior and holes of polygon if they wind in the wrong direction
*
* @param geom
* - a polygon
* @param cw
* - true if the returned polygons should wind Clockwise
* @return a new polygon
*/
private Polygon fixPolygon(Polygon geom, boolean cw) {
LineString ring = geom.getExteriorRing();
LinearRing extRing;
if (RobustCGAlgorithms.isCCW(ring.getCoordinates()) == cw) {
extRing = JTSUtilities.reverseRing((LinearRing) ring);
} else {
extRing = (LinearRing) ring;
}
Polygon ret;
int numInteriorRing = geom.getNumInteriorRing();
if (numInteriorRing > 0) {
LinearRing[] holes = new LinearRing[numInteriorRing];
for (int i = 0; i < numInteriorRing; i++) {
LineString inner = geom.getInteriorRingN(i);
if (RobustCGAlgorithms.isCCW(inner.getCoordinates()) != cw) {
holes[i] = JTSUtilities.reverseRing((LinearRing) inner);
} else {
holes[i] = (LinearRing) inner;
}
}
ret = GEOMFAC.createPolygon(extRing, holes);
} else {
ret = GEOMFAC.createPolygon(extRing);
}
return ret;
}
}
@ianturton
Copy link
Author

This code is an answer to a gis.stackexchange question where the asker needed to make sure of the orientation of polygons before importing them into SQLServer from GeoJSON. The GeoTools library makes it easy to apply this correction to any input type and write them out to any supported format.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment