Skip to content

Commit 6f3fe12

Browse files
committed
Fix OverlayNG coordinate dimemsion handling for EMPTY geometries (#1258)
1 parent 071f7ee commit 6f3fe12

8 files changed

Lines changed: 256 additions & 10 deletions

File tree

include/geos/operation/overlayng/InputGeometry.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class GEOS_DLL InputGeometry {
5959

6060
bool isSingle() const;
6161
int getDimension(uint8_t index) const;
62+
uint8_t getCoordinateDimension(uint8_t index) const;
6263
const Geometry* getGeometry(uint8_t geomIndex) const;
6364
const Envelope* getEnvelope(uint8_t geomIndex) const;
6465
bool isEmpty(uint8_t geomIndex) const;

include/geos/operation/overlayng/OverlayUtil.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,13 @@ class GEOS_DLL OverlayUtil {
132132
static bool isEnvDisjoint(const Geometry* a, const Geometry* b, const PrecisionModel* pm);
133133

134134
/**
135-
* Creates an empty result geometry of the appropriate dimension,
135+
* Creates an empty result geometry of the appropriate dimension and coordinate dimension,
136136
* based on the given overlay operation and the dimensions of the inputs.
137137
* The created geometry is an atomic geometry,
138138
* not a collection (unless the dimension is -1,
139139
* in which case a GEOMETRYCOLLECTION EMPTY is created.)
140140
*/
141-
static std::unique_ptr<Geometry> createEmptyResult(int dim, const GeometryFactory* geomFact);
141+
static std::unique_ptr<Geometry> createEmptyResult(int dim, size_t coordDim, const GeometryFactory* geomFact);
142142

143143
/**
144144
* Computes the dimension of the result of
@@ -155,6 +155,13 @@ class GEOS_DLL OverlayUtil {
155155
*/
156156
static int resultDimension(int opCode, int dim0, int dim1);
157157

158+
/**
159+
* Computes the coordinate dimension of overlaying two geometries.
160+
* This is the smallest of the two coordinate dimensions
161+
* (to avoid having to populate Z and M with unknown values).
162+
*/
163+
static uint8_t resultCoordinateDimension(uint8_t coordDim0, uint8_t coordDim1);
164+
158165
/**
159166
* Creates an overlay result geometry for homogeneous or mixed components.
160167
*/

src/geom/HeuristicOverlay.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,14 @@ StructuredCollection::doUnaryUnion(int resultDim) const
341341
toVector(poly_union.get(), geoms);
342342

343343
if (geoms.size() == 0) {
344+
uint8_t resultCoordDim2 = OverlayUtil::resultCoordinateDimension(
345+
pts_less_polys_lines->getCoordinateDimension(),
346+
lines_less_polys->getCoordinateDimension());
347+
uint8_t resultCoordDim = OverlayUtil::resultCoordinateDimension(
348+
resultCoordDim2,
349+
poly_union->getCoordinateDimension());
344350
return OverlayUtil::createEmptyResult(
345-
resultDim, factory);
351+
resultDim, resultCoordDim, factory);
346352
}
347353
return factory->buildGeometry(geoms.begin(), geoms.end());
348354
}

src/operation/overlayng/InputGeometry.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ InputGeometry::getDimension(uint8_t index) const
4141
return geom[index]->getDimension();
4242
}
4343

44+
/*public*/
45+
uint8_t
46+
InputGeometry::getCoordinateDimension(uint8_t index) const
47+
{
48+
if (geom[index] == nullptr)
49+
return 0;
50+
return geom[index]->getCoordinateDimension();
51+
}
52+
4453
/*public*/
4554
const Geometry*
4655
InputGeometry::getGeometry(uint8_t geomIndex) const

src/operation/overlayng/OverlayNG.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,11 +357,15 @@ OverlayNG::extractResult(int p_opCode, OverlayGraph* graph)
357357
std::unique_ptr<Geometry>
358358
OverlayNG::createEmptyResult()
359359
{
360+
uint8_t coordDim = OverlayUtil::resultCoordinateDimension(
361+
inputGeom.getCoordinateDimension(0),
362+
inputGeom.getCoordinateDimension(1));
360363
return OverlayUtil::createEmptyResult(
361364
OverlayUtil::resultDimension(opCode,
362365
inputGeom.getDimension(0),
363366
inputGeom.getDimension(1)),
364-
geomFact);
367+
coordDim,
368+
geomFact);
365369
}
366370

367371

src/operation/overlayng/OverlayPoints.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,12 @@ OverlayPoints::getResult()
108108
break;
109109
}
110110
}
111-
if (rsltList.empty())
112-
return OverlayUtil::createEmptyResult(0, geometryFactory);
111+
if (rsltList.empty()) {
112+
uint8_t coordDim = OverlayUtil::resultCoordinateDimension(
113+
geom0->getCoordinateDimension(),
114+
geom1->getCoordinateDimension());
115+
return OverlayUtil::createEmptyResult(0, coordDim, geometryFactory);
116+
}
113117

114118
return geometryFactory->buildGeometry(std::move(rsltList));
115119
}

src/operation/overlayng/OverlayUtil.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,18 +173,18 @@ OverlayUtil::isDisjoint(const Envelope* envA, const Envelope* envB, const Precis
173173

174174
/*public static*/
175175
std::unique_ptr<Geometry>
176-
OverlayUtil::createEmptyResult(int dim, const GeometryFactory* geomFact)
176+
OverlayUtil::createEmptyResult(int dim, size_t coordDim, const GeometryFactory* geomFact)
177177
{
178178
std::unique_ptr<Geometry> result(nullptr);
179179
switch (dim) {
180180
case 0:
181-
result = geomFact->createPoint();
181+
result = geomFact->createPoint(coordDim);
182182
break;
183183
case 1:
184-
result = geomFact->createLineString();
184+
result = geomFact->createLineString(coordDim);
185185
break;
186186
case 2:
187-
result = geomFact->createPolygon();
187+
result = geomFact->createPolygon(coordDim);
188188
break;
189189
case -1:
190190
result = geomFact->createGeometryCollection();
@@ -224,6 +224,22 @@ OverlayUtil::resultDimension(int opCode, int dim0, int dim1)
224224
return resultDimension;
225225
}
226226

227+
/* public static */
228+
uint8_t
229+
OverlayUtil::resultCoordinateDimension(uint8_t coordDim0, uint8_t coordDim1)
230+
{
231+
uint8_t resultCoordDim = 4;
232+
//-- handle cases where only one geometry provided
233+
if (coordDim0 >= 2 && coordDim0 < resultCoordDim) {
234+
resultCoordDim = coordDim0;
235+
}
236+
if (coordDim1 >= 2 && coordDim1 < resultCoordDim) {
237+
resultCoordDim = coordDim1;
238+
}
239+
//-- return value must be 2, 3 or 4
240+
return resultCoordDim;
241+
}
242+
227243
/* public static */
228244
bool
229245
OverlayUtil::isResultAreaConsistent(
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
//
2+
// Test Suite for geos::operation::overlayng::OverlayNG coordinate dimension handling
3+
// for EMPTY geometries
4+
5+
#include <tut/tut.hpp>
6+
#include <utility.h>
7+
8+
// geos
9+
#include <geos/operation/overlayng/OverlayNG.h>
10+
11+
// std
12+
#include <memory>
13+
14+
using namespace geos::geom;
15+
using namespace geos::operation::overlayng;
16+
using geos::io::WKTReader;
17+
using geos::io::WKTWriter;
18+
19+
namespace tut {
20+
//
21+
// Test Group
22+
//
23+
24+
// Common data used by all tests
25+
struct test_overlayngcemptyoorddim_data {
26+
27+
WKTReader r;
28+
WKTWriter w;
29+
30+
void
31+
testOverlay(const std::string& a, const std::string& b, int opCode, const std::string& expected)
32+
{
33+
std::unique_ptr<Geometry> geom_a = r.read(a);
34+
std::unique_ptr<Geometry> geom_b = r.read(b);
35+
std::unique_ptr<Geometry> geom_expected = r.read(expected);
36+
std::unique_ptr<Geometry> geom_result = OverlayNG::overlay(geom_a.get(), geom_b.get(), opCode);
37+
// std::string wkt_result = w.write(geom_result.get());
38+
// std::cout << std::endl << wkt_result << std::endl;
39+
ensure_equals_geometry(geom_expected.get(), geom_result.get());
40+
ensure_equals( "Coordinate dimension: ",
41+
(int) geom_result.get()->getCoordinateDimension(),
42+
(int) geom_expected.get()->getCoordinateDimension()
43+
);
44+
}
45+
46+
};
47+
48+
typedef test_group<test_overlayngcemptyoorddim_data> group;
49+
typedef group::object object;
50+
51+
group test_overlayngcoorddim_group("geos::operation::overlayng::OverlayNGEmptyCoordDim");
52+
53+
//
54+
// Test Cases
55+
//
56+
57+
//--------- POINT / POINT
58+
59+
// test ZM dim for empty POINT union
60+
template<>
61+
template<>
62+
void object::test<1> ()
63+
{
64+
testOverlay("POINT ZM EMPTY", "POINT ZM EMPTY",
65+
OverlayNG::UNION, "POINT ZM EMPTY");
66+
}
67+
68+
// test ZM dim for empty POINT intersection
69+
template<>
70+
template<>
71+
void object::test<2> ()
72+
{
73+
testOverlay("POINT ZM EMPTY", "POINT ZM EMPTY",
74+
OverlayNG::INTERSECTION, "POINT ZM EMPTY");
75+
}
76+
77+
// test mixed ZM and XY dim for empty POINT union
78+
template<>
79+
template<>
80+
void object::test<3> ()
81+
{
82+
testOverlay("POINT ZM EMPTY", "POINT EMPTY",
83+
OverlayNG::UNION, "POINT EMPTY");
84+
}
85+
86+
// test mixed ZM and Z dim for empty POINT union
87+
template<>
88+
template<>
89+
void object::test<4> ()
90+
{
91+
testOverlay("POINT ZM EMPTY", "POINT Z EMPTY",
92+
OverlayNG::UNION, "POINT Z EMPTY");
93+
}
94+
95+
//--------- LINESTRING / POINT
96+
97+
template<>
98+
template<>
99+
void object::test<5> ()
100+
{
101+
testOverlay("POINT ZM EMPTY", "LINESTRING ZM EMPTY",
102+
OverlayNG::UNION, "LINESTRING ZM EMPTY");
103+
}
104+
105+
template<>
106+
template<>
107+
void object::test<6> ()
108+
{
109+
testOverlay("POINT ZM EMPTY", "LINESTRING Z EMPTY",
110+
OverlayNG::UNION, "LINESTRING Z EMPTY");
111+
}
112+
113+
template<>
114+
template<>
115+
void object::test<7> ()
116+
{
117+
testOverlay("POINT ZM EMPTY", "LINESTRING EMPTY",
118+
OverlayNG::UNION, "LINESTRING EMPTY");
119+
}
120+
121+
//-- ensure coord dim is lowest of either operand
122+
template<>
123+
template<>
124+
void object::test<8> ()
125+
{
126+
testOverlay("POINT EMPTY", "LINESTRING ZM EMPTY",
127+
OverlayNG::UNION, "LINESTRING EMPTY");
128+
}
129+
130+
//--------- LINESTRING / LINESTRING
131+
132+
// test ZM dim for empty LINESTRING union
133+
template<>
134+
template<>
135+
void object::test<9> ()
136+
{
137+
testOverlay("LINESTRING ZM EMPTY", "LINESTRING ZM EMPTY",
138+
OverlayNG::UNION, "LINESTRING ZM EMPTY");
139+
}
140+
141+
// test mixed ZM and XY dim for empty LINESTRING union
142+
template<>
143+
template<>
144+
void object::test<10> ()
145+
{
146+
testOverlay("LINESTRING ZM EMPTY", "LINESTRING Z EMPTY",
147+
OverlayNG::UNION, "LINESTRING Z EMPTY");
148+
}
149+
150+
// test mixed ZM and Z dim for empty LINESTRING union
151+
template<>
152+
template<>
153+
void object::test<11> ()
154+
{
155+
testOverlay("LINESTRING ZM EMPTY", "LINESTRING EMPTY",
156+
OverlayNG::UNION, "LINESTRING EMPTY");
157+
}
158+
159+
//--------- GEOMETRYCOLLECTION
160+
161+
//-- coord dim of GC (ZM) EMPTY is always 2
162+
template<>
163+
template<>
164+
void object::test<12> ()
165+
{
166+
testOverlay("GEOMETRYCOLLECTION ZM EMPTY",
167+
"POINT ZM EMPTY",
168+
OverlayNG::UNION, "POINT EMPTY");
169+
}
170+
171+
//-- coord dim of GC containing EMPTYs is lowest coord dim of elements
172+
template<>
173+
template<>
174+
void object::test<13> ()
175+
{
176+
testOverlay("GEOMETRYCOLLECTION (POINT ZM EMPTY)",
177+
"GEOMETRYCOLLECTION (POINT ZM EMPTY, LINESTRING ZM EMPTY)",
178+
OverlayNG::UNION, "LINESTRING ZM EMPTY");
179+
}
180+
181+
template<>
182+
template<>
183+
void object::test<14> ()
184+
{
185+
testOverlay("GEOMETRYCOLLECTION (POINT Z EMPTY)",
186+
"GEOMETRYCOLLECTION (POINT ZM EMPTY, LINESTRING ZM EMPTY)",
187+
OverlayNG::UNION, "LINESTRING Z EMPTY");
188+
}
189+
190+
template<>
191+
template<>
192+
void object::test<15> ()
193+
{
194+
testOverlay("GEOMETRYCOLLECTION (POINT EMPTY)",
195+
"GEOMETRYCOLLECTION (POINT ZM EMPTY, LINESTRING ZM EMPTY)",
196+
OverlayNG::UNION, "LINESTRING EMPTY");
197+
}
198+
199+
} // namespace tut

0 commit comments

Comments
 (0)