package bs.bs2d.geom;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.PrecisionModel;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author Djen
*/
public class JTSUtils {
public static PrecisionModel FLOAT_PRECISION_MODEL = new PrecisionModel(PrecisionModel.FLOATING_SINGLE);
private static GeometryFactory GEOMETRY_FACTORY;
/**
* Returns a shared instance of GeometryFactoy with a float precision model.
* @return a float precision GeometryFactory
*/
public static GeometryFactory getFactory(){
if(GEOMETRY_FACTORY == null)
GEOMETRY_FACTORY = new GeometryFactory(FLOAT_PRECISION_MODEL);
return GEOMETRY_FACTORY;
}
/**
* Creates a MultiPolygon from the given shape. All subpaths of shape are
* closed to form Polygon outlines. An even-odd winding rule is assumed
* regardless of the shape's winding rule property.
* @param shape the shape to convert to a MultiPolygon
* @return a MultiPolygon representation of shape
*/
public static MultiPolygon createMultiPolygon(Shape shape){
PathIterator it = shape.getPathIterator(null);
ArrayList<Polygon> rings = new ArrayList<>();
LinkedList<Coordinate> loop = new LinkedList<>();
float[] coords = new float[6];
while(!it.isDone()){
int seg = it.currentSegment(coords);
if(seg == PathIterator.SEG_MOVETO){
// add polyline, start new loop
if(!loop.isEmpty()){
// close loop if necessary
if(!loop.getFirst().equals(loop.getLast()))
loop.add(new Coordinate(loop.get(0)));
Coordinate[] c = loop.toArray(new Coordinate[loop.size()]);
rings.add(getFactory().createPolygon(c));
loop.clear();
}
loop.add(new Coordinate(coords[0], coords[1]));
} else if(seg == PathIterator.SEG_LINETO){
loop.add(new Coordinate(coords[0], coords[1]));
} else if(seg == PathIterator.SEG_QUADTO){
loop.add(new Coordinate(coords[2], coords[3]));
} else if(seg == PathIterator.SEG_CUBICTO){
loop.add(new Coordinate(coords[4], coords[5]));
}
it.next();
}
// add last loop
if(!loop.isEmpty()){
if(!loop.getFirst().equals(loop.getLast()))
loop.add(new Coordinate(loop.get(0)));
Coordinate[] c = loop.toArray(new Coordinate[loop.size()]);
rings.add(getFactory().createPolygon(c));
}
// Geometry geom = rings.get(0);
Geometry geom = getFactory().createMultiPolygon(new Polygon[0]);
for (int i = 0; i < rings.size(); i++) {
Polygon ring = rings.get(i);
geom = geom.symDifference(ring);
}
if(!geom.isSimple()){
System.out.println("!unsimple geometry!");
}
if(geom instanceof MultiPolygon){
return (MultiPolygon) geom;
} else { // Polygon
Polygon[] p = new Polygon[]{(Polygon)geom};
return getFactory().createMultiPolygon(p);
}
}
/**
* Convers the given Geometry to a Java2D shape.
* @param geom the geometry to convert
* @return a Shape representation of geom
*/
public static Shape toShape(Geometry geom){
GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
int count = geom.getNumGeometries();
for (int i = 0; i < count; i++) {
Geometry g = geom.getGeometryN(i);
if(g instanceof GeometryCollection){
p.append(toShape(geom), false);
} else if (g instanceof Polygon){
p.append(toShape((Polygon)g), false);
} else if (g instanceof LineString){
p.append(toShape((LineString)g), false);
} else {
throw new IllegalArgumentException("Unknown Geometry type!");
}
}
return p;
}
private static Shape toShape(Polygon poly){
GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
if(poly.isEmpty())
return p;
p.append(toShape(poly.getExteriorRing()), false);
for (int i = 0; i < poly.getNumInteriorRing(); i++) {
p.append(toShape(poly.getInteriorRingN(i)), false);
}
return p;
}
private static Shape toShape(LineString lines){
GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
if(lines.isEmpty())
return p;
Coordinate[] coords = lines.getCoordinates();
Coordinate c = coords[0];
p.moveTo(c.x, c.y);
for (int j = 1; j < coords.length; j++) {
c = coords[j];
p.lineTo(c.x, c.y);
}
if(coords[0].equals(c))
p.closePath();
return p;
}
/**
* Generates an array of LineStrings consisting of all single line strings
* and polygon bounds in the given geometry.
* @param geom the Geometry to split
* @return an array of singular line strings
*/
public static LineString[] getLineStrings(Geometry geom) {
ArrayList<LineString> lines = new ArrayList<>();
int num = geom.getNumGeometries();
for (int i = 0; i < num; i++) {
Geometry g = geom.getGeometryN(i);
if(g instanceof LineString){
lines.add((LineString) g);
} else if (g instanceof Polygon){
lines.addAll(getLineStrings((Polygon) g));
} else if (g instanceof GeometryCollection) {
lines.addAll(Arrays.asList(getLineStrings(geom)));
} else if (g instanceof Point) {
// ignore
} else {
throw new UnsupportedOperationException("Unsupported geometry type:" + g.getGeometryType());
}
}
return lines.toArray(new LineString[lines.size()]);
}
private static List<LineString> getLineStrings(Polygon poly){
ArrayList<LineString> lines = new ArrayList<>();
lines.add(poly.getExteriorRing());
for (int i = 0; i < poly.getNumInteriorRing(); i++) {
lines.add(poly.getInteriorRingN(i));
}
return lines;
}
/**
* Generates an array of singular Polygons from a Multipolygon
* @param multi the Multipolygon to split
* @return an array of singular Polygons
*/
public static Polygon[] getPolygons(MultiPolygon multi) {
Polygon[] polys = new Polygon[multi.getNumGeometries()];
for (int i = 0; i < polys.length; i++) {
polys[i] = (Polygon) multi.getGeometryN(i);
}
return polys;
}
/**
* Returns a MultiPolygon containig the given Geometry, assuming the
* argument is either a Polygon or Multipolygon. This method can be used to
* quickly cast the result of a boolean operation on a MultiPolygon back to
* a MultiPolygon instance.
* @param geom Geometry containing either a Polygon or MultiPolygon
* @return a Multipolygon containing all geometry from geom
*/
public static MultiPolygon toMultiPolygon(Geometry geom) {
if (geom instanceof MultiPolygon) {
return (MultiPolygon) geom;
} else {
Polygon[] p = new Polygon[]{(Polygon) geom};
return getFactory().createMultiPolygon(p);
}
}
public static Point createPoint2D(double x, double y){
return GEOMETRY_FACTORY.createPoint(new Coordinate(x, y));
}
}