interims commit, compiles but does nothing much yet for QuadTree Node

Change-Id: I59c85da525caad8ed3011e7e62202f7b7071dc88
This commit is contained in:
Axel Uhl
2015-12-23 15:04:24 +01:00
parent 2350f62344
commit 3b848aeef2
7 changed files with 269 additions and 607 deletions
@@ -2,7 +2,13 @@ package com.sap.sailing.domain.common;
import java.io.Serializable;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
public interface Bearing extends Serializable {
Bearing NORTH = new DegreeBearingImpl(0);
Bearing EAST = new DegreeBearingImpl(90);
Bearing SOUTH = new DegreeBearingImpl(180);
Bearing WEST = new DegreeBearingImpl(270);
double getDegrees();
@@ -1,36 +1,12 @@
// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source$
// $RCSfile$
// $Revision: 184 $
// $Date: 2008-09-16 16:17:21 +0200 (Tue, 16 Sep 2008) $
// $Author: axel.uhl $
//
// **********************************************************************
package com.sap.sailing.domain.common.quadtree;
import java.io.Serializable;
import java.util.Collection;
import java.util.Vector;
import com.sap.sailing.domain.common.Bounds;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.BoundsImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.quadtree.impl.QuadTreeNode;
import com.sap.sailing.domain.common.quadtree.impl.Node;
/**
* A spatial data structure that provides efficient (O(log n)) access to nearest neighbors and
@@ -42,121 +18,70 @@ import com.sap.sailing.domain.common.quadtree.impl.QuadTreeNode;
* @author Axel Uhl (D043530)
*/
public class QuadTree<T> implements Serializable {
private static final long serialVersionUID = -5500716775749946017L;
static final long serialVersionUID = -7707825592455579873L;
private QuadTreeNode<T> top;
private final Node<T> root;
private final static int DEFAULT_MAX_NODE_ITEMS = 20;
public QuadTree() {
this(new BoundsImpl(new DegreePosition(-90.0, -180.0), new DegreePosition(90.0, 180.0)), 20, QuadTreeNode.NO_MIN_SIZE);
this(new BoundsImpl(new DegreePosition(-90.0, -180.0), new DegreePosition(90.0, 180.0)), DEFAULT_MAX_NODE_ITEMS);
}
public QuadTree(Position southWest, Position northEast, int maxItems) {
this(new BoundsImpl(southWest, northEast), maxItems, QuadTreeNode.NO_MIN_SIZE);
this(new BoundsImpl(southWest, northEast), maxItems);
}
private QuadTree(Bounds bounds, int maxItems, double minSize) {
top = new QuadTreeNode<T>(bounds, maxItems, minSize);
private QuadTree(Bounds bounds, int maxItems) {
root = new Node<T>(bounds, maxItems);
}
/**
* Add a object into the tree at a location.
*
* @param lat up-down location in QuadTree Grid (latitude, y)
* @param lon left-right location in QuadTree Grid (longitude, x)
* @return true if the insertion worked.
* @throws RuntimeException in case the leaf's lat/lng lies outside of the node's bounds.
* This would typically be caused by the point being outside the whole quad tree's bounds.
*/
public void put(Position point, T obj) {
getTop().put(point, obj);
root.put(point, obj);
}
/**
* Remove a object out of the tree at a location.
*
* @param lat up-down location in QuadTree Grid (latitude, y)
* @param lon left-right location in QuadTree Grid (longitude, x)
* @return the object removed, null if the object not found.
*/
public T remove(Position point, T obj) {
return getTop().remove(point, obj);
return root.remove(point);
}
public void replace(Position point, T newObj) {
getTop().replace(point, newObj);
}
/** Clear the tree. */
/**
* Remove all elements from this tree.
*/
public void clear() {
getTop().clear();
root.clear();
}
/**
* Get an object closest to a lat/lon.
*
* @param lat up-down location in QuadTree Grid (latitude, y)
* @param lon left-right location in QuadTree Grid (longitude, x)
* @return the object that was found.
* Get the value nearest to <code>point</code>. If the tree is empty, <code>null</code> is returned. Distance
* is calculated using the method {@link #getLatLngDistance(Position, Position)} which is an approximation only,
* based on Euklidian geometry with the latitude/longitude values.
*/
public T get(Position point) {
return getTop().get(point);
return root.get(point);
}
/**
* Get an object closest to a lat/lon, within a maximum distance.
* Get the value closest to <code>point</code>, within a maximum distance, where distance
* is computed by the rules of {@link #getLatLngDistance(Position, Position)}. If no key
* is found within that distance, <code>null</code> is returned.
*
* @param lat up-down location in QuadTree Grid (latitude, y)
* @param lon left-right location in QuadTree Grid (longitude, x)
* @param withinDistance maximum get distance. The distance is given
* as the square root of the sum of
* the squares of the latitude and longitude differences, respectively.
* It therefore does not correspond to any distance in meters or
* any euclidian distance at all. However, it should be good enough
* (at least outside the polar regions, and in particular for smaller
* regions), and in particular to find <em>minimum</em> distances.
* @return the object that was found, null if nothing is within
* the maximum distance.
* @param withinDistance
* maximum get distance. The distance is given as the square root of the sum of the squares of the
* latitude and longitude differences, respectively. It therefore does not correspond to any distance in
* meters or any euclidian distance at all. However, it should be good enough (at least outside the polar
* regions, and in particular for smaller regions), and in particular to find <em>minimum</em> distances.
* See {@link #getLatLngDistance(Position, Position)}.
* @return the object that was found, null if nothing is within the maximum distance.
*/
public T get(Position point, double withinDistance) {
return getTop().get(point, withinDistance);
return root.get(point, withinDistance);
}
/**
* Get all the objects within a bounding box.
*
* @return Vector of objects.
* Get all values withing the <code>bounds</code>
*/
public Collection<T> get(Bounds rect) {
return get(rect, new Vector<T>());
}
/**
* Get all the objects within a bounding box, and return the
* objects within a given Vector.
*
* @param vector a vector to add objects to.
* @return Vector of objects.
*/
private Collection<T> get(Bounds rect, Collection<T> vector) {
if (vector == null) {
vector = new Vector<T>();
}
// crossing the dateline, right?? Or at least containing the
// entire earth. Might be trouble for VERY LARGE scales. The
// last check is for micro-errors that happen to lon points
// where there might be a smudge overlap for very small
// scales.
if (rect.getSouthWest().getLngDeg() > rect.getNorthEast().getLngDeg() || (Math.abs(rect.getSouthWest().getLngDeg() - rect.getNorthEast().getLngDeg()) < .001)) {
return getTop().get(new BoundsImpl(rect.getSouthWest(), new DegreePosition(rect.getNorthEast().getLatDeg(), 180)),
getTop().get(new BoundsImpl(new DegreePosition(rect.getSouthWest().getLatDeg(), -180), rect.getNorthEast()), vector));
} else
return getTop().get(rect, vector);
}
private QuadTreeNode<T> getTop() {
return top;
public Iterable<T> get(Bounds bounds) {
return root.get(bounds);
}
/**
@@ -1,34 +0,0 @@
// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source$
// $RCSfile$
// $Revision: 15 $
// $Date: 2008-04-20 02:18:58 +0200 (Sun, 20 Apr 2008) $
// $Author: uhl $
//
// **********************************************************************
package com.sap.sailing.domain.common.quadtree.impl;
/**
* A *really* simple class used as a changable double.
*/
public class MutableDistance {
public double value = 0;
public MutableDistance(double distance) {
value = distance;
}
}
@@ -0,0 +1,229 @@
package com.sap.sailing.domain.common.quadtree.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.sap.sailing.domain.common.Bounds;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.BoundsImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.quadtree.QuadTree;
/**
* A node in a {@link QuadTree}. There may be internal nodes that have no elements in them but have exactly
* four child nodes, or leaf nodes that have no children but contain item elements.
*
* @author Axel Uhl (D043530)
*
* @param <T>
*/
public class Node<T> {
/**
* Either an array of exactly four child nodes which are then all non-<code>null</code>, or <code>null</code>,
* meaning that this node is a child node, having no children but potentially having items. A node that has children
* has no items. Quadrants are numbered as usual in geometry. See {@link #NE}, {@link #NW}, {@link #SW} and
* {@link #SE}.
*/
private Node<T>[] children;
private Map<Position, T> items;
private final Bounds bounds;
/**
* The maximum number of items to hold in {@link #items}. If {@link #put(Position, Object) adding} an item to this node
* would increase the item collection's size beyond this number, the node is {@link #split() split} into four new leaf
* nodes, and its items are distributed across those new leaf nodes. This number must be a positive integer.
*/
private final int maxItems;
/**
* The quadrant index in {@link #children} for the north-east subtree
*/
private final int NE = 0;
/**
* The quadrant index in {@link #children} for the north-west subtree
*/
private final int NW = 1;
/**
* The quadrant index in {@link #children} for the south-west subtree
*/
private final int SW = 2;
/**
* The quadrant index in {@link #children} for the south-east subtree
*/
private final int SE = 3;
/**
* Creates a new node with the <code>bounds</code> as specified. The node starts out empty, as a leaf node that has
* an empty set of items.
*
* @param bounds
* must not have a north latitude less than the south latitude, or an {@link IllegalArgumentException}
* will result
*/
public Node(Bounds bounds, int maxItems) {
if (bounds.getNorthEast().getLatDeg() < bounds.getSouthEast().getLatDeg()) {
throw new IllegalArgumentException("North border of bounds "+bounds+" is further south than its south border");
}
if (maxItems <= 0) {
throw new IllegalArgumentException("Maximum number of items must be positive but was "+maxItems);
}
this.maxItems = maxItems;
this.bounds = bounds;
items = new HashMap<>(maxItems);
}
private void split() {
assert children == null;
assert items != null;
createChildren();
distributeItemsToChildren();
assert children != null;
assert items == null;
}
private void distributeItemsToChildren() {
assert items != null;
assert children != null;
for (final Entry<Position, T> item : items.entrySet()) {
getChild(item.getKey()).put(item.getKey(), item.getValue());
}
assert items == null;
}
private Node<T> getChild(Position key) {
assert children != null;
assert items == null;
assert bounds.contains(key);
for (final Node<T> child : children) {
if (child.bounds.contains(key)) {
return child;
}
}
throw new RuntimeException("Internal error: position "+key+" is within node bounds "+bounds+" but no child contains it");
}
/**
* Adds the <code>value</code> to this node and ensures that the node still meets the requirements regarding size.
* If necessary, the node is split with its items distributed across the new children. If a value already existed at
* position <code>key</code>, it is replaced.
*
* @param key
* the position at which to insert <code>value</code>. Must be contained in this node's {@link #bounds}.
* If it is not, an {@link IllegalArgumentException} will be thrown. Must not be <code>null</code>.
* @return the value previously at position <code>key</code> or <code>null</code> if there was no value at that
* position.
*/
public T put(Position key, T value) {
if (value == null) {
throw new NullPointerException("Cannot insert null values into this node");
}
if (key == null) {
throw new NullPointerException("null keys not allowed");
}
if (!bounds.contains(key)) {
throw new IllegalArgumentException("key "+key+" must be within this node's bounds "+bounds);
}
final T result;
if (items != null) {
result = items.put(key, value);
if (result == null) {
// the size of this node has increased by one; check size constraint
if (items.size() > maxItems) {
split();
}
}
} else {
result = getChild(key).put(key, value);
}
return result;
}
/**
* Removes the element at position <code>key</code> from this node or any child nodes if such an element exists
*
* @return the value removed, or <code>null</code> if no element existed at position <code>key</code> in this node
* or any of its children
*/
public T remove(Position key) {
final T result;
if (key != null) {
if (items != null) {
result = items.remove(key);
} else {
result = getChild(key).remove(key);
}
} else {
result = null;
}
return result;
}
private void createChildren() {
assert children == null;
@SuppressWarnings("unchecked")
final Node<T>[] newChildren = (Node<T>[]) new Node<?>[4];
children = newChildren;
final Position middleWest = new DegreePosition((bounds.getSouthWest().getLatDeg() + bounds.getNorthEast().getLatDeg())/2.,
bounds.getSouthWest().getLngDeg());
final Position southCenter = new DegreePosition(bounds.getSouthWest().getLatDeg(),
(bounds.getNorthEast().getLngDeg() + bounds.getSouthWest().getLngDeg()) / 2. -
// adjust for date line crossing if necessary
bounds.getNorthEast().getLngDeg() >= bounds.getSouthWest().getLngDeg() ? 0. : 360.);
final Position middleCenter = new DegreePosition(middleWest.getLatDeg(), southCenter.getLngDeg());
final Position middleEast = new DegreePosition(middleWest.getLatDeg(), bounds.getNorthEast().getLngDeg());
final Position northCenter = new DegreePosition(bounds.getNorthEast().getLatDeg(), southCenter.getLngDeg());
children[NE] = new Node<T>(new BoundsImpl(middleCenter, bounds.getNorthEast()), maxItems);
children[NW] = new Node<T>(new BoundsImpl(middleWest, northCenter), maxItems);
children[SW] = new Node<T>(new BoundsImpl(bounds.getSouthWest(), middleCenter), maxItems);
children[SE] = new Node<T>(new BoundsImpl(southCenter, middleEast), maxItems);
assert children != null;
assert children.length == 4;
assert children[NE] != null && children[NW] != null && children[SW] != null && children[SE] != null;
}
/**
* Remove all elements from this node by either removing all its items locally or by removing all children
* and converting this back into a leaf node.
*/
public void clear() {
if (items != null) {
items.clear();
} else {
items = new HashMap<>();
children = null;
}
}
/**
* Get the value nearest to <code>point</code>. If the node is empty, <code>null</code> is returned. Distance is
* calculated using the method {@link QuadTree#getLatLngDistance(Position, Position)} which is an approximation
* only, based on Euklidian geometry with the latitude/longitude values.
* <p>
*
* If this is a leaf node, the nearest key by the definition above is used to determine the corresponding value and
* return it. Otherwise, the children are traversed. For the first child, the nearest key is determined recursively.
* Other children only need to be traversed if their bounds are closer to <code>point</code> than the key found so
* far.
*/
public T get(Position point) {
// TODO Auto-generated method stub
return null;
}
public T get(Position point, double withinDistance) {
// TODO Auto-generated method stub
return null;
}
public Collection<T> get(Bounds rect) {
// TODO Auto-generated method stub
return null;
}
}
@@ -1,55 +0,0 @@
// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source:
// /cvs/distapps/openmap/src/openmap/com/bbn/openmap/util/quadtree/QuadTreeLeaf.java,v
// $
// $RCSfile$
// $Revision: 45 $
// $Date: 2008-06-08 22:04:43 +0200 (Sun, 08 Jun 2008) $
// $Author: uhl $
//
// **********************************************************************
package com.sap.sailing.domain.common.quadtree.impl;
import java.io.Serializable;
import com.sap.sailing.domain.common.Position;
public class QuadTreeLeaf<T> implements Serializable {
static final long serialVersionUID = 7885745536157252519L;
private Position point;
private T object;
public QuadTreeLeaf(Position point, T obj) {
this.point = point;
this.object = obj;
}
public Position getPoint() {
return point;
}
public T getObject() {
return object;
}
public String toString() {
return "QuadTreeLeaf at (" + point.getLatDeg() + ", " + point.getLngDeg() + ") with object " + getObject();
}
}
@@ -1,409 +0,0 @@
// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source$
// $RCSfile$
// $Revision: 101 $
// $Date: 2008-07-09 23:09:42 +0200 (Wed, 09 Jul 2008) $
// $Author: axel.uhl $
//
// **********************************************************************
package com.sap.sailing.domain.common.quadtree.impl;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;
import com.sap.sailing.domain.common.Bounds;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.BoundsImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.quadtree.QuadTree;
/**
* The QuadTreeNode is the part of the QuadTree that either holds
* children nodes, or objects as leaves. Currently, the nodes that
* have children do not hold items that span across children
* boundaries, since this was designed to handle point data.
*/
public class QuadTreeNode<T> implements Serializable {
static final long serialVersionUID = -6111633198469889444L;
private final static int NORTHWEST = 0;
private final static int NORTHEAST = 1;
private final static int SOUTHEAST = 2;
private final static int SOUTHWEST = 3;
public final static double NO_MIN_SIZE = -1;
private Vector<QuadTreeLeaf<T>> items;
private QuadTreeNode<T>[] children;
private int maxItems;
private double minSize;
private Bounds bounds;
/**
* Added to avoid problems when a node is completely filled with a
* single point value.
*/
private boolean allTheSamePoint;
private Position firstPoint;
/**
* Constructor to use if you are going to store the objects in
* lat/lon space, and there is really no smallest node size.
*
* @param maximumItems number of items to hold in a node before
* splitting itself into four children and redispensing the
* items into them.
*/
public QuadTreeNode(Bounds rect, int maximumItems) {
this(rect, maximumItems, NO_MIN_SIZE);
}
/**
* Constructor to use if you are going to store the objects in x/y
* space, and there is a smallest node size because you don't want
* the nodes to be smaller than a group of pixels.
*
* @param north northern border of node coverage.
* @param west western border of node coverage.
* @param south southern border of node coverage.
* @param east eastern border of node coverage.
* @param maximumItems number of items to hold in a node before
* splitting itself into four children and redispensing the
* items into them.
* @param minimumSize the minimum difference between the
* boundaries of the node.
*/
public QuadTreeNode(Bounds rect, int maximumItems, double minimumSize) {
bounds = rect;
maxItems = maximumItems;
minSize = minimumSize;
items = new Vector<QuadTreeLeaf<T>>();
}
/** Return true if the node has children. */
public boolean hasChildren() {
if (children != null)
return true;
else
return false;
}
/**
* This method splits the node into four children, and disperses
* the items into the children. The split only happens if the
* boundary size of the node is larger than the minimum size (if
* we care). The items in this node are cleared after they are put
* into the children.
*/
@SuppressWarnings("unchecked")
protected void split() {
// Make sure we're bigger than the minimum, if we care,
if (minSize != NO_MIN_SIZE) {
if (Math.abs(bounds.getNorthEast().getLatDeg() - bounds.getSouthWest().getLatDeg()) < minSize
&& Math.abs(bounds.getNorthEast().getLngDeg() - bounds.getSouthWest().getLngDeg()) < minSize)
return;
}
double nsHalf = (bounds.getNorthEast().getLatDeg() + bounds.getSouthWest().getLatDeg()) / 2.0;
double ewHalf = (bounds.getNorthEast().getLngDeg() + bounds.getSouthWest().getLngDeg()) / 2.0;
children = new QuadTreeNode[4];
children[NORTHWEST] = new QuadTreeNode<T>(new BoundsImpl(new DegreePosition(nsHalf, bounds.getSouthWest().getLngDeg()), new DegreePosition(bounds.getNorthEast().getLatDeg(), ewHalf)), maxItems);
children[NORTHEAST] = new QuadTreeNode<T>(new BoundsImpl(new DegreePosition(nsHalf, ewHalf), bounds.getNorthEast()), maxItems);
children[SOUTHEAST] = new QuadTreeNode<T>(new BoundsImpl(new DegreePosition(bounds.getSouthWest().getLatDeg(), ewHalf), new DegreePosition(nsHalf, bounds.getNorthEast().getLngDeg())), maxItems);
children[SOUTHWEST] = new QuadTreeNode<T>(new BoundsImpl(bounds.getSouthWest(), new DegreePosition(nsHalf, ewHalf)), maxItems);
Vector<QuadTreeLeaf<T>> temp = new Vector<QuadTreeLeaf<T>>(items);
items.removeAllElements();
for (Iterator<QuadTreeLeaf<T>> i=temp.iterator(); i.hasNext(); ) {
put(i.next());
}
}
/**
* Get the node that covers a certain lat/lon pair.
*
* @param lat up-down location in QuadTree Grid (latitude, y)
* @param lon left-right location in QuadTree Grid (longitude, x)
* @return node if child covers the point, null if the point is
* out of range.
*/
protected QuadTreeNode<T> getChild(Position point) {
if (bounds.contains(point)) {
if (children != null) {
for (int i = 0; i < children.length; i++) {
if (children[i].bounds.contains(point))
return children[i].getChild(point);
}
} else
return this; // no children, lat, lon here...
}
return null;
}
/**
* Add a object into the tree at a location.
*
* @param lat up-down location in QuadTree Grid (latitude, y)
* @param lon left-right location in QuadTree Grid (longitude, x)
* @param obj object to add to the tree.
* @return true if the pution worked.
* @throws RuntimeException in case the leaf's lat/lng lies outside of the node's bounds.
* This would typically be caused by the point being outside the whole quad tree's bounds.
*/
public void put(Position point, T obj) {
put(new QuadTreeLeaf<T>(point, obj));
}
public void replace(Position point, T newObj) {
boolean inThis = false;
if (children == null) {
inThis = true;
} else {
QuadTreeNode<T> child = getChild(point);
if (child == null) {
inThis = true;
} else {
child.replace(point, newObj);
}
}
if (inThis) {
items.clear();
items.add(new QuadTreeLeaf<T>(point, newObj));
}
}
/**
* Add a QuadTreeLeaf into the tree at a location.
*
* @param leaf object-location composite
* @return true if the pution worked.
* @throws RuntimeException in case the leaf's lat/lng lies outside of the node's bounds.
* This would typically be caused by the point being outside the whole quad tree's bounds.
*/
public void put(QuadTreeLeaf<T> leaf) {
if (children == null) {
this.items.addElement(leaf);
if (this.items.size() == 1) {
this.allTheSamePoint = true;
this.firstPoint = leaf.getPoint();
} else {
if (!this.firstPoint.equals(leaf.getPoint())) {
this.allTheSamePoint = false;
}
}
if (this.items.size() > maxItems && !this.allTheSamePoint) {
split();
}
} else {
QuadTreeNode<T> node = getChild(leaf.getPoint());
if (node != null) {
node.put(leaf);
} else {
throw new RuntimeException("leaf "+leaf+" not contained in bounds (("+
bounds.getSouthWest().getLatDeg()+", "+bounds.getSouthWest().getLngDeg()+"), ("+
bounds.getNorthEast().getLatDeg()+", "+bounds.getNorthEast().getLngDeg()+"))");
}
}
}
/**
* Remove a object out of the tree at a location.
*
* @param lat up-down location in QuadTree Grid (latitude, y)
* @param lon left-right location in QuadTree Grid (longitude, x)
* @return the object removed, null if the object not found.
*/
public T remove(Position point, T obj) {
return remove(new QuadTreeLeaf<T>(point, obj));
}
/**
* Remove a QuadTreeLeaf out of the tree at a location.
*
* @param leaf object-location composite
* @return the object removed, null if the object not found.
*/
public T remove(QuadTreeLeaf<T> leaf) {
if (children == null) {
// This must be the node that has it...
for (int i = 0; i < items.size(); i++) {
QuadTreeLeaf<T> qtl = items.elementAt(i);
if (leaf.getObject() == qtl.getObject()) {
items.removeElementAt(i);
return qtl.getObject();
}
}
} else {
QuadTreeNode<T> node = getChild(leaf.getPoint());
if (node != null) {
return node.remove(leaf);
}
}
return null;
}
/** Clear the tree below this node. */
public void clear() {
this.items.removeAllElements();
if (children != null) {
for (int i = 0; i < children.length; i++) {
children[i].clear();
}
children = null;
}
}
/**
* Get an object closest to a <tt>point</tt>.
*
* @param lat up-down location in QuadTree Grid (latitude, y)
* @param lon left-right location in QuadTree Grid (longitude, x)
* @return the object that matches the best distance, null if no
* object was found.
*/
public T get(Position point) {
return get(point, Double.POSITIVE_INFINITY);
}
/**
* Get an object closest to a <tt>point</tt>. If there are children at
* this node, then the children are searched. The children are
* checked first, to see if they are closer than the best distance
* already found. If a closer object is found, bestDistance will
* be updated with a new Double object that has the new distance.
*
* @param lat up-down location in QuadTree Grid (latitude, y)
* @param lon left-right location in QuadTree Grid (longitude, x)
* @param withinDistance maximum get distance. The distance is given
* as the square root of the sum of
* the squares of the latitude and longitude differences, respectively.
* It therefore does not correspond to any distance in meters or
* any euclidian distance at all. However, it should be good enough
* (at least outside the polar regions, and in particular for smaller
* regions), and in particular to find <em>minimum</em> distances.
* @return the object that matches the best distance, null if no
* closer object was found.
*/
public T get(Position point, double withinDistance) {
return get(point, new MutableDistance(withinDistance));
}
/**
* Get an object closest to a <tt>point</tt>. If there are children at
* this node, then the children are searched. The children are
* checked first, to see if they are closer than the best distance
* already found. If a closer object is found, bestDistance will
* be updated with a new Double object that has the new distance.
* @param point location in QuadTree Grid
* @param bestDistance the closest distance of the object found so
* far. The distance is given as the square root of the sum of
* the squares of the latitude and longitude differences, respectively.
* It therefore does not correspond to any distance in meters or
* any euclidian distance at all. However, it should be good enough
* (at least outside the polar regions, and in particular for smaller
* regions), and in particular to find <em>minimum</em> distances.
*
* @return the object that matches the best distance, null if no
* closer object was found.
*/
public T get(Position point, MutableDistance bestDistance) {
T closest = null;
if (children == null) {
// This must be the node that has it...
for (QuadTreeLeaf<T> qtl:items) {
double distance = QuadTree.getLatLngDistance(point, qtl.getPoint());
if (distance < bestDistance.value) {
bestDistance.value = distance;
closest = qtl.getObject();
}
}
return closest;
} else {
// Check the distance of the bounds of the children,
// versus the bestDistance. If there is a boundary that
// is closer, then it is possible that another node has an
// object that is closer.
for (int i = 0; i < children.length; i++) {
double childDistance = borderDistance(children[i].bounds, point);
if (childDistance < bestDistance.value) {
T test = children[i].get(point, bestDistance);
if (test != null)
closest = test;
}
}
}
return closest;
}
/**
* A utility method to figure out the closest distance of a bound's border
* to a point. If the point is inside the bounds, return 0.
*
* @return closest distance to the point.
*/
private static double borderDistance(Bounds bounds, Position point) {
double nsdistance;
double ewdistance;
if (bounds.getSouthWest().getLatDeg() <= point.getLatDeg() && point.getLatDeg() <= bounds.getNorthEast().getLatDeg()) {
nsdistance = 0;
} else {
nsdistance = Math.min((Math.abs(point.getLatDeg() - bounds.getNorthEast().getLatDeg())), (Math.abs(point.getLatDeg()
- bounds.getSouthWest().getLatDeg())));
}
if (bounds.getSouthWest().getLngDeg() <= point.getLngDeg() && point.getLngDeg() <= bounds.getNorthEast().getLngDeg()) {
ewdistance = 0;
} else {
ewdistance = Math.min((Math.abs(point.getLngDeg() - bounds.getNorthEast().getLngDeg())),
(Math.abs(point.getLngDeg() - bounds.getSouthWest().getLngDeg())));
}
double distance = Math.sqrt(nsdistance*nsdistance + ewdistance*ewdistance);
return distance;
}
/**
* Get all the objects within a bounding box.
*
* @param rect boundary of area to fill.
* @param vector current vector of objects.
* @return updated Vector of objects.
*/
public Collection<T> get(Bounds rect, Collection<T> vector) {
if (children == null) {
for (Iterator<QuadTreeLeaf<T>> i=items.iterator(); i.hasNext(); ) {
QuadTreeLeaf<T> qtl = i.next();
if (rect.contains(qtl.getPoint())) {
vector.add(qtl.getObject());
}
}
} else {
for (int i = 0; i < children.length; i++) {
if (rect.intersects(children[i].bounds)) {
children[i].get(rect, vector);
}
}
}
return vector;
}
}
@@ -217,7 +217,7 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
private void updateCachedPlacemarks(Position cachedPoint, Double newRadius, List<Placemark> newPlacemarks) {
if (cachedPoint != null) {
synchronized (cache) {
cache.replace(cachedPoint, new Util.Triple<Position, Double, List<Placemark>>(cachedPoint, newRadius, newPlacemarks));
cache.put(cachedPoint, new Util.Triple<Position, Double, List<Placemark>>(cachedPoint, newRadius, newPlacemarks));
}
}
}