Skip to content

Commit

Permalink
Tidy GeometryFlattener
Browse files Browse the repository at this point in the history
  • Loading branch information
dbaston committed Sep 22, 2022
1 parent 879ae5e commit bf92b2b
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 29 deletions.
40 changes: 12 additions & 28 deletions include/geos/operation/cluster/GeometryFlattener.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,18 @@ namespace cluster {

class GEOS_DLL GeometryFlattener {
public:
static std::unique_ptr<geom::Geometry>
flatten(std::unique_ptr<geom::Geometry>&& g) {
if (!g->isCollection()) {
return std::move(g);
}

const geom::GeometryFactory* factory = g->getFactory();

std::vector<std::unique_ptr<geom::Geometry>> components;
flatten(std::move(g), components);

return factory->buildGeometry(std::move(components));
};

static void
flatten(std::unique_ptr<geom::Geometry>&& g, std::vector<std::unique_ptr<geom::Geometry>> & components)
{
if (g->isCollection()) {
auto gc = static_cast<geom::GeometryCollection*>(g.get());

for (auto& gi : gc->releaseGeometries()) {
flatten(std::move(gi), components);
}
} else {
components.push_back(std::move(g));
}
};

/** Flatten a geometry such that it contains no nested components (e.g., MultiPolygons within
* GeometryCollections, etc.). The flattened geometry will use the most narrow representation
* possible (Polygon preferred to MultiPolygon, MultiPolygon preferred to GeometryCollection.)
*
* @brief flatten
* @param g a geometry to be flattened; consumed by the function
* @return a geometry with no nested components
*/
static std::unique_ptr<geom::Geometry> flatten(std::unique_ptr<geom::Geometry>&& g);

private:
static void flatten(std::unique_ptr<geom::Geometry>&& g, std::vector<std::unique_ptr<geom::Geometry>>& components);
};


Expand Down
56 changes: 56 additions & 0 deletions src/operation/cluster/GeometryFlattener.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**********************************************************************
*
* GEOS - Geometry Engine Open Source
* http://geos.osgeo.org
*
* Copyright (C) 2022 ISciences LLC
*
* This is free software; you can redistribute and/or modify it under
* the terms of the GNU Lesser General Public Licence as published
* by the Free Software Foundation.
* See the COPYING file for more information.
*
**********************************************************************/

#include <geos/operation/cluster/GeometryFlattener.h>

namespace geos {
namespace operation {
namespace cluster {

std::unique_ptr<geom::Geometry>
GeometryFlattener::flatten(std::unique_ptr<geom::Geometry>&& g) {
if (!g->isCollection()) {
return std::move(g);
}

if (g->isEmpty()) {
return std::move(g);
}

const geom::GeometryFactory* factory = g->getFactory();

std::vector<std::unique_ptr<geom::Geometry>> components;
flatten(std::move(g), components);

return factory->buildGeometry(std::move(components));
}

void
GeometryFlattener::flatten(std::unique_ptr<geom::Geometry>&& g, std::vector<std::unique_ptr<geom::Geometry>>& components)
{
if (g->isCollection()) {
auto gc = static_cast<geom::GeometryCollection*>(g.get());

for (auto& gi : gc->releaseGeometries()) {
flatten(std::move(gi), components);
}
} else {
components.push_back(std::move(g));
}
}


}
}
}
2 changes: 1 addition & 1 deletion tests/unit/operation/cluster/ClusterTest.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Test Suite for geos::operation::cluster::Cluster class.
// Test Suite for geos::operation::cluster::ClusterFinder classes

// tut
#include <tut/tut.hpp>
Expand Down
81 changes: 81 additions & 0 deletions tests/unit/operation/cluster/GeometryFlattenerTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//
// Test Suite for geos::operation::cluster::GeometryFlattener class.

// tut
#include <tut/tut.hpp>
// geos
#include <geos/operation/cluster/GeometryFlattener.h>
#include <geos/io/WKTReader.h>

namespace tut {
//
// Test Group
//

// Common data used by tests
struct test_flattener_data {
geos::io::WKTReader reader;

void checkFlattener(const std::string& input_wkt, const std::string& expected_wkt) {
const auto& flattened = geos::operation::cluster::GeometryFlattener::flatten(reader.read(input_wkt));
const auto& expected = reader.read(expected_wkt);

ensure_equals(flattened->getGeometryType(), expected->getGeometryType());
ensure(flattened->equals(expected.get()));
}

void checkFlattenerUnchanged(const std::string& input_wkt) {
checkFlattener(input_wkt, input_wkt);
}
};

typedef test_group<test_flattener_data> group;
typedef group::object object;

group test_flattenerrtest_group("geos::operation::cluster::GeometryFlattener");

// empty geometry unchanged
template<>
template<>
void object::test<1>()
{
checkFlattenerUnchanged("POINT EMPTY");
checkFlattenerUnchanged("LINESTRING EMPTY");
checkFlattenerUnchanged("POLYGON EMPTY");
checkFlattenerUnchanged("MULTIPOINT EMPTY");
checkFlattenerUnchanged("MULTILINESTRING EMPTY");
checkFlattenerUnchanged("MULTIPOLYGON EMPTY");
checkFlattenerUnchanged("GEOMETRYCOLLECTION EMPTY");
}

// single-part geometry unchanged
template<>
template<>
void object::test<2>()
{
checkFlattenerUnchanged("POINT (3 8)");
checkFlattenerUnchanged("LINESTRING (3 8, 2 2)");
checkFlattenerUnchanged("POLYGON ((0 0, 0 1, 1 1, 0 0))");
}

// single-part collections simplified
template<>
template<>
void object::test<3>()
{
checkFlattener("GEOMETRYCOLLECTION (POINT (1 1))", "POINT (1 1)");
checkFlattener("MULTIPOINT (1 1)", "POINT (1 1)");
}

// most narrow representation used
template<>
template<>
void object::test<4>()
{
checkFlattener("GEOMETRYCOLLECTION (POINT (1 1), MULTIPOINT (1 2, 1 3), GEOMETRYCOLLECTION (POINT (1 4), POINT EMPTY))",
"MULTIPOINT ((1 1), (1 2), (1 3), (1 4), EMPTY)");
checkFlattener("GEOMETRYCOLLECTION(MULTILINESTRING ((1 1, 2 2)), MULTIPOINT ((3 3), (4 4)))",
"GEOMETRYCOLLECTION(LINESTRING (1 1, 2 2), POINT (3 3), POINT (4 4))");
}

}

0 comments on commit bf92b2b

Please sign in to comment.