Autopsy  4.13.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
KdTree.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019 Basis Technology Corp.
5  * contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.geolocation;
20 
21 import static java.lang.Math.cos;
22 import static java.lang.Math.sin;
23 
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;
33 import java.util.Set;
34 import java.util.TreeSet;
35 
52 public class KdTree<T extends KdTree.XYZPoint> implements Iterable<T> {
53 
54  private int k = 3;
55  private KdNode root = null;
56 
57  private static final Comparator<XYZPoint> X_COMPARATOR = new Comparator<XYZPoint>() {
61  @Override
62  public int compare(XYZPoint o1, XYZPoint o2) {
63  if (o1.x < o2.x)
64  return -1;
65  if (o1.x > o2.x)
66  return 1;
67  return 0;
68  }
69  };
70 
71  private static final Comparator<XYZPoint> Y_COMPARATOR = new Comparator<XYZPoint>() {
75  @Override
76  public int compare(XYZPoint o1, XYZPoint o2) {
77  if (o1.y < o2.y)
78  return -1;
79  if (o1.y > o2.y)
80  return 1;
81  return 0;
82  }
83  };
84 
85  private static final Comparator<XYZPoint> Z_COMPARATOR = new Comparator<XYZPoint>() {
89  @Override
90  public int compare(XYZPoint o1, XYZPoint o2) {
91  if (o1.z < o2.z)
92  return -1;
93  if (o1.z > o2.z)
94  return 1;
95  return 0;
96  }
97  };
98 
99  protected static final int X_AXIS = 0;
100  protected static final int Y_AXIS = 1;
101  protected static final int Z_AXIS = 2;
102 
106  public KdTree() { }
107 
115  public KdTree(List<XYZPoint> list) {
116  super();
117  root = createNode(list, k, 0);
118  }
119 
129  public KdTree(List<XYZPoint> list, int k) {
130  super();
131  root = createNode(list, k, 0);
132  }
133 
145  private static KdNode createNode(List<XYZPoint> list, int k, int depth) {
146  if (list == null || list.size() == 0)
147  return null;
148 
149  int axis = depth % k;
150  if (axis == X_AXIS)
151  Collections.sort(list, X_COMPARATOR);
152  else if (axis == Y_AXIS)
153  Collections.sort(list, Y_COMPARATOR);
154  else
155  Collections.sort(list, Z_COMPARATOR);
156 
157  KdNode node = null;
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);
163  // Process list to see where each non-median point lies
164  for (int i = 0; i < list.size(); i++) {
165  if (i == medianIndex)
166  continue;
167  XYZPoint p = list.get(i);
168  // Cannot assume points before the median are less since they could be equal
169  if (KdNode.compareTo(depth, k, p, node.id) <= 0) {
170  less.add(p);
171  } else {
172  more.add(p);
173  }
174  }
175 
176  if ((medianIndex-1 >= 0) && less.size() > 0) {
177  node.lesser = createNode(less, k, depth + 1);
178  node.lesser.parent = node;
179  }
180 
181  if ((medianIndex <= list.size()-1) && more.size() > 0) {
182  node.greater = createNode(more, k, depth + 1);
183  node.greater.parent = node;
184  }
185  }
186 
187  return node;
188  }
189 
197  public boolean add(T value) {
198  if (value == null)
199  return false;
200 
201  if (root == null) {
202  root = new KdNode(value);
203  return true;
204  }
205 
206  KdNode node = root;
207  while (true) {
208  if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) {
209  // Lesser
210  if (node.lesser == null) {
211  KdNode newNode = new KdNode(value, k, node.depth + 1);
212  newNode.parent = node;
213  node.lesser = newNode;
214  break;
215  }
216  node = node.lesser;
217  } else {
218  // Greater
219  if (node.greater == null) {
220  KdNode newNode = new KdNode(value, k, node.depth + 1);
221  newNode.parent = node;
222  node.greater = newNode;
223  break;
224  }
225  node = node.greater;
226  }
227  }
228 
229  return true;
230  }
231 
239  public boolean contains(T value) {
240  if (value == null || root == null)
241  return false;
242 
243  KdNode node = getNode(this, value);
244  return (node != null);
245  }
246 
256  private static final <T extends KdTree.XYZPoint> KdNode getNode(KdTree<T> tree, T value) {
257  if (tree == null || tree.root == null || value == null)
258  return null;
259 
260  KdNode node = tree.root;
261  while (true) {
262  if (node.id.equals(value)) {
263  return node;
264  } else if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) {
265  // Lesser
266  if (node.lesser == null) {
267  return null;
268  }
269  node = node.lesser;
270  } else {
271  // Greater
272  if (node.greater == null) {
273  return null;
274  }
275  node = node.greater;
276  }
277  }
278  }
279 
287  public boolean remove(T value) {
288  if (value == null || root == null)
289  return false;
290 
291  KdNode node = getNode(this, value);
292  if (node == null)
293  return false;
294 
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;
303  }
304  } else {
305  parent.lesser = null;
306  }
307  } else {
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;
313  }
314  } else {
315  parent.greater = null;
316  }
317  }
318  } else {
319  // root
320  List<XYZPoint> nodes = getTree(node);
321  if (nodes.size() > 0)
322  root = createNode(nodes, node.k, node.depth);
323  else
324  root = null;
325  }
326 
327  return true;
328  }
329 
337  private static final List<XYZPoint> getTree(KdNode root) {
338  List<XYZPoint> list = new ArrayList<XYZPoint>();
339  if (root == null)
340  return list;
341 
342  if (root.lesser != null) {
343  list.add(root.lesser.id);
344  list.addAll(getTree(root.lesser));
345  }
346  if (root.greater != null) {
347  list.add(root.greater.id);
348  list.addAll(getTree(root.greater));
349  }
350 
351  return list;
352  }
353 
364  @SuppressWarnings("unchecked")
365  public Collection<T> nearestNeighbourSearch(int K, T value) {
366  if (value == null || root == null)
367  return Collections.EMPTY_LIST;
368 
369  // Map used for results
370  TreeSet<KdNode> results = new TreeSet<KdNode>(new EuclideanComparator(value));
371 
372  // Find the closest leaf node
373  KdNode prev = null;
374  KdNode node = root;
375  while (node != null) {
376  if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) {
377  // Lesser
378  prev = node;
379  node = node.lesser;
380  } else {
381  // Greater
382  prev = node;
383  node = node.greater;
384  }
385  }
386  KdNode leaf = prev;
387 
388  if (leaf != null) {
389  // Used to not re-examine nodes
390  Set<KdNode> examined = new HashSet<KdNode>();
391 
392  // Go up the tree, looking for better solutions
393  node = leaf;
394  while (node != null) {
395  // Search node
396  searchNode(value, node, K, results, examined);
397  node = node.parent;
398  }
399  }
400 
401  // Load up the collection of the results
402  Collection<T> collection = new ArrayList<T>(K);
403  for (KdNode kdNode : results)
404  collection.add((T) kdNode.id);
405  return collection;
406  }
407 
408  private static final <T extends KdTree.XYZPoint> void searchNode(T value, KdNode node, int K, TreeSet<KdNode> results, Set<KdNode> examined) {
409  examined.add(node);
410 
411  // Search node
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);
417  }
418  Double nodeDistance = node.id.euclideanDistance(value);
419  if (nodeDistance.compareTo(lastDistance) < 0) {
420  results.add(node);
421  } else if (nodeDistance.equals(lastDistance)) {
422  results.add(node);
423  } else if (results.size() < K) {
424  results.add(node);
425  }
426  lastNode = results.last();
427  lastDistance = lastNode.id.euclideanDistance(value);
428 
429  int axis = node.depth % node.k;
430  KdNode lesser = node.lesser;
431  KdNode greater = node.greater;
432 
433  // Search children branches, if axis aligned distance is less than
434  // current distance
435  if (lesser != null && !examined.contains(lesser)) {
436  examined.add(lesser);
437 
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;
446  } else {
447  nodePoint = node.id.z;
448  valuePlusDistance = value.z - lastDistance;
449  }
450  boolean lineIntersectsCube = ((valuePlusDistance <= nodePoint) ? true : false);
451 
452  // Continue down lesser branch
453  if (lineIntersectsCube)
454  searchNode(value, lesser, K, results, examined);
455  }
456  if (greater != null && !examined.contains(greater)) {
457  examined.add(greater);
458 
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;
467  } else {
468  nodePoint = node.id.z;
469  valuePlusDistance = value.z + lastDistance;
470  }
471  boolean lineIntersectsCube = ((valuePlusDistance >= nodePoint) ? true : false);
472 
473  // Continue down greater branch
474  if (lineIntersectsCube)
475  searchNode(value, greater, K, results, examined);
476  }
477  }
478 
488  @SuppressWarnings("unchecked")
489  private static <T extends XYZPoint> void search(final KdNode node, final Deque<T> results) {
490  if (node != null) {
491  results.add((T) node.id);
492  search(node.greater, results);
493  search(node.lesser, results);
494  }
495  }
496 
497  protected static class EuclideanComparator implements Comparator<KdNode> {
498 
499  private final XYZPoint point;
500 
502  this.point = point;
503  }
504 
508  @Override
509  public int compare(KdNode o1, KdNode o2) {
510  Double d1 = point.euclideanDistance(o1.id);
511  Double d2 = point.euclideanDistance(o2.id);
512  if (d1.compareTo(d2) < 0)
513  return -1;
514  else if (d2.compareTo(d1) < 0)
515  return 1;
516  return o1.id.compareTo(o2.id);
517  }
518  }
519 
526  public Iterator<T> iterator() {
527  final Deque<T> results = new ArrayDeque<T>();
528  search(root, results);
529  return results.iterator();
530  }
531 
538  public Iterator<T> reverse_iterator() {
539  final Deque<T> results = new ArrayDeque<T>();
540  search(root, results);
541  return results.descendingIterator();
542  }
543 
544  public static class KdNode implements Comparable<KdNode> {
545 
546  private final XYZPoint id;
547  private final int k;
548  private final int depth;
549 
550  private KdNode parent = null;
551  private KdNode lesser = null;
552  private KdNode greater = null;
553 
554  public KdNode(XYZPoint id) {
555  this.id = id;
556  this.k = 3;
557  this.depth = 0;
558  }
559 
560  public KdNode(XYZPoint id, int k, int depth) {
561  this.id = id;
562  this.k = k;
563  this.depth = depth;
564  }
565 
566  public static int compareTo(int depth, int k, XYZPoint o1, XYZPoint o2) {
567  int axis = depth % k;
568  if (axis == X_AXIS)
569  return X_COMPARATOR.compare(o1, o2);
570  if (axis == Y_AXIS)
571  return Y_COMPARATOR.compare(o1, o2);
572  return Z_COMPARATOR.compare(o1, o2);
573  }
574 
578  @Override
579  public int hashCode() {
580  return 31 * (this.k + this.depth + this.id.hashCode());
581  }
582 
586  @Override
587  public boolean equals(Object obj) {
588  if (obj == null)
589  return false;
590  if (!(obj instanceof KdNode))
591  return false;
592 
593  KdNode kdNode = (KdNode) obj;
594  if (this.compareTo(kdNode) == 0)
595  return true;
596  return false;
597  }
598 
602  @Override
603  public int compareTo(KdNode o) {
604  return compareTo(depth, k, this.id, o.id);
605  }
606 
610  @Override
611  public String toString() {
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();
617  }
618  }
619 
620  public static class XYZPoint implements Comparable<XYZPoint> {
621 
622  protected final double x;
623  protected final double y;
624  protected final double z;
625 
632  public XYZPoint(double x, double y) {
633  this.x = x;
634  this.y = y;
635  this.z = 0;
636  }
637 
645  public XYZPoint(double x, double y, double z) {
646  this.x = x;
647  this.y = y;
648  this.z = z;
649  }
650 
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));
660  }
661 
662  public double getX() {
663  return x;
664  }
665  public double getY() {
666  return y;
667  }
668  public double getZ() {
669  return z;
670  }
671 
679  public double euclideanDistance(XYZPoint o1) {
680  return euclideanDistance(o1, this);
681  }
682 
692  private static final double euclideanDistance(XYZPoint o1, XYZPoint o2) {
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));
694  }
695 
699  @Override
700  public int hashCode() {
701  return 31 * (int)(this.x + this.y + this.z);
702  }
703 
707  @Override
708  public boolean equals(Object obj) {
709  if (obj == null)
710  return false;
711  if (!(obj instanceof XYZPoint))
712  return false;
713 
714  XYZPoint xyzPoint = (XYZPoint) obj;
715  if (Double.compare(this.x, xyzPoint.x)!=0)
716  return false;
717  if (Double.compare(this.y, xyzPoint.y)!=0)
718  return false;
719  if (Double.compare(this.z, xyzPoint.z)!=0)
720  return false;
721  return true;
722  }
723 
727  @Override
728  public int compareTo(XYZPoint o) {
729  int xComp = X_COMPARATOR.compare(this, o);
730  if (xComp != 0)
731  return xComp;
732  int yComp = Y_COMPARATOR.compare(this, o);
733  if (yComp != 0)
734  return yComp;
735  int zComp = Z_COMPARATOR.compare(this, o);
736  return zComp;
737  }
738 
742  @Override
743  public String toString() {
744  StringBuilder builder = new StringBuilder();
745  builder.append("(");
746  builder.append(x).append(", ");
747  builder.append(y).append(", ");
748  builder.append(z);
749  builder.append(")");
750  return builder.toString();
751  }
752  }
753 }
static KdNode createNode(List< XYZPoint > list, int k, int depth)
Definition: KdTree.java:145
XYZPoint(Double latitude, Double longitude)
Definition: KdTree.java:656
static final List< XYZPoint > getTree(KdNode root)
Definition: KdTree.java:337
KdTree(List< XYZPoint > list, int k)
Definition: KdTree.java:129
static final< T extends KdTree.XYZPoint > KdNode getNode(KdTree< T > tree, T value)
Definition: KdTree.java:256
KdNode(XYZPoint id, int k, int depth)
Definition: KdTree.java:560
Collection< T > nearestNeighbourSearch(int K, T value)
Definition: KdTree.java:365
static final Comparator< XYZPoint > Y_COMPARATOR
Definition: KdTree.java:71
static final Comparator< XYZPoint > X_COMPARATOR
Definition: KdTree.java:57
static final double euclideanDistance(XYZPoint o1, XYZPoint o2)
Definition: KdTree.java:692
static final< T extends KdTree.XYZPoint > void searchNode(T value, KdNode node, int K, TreeSet< KdNode > results, Set< KdNode > examined)
Definition: KdTree.java:408
static int compareTo(int depth, int k, XYZPoint o1, XYZPoint o2)
Definition: KdTree.java:566
static< TextendsXYZPoint > void search(final KdNode node, final Deque< T > results)
Definition: KdTree.java:489
static final Comparator< XYZPoint > Z_COMPARATOR
Definition: KdTree.java:85
XYZPoint(double x, double y, double z)
Definition: KdTree.java:645

Copyright © 2012-2019 Basis Technology. Generated on: Tue Jan 7 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.