/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2005, Institut de Recherche pour le Développement
* (C) 2007 - 2008, Geomatys
*
* 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 3 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.
*/
package org.geotoolkit.geometry;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Polygon;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import org.apache.sis.geometry.Envelopes;
import org.apache.sis.geometry.GeneralDirectPosition;
import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.util.ArgumentChecks;
import org.geotoolkit.display.shape.ShapeUtilities;
import org.apache.sis.referencing.CRS;
import org.geotoolkit.referencing.ReferencingUtilities;
import org.apache.sis.referencing.datum.DefaultEllipsoid;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.FactoryException;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.referencing.crs.AbstractCRS;
import org.apache.sis.referencing.cs.AxesConvention;
/**
* A utility class containing methods to manipulate geometries.
*
* @author Guilhem Legal (Geomatys)
* @author Johann Sorel (Geomatys)
* @module
*/
public class GeometricUtilities {
private static final GeometryFactory GF = new GeometryFactory();
public static enum WrapResolution {
/**
* Convert the coordinates without checking the antemeridian.
* If the envelope crosses the antemeridian (lower corner values > upper corner values)
* the created polygon will be wrong since it will define a different area then the envelope.
* Use this method only knowing the envelopes do not cross the antemeridian.
*
* Example :
* ENV(+170 +10, -170 -10)
* POLYGON(+170 +10, -170 +10, -170 -10, +170 -10, +170 +10)
*
*/
NONE,
/**
* Convert the coordinates checking the antemeridian.
* If the envelope crosses the antemeridian (lower corner values > upper corner values)
* the created polygon will go from axis minimum value to axis maximum value.
* This ensure the create polygon contains the envelope but is wider.
*
* Example :
* ENV(+170 +10, -170 -10)
* POLYGON(-180 +10, +180 +10, +180 -10, -180 -10, -180 +10)
*/
EXPAND,
/**
* Convert the coordinates checking the antemeridian.
* If the envelope crosses the antemeridian (lower corner values > upper corner values)
* the created polygon will be cut in 2 polygons on each side of the coordinate system.
* This ensure the create polygon exactly match the envelope but with a more
* complex geometry.
*
* Example :
* ENV(+170 +10, -170 -10)
* MULTI-POLYGON(
* (-180 +10, -170 +10, -170 -10, -180 -10, -180 +10)
* (+170 +10, +180 +10, +180 -10, +170 -10, +170 +10)
* )
*/
SPLIT,
/**
* Convert the coordinates checking the antemeridian.
* If the envelope crosses the antemeridian (lower corner values > upper corner values)
* the created polygon coordinate will increase over the antemeridian making
* a contiguous geometry.
*
* Example :
* ENV(+170 +10, -170 -10)
* POLYGON(+170 +10, +190 +10, +190 -10, +170 -10, +170 +10)
*/
CONTIGUOUS
}
/**
* WGS 1984 ellipsoid with axis in {@linkplain Units#METRE metres}.
*/
private static final DefaultEllipsoid DE = DefaultEllipsoid.castOrCopy(CommonCRS.WGS84.ellipsoid());
private GeometricUtilities() {}
/**
* Return the shortest orthodromic distance between a boundingBox and a point.
* if the boundingBox contains the point it return 0.
*
* @param boundingBox A bounding box
* @param point A point in the same CRS that the boundingBox.
* @param units The units in which the distance will be expressed.
*
* @return the shortest distance between the bounding box and the point.
*/
public static double bboxToPointDistance(final GeneralEnvelope boundingBox, final GeneralDirectPosition point, final String units) {
if (boundingBox.contains(point))
return 0;
final List<Line2D> border = getBorder(boundingBox);
double distance = Double.MAX_VALUE;
for (Line2D l: border) {
final double tempDistance = lineToPointDistance(l, point, units);
if (tempDistance < distance)
distance = tempDistance;
}
return distance;
}
/**
* Return the shortest distance between a line and a point
*
* @param line A line2D.
* @param point A point in the same CRS than the line.
* @param units The units in which the distance will be expressed.
*
* @return the shortest distance between the bounding box and the point.
*/
public static double lineToPointDistance(final Line2D line, final GeneralDirectPosition point, final String units) {
final Point2D pt = ShapeUtilities.nearestColinearPoint(line, new Point2D.Double(point.getOrdinate(0), point.getOrdinate(1)));
return getOrthodromicDistance(point.getOrdinate(0), point.getOrdinate(1),
pt.getX(), pt.getY(), units);
}
/**
* Return the shortest distance between two segment.
*
* @param line1 The first segment.
* @param line2 The second segment.
* @param units The units in which the distance will be expressed.
*
* @return the shortest distance between the two segment.
*/
public static double lineTolineDistance(final Line2D line1, final Line2D line2, final String units) {
double distance = Double.MAX_VALUE;
if (line1.intersectsLine(line2)) {
return 0;
}
Point2D pt = ShapeUtilities.nearestColinearPoint(line2, line1.getP1());
double tempDistance = getOrthodromicDistance(pt.getX(), pt.getY(), line1.getX1(), line1.getY1(), units);
if (tempDistance < distance)
distance = tempDistance;
pt = ShapeUtilities.nearestColinearPoint(line2, line1.getP2());
tempDistance = getOrthodromicDistance(pt.getX(), pt.getY(), line1.getX2(), line1.getY2(), units);
if (tempDistance < distance)
distance = tempDistance;
pt = ShapeUtilities.nearestColinearPoint(line1, line2.getP1());
tempDistance = getOrthodromicDistance(pt.getX(), pt.getY(), line2.getX1(), line2.getY1(), units);
if (tempDistance < distance)
distance = tempDistance;
pt = ShapeUtilities.nearestColinearPoint(line1, line2.getP2());
tempDistance = getOrthodromicDistance(pt.getX(), pt.getY(), line2.getX2(), line2.getY2(), units);
if (tempDistance < distance)
distance = tempDistance;
return distance;
}
/**
* Return the shortest distance between a line and a boundingBox.
* If the line intersect or is contains by the bounding box it return 0;
*
* @param line A line2D.
* @param boundingBox A bounding box in the same CRS than the line
* @param units The units in which the distance will be expressed.
* @return The shortest distance between the bounding box and the line.
*/
public static double lineToBBoxDistance(final Line2D line, final GeneralEnvelope boundingBox, final String units) {
final CoordinateReferenceSystem crs = boundingBox.getCoordinateReferenceSystem();
final GeneralDirectPosition tempPoint1 = new GeneralDirectPosition(line.getX1(), line.getY1());
tempPoint1.setCoordinateReferenceSystem(crs);
final GeneralDirectPosition tempPoint2 = new GeneralDirectPosition(line.getX2(), line.getY2());
tempPoint2.setCoordinateReferenceSystem(crs);
if (boundingBox.contains(tempPoint1) || boundingBox.contains(tempPoint2))
return 0;
final List<Line2D> border = getBorder(boundingBox);
double distance = Double.MAX_VALUE;
for (Line2D l: border) {
if (l.intersectsLine(line))
return 0;
double tempDistance = lineTolineDistance(l, line, units);
if (tempDistance < distance)
distance = tempDistance;
}
return distance;
}
/**
* Return the shortest distance between two bounding box.
* if the two Bounding box intersect it return 0.
*
* @param boundingBox1 The first bounding box.
* @param boundingBox2 The second bounding box.
* @param units The units in which the distance will be expressed.
*
* @return The shortest distance between the bounding box and the line.
*/
public static double bboxToBBoxDistance(final GeneralEnvelope boundingBox1, final GeneralEnvelope boundingBox2, final String units) {
if (boundingBox1.intersects(boundingBox2, false))
return 0;
List<Line2D> border = getBorder(boundingBox1);
double distance = Double.MAX_VALUE;
for (Line2D l: border) {
double tempDistance = lineToBBoxDistance(l, boundingBox2, units);
if (tempDistance < distance)
distance = tempDistance;
}
return distance;
}
/**
* Return the orthodromic distance between two point in the specified units.
*
* @param x1 latitude for point 1.
* @param y1 longitude for point 1.
* @param x2 latitude for point 2.
* @param y2 longitude for point 2.
* @param units the units on which you want the distance be expressed.
* supported units are: meters, kilometers, miles.
*
* @return The distance between 2 points on earth expressed in the specified units
*/
public static double getOrthodromicDistance(final double x1, final double y1, final double x2, final double y2, final String units) {
final double result = DE.orthodromicDistance(y1, x1, y2, x2);
if (units.equals("meters") || units.equals("m"))
return result;
else if (units.equals("kilometers") || units.equals("km"))
return result/1000;
else if (units.equals("centimeters") || units.equals("cm"))
return result * 100;
else if (units.equals("milimeters") || units.equals("mm"))
return result * 1000;
else if (units.equals("miles") || units.equals("mi"))
return (result*0.621371) /1000;
else
throw new IllegalArgumentException("unknow distance units");
}
/**
* return a list of Line2D composing the border of the specified geometry.
*
* @param geometry a geomtry object. supported one are: GeneralEnvelope
* @return a list ordered like following: height-left border, width-top border, height-right border, width-bottom border.
*/
public static List<Line2D> getBorder(final Object geometry) {
final List<Line2D> result = new ArrayList<Line2D>();
if (geometry instanceof GeneralEnvelope) {
final GeneralEnvelope env = (GeneralEnvelope) geometry;
final Line2D heightLeft = new Line2D.Double(env.getMinimum(0), env.getMaximum(1), env.getMinimum(0), env.getMinimum(1));
result.add(heightLeft);
final Line2D widthTop = new Line2D.Double(env.getMinimum(0), env.getMaximum(1), env.getMaximum(0), env.getMaximum(1));
result.add(widthTop);
final Line2D heightRight = new Line2D.Double(env.getMaximum(0), env.getMaximum(1), env.getMaximum(0), env.getMinimum(1));
result.add(heightRight);
final Line2D widthBottom = new Line2D.Double(env.getMinimum(0), env.getMinimum(1), env.getMaximum(0), env.getMinimum(1));
result.add(widthBottom);
}
return result;
}
/**
* return a list of Point2D composing the corner of the specified geometry.
*
* @param geometry a geometry object. supported one are: GeneralEnvelope
*
* @return a list ordered like following: upper-left corner, upper-right corner, bottom-right corner, bottom-left corner.
*/
public static List<Point2D> getCorner(final Object geometry) {
final List<Point2D> result = new ArrayList<Point2D>();
if (geometry instanceof GeneralEnvelope) {
final GeneralEnvelope env = (GeneralEnvelope) geometry;
final Point2D upperLeft = new Point2D.Double(env.getMinimum(0), env.getMaximum(1));
result.add(upperLeft);
final Point2D upperRight = new Point2D.Double(env.getMaximum(0), env.getMaximum(1));
result.add(upperRight);
final Point2D bottomRight = new Point2D.Double(env.getMaximum(0), env.getMinimum(1));
result.add(bottomRight);
final Point2D bottomLeft = new Point2D.Double(env.getMinimum(0), env.getMinimum(1));
result.add(bottomLeft);
}
return result;
}
/**
* Return true if the specified point is located on the border of the specified envelope.
* if one of the parameters is null it return false.
*
* @param boundingBox A GeneralEnvelope.
* @param point A GeneralDirectPosition
*
* @return true if the point is on the border of the envelope.
*/
public static boolean touches(final GeneralEnvelope boundingBox, final GeneralDirectPosition point) {
if (point == null || boundingBox == null)
return false;
final Line2D pointLine = new Line2D.Double(point.getOrdinate(0), point.getOrdinate(1),
point.getOrdinate(0), point.getOrdinate(1));
final List<Line2D> border = getBorder(boundingBox);
for (Line2D l: border) {
if (l.intersectsLine(pointLine))
return true;
}
return false;
}
/**
* Return true if the two line are in touche relation.
* i.e. one of the extremity of a line is a point of the other.
*
* if one of the parameters is null it return false.
*
* @param line1 The first line2D.
* @param line2 The second line2D.
*
* @return true if one of the extremity of a line is a point of the other.
*/
public static boolean touches(final Line2D line1, final Line2D line2) {
if (line1 == null || line2 == null)
return false;
if (line1.intersectsLine(new Line2D.Double(line2.getX1(), line2.getY1(), line2.getX1(), line2.getY1())) ||
line1.intersectsLine(new Line2D.Double(line2.getX2(), line2.getY2(), line2.getX2(), line2.getY2())) ||
line2.intersectsLine(new Line2D.Double(line1.getX1(), line1.getY1(), line1.getX1(), line1.getY1())) ||
line2.intersectsLine(new Line2D.Double(line1.getX2(), line1.getY2(), line1.getX2(), line1.getY2())))
return true;
return false;
}
/**
* Return true if the specified line and the envelope are in touche relation.
* i.e. one of the extremity of the line is a point of the border of the envelope.
*
* if one of the parameters is null it return false.
*
* @param boundingBox The bounding box.
* @param line The line2D.
*
* @return boolean
*/
public static boolean touches(final GeneralEnvelope boundingBox, final Line2D line) {
if (line == null || boundingBox == null)
return false;
final List<Line2D> border = getBorder(boundingBox);
for (Line2D l: border) {
if (l.intersectsLine(line.getX1(), line.getY1(), line.getX1(), line.getY1()) ||
l.intersectsLine(line.getX2(), line.getY2(), line.getX2(), line.getY2()))
return true;
}
return false;
}
/**
* Return true if the envelopes are in touche relation.
* i.e. TODO fill
*
* if one of the parameters is null it return false.
*
* @param boundingBox1 The first envelope.
* @param boundingBox2 The second envelope.
*
* @return boolean
*/
public static boolean touches(final GeneralEnvelope boundingBox1, final GeneralEnvelope boundingBox2) {
if (boundingBox1 == null || boundingBox2 == null)
return false;
boolean touches = false;
// we look if one of the corner of the second bounding Box touch a border of the first box
final List<Line2D> bboxBorder1 = getBorder(boundingBox1);
for (Line2D l : bboxBorder1) {
for (Point2D p : getCorner(boundingBox2)) {
if (l.intersectsLine(p.getX(), p.getY(), p.getX(), p.getY())) {
touches = true;
}
}
}
// then we look if one of the corner of the first bounding box touch one border of the second box
final List<Point2D> filterBoxCorner = GeometricUtilities.getCorner(boundingBox1);
for (Line2D l : getBorder(boundingBox2)) {
for (Point2D p : filterBoxCorner) {
if (l.intersectsLine(p.getX(), p.getY(), p.getX(), p.getY())) {
touches = true;
}
}
}
return touches;
}
/**
* Return true if the intersection between the envelope and the line is empty.
*
* @param boundingBox An envelope.
* @param line A line2D
*
* @return True f the intersection between the envelope and the point is empty.
*/
public static boolean disjoint(final GeneralEnvelope boundingBox, final Line2D line) {
final GeneralDirectPosition tempPoint1 = new GeneralDirectPosition(line.getX1(), line.getY1());
final GeneralDirectPosition tempPoint2 = new GeneralDirectPosition(line.getX2(), line.getY2());
if (!boundingBox.contains(tempPoint1) && !boundingBox.contains(tempPoint2)) {
final List<Line2D> border = getBorder(boundingBox);
for (Line2D l : border) {
if (l.intersectsLine(line)) {
return false;
}
}
return true;
}
return false;
}
/**
* Return true if the intersection between the envelope and the line is not empty.
*
* @param boundingBox An envelope.
* @param line A line2D
*
* @return True f the intersection between the envelope and the point is empty.
*/
public static boolean intersect(final GeneralEnvelope boundingBox, final Line2D line) {
final GeneralDirectPosition tempPoint1 = new GeneralDirectPosition(line.getX1(), line.getY1());
final GeneralDirectPosition tempPoint2 = new GeneralDirectPosition(line.getX2(), line.getY2());
if (boundingBox.contains(tempPoint1) || boundingBox.contains(tempPoint2))
return true;
for (Line2D l: getBorder(boundingBox)) {
if (l.intersectsLine(line))
return true;
}
return false;
}
/**
* Return true if the line crosses the envelope.
* i.e. one point is inside the box, one is outside.
*
* @param boundingBox An envelope.
* @param line A line2D
*
* @return True if the line crosses the envelope.
*/
public static boolean crosses(final GeneralEnvelope boundingBox, final Line2D line) {
final CoordinateReferenceSystem crs = boundingBox.getCoordinateReferenceSystem();
final GeneralDirectPosition tempPoint1 = new GeneralDirectPosition(line.getX1(), line.getY1());
tempPoint1.setCoordinateReferenceSystem(crs);
final GeneralDirectPosition tempPoint2 = new GeneralDirectPosition(line.getX2(), line.getY2());
tempPoint2.setCoordinateReferenceSystem(crs);
// for this case we look if the line have a point inside and a point outside
if ((boundingBox.contains(tempPoint1) && !boundingBox.contains(tempPoint2)) ||
(!boundingBox.contains(tempPoint1) && boundingBox.contains(tempPoint2)) )
return true;
return false;
}
/**
* Return true if the point crosses the envelope.
* i.e. the point is on the border of the envelope.
*
* @param boundingBox An envelope.
* @param point A GeneralDirectPosition.
*
* @return True if the point crosses the envelope.
*/
public static boolean crosses(final GeneralEnvelope boundingBox, final GeneralDirectPosition point) {
for (Line2D l : getBorder(boundingBox)) {
if (l.intersectsLine(point.getOrdinate(0), point.getOrdinate(1), point.getOrdinate(0), point.getOrdinate(1))) {
return true;
}
}
return false;
}
/**
* Return true if the bounding box contain the line.
* i.e. the two point of the line are inside the box.
*
* @param boundingBox An envelope.
* @param line A line2D
*
* @return True if the envelope contain the line.
*/
public static boolean contains(final GeneralEnvelope boundingBox, final Line2D line) {
final CoordinateReferenceSystem crs = boundingBox.getCoordinateReferenceSystem();
final GeneralDirectPosition tempPoint1 = new GeneralDirectPosition(line.getX1(), line.getY1());
tempPoint1.setCoordinateReferenceSystem(crs);
final GeneralDirectPosition tempPoint2 = new GeneralDirectPosition(line.getX2(), line.getY2());
tempPoint2.setCoordinateReferenceSystem(crs);
if ((boundingBox.contains(tempPoint1) && boundingBox.contains(tempPoint2)))
return true;
return false;
}
/**
* Return true if the two bounding box are in overlaps relation.
* i.e. the two box intersect but are not contained in each other.
*
* @param boundingBox1 An envelope.
* @param boundingBox2 An envelope.
*
* @return True if the two envelope are overlaping.
*/
public static boolean overlaps(final GeneralEnvelope boundingBox1, final GeneralEnvelope boundingBox2) {
if ((boundingBox1.contains(boundingBox2, true) || boundingBox2.contains(boundingBox1, true)))
return false;
if (boundingBox1.intersects(boundingBox2, true))
return true;
return false;
}
/**
* Utilities method witch compare two Line2D (replacing Line2D.equals())
*
* @param l1 The first line to compare.
* @param l2 The second line two compare.
*
* @return true if the coordinate of the two line are equals, or if the two line are null.
*/
public static boolean equalsLine(final Line2D l1, final Line2D l2) {
if (l1 == null && l2 == null) {
return true;
} else if (l1 == null || l2 == null) {
return false;
} else {
return l1.getX1() == l2.getX1() &&
l1.getX2() == l2.getX2() &&
l1.getY1() == l2.getY1() &&
l1.getY2() == l2.getY2();
}
}
/**
* Utilities method which write a line2D in a string replacing the Line2D.toString method.
*
* @param line A line to describe.
*
* @return A String representation of the line in the following format: x1=... y1=... x2=... y2=...
*/
public static String logLine2D(final Line2D line) {
if (line == null) {
return "null";
}
return "x1=" + line.getX1() + " y1=" + line.getY1() + " x2=" + line.getX2() + " y2=" + line.getY2();
}
/**
* Reproject the specified geometric object From the sourceCRS to the targetCRS.
*
* @param targetCRSName The coordinate reference system in which we have to reproject the geometry.
* @param sourceCRSName The current coordinate reference system of the geometry.
* @param geometry the geometric object to reproject.
*
* @return The geometric object reprojected in the target coordinate reference system.
*
* @throws org.opengis.referencing.NoSuchAuthorityCodeException
* @throws org.opengis.util.FactoryException
* @throws org.opengis.referencing.operation.TransformException
*/
public static Object reprojectGeometry(final String targetCRSName, final String sourceCRSName, final Object geometry) throws NoSuchAuthorityCodeException, FactoryException, TransformException {
final CoordinateReferenceSystem targetCRS = AbstractCRS.castOrCopy(CRS.forCode(targetCRSName)).forConvention(AxesConvention.RIGHT_HANDED);
final CoordinateReferenceSystem sourceCRS = AbstractCRS.castOrCopy(CRS.forCode(sourceCRSName)).forConvention(AxesConvention.RIGHT_HANDED);
if (geometry instanceof GeneralEnvelope) {
final GeneralEnvelope env = (GeneralEnvelope) geometry;
if (env.getCoordinateReferenceSystem() == null) {
env.setCoordinateReferenceSystem(sourceCRS);
}
return Envelopes.transform((GeneralEnvelope) geometry, targetCRS);
} else if (geometry instanceof GeneralDirectPosition) {
final CoordinateOperation operation;
try {
operation = CRS.findOperation(sourceCRS, targetCRS, null);
} catch (FactoryException exception) {
throw new TransformException("transform exception: " + exception.getMessage());
}
final MathTransform mt = operation.getMathTransform();
mt.transform((GeneralDirectPosition) geometry, (GeneralDirectPosition) geometry);
return geometry;
} else if (geometry instanceof Line2D) {
final Line2D line = (Line2D) geometry;
final GeneralDirectPosition pt1 = new GeneralDirectPosition(line.getX1(), line.getY1());
final GeneralDirectPosition pt2 = new GeneralDirectPosition(line.getX2(), line.getY2());
final CoordinateOperation operation;
try {
operation = CRS.findOperation(sourceCRS, targetCRS, null);
} catch (FactoryException exception) {
throw new TransformException("transform exception: " + exception.getMessage());
}
final MathTransform mt = operation.getMathTransform();
mt.transform(pt1, pt1);
mt.transform(pt2, pt2);
return new Line2D.Double(pt1.getOrdinate(0), pt1.getOrdinate(1), pt2.getOrdinate(0), pt2.getOrdinate(1));
} else {
throw new IllegalArgumentException("Unknow geometry types: allowed ones are: GeneralEnvelope, Line2D, GeneralDirectPosition");
}
}
/**
* Reproject the specified bbox string From the sourceCRS to the targetCRS.
*
* @param targetCRSName The coordinate reference system in which we have to reproject the geometry.
* @param sourceCRSName The current coordinate reference system of the geometry.
* @param boundingBox the bbox object to reproject as a string.
*
* @return The geometric object reprojected in the target coordinate reference system.
*
* @throws org.opengis.referencing.NoSuchAuthorityCodeException
* @throws org.opengis.util.FactoryException
* @throws org.opengis.referencing.operation.TransformException
*/
public static Object reprojectBbox2DString(final String sourceCRSName, final String targetCRSName, final String boundingBox) throws NoSuchAuthorityCodeException, FactoryException, TransformException {
final String[] bbox = boundingBox.split(",");
double[] lowerCorner ={Double.parseDouble(bbox[1]),Double.parseDouble(bbox[0])};
double[] upperCorner ={Double.parseDouble(bbox[3]),Double.parseDouble(bbox[2])};
GeneralEnvelope env = new GeneralEnvelope(lowerCorner,upperCorner);
env.setCoordinateReferenceSystem(AbstractCRS.castOrCopy(CRS.forCode(sourceCRSName)).forConvention(AxesConvention.RIGHT_HANDED));
env = (GeneralEnvelope) reprojectGeometry(targetCRSName, sourceCRSName, env);
lowerCorner = env.getLowerCorner().getCoordinate();
upperCorner = env.getUpperCorner().getCoordinate();
return lowerCorner[0]+","+lowerCorner[1]+","+upperCorner[0]+","+upperCorner[1];
}
/**
* Transform an OpenGIS envelope in JTS Geometry.
*
* @param env envelope to convert, expected to be 2D.
* @param resolution type of solution to use for envelope crossing the antemeridian.
* @return Geometry
*/
public static Geometry toJTSGeometry(Envelope env, WrapResolution resolution) {
return toJTSGeometry(env, resolution, true);
}
/**
* Transform an OpenGIS envelope in JTS Geometry.
*
* @param env envelope to convert, expected to be 2D.
* @param resolution type of solution to use for envelope crossing the antemeridian.
* @param insertMedianPoints if the envelope is very large (superior to half coordinate system wrap axis)
* the method will add 1 or 2 points to ensure each polygon segment direction is not ambiguous.
* @return Geometry
*/
public static Geometry toJTSGeometry(Envelope env, WrapResolution resolution, boolean insertMedianPoints) {
ArgumentChecks.ensureNonNull("resolution", resolution);
//most simple case, do not check anything
if(WrapResolution.NONE == resolution){
final double minX = env.getMinimum(0);
final double minY = env.getMinimum(1);
final double maxX = env.getMaximum(0);
final double maxY = env.getMaximum(1);
final Coordinate[] coordinates = new Coordinate[]{
new Coordinate(minX, minY),
new Coordinate(minX, maxY),
new Coordinate(maxX, maxY),
new Coordinate(maxX, minY),
new Coordinate(minX, minY),
};
return GF.createPolygon(GF.createLinearRing(coordinates), new LinearRing[0]);
}
final CoordinateReferenceSystem crs = env.getCoordinateReferenceSystem();
//ensure the envelope is correctely defined.
final GeneralEnvelope genv = new GeneralEnvelope(env);
genv.normalize();
if(WrapResolution.EXPAND == resolution){
genv.simplify();
final double minX = genv.getMinimum(0);
final double minY = genv.getMinimum(1);
final double maxX = genv.getMaximum(0);
final double maxY = genv.getMaximum(1);
return builEnvelopePiece(minX,minY,maxX,maxY,crs,insertMedianPoints);
}
final boolean wrapOnX = genv.getLowerCorner().getOrdinate(0) > genv.getUpperCorner().getOrdinate(0);
final boolean wrapOnY = genv.getLowerCorner().getOrdinate(1) > genv.getUpperCorner().getOrdinate(1);
if(!wrapOnX && !wrapOnY){
//no need for corrections
final double minX = env.getMinimum(0);
final double minY = env.getMinimum(1);
final double maxX = env.getMaximum(0);
final double maxY = env.getMaximum(1);
return builEnvelopePiece(minX,minY,maxX,maxY,crs,insertMedianPoints);
}
//find the wrap points and axis
final CoordinateSystemAxis axis = crs.getCoordinateSystem().getAxis(wrapOnX ? 0 : 1);
final double axisMin = axis.getMinimumValue();
final double axisMax = axis.getMaximumValue();
final double wrapRange = axis.getMaximumValue() - axis.getMinimumValue() ;
final DirectPosition lowerCorner = genv.getLowerCorner();
final DirectPosition upperCorner = genv.getUpperCorner();
if(WrapResolution.SPLIT == resolution){
double minX;
double minY;
double maxX;
double maxY;
if(wrapOnX){
minX = axisMin;
minY = lowerCorner.getOrdinate(1);
maxX = upperCorner.getOrdinate(0);
maxY = upperCorner.getOrdinate(1);
}else{
minX = lowerCorner.getOrdinate(0);
minY = axisMin;
maxX = upperCorner.getOrdinate(0);
maxY = upperCorner.getOrdinate(1);
}
final Polygon leftpoly = builEnvelopePiece(minX,minY,maxX,maxY,crs,insertMedianPoints);
if(wrapOnX){
minX = lowerCorner.getOrdinate(0);
minY = lowerCorner.getOrdinate(1);
maxX = axisMax;
maxY = upperCorner.getOrdinate(1);
}else{
minX = lowerCorner.getOrdinate(0);
minY = lowerCorner.getOrdinate(1);
maxX = upperCorner.getOrdinate(0);
maxY = axisMax;
}
final Polygon rightpoly = builEnvelopePiece(minX,minY,maxX,maxY,crs,insertMedianPoints);
return GF.createMultiPolygon(new Polygon[]{leftpoly,rightpoly});
}else if(WrapResolution.CONTIGUOUS == resolution){
final double minX;
final double minY;
final double maxX;
final double maxY;
if(wrapOnX){
minX = lowerCorner.getOrdinate(0);
minY = lowerCorner.getOrdinate(1);
maxX = upperCorner.getOrdinate(0) + wrapRange;
maxY = upperCorner.getOrdinate(1);
}else{
minX = lowerCorner.getOrdinate(0);
minY = lowerCorner.getOrdinate(1);
maxX = upperCorner.getOrdinate(0);
maxY = upperCorner.getOrdinate(1) + wrapRange;
}
return builEnvelopePiece(minX,minY,maxX,maxY,crs,insertMedianPoints);
}
throw new IllegalArgumentException("Unknowed or unset wrap resolution : "+resolution);
}
private static Polygon builEnvelopePiece(double minX, double minY, double maxX, double maxY,
CoordinateReferenceSystem crs, boolean insertMedianPoints){
final DirectPosition[] wrapPoints;
try {
wrapPoints = ReferencingUtilities.findWrapAround(crs);
} catch (TransformException ex) {
//normaly, that should never happen
throw new RuntimeException(ex.getMessage(),ex);
}
if(!insertMedianPoints || wrapPoints == null){
//not wrap points to insert
return buildEnvelope(minX, minY, maxX, maxY);
}
//check if we need to insert points
final boolean wrapOnX = wrapPoints[0].getOrdinate(0) != 0 || wrapPoints[1].getOrdinate(0) != 0;
if(wrapOnX){
final double wrapRange = wrapPoints[1].getOrdinate(0) - wrapPoints[0].getOrdinate(0);
final double envRange = maxX-minX;
final int nbPoint = (int)((envRange) / (wrapRange/2));
if(nbPoint==0) return buildEnvelope(minX, minY, maxX, maxY);
final Coordinate[] coordinates = new Coordinate[5+2*nbPoint];
int index=0;
coordinates[index++] = new Coordinate(minX, minY);
coordinates[index++] = new Coordinate(minX, maxY);
for(int i=0;i<nbPoint;i++){
coordinates[index++] = new Coordinate(minX+(i+1)*(envRange/(nbPoint+1)), maxY);
}
coordinates[index++] = new Coordinate(maxX, maxY);
coordinates[index++] = new Coordinate(maxX, minY);
for(int i=0;i<nbPoint;i++){
coordinates[index++] = new Coordinate(maxX-(i+1)*(envRange/(nbPoint+1)), minY);
}
coordinates[index++] = new Coordinate(minX, minY);
return GF.createPolygon(GF.createLinearRing(coordinates), new LinearRing[0]);
}else{
final double wrapRange = wrapPoints[1].getOrdinate(1) - wrapPoints[0].getOrdinate(1);
final double envRange = maxY-minY;
final int nbPoint = (int)((envRange) / wrapRange);
if(nbPoint==0) return buildEnvelope(minX, minY, maxX, maxY);
final Coordinate[] coordinates = new Coordinate[5+2*nbPoint];
int index=0;
coordinates[index++] = new Coordinate(minX, minY);
for(int i=0;i<nbPoint;i++){
coordinates[index++] = new Coordinate(minX, maxY+(i+1)*(envRange/(nbPoint+1)));
}
coordinates[index++] = new Coordinate(minX, maxY);
coordinates[index++] = new Coordinate(maxX, maxY);
for(int i=0;i<nbPoint;i++){
coordinates[index++] = new Coordinate(maxX, maxY-(i+1)*(envRange/(nbPoint+1)));
}
coordinates[index++] = new Coordinate(maxX, minY);
coordinates[index++] = new Coordinate(minX, minY);
return GF.createPolygon(GF.createLinearRing(coordinates), new LinearRing[0]);
}
}
private static Polygon buildEnvelope(double minX, double minY, double maxX, double maxY){
final Coordinate[] coordinates = new Coordinate[]{
new Coordinate(minX, minY),
new Coordinate(minX, maxY),
new Coordinate(maxX, maxY),
new Coordinate(maxX, minY),
new Coordinate(minX, minY),
};
return GF.createPolygon(GF.createLinearRing(coordinates), new LinearRing[0]);
}
}