From dffba3d015cce7cc3e503620efcec78030c63c49 Mon Sep 17 00:00:00 2001 From: dbaston Date: Mon, 8 Feb 2016 21:00:58 -0500 Subject: [PATCH 1/7] initial work for GEOSSTRtree_nearest. --- capi/geos_c.cpp | 8 ++ capi/geos_c.h.in | 7 + capi/geos_ts_c.cpp | 74 +++++++++- include/geos/index/strtree/BoundablePair.h | 114 ++++++++++++++++ .../geos/index/strtree/GeometryItemDistance.h | 45 ++++++ include/geos/index/strtree/ItemDistance.h | 44 ++++++ include/geos/index/strtree/STRtree.h | 6 + src/index/strtree/AbstractSTRtree.cpp | 4 +- src/index/strtree/BoundablePair.cpp | 107 +++++++++++++++ src/index/strtree/GeometryItemDistance.cpp | 30 ++++ src/index/strtree/Makefile.am | 4 +- src/index/strtree/STRtree.cpp | 75 ++++++++++ tests/unit/Makefile.am | 1 + tests/unit/capi/GEOSSTRtreeTest.cpp | 128 ++++++++++++++++++ 14 files changed, 644 insertions(+), 3 deletions(-) create mode 100644 include/geos/index/strtree/BoundablePair.h create mode 100644 include/geos/index/strtree/GeometryItemDistance.h create mode 100644 include/geos/index/strtree/ItemDistance.h create mode 100644 src/index/strtree/BoundablePair.cpp create mode 100644 src/index/strtree/GeometryItemDistance.cpp create mode 100644 tests/unit/capi/GEOSSTRtreeTest.cpp diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp index e8e55053eb..6eb5ceb30b 100644 --- a/capi/geos_c.cpp +++ b/capi/geos_c.cpp @@ -1146,6 +1146,14 @@ GEOSSTRtree_query (geos::index::strtree::STRtree *tree, GEOSSTRtree_query_r( handle, tree, g, cb, userdata ); } +const void * +GEOSSTRtree_nearest (geos::index::strtree::STRtree *tree, + const geos::geom::Geometry *g, + int (*distancefn)(const void* item1, const void* item2, double* distance)) +{ + return GEOSSTRtree_nearest_r( handle, tree, g, distancefn); +} + void GEOSSTRtree_iterate(geos::index::strtree::STRtree *tree, GEOSQueryCallback callback, diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index 6d37d86766..b582e0e123 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -814,6 +814,10 @@ extern void GEOS_DLL GEOSSTRtree_query_r(GEOSContextHandle_t handle, const GEOSGeometry *g, GEOSQueryCallback callback, void *userdata); +extern const void* GEOS_DLL GEOSSTRtree_nearest_r(GEOSContextHandle_t handle, + GEOSSTRtree *tree, + const GEOSGeometry* g, + int (*distancefn)(const void* item1, const void* item2, double* distance)); extern void GEOS_DLL GEOSSTRtree_iterate_r(GEOSContextHandle_t handle, GEOSSTRtree *tree, GEOSQueryCallback callback, @@ -1659,6 +1663,9 @@ extern void GEOS_DLL GEOSSTRtree_query(GEOSSTRtree *tree, const GEOSGeometry *g, GEOSQueryCallback callback, void *userdata); +extern const void* GEOS_DLL GEOSSTRtree_nearest(GEOSSTRtree *tree, + const GEOSGeometry* g, + int (*distancefn)(const void* item1, const void* item2, double* distance)); extern void GEOS_DLL GEOSSTRtree_iterate(GEOSSTRtree *tree, GEOSQueryCallback callback, void *userdata); diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index 79008e090b..86260b39c3 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -34,7 +34,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -5945,6 +5946,77 @@ GEOSSTRtree_query_r(GEOSContextHandle_t extHandle, } } +const void * +GEOSSTRtree_nearest_r(GEOSContextHandle_t extHandle, + geos::index::strtree::STRtree *tree, + const geos::geom::Geometry* g, + int (*distancefn)(const void* item1, const void* item2, double* distance)) +{ + + GEOSContextHandleInternal_t *handle = 0; + try + { + if (distancefn) { + struct CustomItemDistance : public ItemDistance { + CustomItemDistance(int (*distancefn)(const void* item1, const void* item2, double* distance)) + : distancefn(distancefn) {} + + int (*distancefn)(const void* item1, const void* item2, double* distance); + + double distance(const ItemBoundable* item1, const ItemBoundable* item2) { + const void* a = item1->getItem(); + const void* b = item2->getItem(); + double d; + + if (!distancefn(a, b, &d)) { + throw std::runtime_error(std::string("Failed to compute distance.")); + } + + return d; + } + }; + + CustomItemDistance itemDistance(distancefn); + return tree->nearestNeighbour(g->getEnvelopeInternal(), g, &itemDistance); + } else { + GeometryItemDistance itemDistance = GeometryItemDistance(); + return tree->nearestNeighbour(g->getEnvelopeInternal(), g, &itemDistance); + } + } + catch (const std::exception &e) + { + if ( 0 == extHandle ) + { + return NULL; + } + + handle = reinterpret_cast(extHandle); + if ( 0 == handle->initialized ) + { + return NULL; + } + + handle->ERROR_MESSAGE("%s", e.what()); + } + catch (...) + { + if ( 0 == extHandle ) + { + return NULL; + } + + handle = reinterpret_cast(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, diff --git a/include/geos/index/strtree/BoundablePair.h b/include/geos/index/strtree/BoundablePair.h new file mode 100644 index 0000000000..e78038349e --- /dev/null +++ b/include/geos/index/strtree/BoundablePair.h @@ -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 +#include +#include + +/** + * 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, 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 + diff --git a/include/geos/index/strtree/GeometryItemDistance.h b/include/geos/index/strtree/GeometryItemDistance.h new file mode 100644 index 0000000000..e28914779a --- /dev/null +++ b/include/geos/index/strtree/GeometryItemDistance.h @@ -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 +#include + +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 diff --git a/include/geos/index/strtree/ItemDistance.h b/include/geos/index/strtree/ItemDistance.h new file mode 100644 index 0000000000..54de9f49ce --- /dev/null +++ b/include/geos/index/strtree/ItemDistance.h @@ -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 + +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 diff --git a/include/geos/index/strtree/STRtree.h b/include/geos/index/strtree/STRtree.h index 5ea58889f3..05f77df1ff 100644 --- a/include/geos/index/strtree/STRtree.h +++ b/include/geos/index/strtree/STRtree.h @@ -20,6 +20,8 @@ #define GEOS_INDEX_STRTREE_STRTREE_H #include +#include +#include #include // for inheritance #include // for inheritance #include // for inlines @@ -137,6 +139,10 @@ using AbstractSTRtree::query; 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); } diff --git a/src/index/strtree/AbstractSTRtree.cpp b/src/index/strtree/AbstractSTRtree.cpp index 99f4b10a61..f11401aca5 100644 --- a/src/index/strtree/AbstractSTRtree.cpp +++ b/src/index/strtree/AbstractSTRtree.cpp @@ -55,7 +55,9 @@ AbstractSTRtree::~AbstractSTRtree() void AbstractSTRtree::build() { - assert(!built); + if(built) + return; + root=(itemBoundables->empty()?createNode(0):createHigherLevels(itemBoundables,-1)); built=true; } diff --git a/src/index/strtree/BoundablePair.cpp b/src/index/strtree/BoundablePair.cpp new file mode 100644 index 0000000000..403a1d70eb --- /dev/null +++ b/src/index/strtree/BoundablePair.cpp @@ -0,0 +1,107 @@ +/********************************************************************** + * + * 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 +#include +#include +#include + +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(); + 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(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 *children = ((AbstractNode*) bndComposite)->getChildBoundables(); + for(std::vector::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; + } + } +} + diff --git a/src/index/strtree/GeometryItemDistance.cpp b/src/index/strtree/GeometryItemDistance.cpp new file mode 100644 index 0000000000..af446f0190 --- /dev/null +++ b/src/index/strtree/GeometryItemDistance.cpp @@ -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 +#include +#include + +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); +} diff --git a/src/index/strtree/Makefile.am b/src/index/strtree/Makefile.am index de7f3d235b..859de65519 100644 --- a/src/index/strtree/Makefile.am +++ b/src/index/strtree/Makefile.am @@ -8,9 +8,11 @@ AM_CPPFLAGS = -I$(top_srcdir)/include libindexstrtree_la_SOURCES = \ AbstractNode.cpp \ AbstractSTRtree.cpp \ + BoundablePair.cpp \ + GeometryItemDistance.cpp \ Interval.cpp \ ItemBoundable.cpp \ SIRtree.cpp \ - STRtree.cpp + STRtree.cpp libindexstrtree_la_LIBADD = diff --git a/src/index/strtree/STRtree.cpp b/src/index/strtree/STRtree.cpp index 91434413ec..1d5083452a 100644 --- a/src/index/strtree/STRtree.cpp +++ b/src/index/strtree/STRtree.cpp @@ -18,6 +18,7 @@ **********************************************************************/ #include +#include #include #include @@ -183,6 +184,80 @@ STRtree::verticalSlices(BoundableList* childBoundables, size_t sliceCount) 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(minPair->getBoundable(0))->getItem(); + if (minPair != initBndPair) + delete minPair; + + return retval; +} + class STRAbstractNode: public AbstractNode{ public: diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 5427242823..983d1832a4 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -149,6 +149,7 @@ geos_unit_SOURCES = \ capi/GEOSNodeTest.cpp \ capi/GEOSSnapTest.cpp \ capi/GEOSSharedPathsTest.cpp \ + capi/GEOSSTRtreeTest.cpp \ capi/GEOSRelateBoundaryNodeRuleTest.cpp \ capi/GEOSRelatePatternMatchTest.cpp \ capi/GEOSUnaryUnionTest.cpp \ diff --git a/tests/unit/capi/GEOSSTRtreeTest.cpp b/tests/unit/capi/GEOSSTRtreeTest.cpp new file mode 100644 index 0000000000..8637fc995b --- /dev/null +++ b/tests/unit/capi/GEOSSTRtreeTest.cpp @@ -0,0 +1,128 @@ +// +// Test Suite for C-API GEOSSTRtree + +#include +// geos +#include +// std +#include +#include +#include +#include + +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 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 = (GEOSGeometry*) GEOSSTRtree_nearest(tree, g4, + (int (*)(const void* item1, const void* item2, double* distance))(GEOSDistance)); + 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>() + { + int ngeoms = 100; + std::vector geoms; + std::vector queryPoints; + GEOSSTRtree* tree = GEOSSTRtree_create(ngeoms); + + for (int 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 (int 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 (int i = 0; i < ngeoms; i++) { + const GEOSGeometry* nearest = (GEOSGeometry*) GEOSSTRtree_nearest(tree, queryPoints[i], NULL); + const GEOSGeometry* nearestBruteForce = NULL; + double nearestBruteForceDistance; + for (int 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 (int i = 0; i < ngeoms; i++) { + GEOSGeom_destroy(geoms[i]); + GEOSGeom_destroy(queryPoints[i]); + } + + GEOSSTRtree_destroy(tree); + } + + +} // namespace tut + From 6c290c44aabe600a490d4975a048124ed39567e1 Mon Sep 17 00:00:00 2001 From: dbaston Date: Mon, 18 Apr 2016 16:42:44 -0400 Subject: [PATCH 2/7] Revise CAPI so that we can now provide a void* context argment to distancefn --- capi/geos_c.cpp | 16 ++++++-- capi/geos_c.h.in | 91 +++++++++++++++++++++++++++++++++++++++++++--- capi/geos_ts_c.cpp | 25 +++++++------ 3 files changed, 112 insertions(+), 20 deletions(-) diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp index 6eb5ceb30b..3a7a5f43b5 100644 --- a/capi/geos_c.cpp +++ b/capi/geos_c.cpp @@ -1146,12 +1146,20 @@ GEOSSTRtree_query (geos::index::strtree::STRtree *tree, GEOSSTRtree_query_r( handle, tree, g, cb, userdata ); } -const void * +const GEOSGeometry * GEOSSTRtree_nearest (geos::index::strtree::STRtree *tree, - const geos::geom::Geometry *g, - int (*distancefn)(const void* item1, const void* item2, double* distance)) + const geos::geom::Geometry *g) { - return GEOSSTRtree_nearest_r( handle, tree, g, distancefn); + return (const GEOSGeometry*) GEOSSTRtree_nearest_generic( tree, g, g, NULL, NULL); +} + +const void* GEOSSTRtree_nearest_generic(GEOSSTRtree *tree, + const void* item, + const GEOSGeometry* itemEnvelope, + int (*distancefn)(const void* item1, const void* item2, double* distance, void* userdata), + void* userdata) +{ + return GEOSSTRtree_nearest_generic_r( handle, tree, item, itemEnvelope, distancefn, userdata); } void diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index b582e0e123..8dc39fa69a 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -814,10 +814,19 @@ extern void GEOS_DLL GEOSSTRtree_query_r(GEOSContextHandle_t handle, 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 GEOSGeometry* g, - int (*distancefn)(const void* item1, const void* item2, double* distance)); + const void* item, + const GEOSGeometry* itemEnvelope, + int (*distancefn)(const void* item1, const void* item2, double* distance, void* userdata), + void* userdata); + extern void GEOS_DLL GEOSSTRtree_iterate_r(GEOSContextHandle_t handle, GEOSSTRtree *tree, GEOSQueryCallback callback, @@ -1655,20 +1664,92 @@ extern char GEOS_DLL GEOSPreparedWithin(const GEOSPreparedGeometry* pg1, const G * 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); -extern const void* GEOS_DLL GEOSSTRtree_nearest(GEOSSTRtree *tree, - const GEOSGeometry* g, - int (*distancefn)(const void* item1, const void* item2, double* distance)); +/* + * Returns the nearest item in the STRtree to the supplied GEOSGeometry + * + * @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, + int (*distancefn)(const void* item1, const void* item2, double* distance, void* userdata), + 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); diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index 86260b39c3..c6d3ff9c76 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -5947,10 +5947,12 @@ GEOSSTRtree_query_r(GEOSContextHandle_t extHandle, } const void * -GEOSSTRtree_nearest_r(GEOSContextHandle_t extHandle, - geos::index::strtree::STRtree *tree, - const geos::geom::Geometry* g, - int (*distancefn)(const void* item1, const void* item2, double* distance)) +GEOSSTRtree_nearest_generic_r(GEOSContextHandle_t extHandle, + geos::index::strtree::STRtree *tree, + const void* item, + const geos::geom::Geometry* itemEnvelope, + int (*distancefn)(const void* item1, const void* item2, double* distance, void* userdata), + void* userdata) { GEOSContextHandleInternal_t *handle = 0; @@ -5958,17 +5960,18 @@ GEOSSTRtree_nearest_r(GEOSContextHandle_t extHandle, { if (distancefn) { struct CustomItemDistance : public ItemDistance { - CustomItemDistance(int (*distancefn)(const void* item1, const void* item2, double* distance)) - : distancefn(distancefn) {} + CustomItemDistance(int (*p_distancefn)(const void* item1, const void* item2, double* distance, void* p_userdata), void* p_userdata) + : m_distancefn(p_distancefn), m_userdata(p_userdata) {} - int (*distancefn)(const void* item1, const void* item2, double* distance); + int (*m_distancefn)(const void* item1, const void* item2, double* distance, void* userdata); + void* m_userdata; double distance(const ItemBoundable* item1, const ItemBoundable* item2) { const void* a = item1->getItem(); const void* b = item2->getItem(); double d; - if (!distancefn(a, b, &d)) { + if (!m_distancefn(a, b, &d, m_userdata)) { throw std::runtime_error(std::string("Failed to compute distance.")); } @@ -5976,11 +5979,11 @@ GEOSSTRtree_nearest_r(GEOSContextHandle_t extHandle, } }; - CustomItemDistance itemDistance(distancefn); - return tree->nearestNeighbour(g->getEnvelopeInternal(), g, &itemDistance); + CustomItemDistance itemDistance(distancefn, userdata); + return tree->nearestNeighbour(itemEnvelope->getEnvelopeInternal(), item, &itemDistance); } else { GeometryItemDistance itemDistance = GeometryItemDistance(); - return tree->nearestNeighbour(g->getEnvelopeInternal(), g, &itemDistance); + return tree->nearestNeighbour(itemEnvelope->getEnvelopeInternal(), item, &itemDistance); } } catch (const std::exception &e) From d90f14465255739899e68d6de7cbc648f46e7851 Mon Sep 17 00:00:00 2001 From: dbaston Date: Mon, 18 Apr 2016 16:43:12 -0400 Subject: [PATCH 3/7] Avoid null dereference when querying nearest item in empty tree --- src/index/strtree/BoundablePair.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index/strtree/BoundablePair.cpp b/src/index/strtree/BoundablePair.cpp index 403a1d70eb..e346007025 100644 --- a/src/index/strtree/BoundablePair.cpp +++ b/src/index/strtree/BoundablePair.cpp @@ -43,6 +43,9 @@ double BoundablePair::distance() { // 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); } From 19d8c4a2416c1229c1ea00e16a082ab372748ba1 Mon Sep 17 00:00:00 2001 From: dbaston Date: Mon, 18 Apr 2016 16:43:52 -0400 Subject: [PATCH 4/7] Add additional tests for GEOSSTRtree_nearest --- tests/unit/capi/GEOSSTRtreeTest.cpp | 86 +++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 10 deletions(-) diff --git a/tests/unit/capi/GEOSSTRtreeTest.cpp b/tests/unit/capi/GEOSSTRtreeTest.cpp index 8637fc995b..9241997c9a 100644 --- a/tests/unit/capi/GEOSSTRtreeTest.cpp +++ b/tests/unit/capi/GEOSSTRtreeTest.cpp @@ -7,8 +7,32 @@ // std #include #include -#include #include +#include + +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 { @@ -61,8 +85,7 @@ namespace tut GEOSSTRtree_insert(tree, g2, g2); GEOSSTRtree_insert(tree, g3, g3); - const GEOSGeometry* g5 = (GEOSGeometry*) GEOSSTRtree_nearest(tree, g4, - (int (*)(const void* item1, const void* item2, double* distance))(GEOSDistance)); + const GEOSGeometry* g5 = GEOSSTRtree_nearest(tree, g4); ensure(g5 == g2); GEOSGeom_destroy(g1); @@ -78,12 +101,12 @@ namespace tut template<> void object::test<2>() { - int ngeoms = 100; + size_t ngeoms = 100; std::vector geoms; std::vector queryPoints; GEOSSTRtree* tree = GEOSSTRtree_create(ngeoms); - for (int i = 0; i < ngeoms; i++) { + for (auto i = 0; i < ngeoms; i++) { GEOSCoordSequence* seq = GEOSCoordSeq_create(1, 2); GEOSCoordSeq_setX(seq, 0, std::rand()); GEOSCoordSeq_setY(seq, 0, std::rand()); @@ -91,18 +114,18 @@ namespace tut GEOSSTRtree_insert(tree, geoms[i], geoms[i]); } - for (int i = 0; i < ngeoms; i++) { + for (auto 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 (int i = 0; i < ngeoms; i++) { - const GEOSGeometry* nearest = (GEOSGeometry*) GEOSSTRtree_nearest(tree, queryPoints[i], NULL); + for (auto i = 0; i < ngeoms; i++) { + const GEOSGeometry* nearest = GEOSSTRtree_nearest(tree, queryPoints[i]); const GEOSGeometry* nearestBruteForce = NULL; - double nearestBruteForceDistance; - for (int j = 0; j < ngeoms; j++) { + double nearestBruteForceDistance = std::numeric_limits::max(); + for (auto j = 0; j < ngeoms; j++) { double distance; GEOSDistance(queryPoints[i], geoms[j], &distance); @@ -123,6 +146,49 @@ namespace tut 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); + } } // namespace tut + + From 319fff78fd2e03a05bedb7ab1c94b76c8ba8c7d8 Mon Sep 17 00:00:00 2001 From: dbaston Date: Mon, 18 Apr 2016 16:54:09 -0400 Subject: [PATCH 5/7] whoops, guess we're not using C++11 yet --- tests/unit/capi/GEOSSTRtreeTest.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/capi/GEOSSTRtreeTest.cpp b/tests/unit/capi/GEOSSTRtreeTest.cpp index 9241997c9a..07e935262c 100644 --- a/tests/unit/capi/GEOSSTRtreeTest.cpp +++ b/tests/unit/capi/GEOSSTRtreeTest.cpp @@ -11,7 +11,7 @@ #include struct INTPOINT { - INTPOINT(int x, int y) : x{x}, y{y} {} + INTPOINT(int x, int y) : x(x), y(y) {} int x; int y; }; @@ -106,7 +106,7 @@ namespace tut std::vector queryPoints; GEOSSTRtree* tree = GEOSSTRtree_create(ngeoms); - for (auto i = 0; i < ngeoms; 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()); @@ -114,18 +114,18 @@ namespace tut GEOSSTRtree_insert(tree, geoms[i], geoms[i]); } - for (auto i = 0; i < ngeoms; 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 (auto i = 0; i < ngeoms; i++) { + 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::max(); - for (auto j = 0; j < ngeoms; j++) { + for (size_t j = 0; j < ngeoms; j++) { double distance; GEOSDistance(queryPoints[i], geoms[j], &distance); @@ -138,7 +138,7 @@ namespace tut ensure(nearest == nearestBruteForce || GEOSEquals(nearest, nearestBruteForce)); } - for (int i = 0; i < ngeoms; i++) { + for (size_t i = 0; i < ngeoms; i++) { GEOSGeom_destroy(geoms[i]); GEOSGeom_destroy(queryPoints[i]); } From 8ab214c6ec28bc0a77ef6580840120c90e87d7ac Mon Sep 17 00:00:00 2001 From: dbaston Date: Tue, 19 Apr 2016 09:09:45 -0400 Subject: [PATCH 6/7] Add tests for GEOSSTRtree_nearest in trees containing empty geometries --- tests/unit/capi/GEOSSTRtreeTest.cpp | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/unit/capi/GEOSSTRtreeTest.cpp b/tests/unit/capi/GEOSSTRtreeTest.cpp index 07e935262c..08fa236b0b 100644 --- a/tests/unit/capi/GEOSSTRtreeTest.cpp +++ b/tests/unit/capi/GEOSSTRtreeTest.cpp @@ -188,6 +188,48 @@ namespace tut 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 From 724a1b257d9902db2122c0c3524fc2797765fd67 Mon Sep 17 00:00:00 2001 From: dbaston Date: Tue, 19 Apr 2016 09:21:35 -0400 Subject: [PATCH 7/7] add typedef for GEOSDistanceCallback --- capi/geos_c.cpp | 2 +- capi/geos_c.h.in | 11 +++++++---- capi/geos_ts_c.cpp | 6 +++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/capi/geos_c.cpp b/capi/geos_c.cpp index 3a7a5f43b5..2c32635bec 100644 --- a/capi/geos_c.cpp +++ b/capi/geos_c.cpp @@ -1156,7 +1156,7 @@ GEOSSTRtree_nearest (geos::index::strtree::STRtree *tree, const void* GEOSSTRtree_nearest_generic(GEOSSTRtree *tree, const void* item, const GEOSGeometry* itemEnvelope, - int (*distancefn)(const void* item1, const void* item2, double* distance, void* userdata), + GEOSDistanceCallback distancefn, void* userdata) { return GEOSSTRtree_nearest_generic_r( handle, tree, item, itemEnvelope, distancefn, userdata); diff --git a/capi/geos_c.h.in b/capi/geos_c.h.in index 8dc39fa69a..bc12865351 100644 --- a/capi/geos_c.h.in +++ b/capi/geos_c.h.in @@ -153,6 +153,7 @@ enum GEOSByteOrders { }; typedef void (*GEOSQueryCallback)(void *item, void *userdata); +typedef int (*GEOSDistanceCallback)(const void *item1, const void* item2, double* distance, void* userdata); /************************************************************************ * @@ -824,7 +825,7 @@ extern const void* GEOS_DLL GEOSSTRtree_nearest_generic_r(GEOSContextHandle_t ha GEOSSTRtree *tree, const void* item, const GEOSGeometry* itemEnvelope, - int (*distancefn)(const void* item1, const void* item2, double* distance, void* userdata), + GEOSDistanceCallback distancefn, void* userdata); extern void GEOS_DLL GEOSSTRtree_iterate_r(GEOSContextHandle_t handle, @@ -1700,8 +1701,10 @@ extern void GEOS_DLL GEOSSTRtree_query(GEOSSTRtree *tree, GEOSQueryCallback callback, void *userdata); /* - * Returns the nearest item in the STRtree to the supplied GEOSGeometry - * + * 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 @@ -1728,7 +1731,7 @@ extern const GEOSGeometry* GEOS_DLL GEOSSTRtree_nearest(GEOSSTRtree *tree, const extern const void* GEOS_DLL GEOSSTRtree_nearest_generic(GEOSSTRtree *tree, const void* item, const GEOSGeometry* itemEnvelope, - int (*distancefn)(const void* item1, const void* item2, double* distance, void* userdata), + GEOSDistanceCallback distancefn, void* userdata); /* * Iterates over all items in the STRtree diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp index c6d3ff9c76..312db5071a 100644 --- a/capi/geos_ts_c.cpp +++ b/capi/geos_ts_c.cpp @@ -5951,7 +5951,7 @@ GEOSSTRtree_nearest_generic_r(GEOSContextHandle_t extHandle, geos::index::strtree::STRtree *tree, const void* item, const geos::geom::Geometry* itemEnvelope, - int (*distancefn)(const void* item1, const void* item2, double* distance, void* userdata), + GEOSDistanceCallback distancefn, void* userdata) { @@ -5960,10 +5960,10 @@ GEOSSTRtree_nearest_generic_r(GEOSContextHandle_t extHandle, { if (distancefn) { struct CustomItemDistance : public ItemDistance { - CustomItemDistance(int (*p_distancefn)(const void* item1, const void* item2, double* distance, void* p_userdata), void* p_userdata) + CustomItemDistance(GEOSDistanceCallback p_distancefn, void* p_userdata) : m_distancefn(p_distancefn), m_userdata(p_userdata) {} - int (*m_distancefn)(const void* item1, const void* item2, double* distance, void* userdata); + GEOSDistanceCallback m_distancefn; void* m_userdata; double distance(const ItemBoundable* item1, const ItemBoundable* item2) {