mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-20 04:35:32 +00:00
interims commit, compiles but does nothing much yet for QuadTree Node
Change-Id: I59c85da525caad8ed3011e7e62202f7b7071dc88
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
+33
-108
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-34
@@ -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;
|
||||
}
|
||||
}
|
||||
+229
@@ -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;
|
||||
}
|
||||
}
|
||||
-55
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
-409
@@ -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;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user