diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java b/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java
index 3a2f305083..5bb0a8f47f 100755
--- a/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java
+++ b/Core/src/org/sleuthkit/autopsy/geolocation/KdTree.java
@@ -18,9 +18,6 @@
*/
package org.sleuthkit.autopsy.geolocation;
-import static java.lang.Math.cos;
-import static java.lang.Math.sin;
-
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
@@ -29,7 +26,6 @@ import java.util.Comparator;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.Set;
import java.util.TreeSet;
@@ -45,184 +41,103 @@ import java.util.TreeSet;
*
*
* Original other was JustinWetherell .
- *
- * @see Original version
+ *
+ * @see
+ * Original
+ * version
*
*/
public class KdTree implements Iterable {
- private int k = 3;
+ // The code generally supports the idea of a third dimension, but for
+ // simplicity, the code will only use 2 dimensions.
+ private static final int DIMENSIONS = 2;
private KdNode root = null;
+ private static final double EARTH_RADIUS = 6371e3;
+
+ /**
+ * Compares two XYZPoints by their X value.
+ */
private static final Comparator X_COMPARATOR = new Comparator() {
/**
* {@inheritDoc}
*/
@Override
public int compare(XYZPoint o1, XYZPoint o2) {
- if (o1.x < o2.x)
- return -1;
- if (o1.x > o2.x)
- return 1;
- return 0;
+ return Double.compare(o1.x, o2.x);
}
};
+ /**
+ * Compares two XYZPoints by their Y value.
+ */
private static final Comparator Y_COMPARATOR = new Comparator() {
/**
* {@inheritDoc}
*/
@Override
public int compare(XYZPoint o1, XYZPoint o2) {
- if (o1.y < o2.y)
- return -1;
- if (o1.y > o2.y)
- return 1;
- return 0;
+ return Double.compare(o1.y, o2.y);
}
};
+ /**
+ * Compares two XYZPoints by their Z value.
+ */
private static final Comparator Z_COMPARATOR = new Comparator() {
/**
* {@inheritDoc}
*/
@Override
public int compare(XYZPoint o1, XYZPoint o2) {
- if (o1.z < o2.z)
- return -1;
- if (o1.z > o2.z)
- return 1;
- return 0;
+ return Double.compare(o1.z, o2.z);
}
};
- protected static final int X_AXIS = 0;
- protected static final int Y_AXIS = 1;
- protected static final int Z_AXIS = 2;
+ static final int X_AXIS = 0;
+ static final int Y_AXIS = 1;
+ static final int Z_AXIS = 2;
- /**
- * Default constructor.
- */
- public KdTree() { }
-
- /**
- * Constructor for creating a more balanced tree. It uses the
- * "median of points" algorithm.
- *
- * @param list
- * of XYZPoints.
- */
- public KdTree(List list) {
- super();
- root = createNode(list, k, 0);
- }
-
- /**
- * Constructor for creating a more balanced tree. It uses the
- * "median of points" algorithm.
- *
- * @param list
- * of XYZPoints.
- * @param k
- * of the tree.
- */
- public KdTree(List list, int k) {
- super();
- root = createNode(list, k, 0);
- }
-
- /**
- * Creates node from list of XYZPoints.
- *
- * @param list
- * of XYZPoints.
- * @param k
- * of the tree.
- * @param depth
- * depth of the node.
- * @return node created.
- */
- private static KdNode createNode(List list, int k, int depth) {
- if (list == null || list.size() == 0)
- return null;
-
- int axis = depth % k;
- if (axis == X_AXIS)
- Collections.sort(list, X_COMPARATOR);
- else if (axis == Y_AXIS)
- Collections.sort(list, Y_COMPARATOR);
- else
- Collections.sort(list, Z_COMPARATOR);
-
- KdNode node = null;
- List less = new ArrayList(list.size());
- List more = new ArrayList(list.size());
- if (list.size() > 0) {
- int medianIndex = list.size() / 2;
- node = new KdNode(list.get(medianIndex), k, depth);
- // Process list to see where each non-median point lies
- for (int i = 0; i < list.size(); i++) {
- if (i == medianIndex)
- continue;
- XYZPoint p = list.get(i);
- // Cannot assume points before the median are less since they could be equal
- if (KdNode.compareTo(depth, k, p, node.id) <= 0) {
- less.add(p);
- } else {
- more.add(p);
- }
- }
-
- if ((medianIndex-1 >= 0) && less.size() > 0) {
- node.lesser = createNode(less, k, depth + 1);
- node.lesser.parent = node;
- }
-
- if ((medianIndex <= list.size()-1) && more.size() > 0) {
- node.greater = createNode(more, k, depth + 1);
- node.greater.parent = node;
- }
- }
-
- return node;
+ public KdNode getRoot() {
+ return root;
}
/**
* Adds value to the tree. Tree can contain multiple equal values.
*
- * @param value
- * T to add to the tree.
+ * @param value T to add to the tree.
+ *
* @return True if successfully added to tree.
*/
public boolean add(T value) {
- if (value == null)
+ if (value == null) {
return false;
+ }
if (root == null) {
- root = new KdNode(value);
+ root = new KdNode(value, 0, null);
return true;
}
KdNode node = root;
while (true) {
- if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) {
+ if (KdNode.compareTo(node.getDepth(), value, node.getPoint()) <= 0) {
// Lesser
- if (node.lesser == null) {
- KdNode newNode = new KdNode(value, k, node.depth + 1);
- newNode.parent = node;
- node.lesser = newNode;
+ if (node.getLesser() == null) {
+ KdNode newNode = new KdNode(value, node.getDepth() + 1, node);
+ node.setLesser(newNode);
break;
}
- node = node.lesser;
+ node = node.getLesser();
} else {
// Greater
- if (node.greater == null) {
- KdNode newNode = new KdNode(value, k, node.depth + 1);
- newNode.parent = node;
- node.greater = newNode;
+ if (node.getGreater() == null) {
+ KdNode newNode = new KdNode(value, node.getDepth() + 1, node);
+ node.setGreater(newNode);
break;
}
- node = node.greater;
+ node = node.getGreater();
}
}
@@ -232,13 +147,14 @@ public class KdTree implements Iterable {
/**
* Does the tree contain the value.
*
- * @param value
- * T to locate in the tree.
+ * @param value T to locate in the tree.
+ *
* @return True if tree contains value.
*/
public boolean contains(T value) {
- if (value == null || root == null)
+ if (value == null || root == null) {
return false;
+ }
KdNode node = getNode(this, value);
return (node != null);
@@ -247,258 +163,208 @@ public class KdTree implements Iterable {
/**
* Locates T in the tree.
*
- * @param tree
- * to search.
- * @param value
- * to search for.
+ * @param tree to search.
+ * @param value to search for.
+ *
* @return KdNode or NULL if not found
*/
- private static final KdNode getNode(KdTree tree, T value) {
- if (tree == null || tree.root == null || value == null)
+ private KdNode getNode(KdTree tree, T value) {
+ if (tree == null || tree.getRoot() == null || value == null) {
return null;
+ }
- KdNode node = tree.root;
+ KdNode node = tree.getRoot();
while (true) {
- if (node.id.equals(value)) {
+ if (node.getPoint().equals(value)) {
return node;
- } else if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) {
+ } else if (KdNode.compareTo(node.getDepth(), value, node.getPoint()) <= 0) {
// Lesser
- if (node.lesser == null) {
+ if (node.getLesser() == null) {
return null;
}
- node = node.lesser;
+ node = node.getLesser();
} else {
// Greater
- if (node.greater == null) {
+ if (node.getGreater() == null) {
return null;
}
- node = node.greater;
+ node = node.getGreater();
}
}
}
/**
- * Removes first occurrence of value in the tree.
+ * Searches for numNeighbors nearest neighbor.
*
- * @param value
- * T to remove from the tree.
- * @return True if value was removed from the tree.
- */
- public boolean remove(T value) {
- if (value == null || root == null)
- return false;
-
- KdNode node = getNode(this, value);
- if (node == null)
- return false;
-
- KdNode parent = node.parent;
- if (parent != null) {
- if (parent.lesser != null && node.equals(parent.lesser)) {
- List nodes = getTree(node);
- if (nodes.size() > 0) {
- parent.lesser = createNode(nodes, node.k, node.depth);
- if (parent.lesser != null) {
- parent.lesser.parent = parent;
- }
- } else {
- parent.lesser = null;
- }
- } else {
- List nodes = getTree(node);
- if (nodes.size() > 0) {
- parent.greater = createNode(nodes, node.k, node.depth);
- if (parent.greater != null) {
- parent.greater.parent = parent;
- }
- } else {
- parent.greater = null;
- }
- }
- } else {
- // root
- List nodes = getTree(node);
- if (nodes.size() > 0)
- root = createNode(nodes, node.k, node.depth);
- else
- root = null;
- }
-
- return true;
- }
-
- /**
- * Gets the (sub) tree rooted at root.
+ * @param numNeighbors Number of neighbors to retrieve. Can return more than
+ * numNeighbors, if last nodes are equal distances.
+ * @param value to find neighbors of.
*
- * @param root
- * of tree to get nodes for.
- * @return points in (sub) tree, not including root.
- */
- private static final List getTree(KdNode root) {
- List list = new ArrayList();
- if (root == null)
- return list;
-
- if (root.lesser != null) {
- list.add(root.lesser.id);
- list.addAll(getTree(root.lesser));
- }
- if (root.greater != null) {
- list.add(root.greater.id);
- list.addAll(getTree(root.greater));
- }
-
- return list;
- }
-
- /**
- * Searches the K nearest neighbor.
- *
- * @param K
- * Number of neighbors to retrieve. Can return more than K, if
- * last nodes are equal distances.
- * @param value
- * to find neighbors of.
* @return Collection of T neighbors.
*/
@SuppressWarnings("unchecked")
- public Collection nearestNeighbourSearch(int K, T value) {
- if (value == null || root == null)
+ public Collection nearestNeighbourSearch(int numNeighbors, T value) {
+ if (value == null || root == null) {
return Collections.EMPTY_LIST;
+ }
// Map used for results
- TreeSet results = new TreeSet(new EuclideanComparator(value));
+ TreeSet results = new TreeSet<>(new EuclideanComparator(value));
// Find the closest leaf node
KdNode prev = null;
KdNode node = root;
while (node != null) {
- if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) {
+ if (KdNode.compareTo(node.getDepth(), value, node.getPoint()) <= 0) {
// Lesser
prev = node;
- node = node.lesser;
+ node = node.getLesser();
} else {
// Greater
prev = node;
- node = node.greater;
+ node = node.getGreater();
}
}
KdNode leaf = prev;
if (leaf != null) {
// Used to not re-examine nodes
- Set examined = new HashSet();
+ Set examined = new HashSet<>();
// Go up the tree, looking for better solutions
node = leaf;
while (node != null) {
// Search node
- searchNode(value, node, K, results, examined);
- node = node.parent;
+ searchNode(value, node, numNeighbors, results, examined);
+ node = node.getParent();
}
}
// Load up the collection of the results
- Collection collection = new ArrayList(K);
- for (KdNode kdNode : results)
- collection.add((T) kdNode.id);
+ Collection collection = new ArrayList<>(numNeighbors);
+ for (KdNode kdNode : results) {
+ collection.add((T) kdNode.getPoint());
+ }
return collection;
}
- private static final void searchNode(T value, KdNode node, int K, TreeSet results, Set examined) {
+ /**
+ * Searches the tree to find any nodes that are closer than what is
+ * currently in results.
+ *
+ * @param value Nearest value search point
+ * @param node Search starting node
+ * @param numNeighbors Number of nearest neighbors to return
+ * @param results Current result set
+ * @param examined List of examined nodes
+ */
+ private void searchNode(T value, KdNode node, int numNeighbors, TreeSet results, Set examined) {
examined.add(node);
// Search node
- KdNode lastNode = null;
+ KdNode lastNode;
Double lastDistance = Double.MAX_VALUE;
if (results.size() > 0) {
lastNode = results.last();
- lastDistance = lastNode.id.euclideanDistance(value);
+ lastDistance = lastNode.getPoint().euclideanDistance(value);
}
- Double nodeDistance = node.id.euclideanDistance(value);
+ Double nodeDistance = node.getPoint().euclideanDistance(value);
if (nodeDistance.compareTo(lastDistance) < 0) {
results.add(node);
} else if (nodeDistance.equals(lastDistance)) {
results.add(node);
- } else if (results.size() < K) {
+ } else if (results.size() < numNeighbors) {
results.add(node);
}
lastNode = results.last();
- lastDistance = lastNode.id.euclideanDistance(value);
+ lastDistance = lastNode.getPoint().euclideanDistance(value);
- int axis = node.depth % node.k;
- KdNode lesser = node.lesser;
- KdNode greater = node.greater;
+ int axis = node.getDepth() % DIMENSIONS;
+ KdNode lesser = node.getLesser();
+ KdNode greater = node.getGreater();
// Search children branches, if axis aligned distance is less than
// current distance
if (lesser != null && !examined.contains(lesser)) {
examined.add(lesser);
- double nodePoint = Double.MIN_VALUE;
- double valuePlusDistance = Double.MIN_VALUE;
- if (axis == X_AXIS) {
- nodePoint = node.id.x;
- valuePlusDistance = value.x - lastDistance;
- } else if (axis == Y_AXIS) {
- nodePoint = node.id.y;
- valuePlusDistance = value.y - lastDistance;
- } else {
- nodePoint = node.id.z;
- valuePlusDistance = value.z - lastDistance;
+ double nodePoint;
+ double valuePlusDistance;
+ switch (axis) {
+ case X_AXIS:
+ nodePoint = node.getPoint().x;
+ valuePlusDistance = value.x - lastDistance;
+ break;
+ case Y_AXIS:
+ nodePoint = node.getPoint().y;
+ valuePlusDistance = value.y - lastDistance;
+ break;
+ default: // Z_AXIS
+ nodePoint = node.getPoint().z;
+ valuePlusDistance = value.z - lastDistance;
+ break;
}
- boolean lineIntersectsCube = ((valuePlusDistance <= nodePoint) ? true : false);
+ boolean lineIntersectsCube = valuePlusDistance <= nodePoint;
// Continue down lesser branch
- if (lineIntersectsCube)
- searchNode(value, lesser, K, results, examined);
+ if (lineIntersectsCube) {
+ searchNode(value, lesser, numNeighbors, results, examined);
+ }
}
if (greater != null && !examined.contains(greater)) {
examined.add(greater);
- double nodePoint = Double.MIN_VALUE;
- double valuePlusDistance = Double.MIN_VALUE;
- if (axis == X_AXIS) {
- nodePoint = node.id.x;
- valuePlusDistance = value.x + lastDistance;
- } else if (axis == Y_AXIS) {
- nodePoint = node.id.y;
- valuePlusDistance = value.y + lastDistance;
- } else {
- nodePoint = node.id.z;
- valuePlusDistance = value.z + lastDistance;
+ double nodePoint;
+ double valuePlusDistance;
+ switch (axis) {
+ case X_AXIS:
+ nodePoint = node.getPoint().x;
+ valuePlusDistance = value.x + lastDistance;
+ break;
+ case Y_AXIS:
+ nodePoint = node.getPoint().y;
+ valuePlusDistance = value.y + lastDistance;
+ break;
+ default: //Z_AXIS
+ nodePoint = node.getPoint().z;
+ valuePlusDistance = value.z + lastDistance;
+ break;
}
- boolean lineIntersectsCube = ((valuePlusDistance >= nodePoint) ? true : false);
+ boolean lineIntersectsCube = valuePlusDistance >= nodePoint;
// Continue down greater branch
- if (lineIntersectsCube)
- searchNode(value, greater, K, results, examined);
+ if (lineIntersectsCube) {
+ searchNode(value, greater, numNeighbors, results, examined);
+ }
}
}
- /**
- * Adds, in a specified queue, a given node and its related nodes (lesser, greater).
- *
- * @param node
- * Node to check. May be null.
- *
- * @param results
- * Queue containing all found entries. Must not be null.
+ /**
+ * Adds, in a specified queue, a given node and its related nodes (lesser,
+ * greater).
+ *
+ * @param node Node to check. May be null.
+ *
+ * @param results Queue containing all found entries. Must not be null.
*/
@SuppressWarnings("unchecked")
- private static void search(final KdNode node, final Deque results) {
+ private void search(final KdNode node, final Deque results) {
if (node != null) {
- results.add((T) node.id);
- search(node.greater, results);
- search(node.lesser, results);
+ results.add((T) node.getPoint());
+ search(node.getGreater(), results);
+ search(node.getLesser(), results);
}
}
- protected static class EuclideanComparator implements Comparator {
+ /**
+ * A comparator class for doing a euclidean distance comparison of two
+ * KdNodes.
+ */
+ private final class EuclideanComparator implements Comparator {
private final XYZPoint point;
- public EuclideanComparator(XYZPoint point) {
+ EuclideanComparator(XYZPoint point) {
this.point = point;
}
@@ -507,69 +373,158 @@ public class KdTree implements Iterable {
*/
@Override
public int compare(KdNode o1, KdNode o2) {
- Double d1 = point.euclideanDistance(o1.id);
- Double d2 = point.euclideanDistance(o2.id);
- if (d1.compareTo(d2) < 0)
+ Double d1 = point.euclideanDistance(o1.getPoint());
+ Double d2 = point.euclideanDistance(o2.getPoint());
+ if (d1.compareTo(d2) < 0) {
return -1;
- else if (d2.compareTo(d1) < 0)
+ } else if (d2.compareTo(d1) < 0) {
return 1;
- return o1.id.compareTo(o2.id);
+ }
+ return o1.getPoint().compareTo(o2.getPoint());
}
}
- /**
+ /**
* Searches all entries from the first to the last entry.
- *
- * @return Iterator
- * allowing to iterate through a collection containing all found entries.
+ *
+ * @return Iterator allowing to iterate through a collection containing all
+ * found entries.
*/
+ @Override
public Iterator iterator() {
- final Deque results = new ArrayDeque();
+ final Deque results = new ArrayDeque<>();
search(root, results);
return results.iterator();
}
- /**
+ /**
* Searches all entries from the last to the first entry.
- *
- * @return Iterator
- * allowing to iterate through a collection containing all found entries.
+ *
+ * @return Iterator allowing to iterate through a collection containing all
+ * found entries.
*/
public Iterator reverse_iterator() {
- final Deque results = new ArrayDeque();
+ final Deque results = new ArrayDeque<>();
search(root, results);
return results.descendingIterator();
}
+ /**
+ * A node in the KdTree. Each node has a parent node (unless the node is the
+ * root) and a lesser and greater child node.
+ *
+ * The child nodes are set to "lesser" or "greater" depending on a
+ * comparison between the nodes XYZPoint and the child's XYZPoint.
+ */
public static class KdNode implements Comparable {
- private final XYZPoint id;
- private final int k;
+ private final XYZPoint point;
private final int depth;
+ private final KdNode parent;
- private KdNode parent = null;
private KdNode lesser = null;
private KdNode greater = null;
- public KdNode(XYZPoint id) {
- this.id = id;
- this.k = 3;
- this.depth = 0;
- }
-
- public KdNode(XYZPoint id, int k, int depth) {
- this.id = id;
- this.k = k;
+ /**
+ * Constructs a new KdNode.
+ *
+ * @param point Node point
+ * @param depth Depth of node in the tree, set to 0 if root node
+ * @param parent Parent of this node, can be null if root node
+ */
+ public KdNode(XYZPoint point, int depth, KdNode parent) {
+ this.point = point;
this.depth = depth;
+ this.parent = parent;
}
- public static int compareTo(int depth, int k, XYZPoint o1, XYZPoint o2) {
- int axis = depth % k;
- if (axis == X_AXIS)
- return X_COMPARATOR.compare(o1, o2);
- if (axis == Y_AXIS)
- return Y_COMPARATOR.compare(o1, o2);
- return Z_COMPARATOR.compare(o1, o2);
+ /**
+ * Compares two XYZPoints. The value used for the comparision is based
+ * on the depth of the node in the tree and the tree's dimension.
+ *
+ * @param depth Depth of node in the tree
+ * @param point1 First point to compare
+ * @param point2 Second point to compare
+ *
+ * @return 0 if points are equal -1 if point2 is "less than" point1 1 if
+ * point1 is "greater than" point2
+ */
+ public static int compareTo(int depth, XYZPoint point1, XYZPoint point2) {
+ int axis = depth % DIMENSIONS;
+ switch (axis) {
+ case X_AXIS:
+ return X_COMPARATOR.compare(point1, point2);
+ case Y_AXIS:
+ return Y_COMPARATOR.compare(point1, point2);
+ default:
+ return Z_COMPARATOR.compare(point1, point2);
+ }
+ }
+
+ /**
+ * Returns the nodes depth in the kdtree.
+ *
+ * @return Node depth.
+ */
+ int getDepth() {
+ return depth;
+ }
+
+ /**
+ * Returns the parent of this node. If parent is null, the node is the
+ * tree root node.
+ *
+ * @return Returns the parent of this node, or null if node is tree
+ * root.
+ */
+ KdNode getParent() {
+ return parent;
+ }
+
+ /**
+ * Sets the lesser child of this node.
+ *
+ * @param node lesser Child node
+ */
+ void setLesser(KdNode node) {
+ lesser = node;
+ }
+
+ /**
+ * Returns the nodes lesser child node.
+ *
+ * @return Returns KdNode or null if one was not set.
+ */
+ KdNode getLesser() {
+ return lesser;
+ }
+
+ /**
+ * Sets the greater child of this node.
+ *
+ * @param node
+ */
+ void setGreater(KdNode node) {
+ greater = node;
+ }
+
+ /**
+ * Returns the nodes lesser child node.
+ *
+ * @return Returns KdNode or null if one was not set.
+ */
+ KdNode getGreater() {
+ return greater;
+ }
+
+ /**
+ * Returns the XYZ point of this node which contains the longitude and
+ * latitude values.
+ *
+ * @return XYZPoint
+ */
+ XYZPoint getPoint() {
+ return point;
}
/**
@@ -577,7 +532,7 @@ public class KdTree implements Iterable {
*/
@Override
public int hashCode() {
- return 31 * (this.k + this.depth + this.id.hashCode());
+ return 31 * (DIMENSIONS + this.depth + this.getPoint().hashCode());
}
/**
@@ -585,15 +540,15 @@ public class KdTree implements Iterable {
*/
@Override
public boolean equals(Object obj) {
- if (obj == null)
+ if (obj == null) {
return false;
- if (!(obj instanceof KdNode))
+ }
+ if (!(obj instanceof KdNode)) {
return false;
+ }
KdNode kdNode = (KdNode) obj;
- if (this.compareTo(kdNode) == 0)
- return true;
- return false;
+ return (this.compareTo(kdNode) == 0);
}
/**
@@ -601,7 +556,7 @@ public class KdTree implements Iterable {
*/
@Override
public int compareTo(KdNode o) {
- return compareTo(depth, k, this.id, o.id);
+ return compareTo(depth, this.getPoint(), o.getPoint());
}
/**
@@ -609,14 +564,17 @@ public class KdTree implements Iterable {
*/
@Override
public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("k=").append(k);
- builder.append(" depth=").append(depth);
- builder.append(" id=").append(id.toString());
+ StringBuilder builder = new StringBuilder(200);
+ builder.append("dimensions=").append(DIMENSIONS).append(" depth=").append(depth).append(" point=").append(getPoint().toString());
return builder.toString();
}
}
+ /**
+ * An XYZPoint is a representation of a three dimensional point.
+ *
+ * Z value will always been 0 when using latitude and longitude values.
+ */
public static class XYZPoint implements Comparable {
protected final double x;
@@ -624,47 +582,40 @@ public class KdTree implements Iterable {
protected final double z;
/**
- * z is defaulted to zero.
+ * Constructs a new XYZPoint.
*
- * @param x
- * @param y
- */
- public XYZPoint(double x, double y) {
- this.x = x;
- this.y = y;
- this.z = 0;
- }
-
- /**
- * Default constructor
- *
- * @param x
- * @param y
- * @param z
- */
- public XYZPoint(double x, double y, double z) {
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
- /**
- * Does not use R to calculate x, y, and z. Where R is the approximate radius of earth (e.g. 6371KM).
* @param latitude
* @param longitude
*/
public XYZPoint(Double latitude, Double longitude) {
- x = cos(Math.toRadians(latitude)) * cos(Math.toRadians(longitude));
- y = cos(Math.toRadians(latitude)) * sin(Math.toRadians(longitude));
- z = sin(Math.toRadians(latitude));
+ x = latitude;
+ y = longitude;
+ z = 0;
}
+ /**
+ * Returns the x(latitude) value for the point.
+ *
+ * @return x(latitude) value for the point
+ */
public double getX() {
return x;
}
+
+ /**
+ * Returns the y/longitude value for the point.
+ *
+ * @return Longitude for point
+ */
public double getY() {
return y;
}
+
+ /**
+ * Returns the z value, will always be 0.
+ *
+ * @return Always 0.
+ */
public double getZ() {
return z;
}
@@ -672,8 +623,8 @@ public class KdTree implements Iterable {
/**
* Computes the Euclidean distance from this point to the other.
*
- * @param o1
- * other point.
+ * @param o1 other point.
+ *
* @return euclidean distance.
*/
public double euclideanDistance(XYZPoint o1) {
@@ -683,14 +634,32 @@ public class KdTree implements Iterable {
/**
* Computes the Euclidean distance from one point to the other.
*
- * @param o1
- * first point.
- * @param o2
- * second point.
+ * Source for the distance calculation:
+ * https://www.movable-type.co.uk/scripts/latlong.html
+ *
+ * @param o1 first point.
+ * @param o2 second point.
+ *
* @return euclidean distance.
*/
- private static final double euclideanDistance(XYZPoint o1, XYZPoint o2) {
- return Math.sqrt(Math.pow((o1.x - o2.x), 2) + Math.pow((o1.y - o2.y), 2) + Math.pow((o1.z - o2.z), 2));
+ private static double euclideanDistance(XYZPoint o1, XYZPoint o2) {
+ if (o1.equals(o2)) {
+ return 0;
+ }
+
+ double lat1Radians = Math.toRadians(o1.getX());
+ double lat2Radians = Math.toRadians(o2.getX());
+
+ double deltaLatRadians = Math.toRadians(o2.getX() - o1.getX());
+ double deltaLongRadians = Math.toRadians(o2.getY() - o1.getY());
+
+ double a = Math.sin(deltaLatRadians / 2) * Math.sin(deltaLatRadians / 2)
+ + Math.cos(lat1Radians) * Math.cos(lat2Radians)
+ * Math.sin(deltaLongRadians / 2) * Math.sin(deltaLongRadians / 2);
+
+ double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+
+ return EARTH_RADIUS * c;
}
/**
@@ -698,7 +667,7 @@ public class KdTree implements Iterable {
*/
@Override
public int hashCode() {
- return 31 * (int)(this.x + this.y + this.z);
+ return 31 * (int) (this.x + this.y + this.z);
}
/**
@@ -706,19 +675,19 @@ public class KdTree implements Iterable {
*/
@Override
public boolean equals(Object obj) {
- if (obj == null)
+ if (obj == null) {
return false;
- if (!(obj instanceof XYZPoint))
+ }
+
+ if (!(obj instanceof XYZPoint)) {
return false;
+ }
XYZPoint xyzPoint = (XYZPoint) obj;
- if (Double.compare(this.x, xyzPoint.x)!=0)
- return false;
- if (Double.compare(this.y, xyzPoint.y)!=0)
- return false;
- if (Double.compare(this.z, xyzPoint.z)!=0)
- return false;
- return true;
+
+ return ((Double.compare(this.x, xyzPoint.x) == 0)
+ && (Double.compare(this.y, xyzPoint.y) == 0)
+ && (Double.compare(this.z, xyzPoint.z) == 0));
}
/**
@@ -727,13 +696,15 @@ public class KdTree implements Iterable {
@Override
public int compareTo(XYZPoint o) {
int xComp = X_COMPARATOR.compare(this, o);
- if (xComp != 0)
+ if (xComp != 0) {
return xComp;
+ }
+
int yComp = Y_COMPARATOR.compare(this, o);
- if (yComp != 0)
+ if (yComp != 0) {
return yComp;
- int zComp = Z_COMPARATOR.compare(this, o);
- return zComp;
+ }
+ return Z_COMPARATOR.compare(this, o);
}
/**
@@ -741,7 +712,7 @@ public class KdTree implements Iterable {
*/
@Override
public String toString() {
- StringBuilder builder = new StringBuilder();
+ StringBuilder builder = new StringBuilder(200);
builder.append("(");
builder.append(x).append(", ");
builder.append(y).append(", ");
@@ -750,4 +721,4 @@ public class KdTree implements Iterable {
return builder.toString();
}
}
-}
\ No newline at end of file
+}
diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java
index 4d286467b8..8c6e879c76 100755
--- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java
@@ -35,7 +35,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
@@ -58,7 +57,6 @@ import org.jxmapviewer.viewer.DefaultTileFactory;
import org.jxmapviewer.viewer.GeoPosition;
import org.jxmapviewer.viewer.TileFactory;
import org.jxmapviewer.viewer.TileFactoryInfo;
-import org.jxmapviewer.viewer.Waypoint;
import org.jxmapviewer.viewer.WaypointPainter;
import org.jxmapviewer.viewer.WaypointRenderer;
import org.openide.util.NbBundle.Messages;
@@ -69,7 +67,6 @@ import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
import org.sleuthkit.datamodel.TskCoreException;
import javax.imageio.ImageIO;
import javax.swing.SwingUtilities;
-import org.jxmapviewer.viewer.DefaultWaypointRenderer;
/**
* The map panel. This panel contains the jxmapviewer MapViewer
@@ -91,6 +88,9 @@ final public class MapPanel extends javax.swing.JPanel {
private static final int POPUP_HEIGHT = 200;
private static final int POPUP_MARGIN = 10;
+ private BufferedImage defaultWaypointImage;
+ private BufferedImage selectedWaypointImage;
+
private MapWaypoint currentlySelectedWaypoint;
/**
@@ -107,6 +107,13 @@ final public class MapPanel extends javax.swing.JPanel {
currentPopup = null;
popupFactory = new PopupFactory();
+ try {
+ defaultWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_teal.png"));
+ selectedWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_yellow.png"));
+ } catch (IOException ex) {
+ logger.log(Level.WARNING, "Unable to load geolocation waypoint images", ex);
+ }
+
// ComponentListeners do not have a concept of resize event "complete"
// therefore if we move the popup as the window resizes there will be
// a weird blinking behavior. Using the CompnentResizeEndListener the
@@ -124,7 +131,7 @@ final public class MapPanel extends javax.swing.JPanel {
try {
// Tell the old factory to cleanup
mapViewer.getTileFactory().dispose();
-
+
mapViewer.setTileFactory(getTileFactory());
initializeZoomSlider();
} catch (GeoLocationDataException ex) {
@@ -139,20 +146,18 @@ final public class MapPanel extends javax.swing.JPanel {
}
}
});
-
-
}
/**
* Get a list of the waypoints that are currently visible in the viewport.
- *
+ *
* @return A list of waypoints or empty list if none were found.
*/
List getVisibleWaypoints() {
Rectangle viewport = mapViewer.getViewportBounds();
List waypoints = new ArrayList<>();
-
+
Iterator iterator = waypointTree.iterator();
while (iterator.hasNext()) {
MapWaypoint waypoint = iterator.next();
@@ -206,12 +211,7 @@ final public class MapPanel extends javax.swing.JPanel {
return waypointSet;
}
};
-
- try {
- waypointPainter.setRenderer(new MapWaypointRenderer());
- } catch (IOException ex) {
- logger.log(Level.WARNING, "Failed to load waypoint image resource, using DefaultWaypointRenderer", ex);
- }
+ waypointPainter.setRenderer(new MapWaypointRenderer());
mapViewer.setOverlayPainter(waypointPainter);
}
@@ -336,7 +336,7 @@ final public class MapPanel extends javax.swing.JPanel {
try {
List waypoints = findClosestWaypoint(point);
MapWaypoint waypoint = null;
- if(waypoints.size() > 0) {
+ if (waypoints.size() > 0) {
waypoint = waypoints.get(0);
}
showPopupMenu(waypoint, point);
@@ -344,7 +344,7 @@ final public class MapPanel extends javax.swing.JPanel {
// it the popup is currently visible
if (waypoint != null && !waypoint.equals(currentlySelectedWaypoint)) {
currentlySelectedWaypoint = waypoint;
- if(currentPopup != null) {
+ if (currentPopup != null) {
showDetailsPopup();
}
mapViewer.repaint();
@@ -405,10 +405,15 @@ final public class MapPanel extends javax.swing.JPanel {
currentPopup = popupFactory.getPopup(this, detailPane, popupLocation.x, popupLocation.y);
currentPopup.show();
-
- mapViewer.revalidate();
- mapViewer.repaint();
+
+ } else {
+ if (currentPopup != null) {
+ currentPopup.hide();
+ }
}
+
+ mapViewer.revalidate();
+ mapViewer.repaint();
}
/**
@@ -434,16 +439,16 @@ final public class MapPanel extends javax.swing.JPanel {
* @return A waypoint that is within 10 pixels of the given point, or null
* if none was found.
*/
- private List findClosestWaypoint(Point mouseClickPoint) {
+ private List findClosestWaypoint(Point clickPoint) {
if (waypointTree == null) {
- return null;
+ return new ArrayList<>();
}
// Convert the mouse click location to latitude & longitude
- GeoPosition geopos = mapViewer.getTileFactory().pixelToGeo(mouseClickPoint, mapViewer.getZoom());
+ GeoPosition geopos = mapViewer.convertPointToGeoPosition(clickPoint);
- // Get the 5 nearest neightbors to the point
- Collection waypoints = waypointTree.nearestNeighbourSearch(10, MapWaypoint.getDummyWaypoint(geopos));
+ // Get the nearest neightbors to the point
+ Collection waypoints = waypointTree.nearestNeighbourSearch(1, MapWaypoint.getDummyWaypoint(geopos));
if (waypoints == null || waypoints.isEmpty()) {
return null;
@@ -457,13 +462,10 @@ final public class MapPanel extends javax.swing.JPanel {
while (iterator.hasNext()) {
MapWaypoint nextWaypoint = iterator.next();
- Point2D point = mapViewer.getTileFactory().geoToPixel(nextWaypoint.getPosition(), mapViewer.getZoom());
+ Point2D point = mapViewer.convertGeoPositionToPoint(nextWaypoint.getPosition());
+ Rectangle rect = new Rectangle((int) point.getX() - (defaultWaypointImage.getWidth() / 2), (int) point.getY() - defaultWaypointImage.getHeight(), defaultWaypointImage.getWidth(), defaultWaypointImage.getHeight());
- Rectangle rect = mapViewer.getViewportBounds();
- Point converted_gp_pt = new Point((int) point.getX() - rect.x,
- (int) point.getY() - rect.y);
-
- if (converted_gp_pt.distance(mouseClickPoint) < 10) {
+ if (rect.contains(clickPoint)) {
closestPoints.add(nextWaypoint);
}
}
@@ -646,31 +648,30 @@ final public class MapPanel extends javax.swing.JPanel {
}//GEN-LAST:event_mapViewerMouseReleased
private void mapViewerMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseMoved
- GeoPosition geopos = mapViewer.getTileFactory().pixelToGeo(evt.getPoint(), mapViewer.getZoom());
+ GeoPosition geopos = mapViewer.convertPointToGeoPosition(evt.getPoint());
firePropertyChange(CURRENT_MOUSE_GEOPOSITION, null, geopos);
}//GEN-LAST:event_mapViewerMouseMoved
private void mapViewerMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mapViewerMouseClicked
- if(!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) {
+ if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) {
List waypoints = findClosestWaypoint(evt.getPoint());
- if(waypoints.size() > 0) {
+ if (waypoints.size() > 0) {
currentlySelectedWaypoint = waypoints.get(0);
- }
-
-
-// currentlySelectedWaypoint = findClosestWaypoint(evt.getPoint());
+ } else {
+ currentlySelectedWaypoint = null;
+ }
showDetailsPopup();
}
}//GEN-LAST:event_mapViewerMouseClicked
private void zoomInBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInBtnActionPerformed
int currentValue = mapViewer.getZoom();
- setZoom(currentValue-1);
+ setZoom(currentValue - 1);
}//GEN-LAST:event_zoomInBtnActionPerformed
private void zoomOutBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutBtnActionPerformed
int currentValue = mapViewer.getZoom();
- setZoom(currentValue+1);
+ setZoom(currentValue + 1);
}//GEN-LAST:event_zoomOutBtnActionPerformed
@@ -684,30 +685,17 @@ final public class MapPanel extends javax.swing.JPanel {
* Renderer for the map waypoints.
*/
private class MapWaypointRenderer implements WaypointRenderer {
- private final BufferedImage defaultWaypointImage;
- private final BufferedImage selectedWaypointImage;
-
- /**
- * Construct a WaypointRenederer
- *
- * @throws IOException
- */
- MapWaypointRenderer() throws IOException {
- defaultWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_teal.png"));
- selectedWaypointImage = ImageIO.read(getClass().getResource("/org/sleuthkit/autopsy/images/waypoint_yellow.png"));
- }
-
+
@Override
public void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, MapWaypoint waypoint) {
Point2D point = jxmv.getTileFactory().geoToPixel(waypoint.getPosition(), jxmv.getZoom());
- int x = (int)point.getX();
- int y = (int)point.getY();
-
- BufferedImage image = (waypoint == currentlySelectedWaypoint ? selectedWaypointImage: defaultWaypointImage);
+ int x = (int) point.getX();
+ int y = (int) point.getY();
- (gd.create()).drawImage(image, x -image.getWidth() / 2, y -image.getHeight(), null);
+ BufferedImage image = (waypoint == currentlySelectedWaypoint ? selectedWaypointImage : defaultWaypointImage);
+
+ (gd.create()).drawImage(image, x - image.getWidth() / 2, y - image.getHeight(), null);
}
-
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java
index 2defebb97e..b327e1c394 100755
--- a/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java
+++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java
@@ -183,7 +183,7 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe
String getHTMLFormattedWaypointDetails() {
return getFormattedDetails(dataModelWaypoint);
}
-
+
/**
* Returns a list of JMenuItems for the waypoint. The list list may contain
* nulls which should be removed or replaced with JSeparators.