Imported QuadTree as .jar instead

This commit is contained in:
misaochan 2016-01-08 17:19:41 +13:00
parent 94b3a233c0
commit 7ac01e5a01
8 changed files with 4 additions and 694 deletions

View file

@ -2,6 +2,9 @@ package fr.free.nrw.commons.caching;
import android.util.Log;
import com.github.varunpant.quadtree.Point;
import com.github.varunpant.quadtree.QuadTree;
import java.util.ArrayList;
import java.util.List;

View file

@ -1,6 +0,0 @@
package fr.free.nrw.commons.caching;
public interface Func {
public void call(QuadTree quadTree, Node node);
}

View file

@ -1,123 +0,0 @@
package fr.free.nrw.commons.caching;
class Node {
private double x;
private double y;
private double w;
private double h;
private Node opt_parent;
private Point point;
private NodeType nodetype = NodeType.EMPTY;
private Node nw;
private Node ne;
private Node sw;
private Node se;
/**
* Constructs a new quad tree node.
*
* @param {double} x X-coordiate of node.
* @param {double} y Y-coordinate of node.
* @param {double} w Width of node.
* @param {double} h Height of node.
* @param {Node} opt_parent Optional parent node.
* @constructor
*/
public Node(double x, double y, double w, double h, Node opt_parent) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.opt_parent = opt_parent;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public double getW() {
return w;
}
public void setW(double w) {
this.w = w;
}
public double getH() {
return h;
}
public void setH(double h) {
this.h = h;
}
public Node getParent() {
return opt_parent;
}
public void setParent(Node opt_parent) {
this.opt_parent = opt_parent;
}
public void setPoint(Point point) {
this.point = point;
}
public Point getPoint() {
return this.point;
}
public void setNodeType(NodeType nodetype) {
this.nodetype = nodetype;
}
public NodeType getNodeType() {
return this.nodetype;
}
public void setNw(Node nw) {
this.nw = nw;
}
public void setNe(Node ne) {
this.ne = ne;
}
public void setSw(Node sw) {
this.sw = sw;
}
public void setSe(Node se) {
this.se = se;
}
public Node getNe() {
return ne;
}
public Node getNw() {
return nw;
}
public Node getSw() {
return sw;
}
public Node getSe() {
return se;
}
}

View file

@ -1,11 +0,0 @@
package fr.free.nrw.commons.caching;
/**
* Enumeration of node types.
* @enum {number}
*/
public enum NodeType {
EMPTY,
LEAF,
POINTER
}

View file

@ -1,69 +0,0 @@
package fr.free.nrw.commons.caching;
public class Point implements Comparable {
private double x;
private double y;
private Object opt_value;
/**
* Creates a new point object.
*
* @param {double} x The x-coordinate of the point.
* @param {double} y The y-coordinate of the point.
* @param {Object} opt_value Optional value associated with the point.
*/
public Point(double x, double y, Object opt_value) {
this.x = x;
this.y = y;
this.opt_value = opt_value;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public Object getValue() {
return opt_value;
}
public void setValue(Object opt_value) {
this.opt_value = opt_value;
}
@Override
public String toString() {
return "(" + this.x + ", " + this.y + ")";
}
@Override
public int compareTo(Object o) {
Point tmp = (Point) o;
if (this.x < tmp.x) {
return -1;
} else if (this.x > tmp.x) {
return 1;
} else {
if (this.y < tmp.y) {
return -1;
} else if (this.y > tmp.y) {
return 1;
}
return 0;
}
}
}

View file

@ -1,477 +0,0 @@
package fr.free.nrw.commons.caching;
import java.util.ArrayList;
import java.util.List;
/**
* Datastructure: A point Quad Tree for representing 2D data. Each
* region has the same ratio as the bounds for the tree.
* <p/>
* The implementation currently requires pre-determined bounds for data as it
* can not rebalance itself to that degree.
*/
public class QuadTree {
private Node root_;
private int count_ = 0;
/**
* Constructs a new quad tree.
*
* @param {double} minX Minimum x-value that can be held in tree.
* @param {double} minY Minimum y-value that can be held in tree.
* @param {double} maxX Maximum x-value that can be held in tree.
* @param {double} maxY Maximum y-value that can be held in tree.
*/
public QuadTree(double minX, double minY, double maxX, double maxY) {
this.root_ = new Node(minX, minY, maxX - minX, maxY - minY, null);
}
/**
* Returns a reference to the tree's root node. Callers shouldn't modify nodes,
* directly. This is a convenience for visualization and debugging purposes.
*
* @return {Node} The root node.
*/
public Node getRootNode() {
return this.root_;
}
//TODO: Marked by Nicolas as: Set an object at particular coordinates
/**
* Sets the value of an (x, y) point within the quad-tree.
*
* @param {double} x The x-coordinate.
* @param {double} y The y-coordinate.
* @param {Object} value The value associated with the point.
*/
public void set(double x, double y, Object value) {
Node root = this.root_;
if (x < root.getX() || y < root.getY() || x > root.getX() + root.getW() || y > root.getY() + root.getH()) {
throw new QuadTreeException("Out of bounds : (" + x + ", " + y + ")");
}
if (this.insert(root, new Point(x, y, value))) {
this.count_++;
}
}
/**
* Gets the value of the point at (x, y) or null if the point is empty.
*
* @param {double} x The x-coordinate.
* @param {double} y The y-coordinate.
* @param {Object} opt_default The default value to return if the node doesn't
* exist.
* @return {*} The value of the node, the default value if the node
* doesn't exist, or undefined if the node doesn't exist and no default
* has been provided.
*/
public Object get(double x, double y, Object opt_default) {
Node node = this.find(this.root_, x, y);
return node != null ? node.getPoint().getValue() : opt_default;
}
/**
* Removes a point from (x, y) if it exists.
*
* @param {double} x The x-coordinate.
* @param {double} y The y-coordinate.
* @return {Object} The value of the node that was removed, or null if the
* node doesn't exist.
*/
public Object remove(double x, double y) {
Node node = this.find(this.root_, x, y);
if (node != null) {
Object value = node.getPoint().getValue();
node.setPoint(null);
node.setNodeType(NodeType.EMPTY);
this.balance(node);
this.count_--;
return value;
} else {
return null;
}
}
/**
* Returns true if the point at (x, y) exists in the tree.
*
* @param {double} x The x-coordinate.
* @param {double} y The y-coordinate.
* @return {boolean} Whether the tree contains a point at (x, y).
*/
public boolean contains(double x, double y) {
return this.get(x, y, null) != null;
}
/**
* @return {boolean} Whether the tree is empty.
*/
public boolean isEmpty() {
return this.root_.getNodeType() == NodeType.EMPTY;
}
/**
* @return {number} The number of items in the tree.
*/
public int getCount() {
return this.count_;
}
/**
* Removes all items from the tree.
*/
public void clear() {
this.root_.setNw(null);
this.root_.setNe(null);
this.root_.setSw(null);
this.root_.setSe(null);
this.root_.setNodeType(NodeType.EMPTY);
this.root_.setPoint(null);
this.count_ = 0;
}
/**
* Returns an array containing the coordinates of each point stored in the tree.
* @return {Array.<Point>} Array of coordinates.
*/
public Point[] getKeys() {
final List<Point> arr = new ArrayList<Point>();
this.traverse(this.root_, new Func() {
@Override
public void call(QuadTree quadTree, Node node) {
arr.add(node.getPoint());
}
});
return arr.toArray(new Point[arr.size()]);
}
/**
* Returns an array containing all values stored within the tree.
* @return {Array.<Object>} The values stored within the tree.
*/
public Object[] getValues() {
final List<Object> arr = new ArrayList<Object>();
this.traverse(this.root_, new Func() {
@Override
public void call(QuadTree quadTree, Node node) {
arr.add(node.getPoint().getValue());
}
});
return arr.toArray(new Object[arr.size()]);
}
public Point[] searchIntersect(final double xmin, final double ymin, final double xmax, final double ymax) {
final List<Point> arr = new ArrayList<Point>();
this.navigate(this.root_, new Func() {
@Override
public void call(QuadTree quadTree, Node node) {
Point pt = node.getPoint();
if (pt.getX() < xmin || pt.getX() > xmax || pt.getY() < ymin || pt.getY() > ymax) {
// Definitely not within the polygon!
} else {
arr.add(node.getPoint());
}
}
}, xmin, ymin, xmax, ymax);
return arr.toArray(new Point[arr.size()]);
}
//TODO: Marked by Nicolas as: Find objects within a rectangle
public Point[] searchWithin(final double xmin, final double ymin, final double xmax, final double ymax) {
final List<Point> arr = new ArrayList<Point>();
this.navigate(this.root_, new Func() {
@Override
public void call(QuadTree quadTree, Node node) {
Point pt = node.getPoint();
if (pt.getX() > xmin && pt.getX() < xmax && pt.getY() > ymin && pt.getY() < ymax) {
arr.add(node.getPoint());
}
}
}, xmin, ymin, xmax, ymax);
return arr.toArray(new Point[arr.size()]);
}
public void navigate(Node node, Func func, double xmin, double ymin, double xmax, double ymax) {
switch (node.getNodeType()) {
case LEAF:
func.call(this, node);
break;
case POINTER:
if (intersects(xmin, ymax, xmax, ymin, node.getNe()))
this.navigate(node.getNe(), func, xmin, ymin, xmax, ymax);
if (intersects(xmin, ymax, xmax, ymin, node.getSe()))
this.navigate(node.getSe(), func, xmin, ymin, xmax, ymax);
if (intersects(xmin, ymax, xmax, ymin, node.getSw()))
this.navigate(node.getSw(), func, xmin, ymin, xmax, ymax);
if (intersects(xmin, ymax, xmax, ymin, node.getNw()))
this.navigate(node.getNw(), func, xmin, ymin, xmax, ymax);
break;
}
}
private boolean intersects(double left, double bottom, double right, double top, Node node) {
return !(node.getX() > right ||
(node.getX() + node.getW()) < left ||
node.getY() > bottom ||
(node.getY() + node.getH()) < top);
}
/**
* Clones the quad-tree and returns the new instance.
* @return {QuadTree} A clone of the tree.
*/
public QuadTree clone() {
double x1 = this.root_.getX();
double y1 = this.root_.getY();
double x2 = x1 + this.root_.getW();
double y2 = y1 + this.root_.getH();
final QuadTree clone = new QuadTree(x1, y1, x2, y2);
// This is inefficient as the clone needs to recalculate the structure of the
// tree, even though we know it already. But this is easier and can be
// optimized when/if needed.
this.traverse(this.root_, new Func() {
@Override
public void call(QuadTree quadTree, Node node) {
clone.set(node.getPoint().getX(), node.getPoint().getY(), node.getPoint().getValue());
}
});
return clone;
}
/**
* Traverses the tree depth-first, with quadrants being traversed in clockwise
* order (NE, SE, SW, NW). The provided function will be called for each
* leaf node that is encountered.
* @param {QuadTree.Node} node The current node.
* @param {function(QuadTree.Node)} fn The function to call
* for each leaf node. This function takes the node as an argument, and its
* return value is irrelevant.
* @private
*/
public void traverse(Node node, Func func) {
switch (node.getNodeType()) {
case LEAF:
func.call(this, node);
break;
case POINTER:
this.traverse(node.getNe(), func);
this.traverse(node.getSe(), func);
this.traverse(node.getSw(), func);
this.traverse(node.getNw(), func);
break;
}
}
/**
* Finds a leaf node with the same (x, y) coordinates as the target point, or
* null if no point exists.
* @param {QuadTree.Node} node The node to search in.
* @param {number} x The x-coordinate of the point to search for.
* @param {number} y The y-coordinate of the point to search for.
* @return {QuadTree.Node} The leaf node that matches the target,
* or null if it doesn't exist.
* @private
*/
public Node find(Node node, double x, double y) {
Node resposne = null;
switch (node.getNodeType()) {
case EMPTY:
break;
case LEAF:
resposne = node.getPoint().getX() == x && node.getPoint().getY() == y ? node : null;
break;
case POINTER:
resposne = this.find(this.getQuadrantForPoint(node, x, y), x, y);
break;
default:
throw new QuadTreeException("Invalid nodeType");
}
return resposne;
}
/**
* Inserts a point into the tree, updating the tree's structure if necessary.
* @param {.QuadTree.Node} parent The parent to insert the point
* into.
* @param {QuadTree.Point} point The point to insert.
* @return {boolean} True if a new node was added to the tree; False if a node
* already existed with the correpsonding coordinates and had its value
* reset.
* @private
*/
private boolean insert(Node parent, Point point) {
Boolean result = false;
switch (parent.getNodeType()) {
case EMPTY:
this.setPointForNode(parent, point);
result = true;
break;
case LEAF:
if (parent.getPoint().getX() == point.getX() && parent.getPoint().getY() == point.getY()) {
this.setPointForNode(parent, point);
result = false;
} else {
this.split(parent);
result = this.insert(parent, point);
}
break;
case POINTER:
result = this.insert(
this.getQuadrantForPoint(parent, point.getX(), point.getY()), point);
break;
default:
throw new QuadTreeException("Invalid nodeType in parent");
}
return result;
}
/**
* Converts a leaf node to a pointer node and reinserts the node's point into
* the correct child.
* @param {QuadTree.Node} node The node to split.
* @private
*/
private void split(Node node) {
Point oldPoint = node.getPoint();
node.setPoint(null);
node.setNodeType(NodeType.POINTER);
double x = node.getX();
double y = node.getY();
double hw = node.getW() / 2;
double hh = node.getH() / 2;
node.setNw(new Node(x, y, hw, hh, node));
node.setNe(new Node(x + hw, y, hw, hh, node));
node.setSw(new Node(x, y + hh, hw, hh, node));
node.setSe(new Node(x + hw, y + hh, hw, hh, node));
this.insert(node, oldPoint);
}
/**
* Attempts to balance a node. A node will need balancing if all its children
* are empty or it contains just one leaf.
* @param {QuadTree.Node} node The node to balance.
* @private
*/
private void balance(Node node) {
switch (node.getNodeType()) {
case EMPTY:
case LEAF:
if (node.getParent() != null) {
this.balance(node.getParent());
}
break;
case POINTER: {
Node nw = node.getNw();
Node ne = node.getNe();
Node sw = node.getSw();
Node se = node.getSe();
Node firstLeaf = null;
// Look for the first non-empty child, if there is more than one then we
// break as this node can't be balanced.
if (nw.getNodeType() != NodeType.EMPTY) {
firstLeaf = nw;
}
if (ne.getNodeType() != NodeType.EMPTY) {
if (firstLeaf != null) {
break;
}
firstLeaf = ne;
}
if (sw.getNodeType() != NodeType.EMPTY) {
if (firstLeaf != null) {
break;
}
firstLeaf = sw;
}
if (se.getNodeType() != NodeType.EMPTY) {
if (firstLeaf != null) {
break;
}
firstLeaf = se;
}
if (firstLeaf == null) {
// All child nodes are empty: so make this node empty.
node.setNodeType(NodeType.EMPTY);
node.setNw(null);
node.setNe(null);
node.setSw(null);
node.setSe(null);
} else if (firstLeaf.getNodeType() == NodeType.POINTER) {
// Only child was a pointer, therefore we can't rebalance.
break;
} else {
// Only child was a leaf: so update node's point and make it a leaf.
node.setNodeType(NodeType.LEAF);
node.setNw(null);
node.setNe(null);
node.setSw(null);
node.setSe(null);
node.setPoint(firstLeaf.getPoint());
}
// Try and balance the parent as well.
if (node.getParent() != null) {
this.balance(node.getParent());
}
}
break;
}
}
/**
* Returns the child quadrant within a node that contains the given (x, y)
* coordinate.
* @param {QuadTree.Node} parent The node.
* @param {number} x The x-coordinate to look for.
* @param {number} y The y-coordinate to look for.
* @return {QuadTree.Node} The child quadrant that contains the
* point.
* @private
*/
//TODO: Is this the method we want to retrieve the 'area' given a coordinate?
private Node getQuadrantForPoint(Node parent, double x, double y) {
double mx = parent.getX() + parent.getW() / 2;
double my = parent.getY() + parent.getH() / 2;
if (x < mx) {
return y < my ? parent.getNw() : parent.getSw();
} else {
return y < my ? parent.getNe() : parent.getSe();
}
}
/**
* Sets the point for a node, as long as the node is a leaf or empty.
* @param {QuadTree.Node} node The node to set the point for.
* @param {QuadTree.Point} point The point to set.
* @private
*/
private void setPointForNode(Node node, Point point) {
if (node.getNodeType() == NodeType.POINTER) {
throw new QuadTreeException("Can not set point for node of type POINTER");
}
node.setNodeType(NodeType.LEAF);
node.setPoint(point);
}
}

View file

@ -1,8 +0,0 @@
package fr.free.nrw.commons.caching;
public class QuadTreeException extends RuntimeException {
public QuadTreeException(String s) {
super(s);
}
}

View file

@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.List;
public class ShareActivity
extends AuthenticatedActivity
implements SingleUploadFragment.OnUploadActionInitiated,