package mil.nga.giat.geowave.analytic.mapreduce.dbscan;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
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.NeighborList;
import mil.nga.giat.geowave.analytic.nn.NeighborListFactory;
import mil.nga.giat.geowave.core.index.ByteArrayId;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
/**
*
* Maintains a single hull around a set of points.
*
* Intended to run in a single thread. Not Thread Safe.
*
*/
public class SingleItemClusterList extends
DBScanClusterList implements
Cluster
{
private boolean compressed = false;
private Set<Coordinate> clusterPoints = null;
public SingleItemClusterList(
final ByteArrayId centerId,
final ClusterItem center,
final NeighborListFactory<ClusterItem> factory,
final Map<ByteArrayId, Cluster> index ) {
super(
center.getGeometry() instanceof Point || center.isCompressed() ? center.getGeometry() : null,
(int) center.getCount(),
centerId,
index);
final Geometry clusterGeo = center.getGeometry();
compressed = center.isCompressed();
if (compressed) {
getClusterPoints(
true).add(
clusterGeo.getCentroid().getCoordinate());
}
}
protected Set<Coordinate> getClusterPoints(
boolean allowUpdates ) {
if (clusterPoints == null || clusterPoints == Collections.<Coordinate> emptySet())
clusterPoints = allowUpdates ? new HashSet<Coordinate>() : Collections.<Coordinate> emptySet();
return clusterPoints;
}
@Override
public void clear() {
super.clear();
clusterPoints = null;
}
@Override
protected long addAndFetchCount(
final ByteArrayId id,
final ClusterItem newInstance,
final DistanceProfile<?> distanceProfile ) {
final ClusterProfileContext context = (ClusterProfileContext) distanceProfile.getContext();
boolean checkForCompress = false;
final Coordinate centerCoordinate = context.getItem1() == newInstance ? context.getPoint2() : context
.getPoint1();
Geometry thisGeo = getGeometry();
// only need to cluster this new point if it is likely top be an
// inter-segment point
if (thisGeo == null || !(thisGeo instanceof Point)) {
checkForCompress = getClusterPoints(
true).add(
centerCoordinate);
}
// Closest distance points are only added if they are on a segment of a
// complex geometry.
if (!(newInstance.getGeometry() instanceof Point)) {
final Coordinate newInstanceCoordinate = context.getItem2() == newInstance ? context.getPoint2() : context
.getPoint1();
checkForCompress = getClusterPoints(
true).add(
newInstanceCoordinate);
}
if (checkForCompress) checkForCompression();
return 1;
}
@Override
public void merge(
Cluster cluster ) {
if (this == cluster) return;
final SingleItemClusterList singleItemCluster = ((SingleItemClusterList) cluster);
super.merge(cluster);
if (singleItemCluster.clusterGeo != null) {
getClusterPoints(
true).addAll(
Arrays.asList(singleItemCluster.clusterGeo.getCoordinates()));
}
Set<Coordinate> otherPoints = singleItemCluster.getClusterPoints(false);
if (otherPoints.size() > 0) {
// handle any remaining points
getClusterPoints(
true).addAll(
otherPoints);
}
checkForCompression();
}
public boolean isCompressed() {
return compressed;
}
public void finish() {
super.finish();
compressAndUpdate();
}
private void checkForCompression() {
if (getClusterPoints(
false).size() > 50) {
compressAndUpdate();
}
}
private void compressAndUpdate() {
clusterGeo = compress();
clusterPoints = null;
compressed = true;
}
protected Geometry compress() {
if (getClusterPoints(
false).size() > 0) {
return DBScanClusterList.getHullTool().createHullFromGeometry(
clusterGeo,
clusterPoints,
true);
}
return clusterGeo;
}
public static class SingleItemClusterListFactory implements
NeighborListFactory<ClusterItem>
{
private final Map<ByteArrayId, Cluster> index;
public SingleItemClusterListFactory(
final Map<ByteArrayId, Cluster> index ) {
super();
this.index = index;
}
public NeighborList<ClusterItem> buildNeighborList(
final ByteArrayId centerId,
final ClusterItem center ) {
Cluster list = index.get(centerId);
if (list == null) {
list = new SingleItemClusterList(
centerId,
center,
this,
index);
}
return list;
}
}
}