[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