diff --git a/commons/src/main/java/fr/free/nrw/commons/caching/Func.java b/commons/src/main/java/fr/free/nrw/commons/caching/Func.java
new file mode 100644
index 000000000..9703fc98f
--- /dev/null
+++ b/commons/src/main/java/fr/free/nrw/commons/caching/Func.java
@@ -0,0 +1,7 @@
+package fr.free.nrw.commons.caching;
+
+import fr.free.nrw.commons.caching.QuadTree;
+
+public interface Func {
+ public void call(QuadTree quadTree, Node node);
+}
diff --git a/commons/src/main/java/fr/free/nrw/commons/caching/Node.java b/commons/src/main/java/fr/free/nrw/commons/caching/Node.java
new file mode 100644
index 000000000..1ab054a99
--- /dev/null
+++ b/commons/src/main/java/fr/free/nrw/commons/caching/Node.java
@@ -0,0 +1,123 @@
+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;
+ }
+}
diff --git a/commons/src/main/java/fr/free/nrw/commons/caching/NodeType.java b/commons/src/main/java/fr/free/nrw/commons/caching/NodeType.java
new file mode 100644
index 000000000..9aac701fa
--- /dev/null
+++ b/commons/src/main/java/fr/free/nrw/commons/caching/NodeType.java
@@ -0,0 +1,11 @@
+package fr.free.nrw.commons.caching;
+
+/**
+ * Enumeration of node types.
+ * @enum {number}
+ */
+public enum NodeType {
+ EMPTY,
+ LEAF,
+ POINTER
+}
diff --git a/commons/src/main/java/fr/free/nrw/commons/caching/Point.java b/commons/src/main/java/fr/free/nrw/commons/caching/Point.java
new file mode 100644
index 000000000..6b2fdb8c1
--- /dev/null
+++ b/commons/src/main/java/fr/free/nrw/commons/caching/Point.java
@@ -0,0 +1,69 @@
+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;
+ }
+
+ }
+
+}
diff --git a/commons/src/main/java/fr/free/nrw/commons/caching/QuadTree.java b/commons/src/main/java/fr/free/nrw/commons/caching/QuadTree.java
new file mode 100644
index 000000000..1f77a3491
--- /dev/null
+++ b/commons/src/main/java/fr/free/nrw/commons/caching/QuadTree.java
@@ -0,0 +1,477 @@
+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.
+ *
+ * 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.} Array of coordinates.
+ */
+ public Point[] getKeys() {
+ final List arr = new ArrayList();
+ 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.