/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2004-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.geometry.jts; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.CoordinateSequenceFactory; 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.LinearRing; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPoint; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; /** * A builder for {@link Geometry} objects. Primarily intended to * support fluent programming in test code. * <p> * Features include: * <ul> * <li>Both 2D and 3D coordinate dimensions are supported * (assuming the provided {@link CoordinateSequenceFactory} supports them) * <li>Sequences of ordinate values can be supplied in a number of ways * <li>Rings do not need to be explicitly closed; a closing point will be * supplied if needed * <li>Empty geometries of all types can be created * <li>Composite geometries are validated to ensure they have a consistent * GeometryFactory and coordinate sequence dimension * <p> * Examples of intended usage are: * * <pre> * GeometryBuilder gb = new GeometryBuilder(geomFact); * LineString line = gb.linestring(1,2, 3,4); * Polygon poly = gb.polygon(0,0, 0,1, 1,1, 1,0); * Polygon box = gb.box(0,0, 1,1); * Polygon hexagon = gb.circle(0,0, 1,1, 6); * Polygon polyhole = gb.polygon(gb.linearring(0,0, 0,10, 10,10, 10,0), gb.linearring(1,1, 1,9, 9,9, 9,1)) * </pre> * * @author Martin Davis - OpenGeo * */ public class GeometryBuilder { private GeometryFactory geomFact; private CoordinateSequenceFactory csFact; /** * Create a new instance using the default {@link GeometryFactory}. */ public GeometryBuilder() { this(new GeometryFactory()); } /** * Creates a new instance using a provided GeometryFactory. * * @param geomFact the factory to use */ public GeometryBuilder(GeometryFactory geomFact) { this.geomFact = geomFact; csFact = geomFact.getCoordinateSequenceFactory(); } /** * Creates an empty Point * * @return an empty Point */ public Point point() { return geomFact.createPoint(createCS(new double[0], 2)); } /** * Creates an empty Point with coordinate dimension = 3. * * @return an empty Point */ public Point pointZ() { return geomFact.createPoint(createCS(new double[0], 3)); } /** * Creates a 1D Point. * * @param x the X ordinate * @return a Point */ public Point point(double x) { return geomFact.createPoint(createCS(new double[] { x }, 1)); } /** * Creates a 2D Point. * * @param x the X ordinate * @param y the Y ordinate * @return a Point */ public Point point(double x, double y) { return geomFact.createPoint(createCS(new double[] { x, y }, 2)); } /** * Creates a 3D Point. * * @param x the X ordinate * @param y the Y ordinate * @param z the Z ordinate * @return a Point */ public Point pointZ(double x, double y, double z) { return geomFact.createPoint(createCS(new double[] { x, y, z }, 3)); } /** * Creates an empty 2D LineString * * @return an empty LineString */ public LineString lineString() { return geomFact.createLineString(createCS(new double[0], 2)); } /** * Creates an empty 3D LineString * * @return an empty LineString */ public LineString lineStringZ() { return geomFact.createLineString(createCS(new double[0], 3)); } /** * Creates a 2D LineString. * * @param ord the XY ordinates * @return a LineString */ public LineString lineString(double... ord) { return geomFact.createLineString(createCS(ord, 2)); } /** * Creates a 3D LineString. * * @param ord the XYZ ordinates * @return a LineString */ public LineString lineStringZ(double... ord) { return geomFact.createLineString(createCS(ord, 3)); } /** * Creates an empty 2D LinearRing * * @return an empty LinearRing */ public LinearRing linearRing() { return geomFact.createLinearRing(createRingCS(new double[0], 2)); } /** * Creates an empty 3D LinearRing * * @return an empty LinearRing */ public LinearRing linearRingZ() { return geomFact.createLinearRing(createRingCS(new double[0], 3)); } /** * Creates a 2D LinearRing. If the supplied coordinate list is not closed, a * closing coordinate is added. * * @param ord * @return a LinearRing */ public LinearRing linearRing(double... ord) { return geomFact.createLinearRing(createRingCS(ord, 2)); } /** * Creates a 3D LinearRing. If the supplied coordinate list is not closed, a * closing coordinate is added. * * @param ord the XYZ ordinates * @return a LinearRing */ public LinearRing linearRingZ(double... ord) { return geomFact.createLinearRing(createRingCS(ord, 3)); } /** * Creates an empty 2D Polygon. * * @return an empty Polygon */ public Polygon polygon() { return geomFact.createPolygon(linearRing(), null); } /** * Creates an empty 3D Polygon. * * @return an empty Polygon */ public Polygon polygonZ() { return geomFact.createPolygon(linearRingZ(), null); } /** * Creates a Polygon from a list of XY coordinates. * * @param ord a list of XY ordinates * @return a Polygon */ public Polygon polygon(double... ord) { return geomFact.createPolygon(linearRing(ord), null); } /** * Creates a Polygon from a list of XYZ coordinates. * * @param ord a list of XYZ ordinates * @return a Polygon */ public Polygon polygonZ(double... ord) { return geomFact.createPolygon(linearRingZ(ord), null); } /** * Creates a Polygon from an exterior ring. The coordinate dimension of the * Polygon is the dimension of the LinearRing. * * @param shell the exterior ring * @return a Polygon */ public Polygon polygon(LinearRing shell) { return geomFact.createPolygon(shell, null); } /** * Creates a Polygon with a hole from an exterior ring and an interior ring. * * @param shell the exterior ring * @param hole the interior ring * @return a Polygon with a hole */ public Polygon polygon(LinearRing shell, LinearRing hole) { return geomFact.createPolygon(shell, new LinearRing[] { hole }); } /** * Creates a Polygon with a hole from an exterior ring and an interior ring * supplied by the rings of Polygons. * * @param shell the exterior ring * @param hole the interior ring * @return a Polygon with a hole */ public Polygon polygon(Polygon shell, Polygon hole) { return geomFact.createPolygon((LinearRing) shell.getExteriorRing(), new LinearRing[] { (LinearRing) hole.getExteriorRing() }); } /** * Creates a rectangular 2D Polygon from X and Y bounds. * * @param x1 the lower X bound * @param y1 the lower Y bound * @param x2 the upper X bound * @param y2 the upper Y bound * @return a 2D Polygon */ public Polygon box(double x1, double y1, double x2, double y2) { double[] ord = new double[] { x1, y1, x1, y2, x2, y2, x2, y1, x1, y1 }; return polygon(ord); } /** * Creates a rectangular 3D Polygon from X and Y bounds. * * @param x1 the lower X bound * @param y1 the lower Y bound * @param x2 the upper X bound * @param y2 the upper Y bound * @param z the Z value for all coordinates * @return a 3D Polygon */ public Polygon boxZ(double x1, double y1, double x2, double y2, double z) { double[] ord = new double[] { x1, y1, z, x1, y2, z, x2, y2, z, x2, y1, z, x1, y1, z }; return polygonZ(ord); } /** * Creates an elliptical Polygon from a bounding box with a given number of * sides. * * @param x1 * @param y1 * @param x2 * @param y2 * @param nsides * @return a 2D Polygon */ public Polygon ellipse(double x1, double y1, double x2, double y2, int nsides) { double rx = Math.abs(x2 - x1) / 2; double ry = Math.abs(y2 - y1) / 2; double cx = Math.min(x1, x2) + rx; double cy = Math.min(y1, y2) + ry; double[] ord = new double[2 * nsides + 2]; double angInc = 2 * Math.PI / nsides; // create ring in CW order for (int i = 0; i < nsides; i++) { double ang = -(i * angInc); ord[2 * i] = cx + rx * Math.cos(ang); ord[2 * i + 1] = cy + ry * Math.sin(ang); } ord[2 * nsides] = ord[0]; ord[2 * nsides + 1] = ord[1]; return polygon(ord); } /** * Creates a circular Polygon with a given center, radius and number of sides. * * @param x the center X ordinate * @param y the center Y ordinate * @param radius the radius * @param nsides the number of sides * @return a 2D Polygon */ public Polygon circle(double x, double y, double radius, int nsides) { return ellipse(x - radius, y - radius, x + radius, y + radius, nsides); } /** * Creates a MultiPoint with 2 2D Points. * * @param x1 the X ordinate of the first point * @param y1 the Y ordinate of the first point * @param x2 the X ordinate of the second point * @param y2 the Y ordinate of the second point * @return A MultiPoint */ public MultiPoint multiPoint(double x1, double y1, double x2, double y2) { return geomFact .createMultiPoint(new Point[] { point(x1, y1), point(x2, y2) }); } /** * Creates a MultiPoint with 2 3D Points. * * @param x1 the X ordinate of the first point * @param y1 the Y ordinate of the first point * @param z1 the Z ordinate of the first point * @param x2 the X ordinate of the second point * @param y2 the Y ordinate of the second point * @param z2 the Z ordinate of the second point * @return A 3D MultiPoint */ public MultiPoint multiPointZ(double x1, double y1, double z1, double x2, double y2, double z2) { return geomFact.createMultiPoint(new Point[] { pointZ(x1, y1, z1), pointZ(x2, y2, z2) }); } /** * Creates a MultiLineString from a set of LineStrings * * @param lines the component LineStrings * @return a MultiLineString */ public MultiLineString multiLineString(LineString... lines) { return geomFact.createMultiLineString(lines); } /** * Creates a MultiPolygon from a set of Polygons. * * @param polys the component polygons * @return A MultiPolygon */ public MultiPolygon multiPolygon(Polygon... polys) { return geomFact.createMultiPolygon(polys); } /** * Creates a GeometryCollection from a set of Geometrys * * @param geoms the component Geometrys * @return a GeometryCollection */ public GeometryCollection geometryCollection(Geometry... geoms) { return geomFact.createGeometryCollection(geoms); } /** * Tests whether a sequence of ordinates of a given dimension is closed (i.e. * has the first and last coordinate identical). * * @param ord the list of ordinate values * @param dim the dimension of each coordinate * @return true if the sequence is closed */ private boolean isClosed(double[] ord, int dim) { int n = ord.length / dim; if (n == 0) return true; int lastPos = dim * (n - 1); double lastx = ord[lastPos]; double lasty = ord[lastPos + 1]; boolean isClosed = lastx == ord[0] && lasty == ord[1]; return isClosed; } /** * @param ord * @param dim * @return */ private CoordinateSequence createRingCS(double[] ord, int dim) { if (isClosed(ord, dim)) return createCS(ord, dim); double[] ord2 = new double[ord.length + dim]; System.arraycopy(ord, 0, ord2, 0, ord.length); // copy first coord to last int lastPos = ord.length; for (int i = 0; i < dim; i++) { ord2[lastPos + i] = ord2[i]; } return createCS(ord2, dim); } /** * @param ord * @param dim * @return */ private CoordinateSequence createCS(double[] ord, int dim) { if (ord.length % dim != 0) throw new IllegalArgumentException("Ordinate array length " + ord.length + " is not a multiple of dimension " + dim); int n = ord.length / dim; CoordinateSequence cs = csFact.create(n, dim); for (int i = 0; i < n; i++) { for (int d = 0; d < dim; d++) cs.setOrdinate(i, d, ord[dim * i + d]); } return cs; } }