package mil.nga.giat.geowave.analytic.mapreduce.dbscan;
import mil.nga.giat.geowave.analytic.distance.CoordinateCircleDistanceFn;
import mil.nga.giat.geowave.analytic.distance.DistanceFn;
import mil.nga.giat.geowave.analytic.mapreduce.dbscan.ClusterItemDistanceFn.ClusterProfileContext;
import mil.nga.giat.geowave.analytic.nn.DistanceProfile;
import mil.nga.giat.geowave.analytic.nn.DistanceProfileGenerateFn;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.operation.distance.DistanceOp;
/**
* Calculate distance between two cluster items.
*/
public class ClusterItemDistanceFn implements
DistanceFn<ClusterItem>,
DistanceProfileGenerateFn<ClusterProfileContext, ClusterItem>
{
/**
*
*/
private static final long serialVersionUID = 3824608959408031752L;
private DistanceFn<Coordinate> coordinateDistanceFunction = new CoordinateCircleDistanceFn();
/**
* Used to reduce memory GC
*/
private static final ThreadLocal<DistanceProfile<ClusterProfileContext>> profile = new ThreadLocal<DistanceProfile<ClusterProfileContext>>() {
@Override
protected DistanceProfile<ClusterProfileContext> initialValue() {
return new DistanceProfile<ClusterProfileContext>(
0.0,
new ClusterProfileContext());
}
};
public ClusterItemDistanceFn() {}
public ClusterItemDistanceFn(
final DistanceFn<Coordinate> coordinateDistanceFunction ) {
super();
this.coordinateDistanceFunction = coordinateDistanceFunction;
}
public DistanceFn<Coordinate> getCoordinateDistanceFunction() {
return coordinateDistanceFunction;
}
public void setCoordinateDistanceFunction(
final DistanceFn<Coordinate> coordinateDistanceFunction ) {
this.coordinateDistanceFunction = coordinateDistanceFunction;
}
@Override
public double measure(
final ClusterItem x,
final ClusterItem y ) {
final Geometry gx = x.getGeometry();
final Geometry gy = y.getGeometry();
if (gx instanceof Point && gy instanceof Point) {
return coordinateDistanceFunction.measure(
gx.getCoordinate(),
gy.getCoordinate());
}
final DistanceOp op = new DistanceOp(
gx,
gy);
Coordinate[] points = op.nearestPoints();
return coordinateDistanceFunction.measure(
points[0],
points[1]);
}
@Override
public DistanceProfile<ClusterProfileContext> computeProfile(
ClusterItem item1,
ClusterItem item2 ) {
DistanceProfile<ClusterProfileContext> localProfile = profile.get();
ClusterProfileContext context = localProfile.getContext();
final Geometry gx = item1.getGeometry();
final Geometry gy = item2.getGeometry();
context.setItem1(item1);
context.setItem2(item2);
if (gx instanceof Point && gy instanceof Point) {
context.setPoint1(gx.getCoordinate());
context.setPoint2(gy.getCoordinate());
}
else {
final DistanceOp op = new DistanceOp(
gx,
gy);
Coordinate[] points = op.nearestPoints();
context.setPoint1(points[0]);
context.setPoint2(points[1]);
}
localProfile.setDistance(coordinateDistanceFunction.measure(
context.getPoint1(),
context.getPoint2()));
return localProfile;
}
public static class ClusterProfileContext
{
private Coordinate point1;
private ClusterItem item1;
private Coordinate point2;
private ClusterItem item2;
public Coordinate getPoint1() {
return point1;
}
public void setPoint1(
Coordinate point1 ) {
this.point1 = point1;
}
public ClusterItem getItem1() {
return item1;
}
public void setItem1(
ClusterItem item1 ) {
this.item1 = item1;
}
public Coordinate getPoint2() {
return point2;
}
public void setPoint2(
Coordinate point2 ) {
this.point2 = point2;
}
public ClusterItem getItem2() {
return item2;
}
public void setItem2(
ClusterItem item2 ) {
this.item2 = item2;
}
}
}