Last active
November 6, 2015 11:26
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.