19 package org.sleuthkit.autopsy.geolocation;
21 import java.util.ArrayDeque;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.Deque;
27 import java.util.HashSet;
28 import java.util.Iterator;
30 import java.util.TreeSet;
55 private KdNode
root = null;
62 private static final Comparator<XYZPoint>
X_COMPARATOR =
new Comparator<XYZPoint>() {
67 public int compare(XYZPoint o1, XYZPoint o2) {
68 return Double.compare(o1.x, o2.x);
75 private static final Comparator<XYZPoint>
Y_COMPARATOR =
new Comparator<XYZPoint>() {
80 public int compare(XYZPoint o1, XYZPoint o2) {
81 return Double.compare(o1.y, o2.y);
88 private static final Comparator<XYZPoint>
Z_COMPARATOR =
new Comparator<XYZPoint>() {
93 public int compare(XYZPoint o1, XYZPoint o2) {
94 return Double.compare(o1.z, o2.z);
98 static final int X_AXIS = 0;
99 static final int Y_AXIS = 1;
100 static final int Z_AXIS = 2;
113 public boolean add(T value) {
119 root =
new KdNode(value, 0, null);
125 if (KdNode.compareTo(node.getDepth(), value, node.getPoint()) <= 0) {
127 if (node.getLesser() == null) {
128 KdNode newNode =
new KdNode(value, node.getDepth() + 1, node);
129 node.setLesser(newNode);
132 node = node.getLesser();
135 if (node.getGreater() == null) {
136 KdNode newNode =
new KdNode(value, node.getDepth() + 1, node);
137 node.setGreater(newNode);
140 node = node.getGreater();
155 if (value == null || root == null) {
159 KdNode node = getNode(
this, value);
160 return (node != null);
172 if (tree == null || tree.
getRoot() == null || value == null) {
178 if (node.getPoint().equals(value)) {
180 }
else if (KdNode.compareTo(node.getDepth(), value, node.getPoint()) <= 0) {
182 if (node.getLesser() == null) {
185 node = node.getLesser();
188 if (node.getGreater() == null) {
191 node = node.getGreater();
205 @SuppressWarnings(
"unchecked")
207 if (value == null || root == null) {
208 return Collections.EMPTY_LIST;
212 TreeSet<KdNode> results =
new TreeSet<>(
new EuclideanComparator(value));
217 while (node != null) {
218 if (KdNode.compareTo(node.getDepth(), value, node.getPoint()) <= 0) {
221 node = node.getLesser();
225 node = node.getGreater();
232 Set<KdNode> examined =
new HashSet<>();
236 while (node != null) {
238 searchNode(value, node, numNeighbors, results, examined);
239 node = node.getParent();
244 Collection<T> collection =
new ArrayList<>(numNeighbors);
245 for (KdNode kdNode : results) {
246 collection.add((T) kdNode.getPoint());
261 private <T extends
KdTree.
XYZPoint>
void searchNode(T value, KdNode node,
int numNeighbors, TreeSet<KdNode> results, Set<KdNode> examined) {
266 Double lastDistance = Double.MAX_VALUE;
267 if (results.size() > 0) {
268 lastNode = results.last();
271 Double nodeDistance = node.getPoint().euclideanDistance(value);
272 if (nodeDistance.compareTo(lastDistance) < 0) {
274 }
else if (nodeDistance.equals(lastDistance)) {
276 }
else if (results.size() < numNeighbors) {
279 lastNode = results.last();
280 lastDistance = lastNode.getPoint().euclideanDistance(value);
283 KdNode lesser = node.getLesser();
284 KdNode greater = node.getGreater();
288 if (lesser != null && !examined.contains(lesser)) {
289 examined.add(lesser);
292 double valuePlusDistance;
295 nodePoint = node.getPoint().x;
296 valuePlusDistance = value.x - lastDistance;
299 nodePoint = node.getPoint().y;
300 valuePlusDistance = value.y - lastDistance;
303 nodePoint = node.getPoint().z;
304 valuePlusDistance = value.z - lastDistance;
307 boolean lineIntersectsCube = valuePlusDistance <= nodePoint;
310 if (lineIntersectsCube) {
311 searchNode(value, lesser, numNeighbors, results, examined);
314 if (greater != null && !examined.contains(greater)) {
315 examined.add(greater);
318 double valuePlusDistance;
321 nodePoint = node.getPoint().x;
322 valuePlusDistance = value.x + lastDistance;
325 nodePoint = node.getPoint().y;
326 valuePlusDistance = value.y + lastDistance;
329 nodePoint = node.getPoint().z;
330 valuePlusDistance = value.z + lastDistance;
333 boolean lineIntersectsCube = valuePlusDistance >= nodePoint;
336 if (lineIntersectsCube) {
337 searchNode(value, greater, numNeighbors, results, examined);
350 @SuppressWarnings(
"unchecked")
351 private <T extends XYZPoint>
void search(final KdNode node, final Deque<T> results) {
353 results.add((T) node.getPoint());
354 search(node.getGreater(), results);
355 search(node.getLesser(), results);
378 if (d1.compareTo(d2) < 0) {
380 }
else if (d2.compareTo(d1) < 0) {
383 return o1.getPoint().
compareTo(o2.getPoint());
395 final Deque<T> results =
new ArrayDeque<>();
396 search(root, results);
397 return results.iterator();
407 final Deque<T> results =
new ArrayDeque<>();
408 search(root, results);
409 return results.descendingIterator();
419 public static class KdNode implements Comparable<KdNode> {
456 return X_COMPARATOR.compare(point1, point2);
458 return Y_COMPARATOR.compare(point1, point2);
460 return Z_COMPARATOR.compare(point1, point2);
489 void setLesser(
KdNode node) {
507 void setGreater(
KdNode node) {
526 XYZPoint getPoint() {
535 return 31 * (DIMENSIONS + this.depth + this.getPoint().
hashCode());
546 if (!(obj instanceof
KdNode)) {
550 KdNode kdNode = (
KdNode) obj;
559 return compareTo(depth, this.getPoint(), o.getPoint());
567 StringBuilder builder =
new StringBuilder(200);
568 builder.append(
"dimensions=").append(DIMENSIONS).append(
" depth=").append(depth).append(
" point=").append(getPoint().
toString());
569 return builder.toString();
578 public static class XYZPoint implements Comparable<XYZPoint> {
580 protected final double x;
581 protected final double y;
582 protected final double z;
590 public XYZPoint(Double latitude, Double longitude) {
650 double lat1Radians = Math.toRadians(o1.
getX());
651 double lat2Radians = Math.toRadians(o2.
getX());
653 double deltaLatRadians = Math.toRadians(o2.
getX() - o1.
getX());
654 double deltaLongRadians = Math.toRadians(o2.
getY() - o1.
getY());
656 double a = Math.sin(deltaLatRadians / 2) * Math.sin(deltaLatRadians / 2)
657 + Math.cos(lat1Radians) * Math.cos(lat2Radians)
658 * Math.sin(deltaLongRadians / 2) * Math.sin(deltaLongRadians / 2);
660 double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
662 return EARTH_RADIUS * c;
670 return 31 * (int) (this.x + this.y + this.z);
688 return ((Double.compare(
this.x, xyzPoint.
x) == 0)
689 && (Double.compare(
this.y, xyzPoint.
y) == 0)
690 && (Double.compare(
this.z, xyzPoint.
z) == 0));
698 int xComp = X_COMPARATOR.compare(
this, o);
703 int yComp = Y_COMPARATOR.compare(
this, o);
707 return Z_COMPARATOR.compare(
this, o);
715 StringBuilder builder =
new StringBuilder(200);
717 builder.append(x).append(
", ");
718 builder.append(y).append(
", ");
721 return builder.toString();
static int compareTo(int depth, XYZPoint point1, XYZPoint point2)
boolean equals(Object obj)
Collection< T > nearestNeighbourSearch(int numNeighbors, T value)
static final int DIMENSIONS
Iterator< T > reverse_iterator()
XYZPoint(Double latitude, Double longitude)
int compareTo(XYZPoint o)
boolean equals(Object obj)
double euclideanDistance(XYZPoint o1)
static final double EARTH_RADIUS
static double euclideanDistance(XYZPoint o1, XYZPoint o2)
boolean contains(T value)
static final Comparator< XYZPoint > Y_COMPARATOR
int compare(KdNode o1, KdNode o2)
static final Comparator< XYZPoint > X_COMPARATOR
static final Comparator< XYZPoint > Z_COMPARATOR
KdNode(XYZPoint point, int depth, KdNode parent)