[geos-commits] r4184 - in trunk: capi include/geos/index/strtree src/index/strtree tests/unit tests/unit/capi

Sandro Santilli strk at keybit.net
Tue Apr 19 09:04:39 PDT 2016


Author: strk
Date: 2016-04-19 09:04:38 -0700 (Tue, 19 Apr 2016)
New Revision: 4184

Added:
   trunk/include/geos/index/strtree/BoundablePair.h
   trunk/include/geos/index/strtree/GeometryItemDistance.h
   trunk/include/geos/index/strtree/ItemDistance.h
   trunk/src/index/strtree/BoundablePair.cpp
   trunk/src/index/strtree/GeometryItemDistance.cpp
   trunk/tests/unit/capi/GEOSSTRtreeTest.cpp
Modified:
   trunk/capi/geos_c.cpp
   trunk/capi/geos_c.h.in
   trunk/capi/geos_ts_c.cpp
   trunk/include/geos/index/strtree/STRtree.h
   trunk/src/index/strtree/AbstractSTRtree.cpp
   trunk/src/index/strtree/Makefile.am
   trunk/src/index/strtree/STRtree.cpp
   trunk/tests/unit/Makefile.am
Log:
Add GEOSSTRtree_nearest API

Includes tests for the new API and pre-existing STRtree API
Closes #768

Patch by Daniel Baston <dbaston at gmail.com>
via https://github.com/libgeos/libgeos/pull/61

Modified: trunk/capi/geos_c.cpp
===================================================================
--- trunk/capi/geos_c.cpp	2016-04-12 15:17:15 UTC (rev 4183)
+++ trunk/capi/geos_c.cpp	2016-04-19 16:04:38 UTC (rev 4184)
@@ -1146,6 +1146,22 @@
     GEOSSTRtree_query_r( handle, tree, g, cb, userdata );
 }
 
+const GEOSGeometry *
+GEOSSTRtree_nearest (geos::index::strtree::STRtree *tree,
+                     const geos::geom::Geometry *g)
+{
+    return (const GEOSGeometry*) GEOSSTRtree_nearest_generic( tree, g, g, NULL, NULL);
+}
+
+const void* GEOSSTRtree_nearest_generic(GEOSSTRtree *tree,
+                                        const void* item,
+                                        const GEOSGeometry* itemEnvelope,
+                                        GEOSDistanceCallback distancefn,
+                                        void* userdata)
+{
+    return GEOSSTRtree_nearest_generic_r( handle, tree, item, itemEnvelope, distancefn, userdata);
+}
+
 void
 GEOSSTRtree_iterate(geos::index::strtree::STRtree *tree,
                     GEOSQueryCallback callback,

Modified: trunk/capi/geos_c.h.in
===================================================================
--- trunk/capi/geos_c.h.in	2016-04-12 15:17:15 UTC (rev 4183)
+++ trunk/capi/geos_c.h.in	2016-04-19 16:04:38 UTC (rev 4184)
@@ -153,6 +153,7 @@
 };
 
 typedef void (*GEOSQueryCallback)(void *item, void *userdata);
+typedef int (*GEOSDistanceCallback)(const void *item1, const void* item2, double* distance, void* userdata);
 
 /************************************************************************
  *
@@ -814,6 +815,19 @@
                                          const GEOSGeometry *g,
                                          GEOSQueryCallback callback,
                                          void *userdata);
+
+extern const void* GEOS_DLL GEOSSTRtree_nearest_r(GEOSContextHandle_t handle,
+                                                  GEOSSTRtree *tree,
+                                                  const GEOSGeometry* geom);
+
+
+extern const void* GEOS_DLL GEOSSTRtree_nearest_generic_r(GEOSContextHandle_t handle,
+                                                          GEOSSTRtree *tree,
+                                                          const void* item,
+                                                          const GEOSGeometry* itemEnvelope,
+                                                          GEOSDistanceCallback distancefn,
+                                                          void* userdata);
+
 extern void GEOS_DLL GEOSSTRtree_iterate_r(GEOSContextHandle_t handle,
                                        GEOSSTRtree *tree,
                                        GEOSQueryCallback callback,
@@ -1651,17 +1665,94 @@
  * GEOSGeometry ownership is retained by caller
  */
 
+/*
+ * Create a new R-tree using the Sort-Tile-Recursive algorithm (STRtree) for two-dimensional
+ * spatial data.
+ *
+ * @param nodeCapacity the maximum number of child nodes that a node may have.  The minimum
+ *            recommended capacity value is 4.  If unsure, use a default node capacity of 10.
+ * @return a pointer to the created tree
+ */
 extern GEOSSTRtree GEOS_DLL *GEOSSTRtree_create(size_t nodeCapacity);
+
+/*
+ * Insert an item into an STRtree
+ *
+ * @param tree the STRtree in which the item should be inserted
+ * @param g a GEOSGeometry whose envelope corresponds to the extent of 'item'
+ * @param item the item to insert into the tree
+ */
 extern void GEOS_DLL GEOSSTRtree_insert(GEOSSTRtree *tree,
                                         const GEOSGeometry *g,
                                         void *item);
+
+/*
+ * Query an STRtree for items intersecting a specified envelope
+ *
+ * @param tree the STRtree to search
+ * @param g a GEOSGeomety from which a query envelope will be extracted
+ * @param callback a function to be executed for each item in the tree whose envelope intersects
+ *            the envelope of 'g'.  The callback function should take two parameters: a void
+ *            pointer representing the located item in the tree, and a void userdata pointer.
+ * @param userdata an optional pointer to pe passed to 'callback' as an argument
+ */
 extern void GEOS_DLL GEOSSTRtree_query(GEOSSTRtree *tree,
                                        const GEOSGeometry *g,
                                        GEOSQueryCallback callback,
                                        void *userdata);
+/*
+ * Returns the nearest item in the STRtree to the supplied GEOSGeometry.
+ * All items in the tree MUST be of type GEOSGeometry.  If this is not the case, use
+ * GEOSSTRtree_nearest_generic instead.
+*
+ * @param tree the STRtree to search
+ * @param geom the geometry with which the tree should be queried
+ * @return a const pointer to the nearest GEOSGeometry in the tree to 'geom', or NULL in
+ *            case of exception
+ */
+extern const GEOSGeometry* GEOS_DLL GEOSSTRtree_nearest(GEOSSTRtree *tree, const GEOSGeometry* geom);
+
+/*
+ * Returns the nearest item in the STRtree to the supplied item
+ *
+ * @param tree the STRtree to search
+ * @param item the item with which the tree should be queried
+ * @param itemEnvelope a GEOSGeometry having the bounding box of 'item'
+ * @param distancefn a function that can compute the distance between two items
+ *            in the STRtree.  The function should return zero in case of error,
+ *            and should store the computed distance to the location pointed to by
+ *            the 'distance' argument.  The computed distance between two items
+ *            must not exceed the Cartesian distance between their envelopes.
+ * @param userdata optional pointer to arbitrary data; will be passed to distancefn
+ *            each time it is called.
+ * @return a const pointer to the nearest item in the tree to 'item', or NULL in
+ *            case of exception
+ */
+extern const void* GEOS_DLL GEOSSTRtree_nearest_generic(GEOSSTRtree *tree,
+                                                        const void* item,
+                                                        const GEOSGeometry* itemEnvelope,
+                                                        GEOSDistanceCallback distancefn,
+                                                        void* userdata);
+/*
+ * Iterates over all items in the STRtree
+ *
+ * @param tree the STRtree over which to iterate
+ * @param callback a function to be executed for each item in the tree.
+ */
 extern void GEOS_DLL GEOSSTRtree_iterate(GEOSSTRtree *tree,
                                        GEOSQueryCallback callback,
                                        void *userdata);
+
+/*
+ * Removes an item from the STRtree
+ *
+ * @param tree the STRtree from which to remove an item
+ * @param g the envelope of the item to remove
+ * @param the item to remove
+ * @return 0 if the item was not removed;
+ *         1 if the item was removed;
+ *         2 if an exception occurred
+ */
 extern char GEOS_DLL GEOSSTRtree_remove(GEOSSTRtree *tree,
                                         const GEOSGeometry *g,
                                         void *item);

Modified: trunk/capi/geos_ts_c.cpp
===================================================================
--- trunk/capi/geos_ts_c.cpp	2016-04-12 15:17:15 UTC (rev 4183)
+++ trunk/capi/geos_ts_c.cpp	2016-04-19 16:04:38 UTC (rev 4184)
@@ -34,7 +34,8 @@
 #include <geos/geom/Coordinate.h> 
 #include <geos/geom/IntersectionMatrix.h> 
 #include <geos/geom/Envelope.h> 
-#include <geos/index/strtree/STRtree.h> 
+#include <geos/index/strtree/STRtree.h>
+#include <geos/index/strtree/GeometryItemDistance.h>
 #include <geos/index/ItemVisitor.h>
 #include <geos/io/WKTReader.h>
 #include <geos/io/WKBReader.h>
@@ -5945,6 +5946,80 @@
     }
 }
 
+const void *
+GEOSSTRtree_nearest_generic_r(GEOSContextHandle_t extHandle,
+                              geos::index::strtree::STRtree *tree,
+                              const void* item,
+                              const geos::geom::Geometry* itemEnvelope,
+                              GEOSDistanceCallback distancefn,
+                              void* userdata)
+{
+
+    GEOSContextHandleInternal_t *handle = 0;
+    try
+    {
+        if (distancefn) {
+            struct CustomItemDistance : public ItemDistance {
+                CustomItemDistance(GEOSDistanceCallback p_distancefn, void* p_userdata)
+                        : m_distancefn(p_distancefn), m_userdata(p_userdata) {}
+
+                GEOSDistanceCallback m_distancefn;
+                void* m_userdata;
+
+                double distance(const ItemBoundable* item1, const ItemBoundable* item2) {
+                    const void* a = item1->getItem();
+                    const void* b = item2->getItem();
+                    double d;
+
+                    if (!m_distancefn(a, b, &d, m_userdata)) {
+                        throw std::runtime_error(std::string("Failed to compute distance."));
+                    }
+
+                    return d;
+                }
+            };
+
+            CustomItemDistance itemDistance(distancefn, userdata);
+            return tree->nearestNeighbour(itemEnvelope->getEnvelopeInternal(), item, &itemDistance);
+        } else {
+            GeometryItemDistance itemDistance = GeometryItemDistance();
+            return tree->nearestNeighbour(itemEnvelope->getEnvelopeInternal(), item, &itemDistance);
+        }
+    }
+    catch (const std::exception &e)
+    {
+        if ( 0 == extHandle )
+        {
+            return NULL;
+        }
+
+        handle = reinterpret_cast<GEOSContextHandleInternal_t*>(extHandle);
+        if ( 0 == handle->initialized )
+        {
+            return NULL;
+        }
+
+        handle->ERROR_MESSAGE("%s", e.what());
+    }
+    catch (...)
+    {
+        if ( 0 == extHandle )
+        {
+            return NULL;
+        }
+
+        handle = reinterpret_cast<GEOSContextHandleInternal_t*>(extHandle);
+        if ( 0 == handle->initialized )
+        {
+            return NULL;
+        }
+
+        handle->ERROR_MESSAGE("Unknown exception thrown");
+    }
+
+    return NULL;
+}
+
 void 
 GEOSSTRtree_iterate_r(GEOSContextHandle_t extHandle,
                     geos::index::strtree::STRtree *tree,

Added: trunk/include/geos/index/strtree/BoundablePair.h
===================================================================
--- trunk/include/geos/index/strtree/BoundablePair.h	                        (rev 0)
+++ trunk/include/geos/index/strtree/BoundablePair.h	2016-04-19 16:04:38 UTC (rev 4184)
@@ -0,0 +1,114 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2016 Daniel Baston
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************
+ *
+ * Last port: index/strtree/BoundablePair.java (JTS-1.14)
+ *
+ **********************************************************************/
+
+#ifndef GEOS_INDEX_STRTREE_BOUNDABLEPAIR_H
+#define GEOS_INDEX_STRTREE_BOUNDABLEPAIR_H
+
+#include <geos/index/strtree/Boundable.h>
+#include <geos/index/strtree/ItemDistance.h>
+#include <queue>
+
+/**
+ * A pair of {@link Boundable}s, whose leaf items
+ * support a distance metric between them.
+ * Used to compute the distance between the members,
+ * and to expand a member relative to the other
+ * in order to produce new branches of the
+ * Branch-and-Bound evaluation tree.
+ * Provides an ordering based on the distance between the members,
+ * which allows building a priority queue by minimum distance.
+ *
+ * @author Martin Davis
+ *
+ */
+
+using namespace geos::index::strtree;
+
+
+namespace geos {
+namespace index {
+namespace strtree {
+class BoundablePair {
+	private:
+		const Boundable* boundable1;
+		const Boundable* boundable2;
+		ItemDistance* itemDistance;
+		double mDistance;
+
+	public:
+		struct BoundablePairQueueCompare {
+			bool operator()(const BoundablePair* a, const BoundablePair* b) {
+				return a->getDistance() > b->getDistance();
+			}
+		};
+
+		typedef std::priority_queue<BoundablePair*, std::vector<BoundablePair*>, BoundablePairQueueCompare> BoundablePairQueue;
+		BoundablePair(const Boundable* boundable1, const Boundable* boundable2, ItemDistance* itemDistance);
+
+		/**
+		 * Gets one of the member {@link Boundable}s in the pair
+		 * (indexed by [0, 1]).
+		 *
+		 * @param i the index of the member to return (0 or 1)
+		 * @return the chosen member
+		 */
+		const Boundable* getBoundable(int i) const;
+
+		/**
+		 * Computes the distance between the {@link Boundable}s in this pair.
+		 * The boundables are either composites or leaves.
+		 * If either is composite, the distance is computed as the minimum distance
+		 * between the bounds.
+		 * If both are leaves, the distance is computed by {@link #itemDistance(ItemBoundable, ItemBoundable)}.
+		 *
+		 * @return
+		 */
+		double distance();
+
+		/**
+		 * Gets the minimum possible distance between the Boundables in
+		 * this pair.
+		 * If the members are both items, this will be the
+		 * exact distance between them.
+		 * Otherwise, this distance will be a lower bound on
+		 * the distances between the items in the members.
+		 *
+		 * @return the exact or lower bound distance for this pair
+		 */
+		double getDistance() const;
+
+		/**
+		 * Tests if both elements of the pair are leaf nodes
+		 *
+		 * @return true if both pair elements are leaf nodes
+		 */
+		bool isLeaves() const;
+
+		static bool isComposite(const Boundable* item);
+
+		static double area(const Boundable* b);
+
+		void expandToQueue(BoundablePairQueue &, double minDistance);
+		void expand(const Boundable* bndComposite, const Boundable* bndOther, BoundablePairQueue & priQ, double minDistance);
+};
+}
+}
+}
+
+#endif
+

Added: trunk/include/geos/index/strtree/GeometryItemDistance.h
===================================================================
--- trunk/include/geos/index/strtree/GeometryItemDistance.h	                        (rev 0)
+++ trunk/include/geos/index/strtree/GeometryItemDistance.h	2016-04-19 16:04:38 UTC (rev 4184)
@@ -0,0 +1,45 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2016 Daniel Baston
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************
+ *
+ * Last port: index/strtree/GeometryItemDistance.java (JTS-1.14)
+ *
+ **********************************************************************/
+
+#ifndef GEOS_INDEX_STRTREE_GEOMETRYITEMDISTANCE_H
+#define GEOS_INDEX_STRTREE_GEOMETRYITEMDISTANCE_H
+
+#include <geos/geom/Geometry.h>
+#include <geos/index/strtree/ItemDistance.h>
+
+namespace geos {
+namespace index {
+namespace strtree {
+class GeometryItemDistance : public ItemDistance {
+public:
+	/**
+	 * Computes the distance between two {@link Geometry} items,
+	 * using the {@link Geometry#distance(Geometry)} method.
+	 *
+	 * @param item1 an item which is a Geometry
+	 * @param item2 an item which is a Geometry
+	 * @return the distance between the geometries
+	 * @throws ClassCastException if either item is not a Geometry
+	 */
+	double distance(const ItemBoundable* item1, const ItemBoundable* item2);
+};
+}
+}
+}
+
+#endif //GEOS_GEOMETRYITEMDISTANCE_H

Added: trunk/include/geos/index/strtree/ItemDistance.h
===================================================================
--- trunk/include/geos/index/strtree/ItemDistance.h	                        (rev 0)
+++ trunk/include/geos/index/strtree/ItemDistance.h	2016-04-19 16:04:38 UTC (rev 4184)
@@ -0,0 +1,44 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2016 Daniel Baston
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************
+ *
+ * Last port: index/strtree/ItemDistance.java (JTS-1.14)
+ *
+ **********************************************************************/
+
+#ifndef GEOS_INDEX_STRTREE_ITEMDISTANCE_H
+#define GEOS_INDEX_STRTREE_ITEMDISTANCE_H
+
+#include <geos/index/strtree/ItemBoundable.h>
+
+namespace geos {
+namespace index {
+namespace strtree {
+class GEOS_DLL ItemDistance {
+public:
+	/**
+	 * Computes the distance between two items.
+	 *
+	 * @param item1
+	 * @param item2
+	 * @return the distance between the items
+	 *
+	 * @throws IllegalArgumentException if the metric is not applicable to the arguments
+	 */
+	virtual double distance(const ItemBoundable* item1, const ItemBoundable* item2) = 0;
+};
+}
+}
+}
+
+#endif //GEOS_ITEMDISTANCE_H

Modified: trunk/include/geos/index/strtree/STRtree.h
===================================================================
--- trunk/include/geos/index/strtree/STRtree.h	2016-04-12 15:17:15 UTC (rev 4183)
+++ trunk/include/geos/index/strtree/STRtree.h	2016-04-19 16:04:38 UTC (rev 4184)
@@ -20,6 +20,8 @@
 #define GEOS_INDEX_STRTREE_STRTREE_H
 
 #include <geos/export.h>
+#include <geos/index/strtree/ItemDistance.h>
+#include <geos/index/strtree/BoundablePair.h>
 #include <geos/index/strtree/AbstractSTRtree.h> // for inheritance
 #include <geos/index/SpatialIndex.h> // for inheritance
 #include <geos/geom/Envelope.h> // for inlines
@@ -137,6 +139,10 @@
 		return AbstractSTRtree::query(searchEnv, visitor);
 	}
 
+	const void* nearestNeighbour(const geom::Envelope *env, const void* item, ItemDistance* itemDist);
+	const void* nearestNeighbour(BoundablePair* initBndPair);
+	const void* nearestNeighbour(BoundablePair* initBndPair, double maxDistance);
+
 	bool remove(const geom::Envelope *itemEnv, void* item) {
 		return AbstractSTRtree::remove(itemEnv, item);
 	}

Modified: trunk/src/index/strtree/AbstractSTRtree.cpp
===================================================================
--- trunk/src/index/strtree/AbstractSTRtree.cpp	2016-04-12 15:17:15 UTC (rev 4183)
+++ trunk/src/index/strtree/AbstractSTRtree.cpp	2016-04-19 16:04:38 UTC (rev 4184)
@@ -55,7 +55,9 @@
 void
 AbstractSTRtree::build()
 {
-	assert(!built);
+	if(built)
+		return;
+
 	root=(itemBoundables->empty()?createNode(0):createHigherLevels(itemBoundables,-1));
 	built=true;
 }

Added: trunk/src/index/strtree/BoundablePair.cpp
===================================================================
--- trunk/src/index/strtree/BoundablePair.cpp	                        (rev 0)
+++ trunk/src/index/strtree/BoundablePair.cpp	2016-04-19 16:04:38 UTC (rev 4184)
@@ -0,0 +1,110 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2016 Daniel Baston
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************
+ *
+ * Last port: index/strtree/BoundablePair.java (JTS-1.14)
+ *
+ **********************************************************************/
+
+#include <geos/index/strtree/BoundablePair.h>
+#include <geos/geom/Envelope.h>
+#include <geos/index/strtree/AbstractNode.h>
+#include <geos/util/IllegalArgumentException.h>
+
+BoundablePair::BoundablePair(const Boundable* boundable1, const Boundable* boundable2, ItemDistance* itemDistance) :
+	boundable1(boundable1),
+	boundable2(boundable2),
+	itemDistance(itemDistance)
+{
+	mDistance = distance();
+}
+
+const Boundable* BoundablePair::getBoundable(int i) const {
+	if (i == 0)
+		return boundable1;
+	return boundable2;
+}
+
+double BoundablePair::distance() {
+	// if items, compute exact distance
+	if (isLeaves())
+		return itemDistance->distance((ItemBoundable*) boundable1, (ItemBoundable*) boundable2);
+
+	// otherwise compute distance between bounds of boundables
+	const geom::Envelope* e1 = (const geom::Envelope*) boundable1->getBounds();
+	const geom::Envelope* e2 = (const geom::Envelope*) boundable2->getBounds();
+
+	if (!e1 || !e2)
+		throw util::GEOSException("Can't compute envelope of item in BoundablePair");
+	return e1->distance(e2);
+}
+
+double BoundablePair::getDistance() const {
+	return mDistance;
+}
+
+bool BoundablePair::isLeaves() const {
+	return !(isComposite(boundable1) || isComposite(boundable2));
+}
+
+bool BoundablePair::isComposite(const Boundable* item) {
+	return dynamic_cast<const AbstractNode*>(item) != NULL;
+}
+
+double BoundablePair::area(const Boundable* b) {
+	return ((const geos::geom::Envelope*) b->getBounds())->getArea();
+}
+
+void BoundablePair::expandToQueue(BoundablePairQueue & priQ, double minDistance) {
+	bool isComp1 = isComposite(boundable1);
+	bool isComp2 = isComposite(boundable2);
+
+	/**
+	 * HEURISTIC: If both boundables are composite,
+	 * choose the one with largest area to expand.
+	 * Otherwise, simply expand whichever is composite.
+	 */
+	if (isComp1 && isComp2) {
+		if (area(boundable1) > area(boundable2)) {
+			expand(boundable1, boundable2, priQ, minDistance);
+			return;
+		} else {
+			expand(boundable2, boundable1, priQ, minDistance);
+			return;
+		}
+	}
+	else if (isComp1) {
+		expand(boundable1, boundable2, priQ, minDistance);
+		return;
+	}
+	else if (isComp2) {
+		expand(boundable2, boundable1, priQ, minDistance);
+		return;
+	}
+
+	throw new geos::util::IllegalArgumentException("neither boundable is composite");
+}
+
+void BoundablePair::expand(const Boundable* bndComposite, const Boundable* bndOther, BoundablePairQueue & priQ, double minDistance) {
+	std::vector<Boundable*> *children = ((AbstractNode*) bndComposite)->getChildBoundables();
+	for(std::vector<Boundable*>::iterator it = children->begin(); it != children->end(); ++it) {
+		Boundable* child = *it;
+		BoundablePair* bp = new BoundablePair(child, bndOther, itemDistance);
+		if (bp->getDistance() < minDistance) {
+			priQ.push(bp);
+		} else {
+			delete bp;
+		}
+	}
+}
+

Added: trunk/src/index/strtree/GeometryItemDistance.cpp
===================================================================
--- trunk/src/index/strtree/GeometryItemDistance.cpp	                        (rev 0)
+++ trunk/src/index/strtree/GeometryItemDistance.cpp	2016-04-19 16:04:38 UTC (rev 4184)
@@ -0,0 +1,30 @@
+/**********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://geos.osgeo.org
+ *
+ * Copyright (C) 2016 Daniel Baston
+ *
+ * This is free software; you can redistribute and/or modify it under
+ * the terms of the GNU Lesser General Public Licence as published
+ * by the Free Software Foundation.
+ * See the COPYING file for more information.
+ *
+ **********************************************************************
+ *
+ * Last port: index/strtree/GeometryItemDistance.java (JTS-1.14)
+ *
+ **********************************************************************/
+
+#include <geos/geom/Geometry.h>
+#include <geos/index/strtree/ItemBoundable.h>
+#include <geos/index/strtree/GeometryItemDistance.h>
+
+using namespace geos::geom;
+using namespace geos::index::strtree;
+
+double GeometryItemDistance::distance(const ItemBoundable* item1, const ItemBoundable* item2) {
+	const Geometry* g1 = (Geometry*) item1->getItem();
+	const Geometry* g2 = (Geometry*) item2->getItem();
+	return g1->distance(g2);
+}

Modified: trunk/src/index/strtree/Makefile.am
===================================================================
--- trunk/src/index/strtree/Makefile.am	2016-04-12 15:17:15 UTC (rev 4183)
+++ trunk/src/index/strtree/Makefile.am	2016-04-19 16:04:38 UTC (rev 4184)
@@ -8,9 +8,11 @@
 libindexstrtree_la_SOURCES = \
     AbstractNode.cpp \
     AbstractSTRtree.cpp \
+    BoundablePair.cpp \
+    GeometryItemDistance.cpp \
     Interval.cpp \
     ItemBoundable.cpp \
     SIRtree.cpp \
-    STRtree.cpp 
+    STRtree.cpp
 
 libindexstrtree_la_LIBADD = 

Modified: trunk/src/index/strtree/STRtree.cpp
===================================================================
--- trunk/src/index/strtree/STRtree.cpp	2016-04-12 15:17:15 UTC (rev 4183)
+++ trunk/src/index/strtree/STRtree.cpp	2016-04-19 16:04:38 UTC (rev 4184)
@@ -18,6 +18,7 @@
  **********************************************************************/
 
 #include <geos/index/strtree/STRtree.h>
+#include <geos/index/strtree/BoundablePair.h>
 #include <geos/geom/Envelope.h>
 
 #include <vector>
@@ -183,6 +184,80 @@
 	return slices;
 }
 
+/*public*/
+const void* STRtree::nearestNeighbour(const Envelope* env, const void* item, ItemDistance* itemDist) {
+	build();
+
+	ItemBoundable bnd = ItemBoundable(env, (void*) item);
+	BoundablePair bp(getRoot(), &bnd, itemDist);
+
+	return nearestNeighbour(&bp);
+}
+
+const void* STRtree::nearestNeighbour(BoundablePair* initBndPair) {
+	return nearestNeighbour(initBndPair, DoubleInfinity);
+}
+
+const void* STRtree::nearestNeighbour(BoundablePair* initBndPair, double maxDistance) {
+	double distanceLowerBound = maxDistance;
+	BoundablePair* minPair = NULL;
+
+	BoundablePair::BoundablePairQueue priQ;
+	priQ.push(initBndPair);
+
+	while(!priQ.empty() && distanceLowerBound > 0.0) {
+		BoundablePair* bndPair = priQ.top();
+		double currentDistance = bndPair->getDistance();
+
+		/**
+		 * If the distance for the first node in the queue
+		 * is >= the current minimum distance, all other nodes
+		 * in the queue must also have a greater distance.
+		 * So the current minDistance must be the true minimum,
+		 * and we are done.
+		 */
+		if (currentDistance >= distanceLowerBound)
+			break;
+
+		priQ.pop();
+
+		/**
+		 * If the pair members are leaves
+		 * then their distance is the exact lower bound.
+		 * Update the distanceLowerBound to reflect this
+		 * (which must be smaller, due to the test
+		 * immediately prior to this).
+		 */
+		if (bndPair->isLeaves()) {
+			distanceLowerBound = currentDistance;
+			minPair = bndPair;
+		} else {
+			/**
+			 * Otherwise, expand one side of the pair,
+			 * (the choice of which side to expand is heuristically determined)
+			 * and insert the new expanded pairs into the queue
+			 */
+			bndPair->expandToQueue(priQ, distanceLowerBound);
+		}
+
+		if (bndPair != initBndPair && bndPair != minPair)
+            delete bndPair;
+	}
+
+	/* Free any remaining BoundablePairs in the queue */
+	while(!priQ.empty()) {
+		BoundablePair* bp = priQ.top();
+		priQ.pop();
+		delete bp;
+	}
+
+	const void* retval = dynamic_cast<const ItemBoundable*>(minPair->getBoundable(0))->getItem();
+	if (minPair != initBndPair)
+        delete minPair;
+
+	return retval;
+}
+
 class STRAbstractNode: public AbstractNode{
 public:
 

Modified: trunk/tests/unit/Makefile.am
===================================================================
--- trunk/tests/unit/Makefile.am	2016-04-12 15:17:15 UTC (rev 4183)
+++ trunk/tests/unit/Makefile.am	2016-04-19 16:04:38 UTC (rev 4184)
@@ -149,6 +149,7 @@
 	capi/GEOSNodeTest.cpp \
 	capi/GEOSSnapTest.cpp \
 	capi/GEOSSharedPathsTest.cpp \
+	capi/GEOSSTRtreeTest.cpp \
 	capi/GEOSRelateBoundaryNodeRuleTest.cpp \
 	capi/GEOSRelatePatternMatchTest.cpp \
 	capi/GEOSUnaryUnionTest.cpp \

Added: trunk/tests/unit/capi/GEOSSTRtreeTest.cpp
===================================================================
--- trunk/tests/unit/capi/GEOSSTRtreeTest.cpp	                        (rev 0)
+++ trunk/tests/unit/capi/GEOSSTRtreeTest.cpp	2016-04-19 16:04:38 UTC (rev 4184)
@@ -0,0 +1,236 @@
+//
+// Test Suite for C-API GEOSSTRtree
+
+#include <tut.hpp>
+// geos
+#include <geos_c.h>
+// std
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
+#include <cmath>
+
+struct INTPOINT {
+	INTPOINT(int x, int y) : x(x), y(y) {}
+	int x;
+	int y;
+};
+
+static GEOSGeometry* INTPOINT2GEOS(INTPOINT* p) {
+	GEOSCoordSequence* seq = GEOSCoordSeq_create(1, 2);
+	GEOSCoordSeq_setX(seq, 0, p->x);
+	GEOSCoordSeq_setY(seq, 0, p->y);
+	return GEOSGeom_createPoint(seq);
+}
+
+static int INTPOINT_dist(const void* a, const void* b, double* distance, void* userdata) {
+	INTPOINT* p1 = (INTPOINT*) a;
+	INTPOINT* p2 = (INTPOINT*) b;
+
+	int dx = p2->x - p1->x;
+	int dy = p2->y - p1->y;
+
+	*distance = sqrt(dx*dx + dy*dy);
+	return 1;
+}
+
+namespace tut
+{
+	//
+	// Test Group
+	//
+
+	// Common data used in test cases.
+	struct test_capistrtree_data
+	{
+		test_capistrtree_data() {
+			initGEOS(notice, notice);
+		}
+
+		static void notice(const char *fmt, ...)
+		{
+			std::fprintf( stdout, "NOTICE: ");
+
+			va_list ap;
+			va_start(ap, fmt);
+			std::vfprintf(stdout, fmt, ap);
+			va_end(ap);
+
+			std::fprintf(stdout, "\n");
+		}
+
+	};
+
+	typedef test_group<test_capistrtree_data> group;
+	typedef group::object object;
+
+	group test_capistrtree_group("capi::GEOSSTRtree");
+
+	//
+	// Test Cases
+	//
+
+	// Test GEOSSTRtree_nearest with a couple of points
+	template<>
+	template<>
+	void object::test<1>()
+	{
+		GEOSGeometry* g1 = GEOSGeomFromWKT("POINT (3 3)");
+		GEOSGeometry* g2 = GEOSGeomFromWKT("POINT (2 7)");
+		GEOSGeometry* g3 = GEOSGeomFromWKT("POINT (5 4)");
+		GEOSGeometry* g4 = GEOSGeomFromWKT("POINT (3 8)");
+
+		GEOSSTRtree* tree = GEOSSTRtree_create(2);
+		GEOSSTRtree_insert(tree, g1, g1);
+		GEOSSTRtree_insert(tree, g2, g2);
+		GEOSSTRtree_insert(tree, g3, g3);
+
+		const GEOSGeometry* g5 = GEOSSTRtree_nearest(tree, g4);
+		ensure(g5 == g2);
+
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+		GEOSGeom_destroy(g3);
+		GEOSGeom_destroy(g4);
+		GEOSSTRtree_destroy(tree);
+	}
+
+	// Test GEOSSTRtree_nearest with more points.  This is important because we need to make sure the tree
+	// actually has a couple of layers of depth.
+	template<>
+	template<>
+	void object::test<2>()
+	{
+		size_t ngeoms = 100;
+		std::vector<GEOSGeometry*> geoms;
+		std::vector<GEOSGeometry*> queryPoints;
+		GEOSSTRtree* tree = GEOSSTRtree_create(ngeoms);
+
+		for (size_t i = 0; i < ngeoms; i++) {
+			GEOSCoordSequence* seq = GEOSCoordSeq_create(1, 2);
+			GEOSCoordSeq_setX(seq, 0, std::rand());
+			GEOSCoordSeq_setY(seq, 0, std::rand());
+			geoms.push_back(GEOSGeom_createPoint(seq));
+			GEOSSTRtree_insert(tree, geoms[i], geoms[i]);
+		}
+
+		for (size_t i = 0; i < ngeoms; i++) {
+			GEOSCoordSequence* seq = GEOSCoordSeq_create(1, 2);
+			GEOSCoordSeq_setX(seq, 0, std::rand());
+			GEOSCoordSeq_setY(seq, 0, std::rand());
+			queryPoints.push_back(GEOSGeom_createPoint(seq));
+		}
+
+		for (size_t i = 0; i < ngeoms; i++) {
+			const GEOSGeometry* nearest = GEOSSTRtree_nearest(tree, queryPoints[i]);
+			const GEOSGeometry* nearestBruteForce = NULL;
+			double nearestBruteForceDistance = std::numeric_limits<double>::max();
+			for (size_t j = 0; j < ngeoms; j++) {
+				double distance;
+				GEOSDistance(queryPoints[i], geoms[j], &distance);
+
+				if (nearestBruteForce == NULL || distance < nearestBruteForceDistance) {
+					nearestBruteForce = geoms[j];
+					nearestBruteForceDistance = distance;
+				}
+			}
+
+			ensure(nearest == nearestBruteForce || GEOSEquals(nearest, nearestBruteForce));
+		}
+
+		for (size_t i = 0; i < ngeoms; i++) {
+			GEOSGeom_destroy(geoms[i]);
+			GEOSGeom_destroy(queryPoints[i]);
+		}
+
+		GEOSSTRtree_destroy(tree);
+	}
+
+	// GEOSSTRtree_nearest returns NULL on empty tree
+	template<>
+	template<>
+	void object::test<3>()
+	{
+		GEOSSTRtree* tree = GEOSSTRtree_create(10);
+		GEOSGeometry* g1 = GEOSGeomFromWKT("POINT (3 3)");
+		const GEOSGeometry* g2 = GEOSSTRtree_nearest(tree, g1);
+
+		ensure(g2 == NULL);
+
+		GEOSGeom_destroy(g1);
+		GEOSSTRtree_destroy(tree);
+	}
+
+	// GEOSSTRtree_nearest with a user-defined type
+	template<>
+	template<>
+	void object::test<4>()
+	{
+		INTPOINT p1(1, 1);
+		INTPOINT p2(4, 4);
+		INTPOINT p3(3, 3);
+
+		GEOSGeometry* g1 = INTPOINT2GEOS(&p1);
+		GEOSGeometry* g2 = INTPOINT2GEOS(&p2);
+		GEOSGeometry* g3 = INTPOINT2GEOS(&p3);
+
+		GEOSSTRtree* tree = GEOSSTRtree_create(4);
+		GEOSSTRtree_insert(tree, g1, &p1);
+		GEOSSTRtree_insert(tree, g2, &p2);
+
+		const INTPOINT* p4 = (const INTPOINT*) GEOSSTRtree_nearest_generic(tree, &p3, g3, &INTPOINT_dist, NULL);
+
+		ensure (p4 == &p2);
+
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+		GEOSGeom_destroy(g3);
+		GEOSSTRtree_destroy(tree);
+	}
+
+	// GEOSSTRtree_nearest with a tree of empty geometries
+	template<>
+	template<>
+	void object::test<5>() {
+		GEOSGeometry* g1 = GEOSGeomFromWKT("LINESTRING EMPTY");
+		GEOSGeometry* g2 = GEOSGeomFromWKT("POINT (2 7)");
+
+		GEOSSTRtree* tree = GEOSSTRtree_create(4);
+		GEOSSTRtree_insert(tree, g1, g1);
+
+		const GEOSGeometry* g3 = GEOSSTRtree_nearest(tree, g2);
+		ensure(g3 == NULL);
+
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+		GEOSSTRtree_destroy(tree);
+	}
+
+	// GEOSSTRtree_nearest with a tree containing some empty geometries
+	template<>
+	template<>
+	void object::test<6>() {
+		GEOSGeometry* g1 = GEOSGeomFromWKT("LINESTRING EMPTY");
+		GEOSGeometry* g2 = GEOSGeomFromWKT("POINT (2 7)");
+		GEOSGeometry* g3 = GEOSGeomFromWKT("POINT (12 97)");
+		GEOSGeometry* g4 = GEOSGeomFromWKT("LINESTRING (3 8, 4 8)");
+
+		GEOSSTRtree* tree = GEOSSTRtree_create(4);
+		GEOSSTRtree_insert(tree, g1, g1);
+		GEOSSTRtree_insert(tree, g2, g2);
+		GEOSSTRtree_insert(tree, g3, g3);
+
+		const GEOSGeometry* g5 = (const GEOSGeometry*) GEOSSTRtree_nearest(tree, g4);
+		ensure(g5 == g2);
+
+		GEOSGeom_destroy(g1);
+		GEOSGeom_destroy(g2);
+		GEOSGeom_destroy(g3);
+		GEOSGeom_destroy(g4);
+		GEOSSTRtree_destroy(tree);
+	}
+
+} // namespace tut
+
+
+



More information about the geos-commits mailing list