Envelope¶
Envelopes are used to represent the bounds of a geometry, and are used very frequently as a way to quickly check if a geometry is in the area you are interested in.
GeoTools, through virtue of reusing code, has two “Envelope” implementations to contend with:
Java
Rectangle2D
- we haveGeneralBounds
which is spatial extension of the JavaRectangle2D
class. Our implementation makes use of doubles to store coordinates and holds on to aCoordinateReferenceSystem
allowing us to tell where the coordinates are located.JTS
Envelope
- we haveReferencedEnvelope
which is adds aCoordinateReferenceSystem
to a traditional JTS Envelope. A subclass ofReferencedEnvelope
isReferencedEnvelope3D
which supports a third dimension on top of the regular two dimensions.
As you can see ReferencedEnvelope
and ReferencedEnvelope3D
are a bit of a olive branch between the JTS Geometry model and ISO Geometry model.
You will find other “Rectangles” around as you make use of GeoTools in a real world application.
Java
Rectangles
Java
Rectangles
record x,y,w,h:Rectangle2D
Rectangle2D.Double
rectangle working with doublesRectangle2D.Float
rectangle working with floats
GeneralBounds
we have a spatial specific version ofRectangle2D
that implements ISO Geometry EnvelopeRectangle
the original rectangle for working on the screen, measured in integer pixels.
ReferencedEnvelope¶
ReferencedEnvelope
is all of these:
org.locationtech.jts.geom.Envelope
- as defined by the JTS Topology System ( a Simple Feature for SQL concept)org.geotools.api.geometry.BoundingBox
- 2D bounds as defined by the ISO 19107 Geometryorg.geotools.api.geometry.Envelope
- captures 3D bounds as defined by ISO 19107 Geometry.
Note that in order to support 3D bounds (and use a 3D Coordinate Reference System) we must create an instance of the child class ReferencedEnvelope3D
(see below).
In short this is the class to use when you want to represent a bounds in GeoTools. The only other thing of note is the that the constructor expects the input in xMin,xMax,yMin,yMax
order and expects a (2D) CRS:
ReferencedEnvelope envelope =
new ReferencedEnvelope(0, 10, 0, 20, DefaultGeographicCRS.WGS84);
double xMin = envelope.getMinX();
double yMin = envelope.getMinY();
double xMax = envelope.getMaxX();
double yMax = envelope.getMaxY();
double width = envelope.getWidth();
double height = envelope.getHeight();
double xCenter = envelope.getMedian(0);
double yCenter = envelope.getMedian(1);
CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
int dimension = envelope.getDimension();
// Direct access to internal upper and lower positions
Position lower = envelope.getLowerCorner();
Position upper = envelope.getUpperCorner();
// expand to include 15, 30
envelope.include(15, 30);
envelope.isEmpty(); // check if storing width and height are 0
envelope.isNull(); // check if "null" (not storing anything)
envelope.setToNull();
ReferencedEnvelope
does one thing very well; it is an Envelope
that has a CoordinateReferenceSystem
. Because it has a CoordinateReferenceSystem
you can quickly transform it between projections.:
CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");
ReferencedEnvelope envelope = new ReferencedEnvelope(0, 10, 0, 20, sourceCRS);
// Transform using 10 sample points around the envelope
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:23032");
ReferencedEnvelope result = envelope.transform(targetCRS, true, 10);
ReferencedEnvelope
is used in a lot of GeoTools interfaces, basically anywhere we can get away
with it. Using a raw JTS Envelope
without knowing the CoordinateReferenceSystem
is difficult to
use as the information is incomplete forcing client code to make assumptions. Some code assumes
the envelope is in WGS84 while other code assumes it is in the same Coordinate Reference System as
the data being worked on.
Some of our older interfaces that you are forced to read the javadocs in order to figure out the
CoordinateReferenceSystem
for a returned Envelope.
Using a
FeatureSource
withoutReferencedEnvelope
example:Envelope bounds = featureSource.getBounds(); CoordinateReferenceSystem crs = featureSource.getSchema().getDefaultGeometry().getCoordinateSystem();
Using a
FeatureSource
withReferencedEnvelope
:ReferencedEnvelope bounds = (ReferencedEnvelope) featureSource.getBounds(); CoordinateReferenceSystem crs = bounds.getCoordinateReferenceSystem();
ReferencedEnvelope3D¶
ReferencedEnvelope3D
is all of these:
ReferencedEnvelope
including all parent classes and interfacesorg.geotools.api.geometry.BoundingBox3D
- 3D bounds as defined by the ISO 19107 Geometry
This is the class to use when you want to represent a 3D bounds in GeoTools. The constructor expects the input in xMin,xMax,yMin,yMax,zMin,zMax
order and expects a 3D CRS:
ReferencedEnvelope3D envelope =
new ReferencedEnvelope3D(0, 10, 0, 20, 0, 30, DefaultGeographicCRS.WGS84_3D);
double xMin = envelope.getMinX();
double yMin = envelope.getMinY();
double zMin = envelope.getMinZ();
double xMax = envelope.getMaxX();
double yMax = envelope.getMaxY();
double zMax = envelope.getMaxZ();
double width = envelope.getWidth();
double height = envelope.getHeight();
double depth = envelope.getDepth();
double xCenter = envelope.getMedian(0);
double yCenter = envelope.getMedian(1);
double zCenter = envelope.getMedian(2);
CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
int dimension = envelope.getDimension();
// Direct access to internal upper and lower positions
Position lower = envelope.getLowerCorner();
Position upper = envelope.getUpperCorner();
// expand to include 15, 30, 40
envelope.include(15, 30, 40);
envelope.isEmpty(); // check if storing width and height are 0
envelope.isNull(); // check if "null" (not storing anything)
envelope.setToNull();
As explained above, when using a 3D CRS we must create an instance of ReferencedEnvelope3D
and not of its parent class. If we are not sure what dimension we are dealing with,
there are safe ways to create, copy, convert or reference ReferencedEnvelope
instances:
ReferencedEnvelope
env; // can hold both regular ReferencedEnvelope as well as ReferencedEnvelope3D
ReferencedEnvelope original = null; // can be instance of ReferencedEnvelope3D;
CoordinateReferenceSystem crs = null; // can be 2D or 3D
Bounds opengis_env = null; // can be instance of ReferencedEnvelope(3D)
org.locationtech.jts.geom.Envelope jts_env =
null; // can be instance of ReferencedEnvelope(3D)
BoundingBox bbox = null; // can be instance of ReferencedEnvelope(3D)
// safely copy ReferencedEnvelope, uses type of original to determine type
env = ReferencedEnvelope.create(original);
// safely create ReferencedEnvelope from CRS, uses dimension to determine type
env = ReferencedEnvelope.create(crs);
// safely create ReferencedEnvelope from org.geotools.api.geometry.Envelope, uses dimension
// in
// Envelope to determine type
env = ReferencedEnvelope.create(opengis_env, crs);
// safely create ReferencedEnvelope from org.locationtech.jts.geom.Envelope, uses
// dimension in Envelope to determine type
env = ReferencedEnvelope.envelope(jts_env, crs);
// safely reference org.geotools.api.geometry.Envelope as ReferencedEnvelope
// --> if it is a ReferencedEnvelope(3D), simply cast it; if not, create a conversion
env = ReferencedEnvelope.reference(opengis_env);
// safely reference org.locationtech.jts.geom.Envelope as ReferencedEnvelope
// --> if it is a ReferencedEnvelope(3D), simply cast it; if not, create a conversion
env = ReferencedEnvelope.reference(jts_env);
// safely reference BoundingBox as ReferencedEnvelope
// --> if it is a ReferencedEnvelope(3D), simply cast it; if not, create a conversion
env = ReferencedEnvelope.reference(bbox);
OpenGIS Envelope¶
OpenGIS records a “rectangle” as a bounds along the axis mentioned by the CoordinateReferenceSystem
object. You can use this idea to record a simple rectangle in space, a height range and a range in time as needed.
GeneralBounds
- was introduced above; used to bridge to Java2DReferencedEnvelope
- was introduced above; used to bridge to JTS GeometryGeneralEnvelope
- allows you to record spans in multiple dimensions (think depth, height or time)
Since Envelope
is just and interface, so we will use ReferencedEnvelope
for the example:
CoordinateReferenceSystem wsg84 = CRS.decode("EPSG:4326"); Bounds envelope = new ReferencedEnvelope(0, 10, 0, 20, wsg84); double xMin = envelope.getMinimum(0); double yMin = envelope.getMinimum(1); double xMax = envelope.getMaximum(0); double yMax = envelope.getMaximum(1); double width = envelope.getSpan(0); double height = envelope.getSpan(1); double xCenter = envelope.getMedian(0); double yCenter = envelope.getMedian(1); CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem(); // Direct access to internal upper and lower positions Position lower = envelope.getLowerCorner(); Position upper = envelope.getUpperCorner(); // expand to include 15, 30 upper.setOrdinate(0, Math.max(upper.getOrdinate(0), 15)); upper.setOrdinate(1, Math.max(upper.getOrdinate(1), 30)); lower.setOrdinate(0, Math.min(lower.getOrdinate(0), 15)); lower.setOrdinate(1, Math.min(lower.getOrdinate(1), 30));
You can see even in a simple example we should be looking at the CRS to figure out what the axis is actually measuring.
If you are super confident that you are working with data in X/Y order you can directly make use of BoundingBox
box. BoundingBox
is an extension of Envelope
for working with 2D data, and it has been made method compatible with JTS Envelope
where possible.
Since BoundingBox
is just an interface, so we will use ReferencedEnvelope
for this example:
CoordinateReferenceSystem wsg84 = CRS.decode("EPSG:4326"); org.geotools.api.geometry.BoundingBox bbox = new ReferencedEnvelope(0, 10, 0, 20, wsg84); double xMin = bbox.getMinX(); double yMin = bbox.getMinY(); double xMax = bbox.getMaxX(); double yMax = bbox.getMaxY(); double width = bbox.getWidth(); double height = bbox.getHeight(); double xCenter = bbox.getMedian(0); double yCenter = bbox.getMedian(1); CoordinateReferenceSystem crs = bbox.getCoordinateReferenceSystem(); // Direct access to internal upper and lower positions Position lower = bbox.getLowerCorner(); Position upper = bbox.getUpperCorner(); // expand to include 15, 30 bbox.include(15, 30);
JTS Envelope¶
The JTS Topology Suite has the concept of an Envelope
recorded in x1,x2, y1,y2
order.
You can see that the use of JTS Envelope
has the same “assumptions” as the use of BoundingBox
above.:
org.locationtech.jts.geom.Envelope envelope = new Envelope(0, 10, 0, 20); double xMin = envelope.getMinX(); double yMin = envelope.getMinY(); double xMax = envelope.getMaxX(); double yMax = envelope.getMaxY(); double width = envelope.getWidth(); // assuming axis 0 is easting double height = envelope.getHeight(); // assuming axis 1 is nothing // Expand an existing envelope Envelope bbox = new Envelope(); envelope.expandToInclude(bbox); // Use envelope.covers(5, 10); // inside or on edge! envelope.contains(5, 10); // inside only // Null envelope.isNull(); // check if "null" (not storing anything) envelope.setToNull();
Transform an Envelope
using the JTS Utility
class:
CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326"); CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:23032"); Envelope envelope = new Envelope(0, 10, 0, 20); MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS); Envelope quick = JTS.transform(envelope, transform); // Sample 10 points around the envelope Envelope better = JTS.transform(envelope, null, transform, 10);