package mil.nga.giat.geowave.core.geotime;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKBReader;
import com.vividsolutions.jts.io.WKBWriter;
import mil.nga.giat.geowave.core.geotime.index.dimension.LatitudeDefinition;
import mil.nga.giat.geowave.core.geotime.index.dimension.LongitudeDefinition;
import mil.nga.giat.geowave.core.index.dimension.NumericDimensionDefinition;
import mil.nga.giat.geowave.core.index.sfc.data.NumericData;
import mil.nga.giat.geowave.core.index.sfc.data.NumericRange;
import mil.nga.giat.geowave.core.index.sfc.data.NumericValue;
import mil.nga.giat.geowave.core.store.query.BasicQuery.ConstraintData;
import mil.nga.giat.geowave.core.store.query.BasicQuery.ConstraintSet;
import mil.nga.giat.geowave.core.store.query.BasicQuery.Constraints;
/**
* This class contains a set of Geometry utility methods that are generally
* useful throughout the GeoWave core codebase
*/
public class GeometryUtils
{
public static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();
private final static Logger LOGGER = LoggerFactory.getLogger(GeometryUtils.class);
private static final int DEFAULT_DIMENSIONALITY = 2;
public static Constraints basicConstraintsFromGeometry(
final Geometry geometry ) {
final List<ConstraintSet> set = new LinkedList<ConstraintSet>();
constructListOfConstraintSetsFromGeometry(
geometry,
set,
false);
return new Constraints(
set);
}
/**
* This utility method will convert a JTS geometry to contraints that can be
* used in a GeoWave query.
*
* @return Constraints as a mapping of NumericData objects representing
* ranges for a latitude dimension and a longitude dimension
*/
public static GeoConstraintsWrapper basicGeoConstraintsWrapperFromGeometry(
final Geometry geometry ) {
final List<ConstraintSet> set = new LinkedList<ConstraintSet>();
final boolean geometryConstraintsExactMatch = constructListOfConstraintSetsFromGeometry(
geometry,
set,
true);
return new GeoConstraintsWrapper(
new Constraints(
set),
geometryConstraintsExactMatch,
geometry);
}
/**
* Recursively decompose geometry into a set of envelopes to create a single
* set.
*
* @param geometry
* @param destinationListOfSets
* @param checkTopoEquality
*/
private static boolean constructListOfConstraintSetsFromGeometry(
final Geometry geometry,
final List<ConstraintSet> destinationListOfSets,
final boolean checkTopoEquality ) {
// Get the envelope of the geometry being held
final int n = geometry.getNumGeometries();
boolean retVal = true;
if (n > 1) {
retVal = false;
for (int gi = 0; gi < n; gi++) {
constructListOfConstraintSetsFromGeometry(
geometry.getGeometryN(gi),
destinationListOfSets,
checkTopoEquality);
}
}
else {
final Envelope env = geometry.getEnvelopeInternal();
destinationListOfSets.add(basicConstraintSetFromEnvelope(env));
if (checkTopoEquality) {
retVal = new GeometryFactory().toGeometry(
env).equalsTopo(
geometry);
}
}
return retVal;
}
/**
* This utility method will convert a JTS envelope to contraints that can be
* used in a GeoWave query.
*
* @return Constraints as a mapping of NumericData objects representing
* ranges for a latitude dimension and a longitude dimension
*/
public static ConstraintSet basicConstraintSetFromEnvelope(
final Envelope env ) {
// Create a NumericRange object using the x axis
final NumericRange rangeLongitude = new NumericRange(
env.getMinX(),
env.getMaxX());
// Create a NumericRange object using the y axis
final NumericRange rangeLatitude = new NumericRange(
env.getMinY(),
env.getMaxY());
final Map<Class<? extends NumericDimensionDefinition>, ConstraintData> constraintsPerDimension = new HashMap<Class<? extends NumericDimensionDefinition>, ConstraintData>();
// Create and return a new IndexRange array with an x and y axis
// range
constraintsPerDimension.put(
LongitudeDefinition.class,
new ConstraintData(
rangeLongitude,
false));
constraintsPerDimension.put(
LatitudeDefinition.class,
new ConstraintData(
rangeLatitude,
false));
return new ConstraintSet(
constraintsPerDimension);
}
/**
* This utility method will convert a JTS envelope to contraints that can be
* used in a GeoWave query.
*
* @return Constraints as a mapping of NumericData objects representing
* ranges for a latitude dimension and a longitude dimension
*/
public static Constraints basicConstraintsFromEnvelope(
final Envelope env ) {
return new Constraints(
basicConstraintSetFromEnvelope(env));
}
/**
* This utility method will convert a JTS envelope to that can be used in a
* GeoWave query.
*
* @return Constraints as a mapping of NumericData objects representing
* ranges for a latitude dimension and a longitude dimension
*/
public static ConstraintSet basicConstraintsFromPoint(
final double latitudeDegrees,
final double longitudeDegrees ) {
// Create a NumericData object using the x axis
final NumericData latitude = new NumericValue(
latitudeDegrees);
// Create a NumericData object using the y axis
final NumericData longitude = new NumericValue(
longitudeDegrees);
final Map<Class<? extends NumericDimensionDefinition>, ConstraintData> constraintsPerDimension = new HashMap<Class<? extends NumericDimensionDefinition>, ConstraintData>();
// Create and return a new IndexRange array with an x and y axis
// range
constraintsPerDimension.put(
LongitudeDefinition.class,
new ConstraintData(
longitude,
false));
constraintsPerDimension.put(
LatitudeDefinition.class,
new ConstraintData(
latitude,
false));
return new ConstraintSet(
constraintsPerDimension);
}
/**
* Generate a longitude range from a JTS geometry
*
* @param geometry
* The JTS geometry
* @return The longitude range in EPSG:4326
*/
public static NumericData longitudeRangeFromGeometry(
final Geometry geometry ) {
if ((geometry == null) || geometry.isEmpty()) {
return new NumericRange(
0,
0);
}
// Get the envelope of the geometry being held
final Envelope env = geometry.getEnvelopeInternal();
// Create a NumericRange object using the x axis
return new NumericRange(
env.getMinX(),
env.getMaxX());
}
/**
* Generate a latitude range from a JTS geometry
*
* @param geometry
* The JTS geometry
* @return The latitude range in EPSG:4326
*/
public static NumericData latitudeRangeFromGeometry(
final Geometry geometry ) {
if ((geometry == null) || geometry.isEmpty()) {
return new NumericRange(
0,
0);
}
// Get the envelope of the geometry being held
final Envelope env = geometry.getEnvelopeInternal();
// Create a NumericRange object using the y axis
return new NumericRange(
env.getMinY(),
env.getMaxY());
}
/**
* Converts a JTS geometry to binary using JTS a Well Known Binary writer
*
* @param geometry
* The JTS geometry
* @return The binary representation of the geometry
*/
public static byte[] geometryToBinary(
final Geometry geometry ) {
int dimensions = DEFAULT_DIMENSIONALITY;
if (!geometry.isEmpty()) {
dimensions = Double.isNaN(geometry.getCoordinate().getOrdinate(
Coordinate.Z)) ? 2 : 3;
}
return new WKBWriter(
dimensions).write(geometry);
}
/**
* Converts a byte array as well-known binary to a JTS geometry
*
* @param binary
* The well known binary
* @return The JTS geometry
*/
public static Geometry geometryFromBinary(
final byte[] binary ) {
try {
return new WKBReader().read(binary);
}
catch (final ParseException e) {
LOGGER.warn(
"Unable to deserialize geometry data",
e);
}
return null;
}
/**
* This mehtod returns an envelope between negative infinite and positive
* inifinity in both x and y
*
* @return the infinite bounding box
*/
public static Geometry infinity() {
// unless we make this synchronized, we will want to instantiate a new
// geometry factory because geometry factories are not thread safe
return new GeometryFactory().toGeometry(new Envelope(
Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY,
Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY));
}
public static class GeoConstraintsWrapper
{
private final Constraints constraints;
private final boolean constraintsMatchGeometry;
private final Geometry jtsBounds;
public GeoConstraintsWrapper(
final Constraints constraints,
final boolean constraintsMatchGeometry,
final Geometry jtsBounds ) {
this.constraints = constraints;
this.constraintsMatchGeometry = constraintsMatchGeometry;
this.jtsBounds = jtsBounds;
}
public Constraints getConstraints() {
return constraints;
}
public boolean isConstraintsMatchGeometry() {
return constraintsMatchGeometry;
}
public Geometry getGeometry() {
return jtsBounds;
}
}
}