package mil.nga.giat.geowave.core.geotime.store.query; import java.nio.ByteBuffer; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vividsolutions.jts.geom.Geometry; 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.GeometryUtils; import mil.nga.giat.geowave.core.geotime.store.filter.SpatialQueryFilter; import mil.nga.giat.geowave.core.geotime.store.filter.SpatialQueryFilter.CompareOperation; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.index.sfc.data.MultiDimensionalNumericData; import mil.nga.giat.geowave.core.store.dimension.NumericDimensionField; import mil.nga.giat.geowave.core.store.filter.DistributableQueryFilter; import mil.nga.giat.geowave.core.store.index.FilterableConstraints; import mil.nga.giat.geowave.core.store.query.BasicQuery; import mil.nga.giat.geowave.core.store.filter.BasicQueryFilter.BasicQueryCompareOperation; /** * The Spatial Query class represents a query in two dimensions. The constraint * that is applied represents an intersection operation on the query geometry. * */ public class SpatialQuery extends BasicQuery { private final static Logger LOGGER = LoggerFactory.getLogger(SpatialQuery.class); private Geometry queryGeometry; CompareOperation compareOp = CompareOperation.INTERSECTS; BasicQueryCompareOperation nonSpatialCompareOp = BasicQueryCompareOperation.INTERSECTS; /** * Convenience constructor used to construct a SpatialQuery object that has * an X and Y dimension (axis). * * @param queryGeometry * spatial geometry of the query */ public SpatialQuery( final Geometry queryGeometry ) { super( GeometryUtils.basicConstraintsFromGeometry(queryGeometry)); this.queryGeometry = queryGeometry; } public SpatialQuery( final Constraints constraints, final Geometry queryGeometry ) { super( constraints); this.queryGeometry = queryGeometry; } public SpatialQuery( final Geometry queryGeometry, Map<ByteArrayId, FilterableConstraints> additionalConstraints ) { super( GeometryUtils.basicConstraintsFromGeometry(queryGeometry), additionalConstraints); } /** * Convenience constructor used to construct a SpatialQuery object that has * an X and Y dimension (axis). * * @param queryGeometry * spatial geometry of the query * @param overlaps * if false, the only fully contained geometries are requested */ public SpatialQuery( final Geometry queryGeometry, final CompareOperation compareOp ) { super( GeometryUtils.basicConstraintsFromGeometry(queryGeometry)); this.queryGeometry = queryGeometry; this.compareOp = compareOp; } /** * Convenience constructor can be used when you already have linear * constraints for the query. The queryGeometry and compareOp is used for * fine grained post filtering. * * @param constraints * linear constraints * @param queryGeometry * spatial geometry of the query * @param compareOp * predicate associated query geometry */ public SpatialQuery( final Constraints constraints, final Geometry queryGeometry, final CompareOperation compareOp ) { this( constraints, queryGeometry, compareOp, BasicQueryCompareOperation.INTERSECTS); } /** * Convenience constructor can be used when you already have linear * constraints for the query. The queryGeometry and compareOp is used for * fine grained post filtering. * * @param constraints * linear constraints * @param queryGeometry * spatial geometry of the query * @param compareOp * predicate associated query geometry * @param nonSpatialCompareOp * predicate associated non-spatial fields (i.e Time) */ public SpatialQuery( final Constraints constraints, final Geometry queryGeometry, final CompareOperation compareOp, final BasicQueryCompareOperation nonSpatialCompareOp ) { super( constraints); this.queryGeometry = queryGeometry; this.compareOp = compareOp; this.nonSpatialCompareOp = nonSpatialCompareOp; } protected SpatialQuery() { super(); } /** * * @return queryGeometry the spatial geometry of the SpatialQuery object */ public Geometry getQueryGeometry() { return queryGeometry; } @Override protected DistributableQueryFilter createQueryFilter( final MultiDimensionalNumericData constraints, final NumericDimensionField<?>[] orderedConstrainedDimensionFields, final NumericDimensionField<?>[] unconstrainedDimensionDefinitions ) { return new SpatialQueryFilter( constraints, orderedConstrainedDimensionFields, unconstrainedDimensionDefinitions, queryGeometry, compareOp, nonSpatialCompareOp); } @Override public byte[] toBinary() { final byte[] superBinary = super.toBinary(); final byte[] geometryBinary = new WKBWriter().write(queryGeometry); final ByteBuffer buf = ByteBuffer.allocate(superBinary.length + geometryBinary.length + 3 * 4); buf.putInt(compareOp.ordinal()); buf.putInt(nonSpatialCompareOp.ordinal()); buf.putInt(superBinary.length); buf.put(superBinary); buf.put(geometryBinary); return buf.array(); } @Override public void fromBinary( final byte[] bytes ) { final ByteBuffer buf = ByteBuffer.wrap(bytes); compareOp = CompareOperation.values()[buf.getInt()]; nonSpatialCompareOp = BasicQueryCompareOperation.values()[buf.getInt()]; final int superLength = buf.getInt(); final byte[] superBinary = new byte[superLength]; buf.get(superBinary); super.fromBinary(superBinary); final byte[] geometryBinary = new byte[bytes.length - superLength - 3 * 4]; buf.get(geometryBinary); try { queryGeometry = new WKBReader().read(geometryBinary); } catch (final ParseException e) { LOGGER.warn( "Unable to read query geometry as well-known binary", e); } } }