63 private static final Comparator<XYZPoint>
X_COMPARATOR =
new Comparator<XYZPoint>() {
69 return Double.compare(o1.
x, o2.
x);
76 private static final Comparator<XYZPoint>
Y_COMPARATOR =
new Comparator<XYZPoint>() {
82 return Double.compare(o1.
y, o2.
y);
89 private static final Comparator<XYZPoint>
Z_COMPARATOR =
new Comparator<XYZPoint>() {
95 return Double.compare(o1.
z, o2.
z);
114 static final int X_AXIS = 0;
115 static final int Y_AXIS = 1;
116 static final int Z_AXIS = 2;
136 if (points ==
null || points.size() < 1) {
144 int centerPtIdx = points.size() / 2;
145 KdNode thisNode =
new KdNode(points.get(centerPtIdx), depth, parent);
148 List<T> lesserList = centerPtIdx > 0 ? points.subList(0, centerPtIdx) :
null;
150 List<T> greaterList = centerPtIdx < points.size() - 1 ? points.subList(centerPtIdx + 1, points.size()) :
null;
151 thisNode.setGreater(
getBalancedNode(thisNode, greaterList, depth + 1));
163 public boolean add(T value) {
177 if (node.getLesser() ==
null) {
178 KdNode newNode =
new KdNode(value, node.getDepth() + 1, node);
179 node.setLesser(newNode);
182 node = node.getLesser();
185 if (node.getGreater() ==
null) {
186 KdNode newNode =
new KdNode(value, node.getDepth() + 1, node);
187 node.setGreater(newNode);
190 node = node.getGreater();
205 if (value ==
null ||
root ==
null) {
209 KdNode node = getNode(
this, value);
210 return (node !=
null);
222 if (tree ==
null || tree.
getRoot() ==
null || value ==
null) {
228 if (node.getPoint().equals(value)) {
230 }
else if (KdNode.compareTo(node.getDepth(), value, node.getPoint()) <= 0) {
232 if (node.getLesser() ==
null) {
235 node = node.getLesser();
238 if (node.getGreater() ==
null) {
241 node = node.getGreater();
255 @SuppressWarnings(
"unchecked")
257 if (value ==
null ||
root ==
null) {
258 return Collections.EMPTY_LIST;
267 while (node !=
null) {
271 node = node.getLesser();
275 node = node.getGreater();
282 Set<KdNode> examined =
new HashSet<>();
286 while (node !=
null) {
288 searchNode(value, node, numNeighbors, results, examined);
289 node = node.getParent();
294 Collection<T> collection =
new ArrayList<>(numNeighbors);
295 for (
KdNode kdNode : results) {
296 collection.add((T) kdNode.getPoint());
311 private <T extends
KdTree.
XYZPoint>
void searchNode(T value, KdNode node,
int numNeighbors, TreeSet<KdNode> results, Set<KdNode> examined) {
316 Double lastDistance = Double.MAX_VALUE;
317 if (results.size() > 0) {
318 lastNode = results.last();
319 lastDistance = lastNode.getPoint().euclideanDistance(value);
322 if (nodeDistance.compareTo(lastDistance) < 0) {
324 }
else if (nodeDistance.equals(lastDistance)) {
326 }
else if (results.size() < numNeighbors) {
329 lastNode = results.last();
330 lastDistance = lastNode.getPoint().euclideanDistance(value);
333 KdNode lesser = node.getLesser();
334 KdNode greater = node.getGreater();
338 if (lesser !=
null && !examined.contains(lesser)) {
339 examined.add(lesser);
342 double valuePlusDistance;
345 nodePoint = node.getPoint().x;
346 valuePlusDistance = value.x - lastDistance;
349 nodePoint = node.getPoint().y;
350 valuePlusDistance = value.y - lastDistance;
353 nodePoint = node.getPoint().z;
354 valuePlusDistance = value.z - lastDistance;
357 boolean lineIntersectsCube = valuePlusDistance <= nodePoint;
360 if (lineIntersectsCube) {
361 searchNode(value, lesser, numNeighbors, results, examined);
364 if (greater !=
null && !examined.contains(greater)) {
365 examined.add(greater);
368 double valuePlusDistance;
371 nodePoint = node.getPoint().x;
372 valuePlusDistance = value.x + lastDistance;
375 nodePoint = node.getPoint().y;
376 valuePlusDistance = value.y + lastDistance;
379 nodePoint = node.getPoint().z;
380 valuePlusDistance = value.z + lastDistance;
383 boolean lineIntersectsCube = valuePlusDistance >= nodePoint;
386 if (lineIntersectsCube) {
387 searchNode(value, greater, numNeighbors, results, examined);
400 @SuppressWarnings(
"unchecked")
401 private <T extends
XYZPoint>
void search(final
KdNode node, final Deque<T> results) {
403 results.add((T) node.getPoint());
404 search(node.getGreater(), results);
405 search(node.getLesser(), results);
413 private final class EuclideanComparator
implements Comparator<KdNode> {
426 Double d1 =
point.euclideanDistance(o1.getPoint());
427 Double d2 =
point.euclideanDistance(o2.getPoint());
428 if (d1.compareTo(d2) < 0) {
430 }
else if (d2.compareTo(d1) < 0) {
433 return o1.getPoint().
compareTo(o2.getPoint());
445 final Deque<T> results =
new ArrayDeque<>();
446 search(
root, results);
447 return results.iterator();
457 final Deque<T> results =
new ArrayDeque<>();
458 search(
root, results);
459 return results.descendingIterator();
469 public static class KdNode implements Comparable<KdNode> {
539 void setLesser(
KdNode node) {
557 void setGreater(
KdNode node) {
576 XYZPoint getPoint() {
585 return 31 * (
DIMENSIONS + this.depth + this.getPoint().hashCode());
596 if (!(obj instanceof
KdNode)) {
617 StringBuilder builder =
new StringBuilder(200);
618 builder.append(
"dimensions=").append(
DIMENSIONS).append(
" depth=").append(
depth).append(
" point=").append(getPoint().
toString());
619 return builder.toString();
628 public static class XYZPoint implements Comparable<XYZPoint> {
630 protected final double x;
631 protected final double y;
632 protected final double z;
700 double lat1Radians = Math.toRadians(o1.
getX());
701 double lat2Radians = Math.toRadians(o2.
getX());
703 double deltaLatRadians = Math.toRadians(o2.
getX() - o1.
getX());
704 double deltaLongRadians = Math.toRadians(o2.
getY() - o1.
getY());
706 double a = Math.sin(deltaLatRadians / 2) * Math.sin(deltaLatRadians / 2)
707 + Math.cos(lat1Radians) * Math.cos(lat2Radians)
708 * Math.sin(deltaLongRadians / 2) * Math.sin(deltaLongRadians / 2);
710 double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
720 return 31 * (int) (this.x + this.y + this.z);
738 return ((Double.compare(
this.x, xyzPoint.
x) == 0)
739 && (Double.compare(
this.y, xyzPoint.
y) == 0)
740 && (Double.compare(
this.z, xyzPoint.
z) == 0));
765 StringBuilder builder =
new StringBuilder(200);
767 builder.append(
x).append(
", ");
768 builder.append(
y).append(
", ");
771 return builder.toString();