package jeql.std.geom; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.xml.parsers.ParserConfigurationException; import org.xml.sax.SAXException; import jeql.api.function.FunctionClass; import jeql.jts.geom.util.GeometricShapeFactoryExt; import jeql.jts.geom.util.ShapeUtil; import com.vividsolutions.jts.algorithm.MinimumBoundingCircle; import com.vividsolutions.jts.algorithm.MinimumDiameter; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateArrays; import com.vividsolutions.jts.geom.CoordinateFilter; import com.vividsolutions.jts.geom.CoordinateList; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollectionIterator; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.Lineal; import com.vividsolutions.jts.geom.OctagonalEnvelope; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.Polygonal; import com.vividsolutions.jts.geom.Puntal; import com.vividsolutions.jts.geom.TopologyException; import com.vividsolutions.jts.geom.util.AffineTransformation; import com.vividsolutions.jts.geom.util.LinearComponentExtracter; import com.vividsolutions.jts.geom.util.SineStarFactory; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKBReader; import com.vividsolutions.jts.io.WKBWriter; import com.vividsolutions.jts.io.WKTReader; import com.vividsolutions.jts.io.WKTWriter; import com.vividsolutions.jts.io.gml2.GMLReader; import com.vividsolutions.jts.io.gml2.GMLWriter; import com.vividsolutions.jts.operation.distance.DistanceOp; import com.vividsolutions.jts.operation.linemerge.LineMerger; import com.vividsolutions.jts.operation.linemerge.LineSequencer; import com.vividsolutions.jts.simplify.DouglasPeuckerSimplifier; import com.vividsolutions.jts.simplify.TopologyPreservingSimplifier; import com.vividsolutions.jts.util.GeometricShapeFactory; public class GeomFunction implements FunctionClass { public static GeometryFactory geomFactory = new GeometryFactory(); public static double area(Geometry geom) { return geom.getArea(); } public static double length(Geometry geom) { return geom.getLength(); } public static int numPoints(Geometry geom) { return geom.getNumPoints(); } public static int numGeometries(Geometry geom) { return geom.getNumGeometries(); } public static int numInteriorRings(Geometry geom) { int intRingCount = 0; GeometryCollectionIterator it = new GeometryCollectionIterator(geom); while (it.hasNext()) { Geometry g = (Geometry) it.next(); if (g instanceof Polygon) { intRingCount += ((Polygon) g).getNumInteriorRing(); } } return intRingCount; } /** * Gets an interior ring of a Polygon. * The index of Interior rings is 0-based. * * @param geom a Polygon * @param i the index of an interior ring (0-based) * @return the i'th interior ring (LinearRing) */ public static Geometry interiorRing(Geometry geom, int i) { return ((Polygon) geom).getInteriorRingN(i); } /** * Gets the exterior LinearRing of a Polygon * @param geom a Polygon * @return the exterior ring (LinearRing) */ public static Geometry exteriorRing(Geometry geom) { return ((Polygon) geom).getExteriorRing(); } public static int dimension(Geometry geom) { return geom.getDimension(); } public static String geometryType(Geometry geom) { return geom.getGeometryType(); } public static boolean isPolygonal(Geometry geom) { return geom instanceof Polygonal; } public static boolean isLineal(Geometry geom) { return geom instanceof Lineal; } public static boolean isPuntal(Geometry geom) { return geom instanceof Puntal; } public static Geometry envelope(Geometry geom) { return geom.getEnvelope(); } public static Geometry boundary(Geometry geom) { return geom.getBoundary(); } public static Geometry centroid(Geometry geom) { return geom.getCentroid(); } public static Geometry interiorPoint(Geometry geom) { return geom.getInteriorPoint(); } public static Geometry buffer(Geometry geom, double dist) { return geom.buffer(dist); } public static Geometry buffer(Geometry geom, double dist, int quadSegs) { return geom.buffer(dist, quadSegs); } public static Geometry convexHull(Geometry geom) { return geom.convexHull(); } public static Geometry intersection(Geometry g1, Geometry g2) { return g1.intersection(g2); } public static Geometry union(Geometry g1, Geometry g2) { return g1.union(g2); } public static Geometry union(Geometry g1) { return g1.union(); } public static Geometry difference(Geometry g1, Geometry g2) { return g1.difference(g2); } public static Geometry symDifference(Geometry g1, Geometry g2) { try { return g1.symDifference(g2); } catch (TopologyException ex) { System.out.println(g1); } return null; } public static boolean isValid(Geometry g) { return g.isValid(); } public static boolean isEmpty(Geometry g) { return g.isEmpty(); } public static boolean isSimple(Geometry g) { return g.isSimple(); } public static boolean intersects(Geometry g1, Geometry g2) { return g1.intersects(g2); } public static boolean contains(Geometry g1, Geometry g2) { return g1.contains(g2); } public static boolean crosses(Geometry g1, Geometry g2) { return g1.crosses(g2); } public static boolean disjoint(Geometry g1, Geometry g2) { return g1.disjoint(g2); } public static boolean equals(Geometry g1, Geometry g2) { return g1.equals(g2); } public static boolean overlaps(Geometry g1, Geometry g2) { return g1.overlaps(g2); } public static boolean touches(Geometry g1, Geometry g2) { return g1.touches(g2); } public static boolean covers(Geometry g1, Geometry g2) { return g1.covers(g2); } public static boolean coveredBy(Geometry g1, Geometry g2) { return g1.coveredBy(g2); } public static boolean within(Geometry g1, Geometry g2) { return g1.within(g2); } public static boolean relate(Geometry g1, Geometry g2, String relatePattern) { return g1.relate(g2, relatePattern); } public static String relatePattern(Geometry g1, Geometry g2) { return g1.relate(g2).toString(); } /** * Tests whether the geometry envelopes intersect. * * @param g1 * @param g2 * @return true if the envelopes intersect */ public static boolean envelopesIntersect(Geometry g1, Geometry g2) { return g1.getEnvelopeInternal().intersects(g2.getEnvelopeInternal()); } public static double distance(Geometry g1, Geometry g2) { return g1.distance(g2); } public static boolean isWithinDistance(Geometry g1, Geometry g2, double dist) { return GeomUtil.isWithinDistance(g1, g2, dist); // return g1.isWithinDistance(g2, dist); } public static String toString(Geometry g) { return g.toString(); } public static String toString(Geometry geom, int coordsPerLine) { WKTWriter writer = new WKTWriter(); writer.setMaxCoordinatesPerLine(coordsPerLine); return writer.writeFormatted(geom); } public static String toWKT(Geometry g) { return g.toString(); } public static Geometry fromWKT(String wkt) throws ParseException { WKTReader rdr = new WKTReader(); Geometry g = rdr.read(wkt); return g; } public static String toWKB(Geometry g) { WKBWriter writer = new WKBWriter(); return WKBWriter.bytesToHex(writer.write(g)); } public static Geometry fromWKB(String wkbHex) throws ParseException { byte[] wkb = WKBReader.hexToBytes(wkbHex); WKBReader rdr = new WKBReader(); Geometry g = rdr.read(wkb); return g; } public static String toWKB(Geometry g, int dimension) { WKBWriter writer = new WKBWriter(dimension); return WKBWriter.bytesToHex(writer.write(g)); } public static Geometry fromGML2(String gml) throws Exception { String gmlTrim = gml.trim(); if (gmlTrim.length() <= 0) return null; GMLReader rdr = new GMLReader(); return rdr.read(gml, geomFactory); } public static String toGML2(Geometry g) { GMLWriter writer = new GMLWriter(); return writer.write(g); } public static Geometry normalize(Geometry g) { Geometry g2 = (Geometry) g.clone(); g2.normalize(); return g2; } public static double x(Geometry g) { return g.getCoordinate().x; } public static double y(Geometry g) { return g.getCoordinate().y; } public static double z(Geometry g) { return g.getCoordinate().z; } public static Geometry setZ(Geometry g, double z) { if (g == null) return null; Geometry gz = (Geometry) g.clone(); gz.apply(new SetZCoordinateFilter(z)); return gz; } private static class SetZCoordinateFilter implements CoordinateFilter { private double z; public SetZCoordinateFilter(double z) { this.z = z; } public void filter(Coordinate p) { p.z = z; } } public static double maxX(Geometry g) { return g.getEnvelopeInternal().getMaxX(); } public static double minX(Geometry g) { return g.getEnvelopeInternal().getMinX(); } public static double maxY(Geometry g) { return g.getEnvelopeInternal().getMaxY(); } public static double minY(Geometry g) { return g.getEnvelopeInternal().getMinY(); } public static double width(Geometry g) { return g.getEnvelopeInternal().getWidth(); } public static double height(Geometry g) { return g.getEnvelopeInternal().getHeight(); } public static double deltaX(Geometry g) { return g.getEnvelopeInternal().getWidth(); } public static double deltaY(Geometry g) { return g.getEnvelopeInternal().getHeight(); } public static Geometry extractPoint(Geometry geom, int index) { Coordinate[] pts = geom.getCoordinates(); Coordinate pt = pts[index]; return geomFactory.createPoint(pt); } public static Geometry extractComponent(Geometry geom, int index) { return geom.getGeometryN(index); } public static Geometry createPoint(double x, double y) { return geomFactory.createPoint(new Coordinate(x, y)); } public static Geometry createPoint(double x, double y, double z) { return geomFactory.createPoint(new Coordinate(x, y, z)); } public static Geometry createLine(double x0, double y0, double x1, double y1) { return geomFactory.createLineString(new Coordinate[] { new Coordinate(x0, y0), new Coordinate(x1, y1) }); } public static Geometry createLine(Geometry geom) { return geomFactory.createLineString(connectPts(geom, false)); } public static Geometry createLineFromPoints(Point p0, Point p1) { return geomFactory.createLineString( new Coordinate[] { p0.getCoordinate(), p1.getCoordinate() }); } public static Geometry createLineFromPoints(Point p0, Point p1, Point p2) { return geomFactory.createLineString( new Coordinate[] { p0.getCoordinate(), p1.getCoordinate(), p2.getCoordinate() }); } public static Geometry createPolygon(Point p0, Point p1, Point p2) { return geomFactory.createPolygon( geomFactory.createLinearRing( new Coordinate[] { p0.getCoordinate(), p1.getCoordinate(), p2.getCoordinate(), p0.getCoordinate() }), null); } public static Geometry createPolygon(Geometry geom) { return geomFactory.createPolygon(connectPts(geom, true)); } public static Geometry createPolygon(Point p0, Point p1, Point p2, Point p3) { return geomFactory.createPolygon( geomFactory.createLinearRing( new Coordinate[] { p0.getCoordinate(), p1.getCoordinate(), p2.getCoordinate(), p3.getCoordinate(), p0.getCoordinate() }), null); } /* public static Geometry createPolygonFromPoints(Point p0, Point p1, Point p2) { return geomFactory.createPolygon( geomFactory.createLinearRing( new Coordinate[] { p0.getCoordinate(), p1.getCoordinate(), p2.getCoordinate(), p0.getCoordinate() }), null); } public static Geometry createPolygonFromPoints(Point p0, Point p1, Point p2, Point p3) { return geomFactory.createPolygon( geomFactory.createLinearRing( new Coordinate[] { p0.getCoordinate(), p1.getCoordinate(), p2.getCoordinate(), p3.getCoordinate(), p0.getCoordinate() }), null); } */ public static Geometry createBox(double xMin, double yMin, double xMax, double yMax) { return geomFactory.toGeometry(new Envelope(xMin, xMax, yMin, yMax)); } public static Geometry createBoxExtent(double x, double y, double dx, double dy) { return geomFactory.toGeometry(new Envelope(x, x + dx, y, y + dy)); } public static Geometry createBoxOrigin(double x, double y, double dx, double dy) { return geomFactory.toGeometry(new Envelope(x, x + dx, y, y + dy)); } public static Geometry createBoxCenter(double x, double y, double dx, double dy) { return geomFactory.toGeometry(new Envelope(x - dx/2, x + dx/2, y - dy/2, y + dy/2)); } public static Geometry createBoxFromPoints(Point p0, Point p1) { return geomFactory.toGeometry(new Envelope( p0.getCoordinate(), p1.getCoordinate() )); } public static Geometry createCircle(Point p0, double radius, int nSides) { GeometricShapeFactory shape = new GeometricShapeFactory(geomFactory); shape.setCentre(p0.getCoordinate()); shape.setSize(2 * radius); shape.setNumPoints(nSides); return shape.createCircle(); } public static Geometry createCircleArcPolygon(Point p0, double startRad, double sizeRad, double radius, int nSides) { GeometricShapeFactory shape = new GeometricShapeFactory(geomFactory); shape.setCentre(p0.getCoordinate()); shape.setSize(2 * radius); shape.setNumPoints(nSides); return shape.createArcPolygon(startRad, sizeRad); } public static Geometry createEllipticalArcPolygon(Point p0, double startRad, double sizeRad, double width, double height, int nSides) { GeometricShapeFactory shape = new GeometricShapeFactory(geomFactory); shape.setCentre(p0.getCoordinate()); shape.setWidth(width); shape.setHeight(height); shape.setNumPoints(nSides); return shape.createArcPolygon(startRad, sizeRad); } public static Geometry createStar(Point p0, double radius, double armRatio, int nArms) { GeometricShapeFactoryExt shape = new GeometricShapeFactoryExt(geomFactory); shape.setCentre(p0.getCoordinate()); shape.setSize(2 * radius); shape.setNumPoints(nArms * 2); shape.setNumArms(nArms); shape.setArmLengthRatio(armRatio); return shape.createStar(); } public static Geometry createSineStar(Point p0, double radius, double armRatio, int nArms, int nPts) { SineStarFactory shape = new SineStarFactory(geomFactory); shape.setCentre(p0.getCoordinate()); shape.setSize(2 * radius); shape.setNumPoints(nPts); shape.setNumArms(nArms); shape.setArmLengthRatio(armRatio); return shape.createSineStar(); } public static Geometry createText(String s, int pointSize) { return ShapeUtil.fromFont(s, pointSize, geomFactory); } public static Geometry octagonalEnvelope(Geometry g) { if (g == null) return null; OctagonalEnvelope oct = new OctagonalEnvelope(g); return oct.toGeometry(g.getFactory()); } public static Geometry minimumCircle(Geometry g) { if (g == null) return null; MinimumBoundingCircle mbc = new MinimumBoundingCircle(g); return mbc.getCircle(); } public static Geometry minimumRectangle(Geometry g) { if (g == null) return null; MinimumDiameter md = new MinimumDiameter(g); return md.getMinimumRectangle(); } /** * Creates a GeometryCollection from two geometries A and B. * * * @param a * @param b * @return */ // TODO: flatten GeomColls which are input public static Geometry collect(Geometry a, Geometry b) { Geometry[] gs = new Geometry[] { a, b }; return geomFactory.createGeometryCollection(gs); } public static Geometry collect(Geometry a, Geometry b, Geometry c) { Geometry[] gs = new Geometry[] { a, b, c }; return geomFactory.createGeometryCollection(gs); } /** * Converts geometries to equivalent MultiGeometries * * @param geom * @return */ public static Geometry toMulti(Geometry geom) { if (geom instanceof Point) { return geom.getFactory().createMultiPoint(new Point[] { (Point) geom} ); } else if (geom instanceof LineString) { return geom.getFactory().createMultiLineString(new LineString[] { (LineString) geom} ); } else if (geom instanceof Polygon) { return geom.getFactory().createMultiPolygon(new Polygon[] { (Polygon) geom} ); } // assume geom is already a multi (including GeometryCollections) return geom; } //=========== Line Handling ==================== public static Geometry lineMerge(Geometry g) { LineMerger merger = new LineMerger(); merger.add(g); Collection lines = merger.getMergedLineStrings(); return g.getFactory().buildGeometry(lines); } public static Geometry lineSequence(Geometry geom) { LineSequencer sequencer = new LineSequencer(); sequencer.add(geom); return sequencer.getSequencedLineStrings(); } public static Geometry lineConnect(Geometry geom) { CoordinateList pts = new CoordinateList(); pts.add(geom.getCoordinates(), true); return geom.getFactory().createLineString( CoordinateArrays.toCoordinateArray(pts)); } static Coordinate[] connectPts(Geometry geom, boolean close) { CoordinateList pts = new CoordinateList(); pts.add(geom.getCoordinates(), true); if (close) pts.closeRing(); return CoordinateArrays.toCoordinateArray(pts); } public static Geometry lineConnectNoRepeated(Geometry geom) { CoordinateList pts = new CoordinateList(); pts.add(geom.getCoordinates(), false); return geom.getFactory().createLineString( CoordinateArrays.toCoordinateArray(pts)); } //=========== Simplification ==================== public static Geometry simplifyDP(Geometry g, double tolerance) { return DouglasPeuckerSimplifier.simplify(g, tolerance); } public static Geometry simplifyDPSafe(Geometry g, double tolerance) { return TopologyPreservingSimplifier.simplify(g, tolerance); } //=========== Affine transformations ============== // MD - caching may not make much difference here, according to testing public static Geometry translate(Geometry g, double x, double y) { AffineTransformation trans = AffineTransformation.translationInstance(x, y); Geometry newG = (Geometry) g.clone(); newG.apply(trans); return newG; } /** * Rotates the geometry around the centre of its envelope. * * @param g * @param angle * @return */ public static Geometry rotate(Geometry g, double angle) { Coordinate p = g.getEnvelopeInternal().centre(); return rotate(g, angle, p.x, p.y); } /** * Rotates the geometry around the point (x,y) * @param g * @param angle * @param x * @param y * @return */ public static Geometry rotate(Geometry g, double angle, double x, double y) { AffineTransformation trans = AffineTransformation.translationInstance(-x, -y); trans.rotate(angle); trans.translate(x, y); Geometry newG = (Geometry) g.clone(); newG.apply(trans); return newG; } public static Geometry rotateAt(Geometry g, double angle, Geometry ptGeom) { Coordinate pt = ptGeom.getCoordinate(); return rotate(g, angle, pt.x, pt.y); } /** * Reflects the geometry around the line x=y. * * @param g * @param angle * @return */ public static Geometry reflect(Geometry g) { AffineTransformation trans = AffineTransformation.reflectionInstance(1, 1); Geometry newG = (Geometry) g.clone(); newG.apply(trans); return newG; } public static Geometry swapXY(Geometry g) { Geometry newG = (Geometry) g.clone(); newG.apply(new SwapXYFilter()); return newG; } private static class SwapXYFilter implements CoordinateFilter { public void filter(Coordinate coord) { double tmp = coord.x; coord.x = coord.y; coord.y = tmp; } } public static Geometry nearestPoints(Geometry a, Geometry b) { Coordinate[] pts = DistanceOp.nearestPoints(a, b); return geomFactory.createLineString( new Coordinate[] { pts[0], pts[1] }); } //=========== Split functions ============== public static List<Geometry> splitByMember(Geometry geom) { List<Geometry> items = new ArrayList<Geometry>(); for (int i = 0; i < geom.getNumGeometries(); i++) { items.add(geom.getGeometryN(i)); } return items; } public static List<Geometry> splitByVertex(Geometry geom) { List<Geometry> items = new ArrayList<Geometry>(); Coordinate[] pts = geom.getCoordinates(); for (int i = 0; i < pts.length; i++) { Point pt = geomFactory.createPoint(pts[i]); items.add(pt); } return items; } public static List<Geometry> splitBySegment(Geometry geom) { List<Geometry> items = new ArrayList<Geometry>(); List lines = new ArrayList(); LinearComponentExtracter.getLines(geom, lines); for (int icomp = 0; icomp < lines.size(); icomp++) { Coordinate[] pts = ((LineString) lines.get(icomp)).getCoordinates(); for (int i = 0; i < pts.length - 1; i++) { LineString line = geomFactory.createLineString( new Coordinate[] { new Coordinate(pts[i]), new Coordinate(pts[i+1]) }); items.add(line); } } return items; } //=========== Font functions ============== public static Geometry toGeometry(String str) { return null; } }