19 package org.sleuthkit.autopsy.geolocation;
21 import static java.lang.Math.cos;
22 import static java.lang.Math.sin;
24 import java.util.ArrayDeque;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.Deque;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.List;
34 import java.util.TreeSet;
55 private KdNode
root = null;
57 private static final Comparator<XYZPoint>
X_COMPARATOR =
new Comparator<XYZPoint>() {
62 public int compare(XYZPoint o1, XYZPoint o2) {
71 private static final Comparator<XYZPoint>
Y_COMPARATOR =
new Comparator<XYZPoint>() {
76 public int compare(XYZPoint o1, XYZPoint o2) {
85 private static final Comparator<XYZPoint>
Z_COMPARATOR =
new Comparator<XYZPoint>() {
90 public int compare(XYZPoint o1, XYZPoint o2) {
99 protected static final int X_AXIS = 0;
129 public KdTree(List<XYZPoint> list,
int k) {
145 private static KdNode
createNode(List<XYZPoint> list,
int k,
int depth) {
146 if (list == null || list.size() == 0)
149 int axis = depth %
k;
151 Collections.sort(list, X_COMPARATOR);
152 else if (axis == Y_AXIS)
153 Collections.sort(list, Y_COMPARATOR);
155 Collections.sort(list, Z_COMPARATOR);
158 List<XYZPoint> less =
new ArrayList<XYZPoint>(list.size());
159 List<XYZPoint> more =
new ArrayList<XYZPoint>(list.size());
160 if (list.size() > 0) {
161 int medianIndex = list.size() / 2;
162 node =
new KdNode(list.get(medianIndex),
k, depth);
164 for (
int i = 0; i < list.size(); i++) {
165 if (i == medianIndex)
167 XYZPoint p = list.get(i);
169 if (KdNode.compareTo(depth, k, p, node.id) <= 0) {
176 if ((medianIndex-1 >= 0) && less.size() > 0) {
178 node.lesser.parent = node;
181 if ((medianIndex <= list.size()-1) && more.size() > 0) {
182 node.greater =
createNode(more, k, depth + 1);
183 node.greater.parent = node;
197 public boolean add(T value) {
202 root =
new KdNode(value);
208 if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) {
210 if (node.lesser == null) {
211 KdNode newNode =
new KdNode(value, k, node.depth + 1);
212 newNode.parent = node;
213 node.lesser = newNode;
219 if (node.greater == null) {
220 KdNode newNode =
new KdNode(value, k, node.depth + 1);
221 newNode.parent = node;
222 node.greater = newNode;
240 if (value == null || root == null)
243 KdNode node =
getNode(
this, value);
244 return (node != null);
257 if (tree == null || tree.
root == null || value == null)
260 KdNode node = tree.
root;
262 if (node.id.equals(value)) {
264 }
else if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) {
266 if (node.lesser == null) {
272 if (node.greater == null) {
287 public boolean remove(T value) {
288 if (value == null || root == null)
291 KdNode node =
getNode(
this, value);
295 KdNode parent = node.parent;
296 if (parent != null) {
297 if (parent.lesser != null && node.equals(parent.lesser)) {
298 List<XYZPoint> nodes =
getTree(node);
299 if (nodes.size() > 0) {
300 parent.lesser =
createNode(nodes, node.k, node.depth);
301 if (parent.lesser != null) {
302 parent.lesser.parent = parent;
305 parent.lesser = null;
308 List<XYZPoint> nodes =
getTree(node);
309 if (nodes.size() > 0) {
310 parent.greater =
createNode(nodes, node.k, node.depth);
311 if (parent.greater != null) {
312 parent.greater.parent = parent;
315 parent.greater = null;
320 List<XYZPoint> nodes =
getTree(node);
321 if (nodes.size() > 0)
337 private static final List<XYZPoint>
getTree(KdNode root) {
338 List<XYZPoint> list =
new ArrayList<XYZPoint>();
342 if (root.lesser != null) {
343 list.add(root.lesser.id);
344 list.addAll(
getTree(root.lesser));
346 if (root.greater != null) {
347 list.add(root.greater.id);
348 list.addAll(
getTree(root.greater));
364 @SuppressWarnings(
"unchecked")
366 if (value == null || root == null)
367 return Collections.EMPTY_LIST;
370 TreeSet<KdNode> results =
new TreeSet<KdNode>(
new EuclideanComparator(value));
375 while (node != null) {
376 if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) {
390 Set<KdNode> examined =
new HashSet<KdNode>();
394 while (node != null) {
396 searchNode(value, node, K, results, examined);
402 Collection<T> collection =
new ArrayList<T>(K);
403 for (KdNode kdNode : results)
404 collection.add((T) kdNode.id);
408 private static final <T extends
KdTree.
XYZPoint>
void searchNode(T value, KdNode node,
int K, TreeSet<KdNode> results, Set<KdNode> examined) {
412 KdNode lastNode = null;
413 Double lastDistance = Double.MAX_VALUE;
414 if (results.size() > 0) {
415 lastNode = results.last();
416 lastDistance = lastNode.id.euclideanDistance(value);
418 Double nodeDistance = node.id.euclideanDistance(value);
419 if (nodeDistance.compareTo(lastDistance) < 0) {
421 }
else if (nodeDistance.equals(lastDistance)) {
423 }
else if (results.size() < K) {
426 lastNode = results.last();
427 lastDistance = lastNode.id.euclideanDistance(value);
429 int axis = node.depth % node.k;
430 KdNode lesser = node.lesser;
431 KdNode greater = node.greater;
435 if (lesser != null && !examined.contains(lesser)) {
436 examined.add(lesser);
438 double nodePoint = Double.MIN_VALUE;
439 double valuePlusDistance = Double.MIN_VALUE;
440 if (axis == X_AXIS) {
441 nodePoint = node.id.x;
442 valuePlusDistance = value.x - lastDistance;
443 }
else if (axis == Y_AXIS) {
444 nodePoint = node.id.y;
445 valuePlusDistance = value.y - lastDistance;
447 nodePoint = node.id.z;
448 valuePlusDistance = value.z - lastDistance;
450 boolean lineIntersectsCube = ((valuePlusDistance <= nodePoint) ?
true :
false);
453 if (lineIntersectsCube)
454 searchNode(value, lesser, K, results, examined);
456 if (greater != null && !examined.contains(greater)) {
457 examined.add(greater);
459 double nodePoint = Double.MIN_VALUE;
460 double valuePlusDistance = Double.MIN_VALUE;
461 if (axis == X_AXIS) {
462 nodePoint = node.id.x;
463 valuePlusDistance = value.x + lastDistance;
464 }
else if (axis == Y_AXIS) {
465 nodePoint = node.id.y;
466 valuePlusDistance = value.y + lastDistance;
468 nodePoint = node.id.z;
469 valuePlusDistance = value.z + lastDistance;
471 boolean lineIntersectsCube = ((valuePlusDistance >= nodePoint) ?
true :
false);
474 if (lineIntersectsCube)
475 searchNode(value, greater, K, results, examined);
488 @SuppressWarnings(
"unchecked")
489 private static <T extends XYZPoint>
void search(final KdNode node, final Deque<T> results) {
491 results.add((T) node.id);
492 search(node.greater, results);
493 search(node.lesser, results);
512 if (d1.compareTo(d2) < 0)
514 else if (d2.compareTo(d1) < 0)
527 final Deque<T> results =
new ArrayDeque<T>();
529 return results.iterator();
539 final Deque<T> results =
new ArrayDeque<T>();
541 return results.descendingIterator();
544 public static class KdNode implements Comparable<KdNode> {
567 int axis = depth %
k;
569 return X_COMPARATOR.compare(o1, o2);
571 return Y_COMPARATOR.compare(o1, o2);
572 return Z_COMPARATOR.compare(o1, o2);
580 return 31 * (this.k + this.depth + this.
id.hashCode());
590 if (!(obj instanceof
KdNode))
593 KdNode kdNode = (
KdNode) obj;
612 StringBuilder builder =
new StringBuilder();
613 builder.append(
"k=").append(k);
614 builder.append(
" depth=").append(depth);
615 builder.append(
" id=").append(
id.
toString());
616 return builder.toString();
620 public static class XYZPoint implements Comparable<XYZPoint> {
622 protected final double x;
623 protected final double y;
624 protected final double z;
656 public XYZPoint(Double latitude, Double longitude) {
657 x = cos(Math.toRadians(latitude)) * cos(Math.toRadians(longitude));
658 y = cos(Math.toRadians(latitude)) * sin(Math.toRadians(longitude));
659 z = sin(Math.toRadians(latitude));
693 return Math.sqrt(Math.pow((o1.
x - o2.
x), 2) + Math.pow((o1.
y - o2.
y), 2) + Math.pow((o1.
z - o2.
z), 2));
701 return 31 * (int)(this.x + this.y + this.z);
715 if (Double.compare(
this.x, xyzPoint.
x)!=0)
717 if (Double.compare(
this.y, xyzPoint.
y)!=0)
719 if (Double.compare(
this.z, xyzPoint.
z)!=0)
729 int xComp = X_COMPARATOR.compare(
this, o);
732 int yComp = Y_COMPARATOR.compare(
this, o);
735 int zComp = Z_COMPARATOR.compare(
this, o);
744 StringBuilder builder =
new StringBuilder();
746 builder.append(x).append(
", ");
747 builder.append(y).append(
", ");
750 return builder.toString();
boolean equals(Object obj)
EuclideanComparator(XYZPoint point)
static KdNode createNode(List< XYZPoint > list, int k, int depth)
XYZPoint(double x, double y)
Iterator< T > reverse_iterator()
XYZPoint(Double latitude, Double longitude)
static final List< XYZPoint > getTree(KdNode root)
KdTree(List< XYZPoint > list, int k)
int compareTo(XYZPoint o)
static final< T extends KdTree.XYZPoint > KdNode getNode(KdTree< T > tree, T value)
boolean equals(Object obj)
double euclideanDistance(XYZPoint o1)
KdNode(XYZPoint id, int k, int depth)
Collection< T > nearestNeighbourSearch(int K, T value)
boolean contains(T value)
static final Comparator< XYZPoint > Y_COMPARATOR
int compare(KdNode o1, KdNode o2)
static final Comparator< XYZPoint > X_COMPARATOR
static final double euclideanDistance(XYZPoint o1, XYZPoint o2)
static final< T extends KdTree.XYZPoint > void searchNode(T value, KdNode node, int K, TreeSet< KdNode > results, Set< KdNode > examined)
static int compareTo(int depth, int k, XYZPoint o1, XYZPoint o2)
static< TextendsXYZPoint > void search(final KdNode node, final Deque< T > results)
static final Comparator< XYZPoint > Z_COMPARATOR
KdTree(List< XYZPoint > list)
XYZPoint(double x, double y, double z)