Skip to content

Commit 8f2364f

Browse files
authored
DistanceOp::distance(): make it return +inf instead of 0 if one of the geometry is empty (#1345)
* DistanceOp::distance(): make it return NaN instead of 0 if one of the geometry is empty Fixes OSGeo/gdal#12978 * DistanceOp::distance(): make it return inf instead of 0 if one of the geometry is empty
1 parent a02e30f commit 8f2364f

File tree

5 files changed

+34
-23
lines changed

5 files changed

+34
-23
lines changed

capi/geos_c.h.in

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3405,7 +3405,7 @@ extern char GEOS_DLL GEOSisSimple(const GEOSGeometry* g);
34053405
* In one step, calculate and return whether a geometry is simple
34063406
* and one more more points at which the geometry self-intersects
34073407
* at interior points.
3408-
* Caller has the responsibility to destroy 'location' with
3408+
* Caller has the responsibility to destroy 'location' with
34093409
* GEOSGeom_destroy()
34103410
*
34113411
* \param g The geometry to test
@@ -3649,7 +3649,8 @@ extern int GEOS_DLL GEOSGeomGetLength(
36493649
* Calculate the distance between two geometries.
36503650
* \param[in] g1 Input geometry
36513651
* \param[in] g2 Input geometry
3652-
* \param[out] dist Pointer to be filled in with distance result
3652+
* \param[out] dist Pointer to be filled in with distance result. Positive
3653+
* infinity is returned if one of the geometry is empty.
36533654
* \return 1 on success, 0 on exception.
36543655
* \since 2.2
36553656
*/
@@ -5531,7 +5532,7 @@ extern char GEOS_DLL GEOSIntersects(const GEOSGeometry* g1, const GEOSGeometry*
55315532
extern char GEOS_DLL GEOSCrosses(const GEOSGeometry* g1, const GEOSGeometry* g2);
55325533

55335534
/**
5534-
* Tests if geometry g1 is completely within g2,
5535+
* Tests if geometry g1 is completely within g2,
55355536
* but not wholly contained in the boundary of g2.
55365537
* \param g1 Input geometry
55375538
* \param g2 Input geometry

src/operation/distance/DistanceOp.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <geos/util/IllegalArgumentException.h>
3737
#include <geos/util.h>
3838

39+
#include <limits>
3940
#include <vector>
4041
#include <iostream>
4142

@@ -97,7 +98,7 @@ DistanceOp::DistanceOp(const Geometry& g0, const Geometry& g1, double tdist)
9798
/**
9899
* Report the distance between the closest points on the input geometries.
99100
*
100-
* @return the distance between the geometries
101+
* @return the distance between the geometries, or positive infinity if one of them is empty.
101102
*/
102103
double
103104
DistanceOp::distance()
@@ -111,7 +112,7 @@ DistanceOp::distance()
111112
throw IllegalArgumentException("null geometries are not supported");
112113
}
113114
if(geom[0]->isEmpty() || geom[1]->isEmpty()) {
114-
return 0.0;
115+
return std::numeric_limits<double>::infinity();
115116
}
116117
if(geom[0]->getGeometryTypeId() == GEOS_POINT && geom[1]->getGeometryTypeId() == GEOS_POINT) {
117118
return static_cast<const Point*>(geom[0])->getCoordinate()->distance(*static_cast<const Point*>(geom[1])->getCoordinate());

tests/unit/operation/distance/DistanceOpTest.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ void object::test<8>
228228

229229
DistanceOp dist(g0.get(), g1.get());
230230

231-
ensure_equals(dist.distance(), 0);
231+
ensure(std::isinf(dist.distance()));
232232

233233
ensure(dist.nearestPoints() == nullptr);
234234
}
@@ -416,7 +416,7 @@ void object::test<16>
416416

417417
DistanceOp dist(g0.get(), g1.get());
418418

419-
ensure_equals(dist.distance(), 0);
419+
ensure(std::isinf(dist.distance()));
420420

421421
ensure(dist.nearestPoints() == nullptr);
422422
}
@@ -497,7 +497,7 @@ void object::test<19>
497497
ensure(g1->isValid());
498498
ensure(g2->isValid());
499499

500-
ensure_equals(g1->distance(g2.get()), 0);
500+
ensure(std::isinf(g1->distance(g2.get())));
501501
}
502502

503503
// Test case reported in Shapely
@@ -580,7 +580,6 @@ void object::test<22>()
580580
ensure_equals(g2->distance(g1.get()), 1);
581581
}
582582

583-
// Empty is same as empty so zero...?
584583
template<>
585584
template<>
586585
void object::test<23>()
@@ -589,8 +588,8 @@ void object::test<23>()
589588
auto g2 = wktreader.read("LINESTRING EMPTY");
590589

591590
ensure(g1 != nullptr && g2 != nullptr);
592-
ensure_equals(g1->distance(g2.get()), 0);
593-
ensure_equals(g2->distance(g1.get()), 0);
591+
ensure(std::isinf(g1->distance(g2.get())));
592+
ensure(std::isinf(g2->distance(g1.get())));
594593
}
595594

596595
template<>
@@ -601,8 +600,8 @@ void object::test<24>()
601600
auto g2 = wktreader.read("LINESTRING EMPTY");
602601

603602
ensure(g1 != nullptr && g2 != nullptr);
604-
ensure_equals(g1->distance(g2.get()), 0);
605-
ensure_equals(g2->distance(g1.get()), 0);
603+
ensure(std::isinf(g1->distance(g2.get())));
604+
ensure(std::isinf(g2->distance(g1.get())));
606605
}
607606

608607
// But ignore empty if there's a real distance?
@@ -638,8 +637,8 @@ void object::test<27>()
638637
auto g2 = wktreader.read("GEOMETRYCOLLECTION(POINT(1 0))");
639638

640639
ensure(g1 != nullptr && g2 != nullptr);
641-
ensure_equals(g1->distance(g2.get()), 0);
642-
ensure_equals(g2->distance(g1.get()), 0);
640+
ensure(std::isinf(g1->distance(g2.get())));
641+
ensure(std::isinf(g2->distance(g1.get())));
643642
}
644643

645644

tests/xmltester/XMLTester.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -894,8 +894,12 @@ Test::checkResult( bool result )
894894
void
895895
Test::checkResult( double result)
896896
{
897-
char* rest;
898-
double expectedRes = std::strtod(opResult.c_str(), &rest);
897+
char* rest = nullptr;
898+
const double expectedRes = (opResult == "NaN" || opResult == "nan") ?
899+
std::numeric_limits<double>::quiet_NaN() :
900+
(opResult == "Inf" || opResult == "inf") ?
901+
std::numeric_limits<double>::infinity() :
902+
std::strtod(opResult.c_str(), &rest);
899903
if(rest == opResult.c_str()) {
900904
throw std::runtime_error("malformed testcase: missing expected double value");
901905
}
@@ -904,6 +908,12 @@ Test::checkResult( double result)
904908
isSuccess = true;
905909
}
906910
}
911+
else if (std::isnan(expectedRes)) {
912+
isSuccess = std::isnan(result);
913+
}
914+
else if( expectedRes == result ) {
915+
isSuccess = true;
916+
}
907917
else {
908918
if (std::abs(expectedRes - result) / expectedRes < 1e-3) {
909919
isSuccess = true;

tests/xmltester/tests/general/TestDistance.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
<desc>PeP - point to an empty point</desc>
55
<a> POINT(10 10) </a>
66
<b> POINT EMPTY </b>
7-
<test><op name="distance" arg1="A" arg2="B"> 0.0 </op></test>
8-
<test><op name="distance" arg1="B" arg2="A"> 0.0 </op></test>
7+
<test><op name="distance" arg1="A" arg2="B"> inf </op></test>
8+
<test><op name="distance" arg1="B" arg2="A"> inf </op></test>
99
</case>
1010

1111
<case>
@@ -36,8 +36,8 @@
3636
<desc>LL - line to empty line</desc>
3737
<a> LINESTRING (0 0, 0 10) </a>
3838
<b> LINESTRING EMPTY </b>
39-
<test><op name="distance" arg1="A" arg2="B"> 0.0 </op></test>
40-
<test><op name="distance" arg1="B" arg2="A"> 0.0 </op></test>
39+
<test><op name="distance" arg1="A" arg2="B"> inf </op></test>
40+
<test><op name="distance" arg1="B" arg2="A"> inf </op></test>
4141
</case>
4242

4343
<case>
@@ -68,8 +68,8 @@
6868
<desc>PA - point to empty polygon</desc>
6969
<a> POINT (240 160) </a>
7070
<b> POLYGON EMPTY </b>
71-
<test><op name="distance" arg1="A" arg2="B" > 0.0 </op></test>
72-
<test><op name="distance" arg1="B" arg2="A" > 0.0 </op></test>
71+
<test><op name="distance" arg1="A" arg2="B" > inf </op></test>
72+
<test><op name="distance" arg1="B" arg2="A" > inf </op></test>
7373
</case>
7474

7575
<case>

0 commit comments

Comments
 (0)