Update upstream source from tag 'upstream/3.10.2'
Update to upstream version '3.10.2'
with Debian dir 8756884aa56f02f948e73f75d784a654758c09e8
Bas Couwenberg
2 years ago
367 | 367 | "${CMAKE_CURRENT_BINARY_DIR}/include/geos" |
368 | 368 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} |
369 | 369 | FILES_MATCHING PATTERN "*.h") |
370 | install(DIRECTORY | |
371 | "${CMAKE_CURRENT_LIST_DIR}/include/geos" | |
372 | "${CMAKE_CURRENT_BINARY_DIR}/include/geos" | |
373 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} | |
374 | FILES_MATCHING PATTERN "*.hpp") | |
370 | 375 | if(NOT DISABLE_GEOS_INLINE) |
371 | 376 | install(DIRECTORY |
372 | 377 | "${CMAKE_CURRENT_LIST_DIR}/include/geos" |
0 | ||
1 | Changes in 3.10.2 | |
2 | 2022-01-15 | |
3 | ||
4 | - Fixes/Improvements: | |
5 | - Fix crash in MonotoneChain with empty CoordinateSequence (GH-539, Sandro Santilli) | |
6 | - Fix crash in GeoJSONWriter in case of empty points (TRAC-1139, Paul Ramsey) | |
7 | - Improve BuildArea performance (TRAC-1122, Sandro Santilli) | |
8 | - Fix unaryUnion to avoid segfault with empty polygon (GH-501, Mike Taves) | |
9 | - Fix memory leak on exception in prepared geometry (GH-506, Dan Baston) | |
10 | - Iterator invalidation in rare cases (GH-508, Momtchil Momtchev) | |
11 | - Infinite loop on collapsed input to MaximumInscribedCircle (Paul Ramsey) | |
12 | - Write LinearRing to GeoJSON as LineString (TRAC-1140, Paul Ramsey) | |
13 | - Fix PolygonEarClipper to handle collapsed corners (GH-526, Martin Davis) | |
14 | - Fix GEOSSTRtree_remove for empty tree (GH-544, Dan Baston) | |
15 | - Fix crash on query of STRtree with removed items (GH-545, Dan Baston) | |
16 | ||
17 | ||
0 | 18 | Changes in 3.10.1 |
1 | 19 | 2021-11-2 |
2 | 20 |
0 | 0 | GEOS -- Geometry Engine, Open Source |
1 | 1 | ==================================== |
2 | 2 | |
3 | GEOS is a C++11 library for performing operations on two-dimensional vector | |
3 | GEOS is a C++ library for performing operations on two-dimensional vector | |
4 | 4 | geometries. It is primarily a port of the [JTS Topology |
5 | 5 | Suite](https://github.com/locationtech/jts) Java library. It provides many of |
6 | 6 | the algorithms used by [PostGIS](http://www.postgis.net/), the |
7 | 7 | [Shapely](https://pypi.org/project/Shapely/) package for Python, the |
8 | 8 | [sf](https://github.com/r-spatial/sf) package for R, and others. |
9 | 9 | |
10 | More information is available the [project homepage](https://trac.osgeo.org/geos). | |
10 | More information is available the [project homepage](https://libgeos.org). | |
11 | 11 | |
12 | The official Git repository is at [OSGEO Gitea](https://git.osgeo.org/gitea/geos). | |
12 | The official Git repository is at [GitHub](https://github.com/libgeos/geos). | |
13 | 13 | |
14 | 14 | ## Build status |
15 | 15 |
1 | 1 | # GEOS Versions |
2 | 2 | GEOS_VERSION_MAJOR=3 |
3 | 3 | GEOS_VERSION_MINOR=10 |
4 | GEOS_VERSION_PATCH=1 | |
4 | GEOS_VERSION_PATCH=2 | |
5 | 5 | |
6 | 6 | # OPTIONS: "", "dev", "rc1" etc. |
7 | 7 | GEOS_PATCH_WORD= |
158 | 158 | static CoordinateSequence* atLeastNCoordinatesOrNothing(std::size_t n, |
159 | 159 | CoordinateSequence* c); |
160 | 160 | |
161 | /// Return position of a Coordinate, or -1 if not found | |
162 | /// | |
163 | /// FIXME: return std::size_t, using numeric_limits<std::size_t>::max | |
164 | /// as 'not found' value. | |
161 | /// Return position of a Coordinate | |
162 | // | |
163 | /// or numeric_limits<std::size_t>::max() if not found | |
165 | 164 | /// |
166 | 165 | static std::size_t indexOf(const Coordinate* coordinate, |
167 | 166 | const CoordinateSequence* cl); |
70 | 70 | * intersect the query envelope. |
71 | 71 | * |
72 | 72 | * @param searchEnv the envelope to query for |
73 | * @return a list of the items found by the query in a newly allocated vector | |
74 | 73 | */ |
75 | //virtual std::vector<void*>* query(const geom::Envelope *searchEnv)=0; | |
76 | 74 | virtual void query(const geom::Envelope* searchEnv, std::vector<void*>&) = 0; |
77 | 75 | |
78 | 76 | /** \brief |
305 | 305 | /// @{ |
306 | 306 | |
307 | 307 | bool remove(const BoundsType& itemEnv, const ItemType& item) { |
308 | build(); | |
309 | ||
308 | 310 | if (root == nullptr) { |
309 | 311 | return false; |
310 | 312 | } |
358 | 360 | |
359 | 361 | // begin and end define a range of nodes needing parents |
360 | 362 | auto begin = nodes.begin(); |
361 | auto end = nodes.end(); | |
362 | ||
363 | while (std::distance(begin, end) > 1) { | |
364 | createParentNodes(begin, end); | |
365 | begin = end; // parents just added become children in the next round | |
366 | end = nodes.end(); | |
363 | auto number = static_cast<size_t>(std::distance(begin, nodes.end())); | |
364 | ||
365 | while (number > 1) { | |
366 | createParentNodes(begin, number); | |
367 | std::advance(begin, static_cast<long>(number)); // parents just added become children in the next round | |
368 | number = static_cast<size_t>(std::distance(begin, nodes.end())); | |
367 | 369 | } |
368 | 370 | |
369 | 371 | assert(finalSize == nodes.size()); |
420 | 422 | return nodesInTree; |
421 | 423 | } |
422 | 424 | |
423 | void createParentNodes(const NodeListIterator& begin, const NodeListIterator& end) { | |
425 | void createParentNodes(const NodeListIterator& begin, size_t number) { | |
424 | 426 | // Arrange child nodes in two dimensions. |
425 | 427 | // First, divide them into vertical slices of a given size (left-to-right) |
426 | 428 | // Then create nodes within those slices (bottom-to-top) |
427 | auto numChildren = static_cast<std::size_t>(std::distance(begin, end)); | |
428 | auto numSlices = sliceCount(numChildren); | |
429 | std::size_t nodesPerSlice = sliceCapacity(numChildren, numSlices); | |
429 | auto numSlices = sliceCount(number); | |
430 | std::size_t nodesPerSlice = sliceCapacity(number, numSlices); | |
430 | 431 | |
431 | 432 | // We could sort all of the nodes here, but we don't actually need them to be |
432 | 433 | // completely sorted. They need to be sorted enough for each node to end up |
433 | 434 | // in the right vertical slice, but their relative position within the slice |
434 | 435 | // doesn't matter. So we do a partial sort for each slice below instead. |
436 | auto end = begin + static_cast<long>(number); | |
435 | 437 | sortNodesX(begin, end); |
436 | 438 | |
437 | 439 | auto startOfSlice = begin; |
438 | 440 | for (decltype(numSlices) j = 0; j < numSlices; j++) { |
441 | // end iterator is being invalidated at each iteration | |
442 | end = begin + static_cast<long>(number); | |
439 | 443 | auto nodesRemaining = static_cast<size_t>(std::distance(startOfSlice, end)); |
440 | 444 | auto nodesInSlice = std::min(nodesRemaining, nodesPerSlice); |
441 | 445 | auto endOfSlice = std::next(startOfSlice, static_cast<long>(nodesInSlice)); |
543 | 547 | |
544 | 548 | for (auto *child = node.beginChildren(); child < node.endChildren(); ++child) { |
545 | 549 | if (child->boundsIntersect(queryEnv)) { |
546 | if (child->isLeaf() && !child->isDeleted()) { | |
547 | if (!visitLeaf(visitor, *child)) { | |
548 | return; | |
550 | if (child->isLeaf()) { | |
551 | if (!child->isDeleted()) { | |
552 | if (!visitLeaf(visitor, *child)) { | |
553 | return; | |
554 | } | |
549 | 555 | } |
550 | 556 | } else { |
551 | 557 | query(queryEnv, *child, visitor); |
74 | 74 | * |
75 | 75 | * @param geom the Geometry from which to remove the common |
76 | 76 | * coordinate bits |
77 | * @return the shifted Geometry | |
78 | 77 | */ |
79 | 78 | void removeCommonBits(geom::Geometry* geom); |
80 | 79 |
163 | 163 | |
164 | 164 | bool isFlat(const std::array<Coordinate, 3>& pts) const; |
165 | 165 | |
166 | bool hasRepeatedPoint(const std::array<Coordinate, 3>& pts) const; | |
166 | /** | |
167 | * Detects if a corner has repeated points (AAB or ABB), or is collapsed (ABA). | |
168 | * @param pts the corner points | |
169 | * @return true if the corner is flat or collapsed | |
170 | */ | |
171 | bool isCornerInvalid(const std::array<Coordinate, 3>& pts) const; | |
167 | 172 | |
168 | 173 | |
169 | 174 | public: |
50 | 50 | |
51 | 51 | |
52 | 52 | /** |
53 | * Transforms a polygon with holes into a single self-touching ring | |
53 | * Transforms a polygon with holes into a single self-touching (invalid) ring | |
54 | 54 | * by connecting holes to the exterior shell or to another hole. |
55 | 55 | * The holes are added from the lowest upwards. |
56 | 56 | * As the resulting shell develops, a hole might be added to what was |
57 | 57 | * originally another hole. |
58 | * | |
59 | * There is no attempt to optimize the quality of the join lines. | |
60 | * In particular, a hole which already touches at a vertex may be | |
61 | * joined at a different vertex. | |
58 | 62 | */ |
59 | 63 | class GEOS_DLL PolygonHoleJoiner { |
60 | 64 |
119 | 119 | double height = env->getHeight(); |
120 | 120 | double cellSize = std::min(width, height); |
121 | 121 | double hSize = cellSize / 2.0; |
122 | ||
123 | // Collapsed geometries just end up using the centroid | |
124 | // as the answer and skip all the other machinery | |
125 | if (cellSize == 0) return; | |
122 | 126 | |
123 | 127 | // compute initial grid of cells to cover area |
124 | 128 | for (double x = minX; x < maxX; x += cellSize) { |
0 | # | |
1 | # List of directories use by makefile.vc to clean .obj files | |
2 | # | |
3 | ||
4 | GEOS_DIRLIST = \ | |
5 | algorithm \ | |
6 | algorithm\distance \ | |
7 | algorithm\locate \ | |
8 | examples \ | |
9 | geom \ | |
10 | geom\util \ | |
11 | geom\prep \ | |
12 | geomgraph \ | |
13 | geomgraph\index \ | |
14 | headers \ | |
15 | headers\geos \ | |
16 | index \ | |
17 | index\bintree \ | |
18 | index\chain \ | |
19 | index\intervalrtree \ | |
20 | index\quadtree \ | |
21 | index\strtree \ | |
22 | index\sweepline \ | |
23 | io \ | |
24 | linearref \ | |
25 | noding \ | |
26 | noding\snapround \ | |
27 | operation \ | |
28 | operation\buffer \ | |
29 | operation\distance \ | |
30 | operation\intersection \ | |
31 | operation\linemerge \ | |
32 | operation\overlay \ | |
33 | operation\overlay\snap \ | |
34 | operation\overlay\validate \ | |
35 | operation\polygonize \ | |
36 | operation\predicate \ | |
37 | operation\relate \ | |
38 | operation\sharedpaths \ | |
39 | operation\union \ | |
40 | operation\valid \ | |
41 | planargraph \ | |
42 | planargraph\algorithm \ | |
43 | precision \ | |
44 | simplify \ | |
45 | triangulate \ | |
46 | triangulate\quadedge \ | |
47 | util |
28 | 28 | #include <geos/util.h> |
29 | 29 | |
30 | 30 | #include <cmath> |
31 | #include <memory> | |
31 | 32 | #include <string> |
32 | 33 | #include <sstream> |
33 | 34 | #include <vector> |
131 | 132 | } |
132 | 133 | |
133 | 134 | void |
134 | Node::add(EdgeEnd* e) | |
135 | { | |
135 | Node::add(EdgeEnd* p_e) | |
136 | { | |
137 | std::unique_ptr<EdgeEnd> e(p_e); | |
136 | 138 | assert(e); |
137 | 139 | #if GEOS_DEBUG |
138 | 140 | std::cerr << "[" << this << "] Node::add(" << e->print() << ")" << std::endl; |
151 | 153 | assert(edges); |
152 | 154 | //if (edges==NULL) return; |
153 | 155 | |
154 | edges->insert(e); | |
155 | e->setNode(this); | |
156 | edges->insert(e.release()); | |
157 | p_e->setNode(this); | |
156 | 158 | #if COMPUTE_Z |
157 | addZ(e->getCoordinate().z); | |
159 | addZ(p_e->getCoordinate().z); | |
158 | 160 | #endif |
159 | 161 | testInvariant(); |
160 | 162 | } |
63 | 63 | |
64 | 64 | private: |
65 | 65 | void finishChain() { |
66 | if ( m_i == 0 ) return; | |
66 | 67 | m_ends.push_back(m_i - 1); |
67 | 68 | } |
68 | 69 |
179 | 179 | auto line = static_cast<const geom::LineString*>(geometry); |
180 | 180 | encodeLineString(line, j); |
181 | 181 | } |
182 | else if (type == GEOS_LINEARRING) { | |
183 | auto line = static_cast<const geom::LineString*>(geometry); | |
184 | encodeLineString(line, j); | |
185 | } | |
182 | 186 | else if (type == GEOS_POLYGON) { |
183 | 187 | auto poly = static_cast<const geom::Polygon*>(geometry); |
184 | 188 | encodePolygon(poly, j); |
204 | 208 | void GeoJSONWriter::encodePoint(const geom::Point* point, geos_nlohmann::ordered_json& j) |
205 | 209 | { |
206 | 210 | j["type"] = "Point"; |
207 | j["coordinates"] = convertCoordinate(point->getCoordinate()); | |
211 | if (!point->isEmpty()) { | |
212 | j["coordinates"] = convertCoordinate(point->getCoordinate()); | |
213 | } | |
214 | else { | |
215 | j["coordinates"] = j.array(); | |
216 | } | |
208 | 217 | } |
209 | 218 | |
210 | 219 | void GeoJSONWriter::encodeLineString(const geom::LineString* line, geos_nlohmann::ordered_json& j) |
64 | 64 | return pcount; |
65 | 65 | } |
66 | 66 | }; |
67 | ||
68 | static bool ringsEqualAnyDirection(const LinearRing* r1, const LinearRing* r2) | |
69 | { | |
70 | ||
71 | const CoordinateSequence *cs1 = r1->getCoordinatesRO(); | |
72 | const CoordinateSequence *cs2 = r2->getCoordinatesRO(); | |
73 | ||
74 | /* Check same number of points */ | |
75 | size_t npoints = cs1->size(); | |
76 | if ( npoints != cs2->size() ) | |
77 | { | |
78 | return false; | |
79 | } | |
80 | ||
81 | if ( npoints == 0 ) return true; /* if empty, they are equal */ | |
82 | ||
83 | /* Check same envelope (probably better avoid, as it'd need to | |
84 | * compute the envelope doing an additional scan for each) */ | |
85 | if ( ! r1->getEnvelopeInternal()->equals(r2->getEnvelopeInternal()) ) | |
86 | { | |
87 | return false; | |
88 | } | |
89 | ||
90 | /* Pretend the rings had one less point, as the last one will be | |
91 | * equal to the first one anyway */ | |
92 | --npoints; | |
93 | ||
94 | const Coordinate& firstPoint = cs1->getAt(0); | |
95 | size_t offset = CoordinateSequence::indexOf(&firstPoint, cs2); | |
96 | if ( offset == std::numeric_limits<std::size_t>::max() ) return false; | |
97 | ||
98 | bool equal = true; | |
99 | ||
100 | /* Check equals forward (skip first point, we checked it alread) */ | |
101 | for (size_t i=1; i<npoints; ++i) | |
102 | { | |
103 | size_t j = ( i + offset ) % npoints; | |
104 | const Coordinate& c1 = cs1->getAt(i); | |
105 | const Coordinate& c2 = cs2->getAt(j); | |
106 | if ( ! c1.equals(c2) ) { | |
107 | equal = false; | |
108 | break; | |
109 | } | |
110 | } | |
111 | ||
112 | if ( equal ) return true; | |
113 | ||
114 | /* Check equals backward (skip first point, we checked it already) */ | |
115 | equal = true; | |
116 | for (size_t i=1; i<npoints; ++i) | |
117 | { | |
118 | size_t j; | |
119 | if ( i <= offset ) { | |
120 | j = offset - i; | |
121 | } else { | |
122 | j = npoints - ( i - offset ); | |
123 | } | |
124 | ||
125 | const Coordinate& c1 = cs1->getAt(i); | |
126 | const Coordinate& c2 = cs2->getAt(j); | |
127 | if ( ! c1.equals(c2) ) { | |
128 | equal = false; | |
129 | break; | |
130 | } | |
131 | } | |
132 | ||
133 | return equal; | |
134 | } | |
67 | 135 | |
68 | 136 | static std::unique_ptr<Face> newFace(const geom::Polygon* p) { |
69 | 137 | auto f = std::unique_ptr<Face>(new Face()); |
99 | 167 | if( f2->parent ) { |
100 | 168 | continue; /* hole already assigned */ |
101 | 169 | } |
102 | const auto f2er = f2->poly->getExteriorRing(); | |
103 | /* TODO: can be optimized as the ring would have the | |
104 | * same vertices, possibly in different order. | |
105 | * maybe comparing number of points could already be | |
106 | * useful. | |
107 | */ | |
108 | if( f2er->equals(hole) ) { | |
170 | const auto shell = f2->poly->getExteriorRing(); | |
171 | if( ringsEqualAnyDirection(shell, hole) ) { | |
109 | 172 | f2->parent = f.get(); |
110 | 173 | break; |
111 | 174 | } |
101 | 101 | CascadedPolygonUnion::binaryUnion(const std::vector<const geom::Geometry*> & geoms, |
102 | 102 | std::size_t start, std::size_t end) |
103 | 103 | { |
104 | if(end - start <= 1) { | |
104 | if(end - start == 0) { | |
105 | return nullptr; | |
106 | } | |
107 | else if(end - start == 1) { | |
105 | 108 | return unionSafe(geoms[start], nullptr); |
106 | 109 | } |
107 | 110 | else if(end - start == 2) { |
99 | 99 | * after enough ears are removed) |
100 | 100 | */ |
101 | 101 | if (! isConvex(corner)) { |
102 | // remove the corner if it is flat or a repeated point | |
103 | bool isCornerRemoved = hasRepeatedPoint(corner) | |
102 | // remove the corner if it is invalid or flat (if required) | |
103 | bool isCornerRemoved = isCornerInvalid(corner) | |
104 | 104 | || (isFlatCornersSkipped && isFlat(corner)); |
105 | 105 | if (isCornerRemoved) { |
106 | 106 | removeCorner(); |
336 | 336 | |
337 | 337 | /* private static */ |
338 | 338 | bool |
339 | PolygonEarClipper::hasRepeatedPoint(const std::array<Coordinate, 3>& pts) const | |
340 | { | |
341 | return pts[1].equals2D(pts[0]) || pts[1].equals2D(pts[2]); | |
339 | PolygonEarClipper::isCornerInvalid(const std::array<Coordinate, 3>& pts) const | |
340 | { | |
341 | return pts[1].equals2D(pts[0]) || pts[1].equals2D(pts[2]) || pts[0].equals2D(pts[2]); | |
342 | 342 | } |
343 | 343 | |
344 | 344 |
173 | 173 | } |
174 | 174 | |
175 | 175 | |
176 | // // | |
177 | // // Invalid polygon collapsed to a line | |
178 | // // | |
179 | template<> | |
180 | template<> | |
181 | void object::test<7> | |
182 | () | |
183 | { | |
184 | checkCircle("POLYGON((1 2, 1 2, 1 2, 1 2, 3 2, 1 2))", | |
185 | 0.01, 2, 2, 0 ); | |
186 | } | |
187 | ||
176 | 188 | |
177 | 189 | |
178 | 190 | } // namespace tut |
404 | 404 | ensure_equals(ret, 0); |
405 | 405 | } |
406 | 406 | |
407 | // Verify no memory leak on exception (https://github.com/libgeos/geos/issues/505) | |
408 | template<> | |
409 | template<> | |
410 | void object::test<14> | |
411 | () | |
412 | { | |
413 | geom1_ = GEOSGeomFromWKT("LINESTRING(0 0, 1 NaN)"); | |
414 | geom2_ = GEOSGeomFromWKT("POINT(0 0)"); | |
415 | prepGeom1_ = GEOSPrepare(geom1_); | |
416 | ||
417 | ensure(nullptr != prepGeom1_); | |
418 | ensure(nullptr != geom2_); | |
419 | ||
420 | int ret = GEOSPreparedTouches(prepGeom1_, geom2_); | |
421 | ensure_equals(ret, 2); | |
422 | } | |
423 | ||
424 | ||
407 | 425 | } // namespace tut |
408 | 426 |
381 | 381 | GEOSSTRtree_destroy(tree); |
382 | 382 | } |
383 | 383 | |
384 | template<> | |
385 | template<> | |
386 | void object::test<12>() | |
387 | { | |
388 | GEOSSTRtree* tree = GEOSSTRtree_create(10); | |
389 | ||
390 | GEOSGeometry* g1 = GEOSGeomFromWKT("LINESTRING (0 0, 10 10)"); | |
391 | GEOSGeometry* g2 = GEOSGeomFromWKT("LINESTRING (20 20, 30 30)"); | |
392 | GEOSGeometry* g3 = GEOSGeomFromWKT("LINESTRING (20 20, 30 30)"); | |
393 | ||
394 | GEOSSTRtree_insert(tree, g1, g1); | |
395 | GEOSSTRtree_insert(tree, g2, g2); | |
396 | GEOSSTRtree_insert(tree, g3, g3); | |
397 | ||
398 | GEOSGeometry* p = GEOSGeomFromWKT("POINT (5 5)"); | |
399 | ||
400 | ensure_equals(GEOSSTRtree_remove(tree, p, g1), 1); | |
401 | ||
402 | std::vector<GEOSGeometry*> hits; | |
403 | GEOSSTRtree_query(tree, p, [](void* item, void* userdata) { | |
404 | auto h = static_cast<std::vector<GEOSGeometry*>*>(userdata); | |
405 | h->push_back(static_cast<GEOSGeometry*>(item)); | |
406 | }, &hits); | |
407 | ||
408 | ensure(hits.empty()); | |
409 | ||
410 | GEOSGeom_destroy(g1); | |
411 | GEOSGeom_destroy(g2); | |
412 | GEOSGeom_destroy(g3); | |
413 | GEOSGeom_destroy(p); | |
414 | ||
415 | GEOSSTRtree_destroy(tree); | |
416 | } | |
417 | ||
384 | 418 | |
385 | 419 | } // namespace tut |
386 | 420 |
0 | // | |
1 | // Test Suite for geos::geom::Geometry::difference() method. | |
2 | ||
3 | #include <tut/tut.hpp> | |
4 | #include <utility.h> | |
5 | // geos | |
6 | #include <geos/io/WKTReader.h> | |
7 | #include <geos/io/WKTWriter.h> | |
8 | #include <geos/geom/PrecisionModel.h> | |
9 | #include <geos/geom/GeometryFactory.h> | |
10 | #include <geos/geom/Geometry.h> | |
11 | #include <geos/geom/Point.h> | |
12 | #include <geos/geom/LineString.h> | |
13 | #include <geos/util.h> | |
14 | ||
15 | namespace tut { | |
16 | // dummy data, not used | |
17 | struct test_geometry_difference_data { | |
18 | geos::io::WKTReader wktreader; | |
19 | geos::io::WKTWriter wktwriter; | |
20 | ||
21 | test_geometry_difference_data() | |
22 | : | |
23 | wktreader() | |
24 | {} | |
25 | }; | |
26 | ||
27 | typedef test_group<test_geometry_difference_data> group; | |
28 | typedef group::object object; | |
29 | ||
30 | group test_geometry_difference_group("geos::geom::Geometry::difference"); | |
31 | ||
32 | // | |
33 | // Test Cases | |
34 | ||
35 | ||
36 | // This issue exercised a bug in MonotoneChainBuilder | |
37 | // https://github.com/libgeos/geos/issues/290 | |
38 | template<> | |
39 | template<> | |
40 | void object::test<1> | |
41 | () | |
42 | { | |
43 | std::string wkt1("POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))"); | |
44 | std::unique_ptr<geos::geom::Geometry> g1(wktreader.read(wkt1)); | |
45 | ||
46 | std::string wkt2("POLYGON((0.1 0.1, 4.0 0.1, 4.0 1.9, 0.1 1.9, 0.1 0.1))"); | |
47 | std::unique_ptr<geos::geom::Geometry> g2(wktreader.read(wkt2)); | |
48 | ||
49 | auto result = g2->difference(g1.get()); | |
50 | result->normalize(); | |
51 | ||
52 | std::string wktExpected("POLYGON ((2 0.1, 2 1.9, 4 1.9, 4 0.1, 2 0.1))"); | |
53 | std::unique_ptr<geos::geom::Geometry> expected(wktreader.read(wktExpected)); | |
54 | ||
55 | ensure_equals_geometry(expected.get(), result.get()); | |
56 | } | |
57 | ||
58 | ||
59 | } // namespace tut |
3 | 3 | #include <tut/tut.hpp> |
4 | 4 | #include <utility.h> |
5 | 5 | // geos |
6 | #include <geos/io/WKTReader.h> | |
7 | #include <geos/io/WKTWriter.h> | |
8 | #include <geos/geom/PrecisionModel.h> | |
9 | #include <geos/geom/GeometryFactory.h> | |
10 | #include <geos/geom/Geometry.h> | |
11 | #include <geos/geom/Point.h> | |
12 | #include <geos/geom/LineString.h> | |
13 | #include <geos/util.h> | |
6 | #include <geos/geom/Coordinate.h> | |
7 | #include <geos/geom/CoordinateArraySequence.h> | |
8 | #include <geos/index/chain/MonotoneChain.h> | |
9 | #include <geos/index/chain/MonotoneChainBuilder.h> | |
10 | ||
14 | 11 | |
15 | 12 | namespace tut { |
16 | 13 | // dummy data, not used |
32 | 29 | // |
33 | 30 | // Test Cases |
34 | 31 | |
35 | ||
36 | 32 | // This issue exercised a bug in MonotoneChainBuilder |
37 | // https://github.com/libgeos/geos/issues/290 | |
33 | // https://github.com/libgeos/geos/issues/539 | |
38 | 34 | template<> |
39 | 35 | template<> |
40 | 36 | void object::test<1> |
41 | 37 | () |
42 | 38 | { |
43 | std::string wkt1("POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))"); | |
44 | std::unique_ptr<geos::geom::Geometry> g1(wktreader.read(wkt1)); | |
39 | std::vector<geos::index::chain::MonotoneChain> chain; | |
40 | geos::geom::CoordinateArraySequence pts; | |
45 | 41 | |
46 | std::string wkt2("POLYGON((0.1 0.1, 4.0 0.1, 4.0 1.9, 0.1 1.9, 0.1 0.1))"); | |
47 | std::unique_ptr<geos::geom::Geometry> g2(wktreader.read(wkt2)); | |
42 | geos::index::chain::MonotoneChainBuilder::getChains(&pts, 0, chain); | |
48 | 43 | |
49 | auto result = g2->difference(g1.get()); | |
50 | result->normalize(); | |
51 | ||
52 | std::string wktExpected("POLYGON ((2 0.1, 2 1.9, 4 1.9, 4 0.1, 2 0.1))"); | |
53 | std::unique_ptr<geos::geom::Geometry> expected(wktreader.read(wktExpected)); | |
54 | ||
55 | ensure_equals_geometry(expected.get(), result.get()); | |
44 | ensure_equals(chain.size(), 0u); | |
56 | 45 | } |
57 | 46 | |
58 | ||
59 | 47 | } // namespace tut |
251 | 251 | ensure_equals(result, "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-117.0,33.0]},\"properties\":{\"id\":1.0,\"name\":\"One\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[-127.0,53.0]},\"properties\":{\"id\":2.0,\"name\":\"Two\"}}]}"); |
252 | 252 | } |
253 | 253 | |
254 | } | |
254 | // Write an empty point | |
255 | template<> | |
256 | template<> | |
257 | void object::test<15> | |
258 | () | |
259 | { | |
260 | GeomPtr geom(wktreader.read("POINT EMPTY")); | |
261 | std::string result = geojsonwriter.write(geom.get()); | |
262 | ensure_equals(result, "{\"type\":\"Point\",\"coordinates\":[]}"); | |
263 | } | |
264 | ||
265 | // Write an empty linestring | |
266 | template<> | |
267 | template<> | |
268 | void object::test<16> | |
269 | () | |
270 | { | |
271 | GeomPtr geom(wktreader.read("LINESTRING EMPTY")); | |
272 | std::string result = geojsonwriter.write(geom.get()); | |
273 | ensure_equals(result, "{\"type\":\"LineString\",\"coordinates\":[]}"); | |
274 | } | |
275 | ||
276 | // Write an empty polygon | |
277 | template<> | |
278 | template<> | |
279 | void object::test<17> | |
280 | () | |
281 | { | |
282 | GeomPtr geom(wktreader.read("POLYGON EMPTY")); | |
283 | std::string result = geojsonwriter.write(geom.get()); | |
284 | ensure_equals(result, "{\"type\":\"Polygon\",\"coordinates\":[[]]}"); | |
285 | } | |
286 | ||
287 | // Write an empty polygon | |
288 | template<> | |
289 | template<> | |
290 | void object::test<18> | |
291 | () | |
292 | { | |
293 | GeomPtr geom(wktreader.read("GEOMETRYCOLLECTION EMPTY")); | |
294 | std::string result = geojsonwriter.write(geom.get()); | |
295 | ensure_equals(result, "{\"type\":\"GeometryCollection\",\"geometries\":[]}"); | |
296 | } | |
297 | ||
298 | // Write a linear ring (as a linestring) | |
299 | template<> | |
300 | template<> | |
301 | void object::test<19> | |
302 | () | |
303 | { | |
304 | GeomPtr geom(wktreader.read("LINEARRING (0 0, 1 1, 1 0, 0 0)")); | |
305 | std::string result = geojsonwriter.write(geom.get()); | |
306 | ensure_equals(result, "{\"type\":\"LineString\",\"coordinates\":[[0.0,0.0],[1.0,1.0],[1.0,0.0],[0.0,0.0]]}"); | |
307 | } | |
308 | ||
309 | ||
310 | } |
180 | 180 | ); |
181 | 181 | } |
182 | 182 | |
183 | ||
184 | // testCollapsedCorner | |
185 | template<> | |
186 | template<> | |
187 | void object::test<12>() | |
188 | { | |
189 | checkTri( | |
190 | "POLYGON ((186 90, 71 17, 74 10, 65 0, 0 121, 186 90), (73 34, 67 41, 71 17, 73 34))" | |
191 | ); | |
192 | } | |
193 | ||
194 | ||
195 | ||
183 | 196 | } // namespace tut |
184 | 197 | |
185 | 198 |
155 | 155 | </test> |
156 | 156 | </case> |
157 | 157 | |
158 | ||
158 | <case> | |
159 | <desc>P - empty point</desc> | |
160 | <a>POINT EMPTY</a> | |
161 | <test> | |
162 | <op name="union" arg1="A">POINT EMPTY</op> | |
163 | </test> | |
164 | </case> | |
165 | ||
166 | <case> | |
167 | <desc>mP - empty multi point</desc> | |
168 | <a>MULTIPOINT EMPTY</a> | |
169 | <test> | |
170 | <op name="union" arg1="A">MULTIPOINT EMPTY</op> | |
171 | </test> | |
172 | </case> | |
173 | ||
174 | <case> | |
175 | <desc>L - empty line</desc> | |
176 | <a>LINESTRING EMPTY</a> | |
177 | <test> | |
178 | <op name="union" arg1="A">LINESTRING EMPTY</op> | |
179 | </test> | |
180 | </case> | |
181 | ||
182 | <case> | |
183 | <desc>mL - empty multi line</desc> | |
184 | <a>MULTILINESTRING EMPTY</a> | |
185 | <test> | |
186 | <op name="union" arg1="A">MULTILINESTRING EMPTY</op> | |
187 | </test> | |
188 | </case> | |
189 | ||
190 | <case> | |
191 | <desc>L - empty linear ring</desc> | |
192 | <a>LINEARRING EMPTY</a> | |
193 | <test> | |
194 | <op name="union" arg1="A">LINEARRING EMPTY</op> | |
195 | </test> | |
196 | </case> | |
197 | ||
198 | <case> | |
199 | <desc>P - empty polygon</desc> | |
200 | <a>POLYGON EMPTY</a> | |
201 | <test> | |
202 | <op name="union" arg1="A">POLYGON EMPTY</op> | |
203 | </test> | |
204 | </case> | |
205 | ||
206 | <case> | |
207 | <desc>mP - empty multi polygon</desc> | |
208 | <a>MULTIPOLYGON EMPTY</a> | |
209 | <test> | |
210 | <op name="union" arg1="A">MULTIPOLYGON EMPTY</op> | |
211 | </test> | |
212 | </case> | |
213 | ||
214 | <case> | |
215 | <desc>GC - empty geometry collection</desc> | |
216 | <a>GEOMETRYCOLLECTION EMPTY</a> | |
217 | <test> | |
218 | <op name="union" arg1="A">GEOMETRYCOLLECTION EMPTY</op> | |
219 | </test> | |
220 | </case> | |
159 | 221 | |
160 | 222 | </run> |