com.datastax.driver.dse.geometry.Distance Maven / Gradle / Ivy
/*
* Copyright DataStax, Inc.
*
* This software can be used solely with DataStax Enterprise. Please consult the license at
* http://www.datastax.com/terms/datastax-dse-driver-license-terms
*/
package com.datastax.driver.dse.geometry;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
import com.datastax.driver.core.exceptions.InvalidTypeException;
import com.esri.core.geometry.MultiPath;
import com.esri.core.geometry.ogc.OGCGeometry;
import com.esri.core.geometry.ogc.OGCPoint;
import java.nio.ByteBuffer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* The driver-side representation of DSE's {@code Geo.distance}.
*
* This is a circle in a two-dimensional XY plane represented by its center point and radius. It
* is used as a search criteria to determine whether or not another geospatial object lies within a
* circular area.
*
*
Note that this shape has no equivalent in the OGC and GeoJSON standards: as a consequence,
* {@link #asWellKnownText()} returns a custom format, and {@link #asWellKnownBinary()}, and {@link
* #asGeoJson()} are not supported.
*/
public class Distance extends Geometry {
private static final long serialVersionUID = 1606167076672888314L;
private static final Pattern WKT_PATTERN =
Pattern.compile(
"distance *\\( *\\( *([\\d\\.-]+) *([\\d+\\.-]+) *\\) *([\\d+\\.-]+) *\\)",
CASE_INSENSITIVE);
/**
* Creates a distance from its Well-known
* Text (WKT) representation.
*
* @param source the Well-known Text representation to parse.
* @return the point represented by the WKT.
* @throws InvalidTypeException if the string does not contain a valid Well-known Text
* representation.
* @see Distance#asWellKnownText()
*/
public static Distance fromWellKnownText(String source) {
Matcher matcher = WKT_PATTERN.matcher(source.trim());
if (matcher.matches() && matcher.groupCount() == 3) {
try {
return new Distance(
new Point(Double.parseDouble(matcher.group(1)), Double.parseDouble(matcher.group(2))),
Double.parseDouble(matcher.group(3)));
} catch (NumberFormatException var3) {
throw new InvalidTypeException(String.format("Unable to parse %s", source));
}
} else {
throw new InvalidTypeException(String.format("Unable to parse %s", source));
}
}
private final Point center;
private final double radius;
/**
* Creates a new distance with the given center and radius.
*
* @param center The center point.
* @param radius The radius of the circle representing distance.
*/
public Distance(Point center, double radius) {
super(center.getOgcGeometry());
checkNotNull(center);
checkNotNull(radius);
this.center = center;
this.radius = radius;
}
/** @return The center point of the circle representing this distance. */
public Point getCenter() {
return center;
}
/** @return The radius of the circle representing this distance. */
public double getRadius() {
return radius;
}
/**
* Returns a Well-known Text (WKT)
* representation of this geospatial type.
*
*
Since there is no Well-known Text specification for Distance, this returns a custom format
* of:
*
*
DISTANCE((center.x center.y) radius)
*
* @return a Well-known Text representation of this object.
*/
@Override
public String asWellKnownText() {
return String.format("DISTANCE((%s %s) %s)", this.center.X(), this.center.Y(), this.radius);
}
/**
* The distance type has no equivalent in the OGC standard: this method throws an {@link
* UnsupportedOperationException}.
*/
@Override
OGCGeometry getOgcGeometry() {
throw new UnsupportedOperationException();
}
/**
* The distance type has no equivalent in the OGC standard: this method throws an {@link
* UnsupportedOperationException}.
*/
@Override
public ByteBuffer asWellKnownBinary() {
throw new UnsupportedOperationException();
}
/**
* The distance type has no equivalent in the GeoJSON standard: this method throws an {@link
* UnsupportedOperationException}.
*/
@Override
public String asGeoJson() {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
Distance distance = (Distance) o;
return Double.compare(distance.radius, this.radius) == 0
&& this.center.equals(distance.center);
} else {
return false;
}
}
@Override
public int hashCode() {
int result = this.center.hashCode();
long temp = Double.doubleToLongBits(this.radius);
result = 31 * result + (int) (temp ^ temp >>> 32);
return result;
}
@SuppressWarnings("SimplifiableConditionalExpression")
@Override
public boolean contains(Geometry geometry) {
return geometry instanceof Distance
? this.containsDistance((Distance) geometry)
: geometry instanceof Point
? this.containsPoint((Point) geometry)
: geometry instanceof LineString
? this.containsLineString((LineString) geometry)
: geometry instanceof Polygon ? this.containsPolygon((Polygon) geometry) : false;
}
private boolean containsDistance(Distance distance) {
return this.center.getOgcGeometry().distance(distance.center.getOgcGeometry()) + distance.radius
<= this.radius;
}
private boolean containsPoint(Point point) {
return this.containsOGCPoint(point.getOgcGeometry());
}
private boolean containsLineString(LineString lineString) {
MultiPath multiPath = (MultiPath) lineString.getOgcGeometry().getEsriGeometry();
return containsMultiPath(multiPath);
}
private boolean containsPolygon(Polygon polygon) {
MultiPath multiPath =
(com.esri.core.geometry.Polygon) polygon.getOgcGeometry().getEsriGeometry();
return containsMultiPath(multiPath);
}
private boolean containsMultiPath(MultiPath multiPath) {
int numPoints = multiPath.getPointCount();
for (int i = 0; i < numPoints; ++i) {
OGCPoint point = new OGCPoint(multiPath.getPoint(i), Geometry.SPATIAL_REFERENCE_4326);
if (!this.containsOGCPoint(point)) {
return false;
}
}
return true;
}
private boolean containsOGCPoint(OGCPoint point) {
return this.center.getOgcGeometry().distance(point) <= this.radius;
}
/**
* This object gets replaced by an internal proxy for serialization.
*
* @serialData Point (wkb) for center followed by double for radius
*/
private Object writeReplace() {
return new DistanceSerializationProxy(this);
}
}