com.vividsolutions.jts.util.GeometricShapeFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jts Show documentation
Show all versions of jts Show documentation
The JTS Topology Suite is an API for modelling and
manipulating 2-dimensional linear geometry. It provides
numerous geometric predicates and functions. JTS
conforms to the Simple Features Specification for
SQL published by the Open GIS Consortium.
/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jts.util;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.geom.util.AffineTransformation;
/**
* Computes various kinds of common geometric shapes.
* Provides various ways of specifying the location and extent
* and rotations of the generated shapes,
* as well as number of line segments used to form them.
*
* Example of usage:
*
* GeometricShapeFactory gsf = new GeometricShapeFactory();
* gsf.setSize(100);
* gsf.setNumPoints(100);
* gsf.setBase(new Coordinate(100, 100));
* gsf.setRotation(0.5);
* Polygon rect = gsf.createRectangle();
*
*
* @version 1.7
*/
public class GeometricShapeFactory
{
protected GeometryFactory geomFact;
protected PrecisionModel precModel = null;
protected Dimensions dim = new Dimensions();
protected int nPts = 100;
/**
* Default is no rotation.
*/
protected double rotationAngle = 0.0;
/**
* Create a shape factory which will create shapes using the default
* {@link GeometryFactory}.
*/
public GeometricShapeFactory()
{
this(new GeometryFactory());
}
/**
* Create a shape factory which will create shapes using the given
* {@link GeometryFactory}.
*
* @param geomFact the factory to use
*/
public GeometricShapeFactory(GeometryFactory geomFact)
{
this.geomFact = geomFact;
precModel = geomFact.getPrecisionModel();
}
public void setEnvelope(Envelope env)
{
dim.setEnvelope(env);
}
/**
* Sets the location of the shape by specifying the base coordinate
* (which in most cases is the
* lower left point of the envelope containing the shape).
*
* @param base the base coordinate of the shape
*/
public void setBase(Coordinate base) { dim.setBase(base); }
/**
* Sets the location of the shape by specifying the centre of
* the shape's bounding box
*
* @param centre the centre coordinate of the shape
*/
public void setCentre(Coordinate centre) { dim.setCentre(centre); }
/**
* Sets the total number of points in the created {@link Geometry}.
* The created geometry will have no more than this number of points,
* unless more are needed to create a valid geometry.
*/
public void setNumPoints(int nPts) { this.nPts = nPts; }
/**
* Sets the size of the extent of the shape in both x and y directions.
*
* @param size the size of the shape's extent
*/
public void setSize(double size) { dim.setSize(size); }
/**
* Sets the width of the shape.
*
* @param width the width of the shape
*/
public void setWidth(double width) { dim.setWidth(width); }
/**
* Sets the height of the shape.
*
* @param height the height of the shape
*/
public void setHeight(double height) { dim.setHeight(height); }
/**
* Sets the rotation angle to use for the shape.
* The rotation is applied relative to the centre of the shape.
*
* @param radians the rotation angle in radians.
*/
public void setRotation(double radians)
{
rotationAngle = radians;
}
protected Geometry rotate(Geometry geom)
{
if (rotationAngle != 0.0) {
AffineTransformation trans = AffineTransformation.rotationInstance(rotationAngle,
dim.getCentre().x, dim.getCentre().y);
geom.apply(trans);
}
return geom;
}
/**
* Creates a rectangular {@link Polygon}.
*
* @return a rectangular Polygon
*
*/
public Polygon createRectangle()
{
int i;
int ipt = 0;
int nSide = nPts / 4;
if (nSide < 1) nSide = 1;
double XsegLen = dim.getEnvelope().getWidth() / nSide;
double YsegLen = dim.getEnvelope().getHeight() / nSide;
Coordinate[] pts = new Coordinate[4 * nSide + 1];
Envelope env = dim.getEnvelope();
//double maxx = env.getMinX() + nSide * XsegLen;
//double maxy = env.getMinY() + nSide * XsegLen;
for (i = 0; i < nSide; i++) {
double x = env.getMinX() + i * XsegLen;
double y = env.getMinY();
pts[ipt++] = coord(x, y);
}
for (i = 0; i < nSide; i++) {
double x = env.getMaxX();
double y = env.getMinY() + i * YsegLen;
pts[ipt++] = coord(x, y);
}
for (i = 0; i < nSide; i++) {
double x = env.getMaxX() - i * XsegLen;
double y = env.getMaxY();
pts[ipt++] = coord(x, y);
}
for (i = 0; i < nSide; i++) {
double x = env.getMinX();
double y = env.getMaxY() - i * YsegLen;
pts[ipt++] = coord(x, y);
}
pts[ipt++] = new Coordinate(pts[0]);
LinearRing ring = geomFact.createLinearRing(pts);
Polygon poly = geomFact.createPolygon(ring, null);
return (Polygon) rotate(poly);
}
//* @deprecated use {@link createEllipse} instead
/**
* Creates a circular or elliptical {@link Polygon}.
*
* @return a circle or ellipse
*/
public Polygon createCircle()
{
return createEllipse();
}
/**
* Creates an elliptical {@link Polygon}.
* If the supplied envelope is square the
* result will be a circle.
*
* @return an ellipse or circle
*/
public Polygon createEllipse()
{
Envelope env = dim.getEnvelope();
double xRadius = env.getWidth() / 2.0;
double yRadius = env.getHeight() / 2.0;
double centreX = env.getMinX() + xRadius;
double centreY = env.getMinY() + yRadius;
Coordinate[] pts = new Coordinate[nPts + 1];
int iPt = 0;
for (int i = 0; i < nPts; i++) {
double ang = i * (2 * Math.PI / nPts);
double x = xRadius * Math.cos(ang) + centreX;
double y = yRadius * Math.sin(ang) + centreY;
pts[iPt++] = coord(x, y);
}
pts[iPt] = new Coordinate(pts[0]);
LinearRing ring = geomFact.createLinearRing(pts);
Polygon poly = geomFact.createPolygon(ring, null);
return (Polygon) rotate(poly);
}
/**
* Creates a squircular {@link Polygon}.
*
* @return a squircle
*/
public Polygon createSquircle()
/**
* Creates a squircular {@link Polygon}.
*
* @return a squircle
*/
{
return createSupercircle(4);
}
/**
* Creates a supercircular {@link Polygon}
* of a given positive power.
*
* @return a supercircle
*/
public Polygon createSupercircle(double power)
{
double recipPow = 1.0 / power;
double radius = dim.getMinSize() / 2;
Coordinate centre = dim.getCentre();
double r4 = Math.pow(radius, power);
double y0 = radius;
double xyInt = Math.pow(r4 / 2, recipPow);
int nSegsInOct = nPts / 8;
int totPts = nSegsInOct * 8 + 1;
Coordinate[] pts = new Coordinate[totPts];
double xInc = xyInt / nSegsInOct;
for (int i = 0; i <= nSegsInOct; i++) {
double x = 0.0;
double y = y0;
if (i != 0) {
x = xInc * i;
double x4 = Math.pow(x, power);
y = Math.pow(r4 - x4, recipPow);
}
pts[i] = coordTrans(x, y, centre);
pts[2 * nSegsInOct - i] = coordTrans(y, x, centre);
pts[2 * nSegsInOct + i] = coordTrans(y, -x, centre);
pts[4 * nSegsInOct - i] = coordTrans(x, -y, centre);
pts[4 * nSegsInOct + i] = coordTrans(-x, -y, centre);
pts[6 * nSegsInOct - i] = coordTrans(-y, -x, centre);
pts[6 * nSegsInOct + i] = coordTrans(-y, x, centre);
pts[8 * nSegsInOct - i] = coordTrans(-x, y, centre);
}
pts[pts.length-1] = new Coordinate(pts[0]);
LinearRing ring = geomFact.createLinearRing(pts);
Polygon poly = geomFact.createPolygon(ring, null);
return (Polygon) rotate(poly);
}
/**
* Creates an elliptical arc, as a {@link LineString}.
* The arc is always created in a counter-clockwise direction.
* This can easily be reversed if required by using
* {#link LineString.reverse()}
*
* @param startAng start angle in radians
* @param angExtent size of angle in radians
* @return an elliptical arc
*/
public LineString createArc(
double startAng,
double angExtent)
{
Envelope env = dim.getEnvelope();
double xRadius = env.getWidth() / 2.0;
double yRadius = env.getHeight() / 2.0;
double centreX = env.getMinX() + xRadius;
double centreY = env.getMinY() + yRadius;
double angSize = angExtent;
if (angSize <= 0.0 || angSize > 2 * Math.PI)
angSize = 2 * Math.PI;
double angInc = angSize / (nPts - 1);
Coordinate[] pts = new Coordinate[nPts];
int iPt = 0;
for (int i = 0; i < nPts; i++) {
double ang = startAng + i * angInc;
double x = xRadius * Math.cos(ang) + centreX;
double y = yRadius * Math.sin(ang) + centreY;
pts[iPt++] = coord(x, y);
}
LineString line = geomFact.createLineString(pts);
return (LineString) rotate(line);
}
/**
* Creates an elliptical arc polygon.
* The polygon is formed from the specified arc of an ellipse
* and the two radii connecting the endpoints to the centre of the ellipse.
*
* @param startAng start angle in radians
* @param angExtent size of angle in radians
* @return an elliptical arc polygon
*/
public Polygon createArcPolygon(double startAng, double angExtent) {
Envelope env = dim.getEnvelope();
double xRadius = env.getWidth() / 2.0;
double yRadius = env.getHeight() / 2.0;
double centreX = env.getMinX() + xRadius;
double centreY = env.getMinY() + yRadius;
double angSize = angExtent;
if (angSize <= 0.0 || angSize > 2 * Math.PI)
angSize = 2 * Math.PI;
double angInc = angSize / (nPts - 1);
// double check = angInc * nPts;
// double checkEndAng = startAng + check;
Coordinate[] pts = new Coordinate[nPts + 2];
int iPt = 0;
pts[iPt++] = coord(centreX, centreY);
for (int i = 0; i < nPts; i++) {
double ang = startAng + angInc * i;
double x = xRadius * Math.cos(ang) + centreX;
double y = yRadius * Math.sin(ang) + centreY;
pts[iPt++] = coord(x, y);
}
pts[iPt++] = coord(centreX, centreY);
LinearRing ring = geomFact.createLinearRing(pts);
Polygon poly = geomFact.createPolygon(ring, null);
return (Polygon) rotate(poly);
}
protected Coordinate coord(double x, double y)
{
Coordinate pt = new Coordinate(x, y);
precModel.makePrecise(pt);
return pt;
}
protected Coordinate coordTrans(double x, double y, Coordinate trans)
{
return coord(x + trans.x, y + trans.y);
}
protected class Dimensions
{
public Coordinate base;
public Coordinate centre;
public double width;
public double height;
public void setBase(Coordinate base) { this.base = base; }
public Coordinate getBase() { return base; }
public void setCentre(Coordinate centre) { this.centre = centre; }
public Coordinate getCentre() { return centre; }
public void setSize(double size)
{
height = size;
width = size;
}
public double getMinSize()
{
return Math.min(width, height);
}
public void setWidth(double width) { this.width = width; }
public double getWidth() { return width; }
public double getHeight() { return height; }
public void setHeight(double height) { this.height = height; }
public void setEnvelope(Envelope env)
{
this.width = env.getWidth();
this.height = env.getHeight();
this.base = new Coordinate(env.getMinX(), env.getMinY());
this.centre = new Coordinate(env.centre());
}
public Envelope getEnvelope() {
if (base != null) {
return new Envelope(base.x, base.x + width, base.y, base.y + height);
}
if (centre != null) {
return new Envelope(centre.x - width/2, centre.x + width/2,
centre.y - height/2, centre.y + height/2);
}
return new Envelope(0, width, 0, height);
}
}
}