Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CoordinateFilter: Support multiple CoordinateSequence dimensions #799

Merged
merged 1 commit into from
Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class GEOS_DLL DiscreteHausdorffDistance {
{}

void
filter_ro(const geom::Coordinate* pt) override
filter_ro(const geom::CoordinateXY* pt) override
{
minPtDist.initialize();
DistanceToPoint::computeDistance(geom, *pt,
Expand Down
70 changes: 64 additions & 6 deletions include/geos/geom/CoordinateFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
#pragma once

#include <geos/export.h>
#include <geos/geom/Coordinate.h>

#include <cassert>

namespace geos {
namespace geom { // geos::geom

class Coordinate;

/** \brief
* Geometry classes support the concept of applying a
* coordinate filter to every coordinate in the Geometry.
Expand All @@ -34,8 +33,11 @@ class Coordinate;
* used to implement such things as coordinate transformations, centroid and
* envelope computation, and many other functions.
*
* TODO: provide geom::CoordinateInspector and geom::CoordinateMutator instead
* of having the two versions of filter_rw and filter_ro
* A CoordinateFilter should be able to process a CoordinateXY object and can
* optionally provide specialized implementations for higher-dimensionality
* Coordinates. If the behavior can be templated on coordinate type, then
* a filter may inherit from CoordinateInspector or CoordinateMutator to
* generate these implementations from a template.
*
*/
class GEOS_DLL CoordinateFilter {
Expand All @@ -49,7 +51,7 @@ class GEOS_DLL CoordinateFilter {
* **param** `coord` a Coordinate to which the filter is applied.
*/
virtual void
filter_rw(Coordinate* /*coord*/) const
filter_rw(CoordinateXY* /*coord*/) const
{
assert(0);
}
Expand All @@ -60,10 +62,66 @@ class GEOS_DLL CoordinateFilter {
* **param** `coord` a Coordinate to which the filter is applied.
*/
virtual void
filter_ro(const Coordinate* /*coord*/)
filter_ro(const CoordinateXY* /*coord*/)
{
assert(0);
}

virtual void
filter_rw(Coordinate* c) const
{
filter_rw(static_cast<CoordinateXY*>(c));
}

virtual void
filter_ro(const Coordinate* c)
{
filter_ro(static_cast<const CoordinateXY*>(c));
}

virtual void
filter_rw(CoordinateXYM* c) const
{
filter_rw(static_cast<CoordinateXY*>(c));
}

virtual void
filter_ro(const CoordinateXYM* c)
{
filter_ro(static_cast<const CoordinateXY*>(c));
}

virtual void
filter_rw(CoordinateXYZM* c) const
{
filter_rw(static_cast<Coordinate*>(c));
}

virtual void
filter_ro(const CoordinateXYZM* c)
{
filter_ro(static_cast<const Coordinate*>(c));
}
};

template<class Derived>
class CoordinateInspector : public CoordinateFilter
{
public:
virtual void filter_ro(const CoordinateXY* c) override { static_cast<Derived*>(this)->filter(c); }
virtual void filter_ro(const Coordinate* c) override { static_cast<Derived*>(this)->filter(c); }
virtual void filter_ro(const CoordinateXYM* c) override { static_cast<Derived*>(this)->filter(c); }
virtual void filter_ro(const CoordinateXYZM* c) override { static_cast<Derived*>(this)->filter(c); }
};

template<class Derived>
class CoordinateMutator : public CoordinateFilter
{
public:
virtual void filter_rw(CoordinateXY* c) const override { static_cast<const Derived*>(this)->filter(c); }
virtual void filter_rw(Coordinate* c) const override { static_cast<const Derived*>(this)->filter(c); }
virtual void filter_rw(CoordinateXYM* c) const override { static_cast<const Derived*>(this)->filter(c); }
virtual void filter_rw(CoordinateXYZM* c) const override { static_cast<const Derived*>(this)->filter(c); }
};

} // namespace geos::geom
Expand Down
22 changes: 20 additions & 2 deletions include/geos/geom/CoordinateSequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -608,8 +608,26 @@ class GEOS_DLL CoordinateSequence {
/// \defgroup iterate Iteration
/// @{

void apply_rw(const CoordinateFilter* filter);
void apply_ro(CoordinateFilter* filter) const;
template<typename Filter>
void apply_rw(const Filter* filter) {
switch(getCoordinateType()) {
case CoordinateType::XY: for (auto& c : items<CoordinateXY>()) { filter->filter_rw(&c); } break;
case CoordinateType::XYZ: for (auto& c : items<Coordinate>()) { filter->filter_rw(&c); } break;
case CoordinateType::XYM: for (auto& c : items<CoordinateXYM>()) { filter->filter_rw(&c); } break;
case CoordinateType::XYZM: for (auto& c : items<CoordinateXYZM>()) { filter->filter_rw(&c); } break;
}
m_hasdim = m_hasz = false; // re-check (see http://trac.osgeo.org/geos/ticket/435)
}

template<typename Filter>
void apply_ro(Filter* filter) const {
switch(getCoordinateType()) {
case CoordinateType::XY: for (const auto& c : items<CoordinateXY>()) { filter->filter_ro(&c); } break;
case CoordinateType::XYZ: for (const auto& c : items<Coordinate>()) { filter->filter_ro(&c); } break;
case CoordinateType::XYM: for (const auto& c : items<CoordinateXYM>()) { filter->filter_ro(&c); } break;
case CoordinateType::XYZM: for (const auto& c : items<CoordinateXYZM>()) { filter->filter_ro(&c); } break;
}
}

template<typename T, typename F>
void forEach(F&& fun) const
Expand Down
2 changes: 1 addition & 1 deletion include/geos/geom/Quadrant.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class GEOS_DLL Quadrant {
*
* @throws IllegalArgumentException if the points are equal
*/
static int quadrant(const geom::Coordinate& p0, const geom::Coordinate& p1)
static int quadrant(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1)
{
if(p1.x == p0.x && p1.y == p0.y) {
throw util::IllegalArgumentException("Cannot compute the quadrant for two identical points " + p0.toString());
Expand Down
22 changes: 16 additions & 6 deletions include/geos/util/UniqueCoordinateArrayFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ namespace util { // geos::util
*
* Last port: util/UniqueCoordinateArrayFilter.java rev. 1.17
*/
class GEOS_DLL UniqueCoordinateArrayFilter: public geom::CoordinateFilter {
class GEOS_DLL UniqueCoordinateArrayFilter : public geom::CoordinateInspector<UniqueCoordinateArrayFilter> {
public:
/**
* Constructs a CoordinateArrayFilter.
*
* @param target The destination set.
*/
UniqueCoordinateArrayFilter(geom::Coordinate::ConstVect& target)
UniqueCoordinateArrayFilter(std::vector<const geom::Coordinate*>& target)
: pts(target)
{}

Expand All @@ -62,17 +62,27 @@ class GEOS_DLL UniqueCoordinateArrayFilter: public geom::CoordinateFilter {
* @param coord The "read-only" Coordinate to which
* the filter is applied.
*/
void
filter_ro(const geom::Coordinate* coord) override
template<typename CoordType>
void filter(const CoordType* coord)
{
if(uniqPts.insert(coord).second) {
// TODO make `pts` a CoordinateSequence rather than coercing the type
pts.push_back(coord);
}
}

void filter(const geom::CoordinateXY*) {
assert(0); // not supported
}


void filter(const geom::CoordinateXYM*) {
assert(0); // not supported
}

private:
geom::Coordinate::ConstVect& pts; // target set reference
geom::Coordinate::ConstSet uniqPts; // unique points set
std::vector<const geom::Coordinate*>& pts; // target set reference
std::set<const geom::CoordinateXY*, geom::CoordinateLessThen> uniqPts; // unique points set

// Declare type as noncopyable
UniqueCoordinateArrayFilter(const UniqueCoordinateArrayFilter& other) = delete;
Expand Down
19 changes: 0 additions & 19 deletions src/geom/CoordinateSequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,25 +239,6 @@ CoordinateSequence::add(const CoordinateSequence& cl, bool allowRepeated, bool f

/*public*/

void
CoordinateSequence::apply_rw(const CoordinateFilter* filter)
{
// FIXME dispatch on dimension?
for(auto& c : items<Coordinate>()) {
filter->filter_rw(&c);
}
m_hasdim = m_hasz = false; // re-check (see http://trac.osgeo.org/geos/ticket/435)
}

void
CoordinateSequence::apply_ro(CoordinateFilter* filter) const
{
// FIXME dispatch on dimension?
for(const auto& c : items<Coordinate>()) {
filter->filter_ro(&c);
};
}

std::unique_ptr<CoordinateSequence>
CoordinateSequence::clone() const
{
Expand Down
6 changes: 3 additions & 3 deletions src/index/chain/MonotoneChainBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class ChainBuilder : public CoordinateFilter {
m_context(context),
m_list(list) {}

void filter_ro(const Coordinate* c) override final {
void filter_ro(const CoordinateXY* c) override {
process(c);

m_prev = c;
Expand All @@ -76,7 +76,7 @@ class ChainBuilder : public CoordinateFilter {
m_start = chainEnd;
}

void process(const Coordinate* curr) {
void process(const CoordinateXY* curr) {
if (m_prev == nullptr || curr->equals2D(*m_prev)) {
return;
}
Expand All @@ -93,7 +93,7 @@ class ChainBuilder : public CoordinateFilter {
}
}

const Coordinate* m_prev;
const CoordinateXY* m_prev;
std::size_t m_i;
int m_quadrant;
std::size_t m_start;
Expand Down
6 changes: 3 additions & 3 deletions src/noding/ScaledNoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class ScaledNoder::Scaler : public geom::CoordinateFilter {
//void filter_ro(const geom::Coordinate* c) { assert(0); }

void
filter_rw(geom::Coordinate* c) const override
filter_rw(geom::CoordinateXY* c) const override
{
c->x = util::round((c->x - sn.offsetX) * sn.scaleFactor);
c->y = util::round((c->y - sn.offsetY) * sn.scaleFactor);
Expand All @@ -114,14 +114,14 @@ class ScaledNoder::ReScaler: public geom::CoordinateFilter {
}

void
filter_ro(const geom::Coordinate* c) override
filter_ro(const geom::CoordinateXY* c) override
{
::geos::ignore_unused_variable_warning(c);
assert(0);
}

void
filter_rw(geom::Coordinate* c) const override
filter_rw(geom::CoordinateXY* c) const override
{
c->x = c->x / sn.scaleFactor + sn.offsetX;
c->y = c->y / sn.scaleFactor + sn.offsetY;
Expand Down
38 changes: 15 additions & 23 deletions src/operation/valid/RepeatedPointRemover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,16 @@ namespace geos {
namespace operation {
namespace valid {

class RepeatedPointFilter : public CoordinateFilter {
class RepeatedPointFilter : public geom::CoordinateInspector<RepeatedPointFilter> {
public:

RepeatedPointFilter()
: m_coords(detail::make_unique<CoordinateSequence>())
, m_prev(nullptr)
, sqTolerance(0.0) {}

RepeatedPointFilter(double tolerance)
: m_coords(detail::make_unique<CoordinateSequence>())
RepeatedPointFilter(bool has_z, bool has_m, double tolerance = 0.0)
: m_coords(detail::make_unique<CoordinateSequence>(0u, has_z, has_m))
, m_prev(nullptr)
, sqTolerance(tolerance*tolerance) {}

void filter_ro(const Coordinate* curr) override final {
template<typename CoordType>
void filter(const CoordType* curr) {

// skip duplicate point or too-close poinnt
if (m_prev != nullptr && (
Expand All @@ -70,7 +66,7 @@ class RepeatedPointFilter : public CoordinateFilter {
private:

std::unique_ptr<CoordinateSequence> m_coords;
const Coordinate* m_prev;
const geom::CoordinateXY* m_prev;
double sqTolerance;
};

Expand All @@ -90,26 +86,22 @@ RepeatedPointRemover::removeRepeatedPoints(const CoordinateSequence* seq, double
return ret;
}

RepeatedPointFilter filter(tolerance);
RepeatedPointFilter filter(seq->hasZ(), seq->hasM(), tolerance);
seq->apply_ro(&filter);
return filter.getCoords();
}


class RepeatedInvalidPointFilter : public CoordinateFilter {
class RepeatedInvalidPointFilter : public geom::CoordinateInspector<RepeatedInvalidPointFilter> {
public:

RepeatedInvalidPointFilter()
: m_coords(detail::make_unique<CoordinateSequence>())
, m_prev(nullptr)
, sqTolerance(0.0) {}

RepeatedInvalidPointFilter(double tolerance)
: m_coords(detail::make_unique<CoordinateSequence>())
RepeatedInvalidPointFilter(bool has_z, bool has_m, double tolerance = 0.0)
: m_coords(detail::make_unique<CoordinateSequence>(0u, has_z, has_m))
, m_prev(nullptr)
, sqTolerance(tolerance*tolerance) {}

void filter_ro(const Coordinate* curr) override final {
template<typename CoordType>
void filter(const CoordType* curr) {

bool invalid = ! curr->isValid();
// skip initial invalids
Expand All @@ -136,7 +128,7 @@ class RepeatedInvalidPointFilter : public CoordinateFilter {
private:

std::unique_ptr<CoordinateSequence> m_coords;
const Coordinate* m_prev;
const geom::CoordinateXY* m_prev;
double sqTolerance;
};

Expand All @@ -149,7 +141,7 @@ RepeatedPointRemover::removeRepeatedAndInvalidPoints(const CoordinateSequence* s
return detail::make_unique<CoordinateSequence>(0u, seq->getDimension());
}

RepeatedInvalidPointFilter filter(tolerance);
RepeatedInvalidPointFilter filter(seq->hasZ(), seq->hasM(), tolerance);
seq->apply_ro(&filter);
return filter.getCoords();
}
Expand Down Expand Up @@ -203,7 +195,7 @@ class RepeatedPointCoordinateOperation : public CoordinateOperation

// Create new vector of coordinates filtered to the
// the tolerance.
RepeatedInvalidPointFilter filter(tolerance);
RepeatedInvalidPointFilter filter(coordinates->hasZ(), coordinates->hasM(), tolerance);
coordinates->apply_ro(&filter);
auto filtCoords = filter.getCoords();

Expand Down
Loading