Skip to content

Commit

Permalink
Fix WKT for atomic multi-geometries with one or more empties
Browse files Browse the repository at this point in the history
  • Loading branch information
mwtoews committed Aug 29, 2023
1 parent 15185e6 commit 6429ffd
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 15 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- ConvexHull: Performance improvement for larger geometries (JTS-985, Martin Davis)
- Intersection: change to using DoubleDouble computation to improve robustness (GH-937, Martin Davis)
- Fix LargestEmptyCircle to respect polygonal obstacles (GH-939, Martin Davis)
- Fix WKT for atomic multi-geometries with one or more empties (GH-952, Mike Taves)


## Changes in 3.12.0
Expand Down
29 changes: 15 additions & 14 deletions src/io/WKTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,12 +477,13 @@ void
WKTWriter::appendMultiPointText(const MultiPoint& multiPoint, OrdinateSet outputOrdinates,
int /*level*/, Writer& writer) const
{
if(multiPoint.isEmpty()) {
const std::size_t n = multiPoint.getNumGeometries();
if(n == 0) {
writer.write("EMPTY");
}
else {
writer.write("(");
for(std::size_t i = 0, n = multiPoint.getNumGeometries(); i < n; ++i) {
for(std::size_t i = 0; i < n; ++i) {
if(i > 0) {
writer.write(", ");
}
Expand All @@ -506,15 +507,15 @@ void
WKTWriter::appendMultiLineStringText(const MultiLineString& multiLineString, OrdinateSet outputOrdinates, int p_level, bool indentFirst,
Writer& writer) const
{
if(multiLineString.isEmpty()) {
const std::size_t n = multiLineString.getNumGeometries();
if(n == 0) {
writer.write("EMPTY");
}
else {
int level2 = p_level;
bool doIndent = indentFirst;
writer.write("(");
for(std::size_t i = 0, n = multiLineString.getNumGeometries();
i < n; ++i) {
for(std::size_t i = 0; i < n; ++i) {
if(i > 0) {
writer.write(", ");
level2 = p_level + 1;
Expand All @@ -530,15 +531,15 @@ WKTWriter::appendMultiLineStringText(const MultiLineString& multiLineString, Ord
void
WKTWriter::appendMultiPolygonText(const MultiPolygon& multiPolygon, OrdinateSet outputOrdinates, int p_level, Writer& writer) const
{
if(multiPolygon.isEmpty()) {
const std::size_t n = multiPolygon.getNumGeometries();
if(n == 0) {
writer.write("EMPTY");
}
else {
int level2 = p_level;
bool doIndent = false;
writer.write("(");
for(std::size_t i = 0, n = multiPolygon.getNumGeometries();
i < n; ++i) {
for(std::size_t i = 0; i < n; ++i) {
if(i > 0) {
writer.write(", ");
level2 = p_level + 1;
Expand All @@ -558,11 +559,14 @@ WKTWriter::appendGeometryCollectionText(
int p_level,
Writer& writer) const
{
if(geometryCollection.getNumGeometries() > 0) {
const std::size_t n = geometryCollection.getNumGeometries();
if(n == 0) {
writer.write("EMPTY");
}
else {
int level2 = p_level;
writer.write("(");
for(std::size_t i = 0, n = geometryCollection.getNumGeometries();
i < n; ++i) {
for(std::size_t i = 0; i < n; ++i) {
if(i > 0) {
writer.write(", ");
level2 = p_level + 1;
Expand All @@ -571,9 +575,6 @@ WKTWriter::appendGeometryCollectionText(
}
writer.write(")");
}
else {
writer.write("EMPTY");
}
}

void
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/io/GeoJSONReaderTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ void object::test<28>
{
std::string geojson { "{\"type\":\"MultiLineString\",\"coordinates\":[[],[],[]]}" };
GeomPtr geom(geojsonreader.read(geojson));
ensure_equals(geom->toText(), "MULTILINESTRING EMPTY");
ensure_equals(geom->toText(), "MULTILINESTRING (EMPTY, EMPTY, EMPTY)");
ensure_equals(static_cast<size_t>(geom->getCoordinateDimension()), 2u);
}

Expand Down
53 changes: 53 additions & 0 deletions tests/unit/io/WKTWriterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,4 +405,57 @@ void object::test<14>
ensure_equals(wktwriter.write(*g), "LINESTRING M (1 2 3, 4 5 NaN)");
}

// Test multi-part geometries with zero or more empty parts
// https://github.com/libgeos/geos/issues/951
template<>
template<>
void object::test<15>
()
{
// Run all combinations of atomic multi-geometry types and dimensions
std::vector<std::string> geom_types{"POINT", "LINESTRING", "POLYGON"};
std::vector<std::string> dim_types{"", " Z", " M", " ZM"};
for (const auto& geom_type : geom_types) {
// zero empties -- but don't check dim_types
// https://github.com/libgeos/geos/issues/888
const auto wkt0 = "MULTI" + geom_type + " EMPTY";
const auto g0 = wktreader.read(wkt0);
ensure_equals(g0->getNumGeometries(), 0u);
ensure_equals(wktwriter.write(*g0), wkt0);
for (const auto& dim_type : dim_types) {
// single empty
const auto wkt1 = "MULTI" + geom_type + dim_type + " (EMPTY)";
const auto g1 = wktreader.read(wkt1);
ensure_equals(g1->getNumGeometries(), 1u);
ensure_equals(wktwriter.write(*g1), wkt1);
// two empties
const auto wkt2 = "MULTI" + geom_type + " (EMPTY, EMPTY)";
const auto g2 = wktreader.read(wkt2);
ensure_equals(g2->getNumGeometries(), 2u);
ensure_equals(wktwriter.write(*g2), wkt2);
}
}

// Run remaining combinations with GeometryCollection
const auto wkt0 = "GEOMETRYCOLLECTION EMPTY";
const auto g0 = wktreader.read(wkt0);
ensure_equals(g0->getNumGeometries(), 0u);
ensure_equals(wktwriter.write(*g0), wkt0);
for (const auto& dim_type : dim_types) {
// single empty
const auto wkt1 = "GEOMETRYCOLLECTION" + dim_type +
" (POINT" + dim_type + " EMPTY)";
const auto g1 = wktreader.read(wkt1);
ensure_equals(g1->getNumGeometries(), 1u);
ensure_equals(wktwriter.write(*g1), wkt1);
// two empties
const auto wkt2 = "GEOMETRYCOLLECTION" + dim_type +
" (POINT" + dim_type + " EMPTY, LINESTRING" + dim_type + " EMPTY)";
const auto g2 = wktreader.read(wkt2);
ensure_equals(g2->getNumGeometries(), 2u);
ensure_equals(wktwriter.write(*g2), wkt2);
}

}

} // namespace tut

0 comments on commit 6429ffd

Please sign in to comment.