Codebase list geos / 0bf4d72
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
26 changed file(s) with 428 addition(s) and 117 deletion(s). Raw diff Collapse all Expand all
367367 "${CMAKE_CURRENT_BINARY_DIR}/include/geos"
368368 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
369369 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")
370375 if(NOT DISABLE_GEOS_INLINE)
371376 install(DIRECTORY
372377 "${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
018 Changes in 3.10.1
119 2021-11-2
220
00 GEOS -- Geometry Engine, Open Source
11 ====================================
22
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
44 geometries. It is primarily a port of the [JTS Topology
55 Suite](https://github.com/locationtech/jts) Java library. It provides many of
66 the algorithms used by [PostGIS](http://www.postgis.net/), the
77 [Shapely](https://pypi.org/project/Shapely/) package for Python, the
88 [sf](https://github.com/r-spatial/sf) package for R, and others.
99
10 More information is available the [project homepage](https://trac.osgeo.org/geos).
10 More information is available the [project homepage](https://libgeos.org).
1111
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).
1313
1414 ## Build status
1515
11 # GEOS Versions
22 GEOS_VERSION_MAJOR=3
33 GEOS_VERSION_MINOR=10
4 GEOS_VERSION_PATCH=1
4 GEOS_VERSION_PATCH=2
55
66 # OPTIONS: "", "dev", "rc1" etc.
77 GEOS_PATCH_WORD=
158158 static CoordinateSequence* atLeastNCoordinatesOrNothing(std::size_t n,
159159 CoordinateSequence* c);
160160
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
165164 ///
166165 static std::size_t indexOf(const Coordinate* coordinate,
167166 const CoordinateSequence* cl);
7070 * intersect the query envelope.
7171 *
7272 * @param searchEnv the envelope to query for
73 * @return a list of the items found by the query in a newly allocated vector
7473 */
75 //virtual std::vector<void*>* query(const geom::Envelope *searchEnv)=0;
7674 virtual void query(const geom::Envelope* searchEnv, std::vector<void*>&) = 0;
7775
7876 /** \brief
305305 /// @{
306306
307307 bool remove(const BoundsType& itemEnv, const ItemType& item) {
308 build();
309
308310 if (root == nullptr) {
309311 return false;
310312 }
358360
359361 // begin and end define a range of nodes needing parents
360362 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()));
367369 }
368370
369371 assert(finalSize == nodes.size());
420422 return nodesInTree;
421423 }
422424
423 void createParentNodes(const NodeListIterator& begin, const NodeListIterator& end) {
425 void createParentNodes(const NodeListIterator& begin, size_t number) {
424426 // Arrange child nodes in two dimensions.
425427 // First, divide them into vertical slices of a given size (left-to-right)
426428 // 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);
430431
431432 // We could sort all of the nodes here, but we don't actually need them to be
432433 // completely sorted. They need to be sorted enough for each node to end up
433434 // in the right vertical slice, but their relative position within the slice
434435 // doesn't matter. So we do a partial sort for each slice below instead.
436 auto end = begin + static_cast<long>(number);
435437 sortNodesX(begin, end);
436438
437439 auto startOfSlice = begin;
438440 for (decltype(numSlices) j = 0; j < numSlices; j++) {
441 // end iterator is being invalidated at each iteration
442 end = begin + static_cast<long>(number);
439443 auto nodesRemaining = static_cast<size_t>(std::distance(startOfSlice, end));
440444 auto nodesInSlice = std::min(nodesRemaining, nodesPerSlice);
441445 auto endOfSlice = std::next(startOfSlice, static_cast<long>(nodesInSlice));
543547
544548 for (auto *child = node.beginChildren(); child < node.endChildren(); ++child) {
545549 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 }
549555 }
550556 } else {
551557 query(queryEnv, *child, visitor);
7474 *
7575 * @param geom the Geometry from which to remove the common
7676 * coordinate bits
77 * @return the shifted Geometry
7877 */
7978 void removeCommonBits(geom::Geometry* geom);
8079
163163
164164 bool isFlat(const std::array<Coordinate, 3>& pts) const;
165165
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;
167172
168173
169174 public:
5050
5151
5252 /**
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
5454 * by connecting holes to the exterior shell or to another hole.
5555 * The holes are added from the lowest upwards.
5656 * As the resulting shell develops, a hole might be added to what was
5757 * 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.
5862 */
5963 class GEOS_DLL PolygonHoleJoiner {
6064
119119 double height = env->getHeight();
120120 double cellSize = std::min(width, height);
121121 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;
122126
123127 // compute initial grid of cells to cover area
124128 for (double x = minX; x < maxX; x += cellSize) {
+0
-48
src/dirlist.mk less more
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
2828 #include <geos/util.h>
2929
3030 #include <cmath>
31 #include <memory>
3132 #include <string>
3233 #include <sstream>
3334 #include <vector>
131132 }
132133
133134 void
134 Node::add(EdgeEnd* e)
135 {
135 Node::add(EdgeEnd* p_e)
136 {
137 std::unique_ptr<EdgeEnd> e(p_e);
136138 assert(e);
137139 #if GEOS_DEBUG
138140 std::cerr << "[" << this << "] Node::add(" << e->print() << ")" << std::endl;
151153 assert(edges);
152154 //if (edges==NULL) return;
153155
154 edges->insert(e);
155 e->setNode(this);
156 edges->insert(e.release());
157 p_e->setNode(this);
156158 #if COMPUTE_Z
157 addZ(e->getCoordinate().z);
159 addZ(p_e->getCoordinate().z);
158160 #endif
159161 testInvariant();
160162 }
6363
6464 private:
6565 void finishChain() {
66 if ( m_i == 0 ) return;
6667 m_ends.push_back(m_i - 1);
6768 }
6869
179179 auto line = static_cast<const geom::LineString*>(geometry);
180180 encodeLineString(line, j);
181181 }
182 else if (type == GEOS_LINEARRING) {
183 auto line = static_cast<const geom::LineString*>(geometry);
184 encodeLineString(line, j);
185 }
182186 else if (type == GEOS_POLYGON) {
183187 auto poly = static_cast<const geom::Polygon*>(geometry);
184188 encodePolygon(poly, j);
204208 void GeoJSONWriter::encodePoint(const geom::Point* point, geos_nlohmann::ordered_json& j)
205209 {
206210 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 }
208217 }
209218
210219 void GeoJSONWriter::encodeLineString(const geom::LineString* line, geos_nlohmann::ordered_json& j)
6464 return pcount;
6565 }
6666 };
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 }
67135
68136 static std::unique_ptr<Face> newFace(const geom::Polygon* p) {
69137 auto f = std::unique_ptr<Face>(new Face());
99167 if( f2->parent ) {
100168 continue; /* hole already assigned */
101169 }
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) ) {
109172 f2->parent = f.get();
110173 break;
111174 }
101101 CascadedPolygonUnion::binaryUnion(const std::vector<const geom::Geometry*> & geoms,
102102 std::size_t start, std::size_t end)
103103 {
104 if(end - start <= 1) {
104 if(end - start == 0) {
105 return nullptr;
106 }
107 else if(end - start == 1) {
105108 return unionSafe(geoms[start], nullptr);
106109 }
107110 else if(end - start == 2) {
9999 * after enough ears are removed)
100100 */
101101 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)
104104 || (isFlatCornersSkipped && isFlat(corner));
105105 if (isCornerRemoved) {
106106 removeCorner();
336336
337337 /* private static */
338338 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]);
342342 }
343343
344344
173173 }
174174
175175
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
176188
177189
178190 } // namespace tut
404404 ensure_equals(ret, 0);
405405 }
406406
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
407425 } // namespace tut
408426
381381 GEOSSTRtree_destroy(tree);
382382 }
383383
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
384418
385419 } // namespace tut
386420
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
33 #include <tut/tut.hpp>
44 #include <utility.h>
55 // 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
1411
1512 namespace tut {
1613 // dummy data, not used
3229 //
3330 // Test Cases
3431
35
3632 // This issue exercised a bug in MonotoneChainBuilder
37 // https://github.com/libgeos/geos/issues/290
33 // https://github.com/libgeos/geos/issues/539
3834 template<>
3935 template<>
4036 void object::test<1>
4137 ()
4238 {
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;
4541
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);
4843
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);
5645 }
5746
58
5947 } // namespace tut
251251 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\"}}]}");
252252 }
253253
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 }
180180 );
181181 }
182182
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
183196 } // namespace tut
184197
185198
155155 </test>
156156 </case>
157157
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>
159221
160222 </run>