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

Implement GeometryFixer #433

Closed
wants to merge 18 commits into from
Closed
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
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Changes in 3.10.0
- CAPI: GEOSDensify (Brendan Ward)
- CAPI: GEOSCoordSeq_copyFromArrays, GEOSCoordSeq_copyFromBuffer,
GEOSCoordSeq_copyToArrays, GEOSCoordSeq_copyToBuffer (Daniel Baston)
- CAPI: GEOSFixGeometry new validity enforcement approach from
https://github.com/locationtech/jts/pull/704 (Paul Ramsey, Martin Davis)

- Fixes/Improvements:
- Preserve ordering of lines in overlay results (Martin Davis)
Expand Down
37 changes: 37 additions & 0 deletions capi/geos_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#define GEOSWKBReader geos::io::WKBReader
#define GEOSWKBWriter geos::io::WKBWriter
typedef struct GEOSBufParams_t GEOSBufferParams;
typedef struct GEOSMakeValidParams_t GEOSMakeValidParams;

#include "geos_c.h"

Expand Down Expand Up @@ -795,6 +796,42 @@ extern "C" {
return GEOSMakeValid_r(handle, g);
}

GEOSMakeValidParams*
GEOSMakeValidParams_create()
{
return GEOSMakeValidParams_create_r(handle);
}

void
GEOSMakeValidParams_destroy(GEOSMakeValidParams* parms)
{
return GEOSMakeValidParams_destroy_r(handle, parms);
}

int
GEOSMakeValidParams_setMethod(
GEOSMakeValidParams* p,
GEOSMakeValidMethods method)
{
return GEOSMakeValidParams_setMethod_r(handle, p, method);
}

int
GEOSMakeValidParams_setKeepCollapsed(
GEOSMakeValidParams* p,
int keepCollapsed)
{
return GEOSMakeValidParams_setKeepCollapsed_r(handle, p, keepCollapsed);
}

Geometry*
GEOSMakeValidWithParams(
const Geometry* g,
const GEOSMakeValidParams* params)
{
return GEOSMakeValidWithParams_r(handle, g, params);
}

Geometry*
GEOSLineMerge(const Geometry* g)
{
Expand Down
114 changes: 111 additions & 3 deletions capi/geos_c.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,19 @@ typedef struct GEOSCoordSeq_t GEOSCoordSequence;
typedef struct GEOSSTRtree_t GEOSSTRtree;

/**
* Paramter object for buffering.
* Parameter object for buffering.
* \see GEOSBufferParams_create()
* \see GEOSBufferParams_destroy()
*/
typedef struct GEOSBufParams_t GEOSBufferParams;

/**
* Parameter object for validity enforcement.
* \see GEOSMakeValidParams_create()
* \see GEOSMakeValidParams_destroy()
*/
typedef struct GEOSMakeValidParams_t GEOSMakeValidParams;

#endif

/** \cond */
Expand Down Expand Up @@ -1244,11 +1251,58 @@ extern char GEOS_DLL GEOSisValidDetail_r(
char** reason,
GEOSGeometry** location);

/* ========== Make Valid ========== */

/**
* Algorithm to use when repairing invalid geometries.
*
* \see GEOSMakeValidWithParams
*/
enum GEOSMakeValidMethods {
/** Original method, combines all rings into
a set of noded lines and then extracts valid
polygons from that linework. */
GEOS_MAKE_VALID_LINEWORK = 0,
/** Structured method, first makes all rings valid
then merges shells and subtracts holes from
shells to generate valid result. Assumes that
holes and shells are correctly categorized. */
GEOS_MAKE_VALID_STRUCTURE = 1
};

/** \see GEOSMakeValidParams_create */
extern GEOSMakeValidParams GEOS_DLL *GEOSMakeValidParams_create_r(
GEOSContextHandle_t extHandle);

/** \see GEOSMakeValidParams_destroy */
extern void GEOS_DLL GEOSMakeValidParams_destroy_r(
GEOSContextHandle_t handle,
GEOSMakeValidParams* parms);

/** \see GEOSMakeValidParams_setKeepCollapsed */
extern int GEOS_DLL GEOSMakeValidParams_setKeepCollapsed_r(
GEOSContextHandle_t handle,
GEOSMakeValidParams* p,
int style);

/** \see GEOSMakeValidParams_setMethod */
extern int GEOS_DLL GEOSMakeValidParams_setMethod_r(
GEOSContextHandle_t handle,
GEOSMakeValidParams* p,
enum GEOSMakeValidMethods method);

/** \see GEOSMakeValid */
extern GEOSGeometry GEOS_DLL *GEOSMakeValid_r(
GEOSContextHandle_t handle,
const GEOSGeometry* g);

/** \see GEOSMakeValidWithParams */
extern GEOSGeometry GEOS_DLL *GEOSMakeValidWithParams_r(
GEOSContextHandle_t handle,
const GEOSGeometry* g,
const GEOSMakeValidParams* makeValidParams);


/* ========== Geometry info ========== */

/** \see GEOSGeomType */
Expand Down Expand Up @@ -3418,10 +3472,64 @@ extern char GEOS_DLL GEOSisValidDetail(

/**
* Repair an invalid geometry, returning a valid output.
* \param g The geometry to test
* \param g The geometry to repair
* \return The repaired geometry. Caller must free with GEOSGeom_destroy().
*/
extern GEOSGeometry GEOS_DLL *GEOSMakeValid(const GEOSGeometry* g);
extern GEOSGeometry GEOS_DLL *GEOSMakeValid(
const GEOSGeometry* g);

/**
* Repair an invalid geometry, returning a valid output, using the
* indicated GEOSMakeValidMethods algorithm and options.
* \param g is the geometry to test.
* \param makeValidParams is a GEOSMakeValidParams with the desired parameters set on it.
* \return A repaired geometry. Caller must free with GEOSGeom_destroy().
* \see GEOSMakeValidParams_create
* \see GEOSMakeValidParams_destroy
* \see GEOSMakeValidParams_setMethod
* \see GEOSMakeValidParams_setKeepCollapsed
*/
extern GEOSGeometry GEOS_DLL *GEOSMakeValidWithParams(
const GEOSGeometry* g,
const GEOSMakeValidParams *makeValidParams);

/**
* Create a GEOSMakeValidParams to hold the desired parameters
* to control the algorithm and behavior of the validation process.
* \return a parameter object
* \see GEOSMakeValidWithParams
*/
extern GEOSMakeValidParams GEOS_DLL *GEOSMakeValidParams_create();

/**
* Destroy a GEOSMakeValidParams.
* \param parms the object to destroy
* \see GEOSMakeValidWithParams
*/
extern void GEOS_DLL GEOSMakeValidParams_destroy(GEOSMakeValidParams* parms);

/**
* Set the GEOSMakeValidMethods to use in making the geometry
* valid.
* \return 0 on exception, 1 on success.
* \see GEOSMakeValidWithParams
*/
extern int GEOS_DLL GEOSMakeValidParams_setMethod(
GEOSMakeValidParams* p,
enum GEOSMakeValidMethods method);

/**
* When this parameter is not set to 0, the GEOS_MAKE_VALID_STRUCTURE method will drop
* any component that has collapsed into a lower dimensionality.
* For example, a ring collapsing to a line, or a line collapsing
* to a point.
* \return 0 on exception, 1 on success.
* \see GEOSMakeValidWithParams
*/
extern int GEOS_DLL GEOSMakeValidParams_setKeepCollapsed(
GEOSMakeValidParams* p,
int keepCollapsed);


/* ========== Geometry info ========== */

Expand Down
82 changes: 77 additions & 5 deletions capi/geos_ts_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <geos/geom/IntersectionMatrix.h>
#include <geos/geom/Envelope.h>
#include <geos/geom/util/Densifier.h>
#include <geos/geom/util/GeometryFixer.h>
#include <geos/index/strtree/TemplateSTRtree.h>
#include <geos/index/ItemVisitor.h>
#include <geos/io/WKTReader.h>
Expand Down Expand Up @@ -118,6 +119,12 @@
#define GEOSWKBReader geos::io::WKBReader
#define GEOSWKBWriter geos::io::WKBWriter

// Implementation struct for the GEOSMakeValidParams object
typedef struct {
int method;
int keepCollapsed;
} GEOSMakeValidParams;

#include "geos_c.h"

// Intentional, to allow non-standard C elements like C99 functions to be
Expand Down Expand Up @@ -1931,16 +1938,81 @@ extern "C" {
Geometry*
GEOSMakeValid_r(GEOSContextHandle_t extHandle, const Geometry* g)
{
using geos::operation::valid::MakeValid;
GEOSMakeValidParams params;
params.method = GEOS_MAKE_VALID_LINEWORK;
params.keepCollapsed = 1;
return GEOSMakeValidWithParams_r(extHandle, g, &params);
}

GEOSMakeValidParams*
GEOSMakeValidParams_create_r(GEOSContextHandle_t extHandle)
{
return execute(extHandle, [&]() {
MakeValid makeValid;
auto out = makeValid.build(g);
out->setSRID(g->getSRID());
return out.release();
GEOSMakeValidParams* p = new GEOSMakeValidParams();
p->method = GEOS_MAKE_VALID_LINEWORK;
p->keepCollapsed = 0;
return p;
});
}

void
GEOSMakeValidParams_destroy_r(GEOSContextHandle_t extHandle, GEOSMakeValidParams* parms)
{
(void)extHandle;
delete parms;
}

int
GEOSMakeValidParams_setKeepCollapsed_r(GEOSContextHandle_t extHandle,
GEOSMakeValidParams* p, int keepCollapsed)
{
return execute(extHandle, 0, [&]() {
p->keepCollapsed = keepCollapsed;
return 1;
});
}

int
GEOSMakeValidParams_setMethod_r(GEOSContextHandle_t extHandle,
GEOSMakeValidParams* p, GEOSMakeValidMethods method)
{
return execute(extHandle, 0, [&]() {
p->method = method;
return 1;
});
}

Geometry*
GEOSMakeValidWithParams_r(
GEOSContextHandle_t extHandle,
const Geometry* g,
const GEOSMakeValidParams* params)
{
using geos::geom::util::GeometryFixer;
using geos::operation::valid::MakeValid;

if (params && params->method == GEOS_MAKE_VALID_LINEWORK) {
return execute(extHandle, [&]() {
MakeValid makeValid;
auto out = makeValid.build(g);
out->setSRID(g->getSRID());
return out.release();
});
}
else if (params && params->method == GEOS_MAKE_VALID_STRUCTURE) {
return execute(extHandle, [&]() {
GeometryFixer fixer(g);
fixer.setKeepCollapsed(params->keepCollapsed == 0 ? false : true);
auto out = fixer.getResult();
out->setSRID(g->getSRID());
return out.release();
});
}
else {
throw IllegalArgumentException("Unknown method in GEOSMakeValidParams");
}
}

Geometry*
GEOSPolygonizer_getCutEdges_r(GEOSContextHandle_t extHandle, const Geometry* const* g, unsigned int ngeoms)
{
Expand Down
2 changes: 2 additions & 0 deletions include/geos/geom/Coordinate.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ class GEOS_DLL Coordinate {

bool isNull() const;

bool isValid() const;

Coordinate();

Coordinate(double xNew, double yNew, double zNew = DoubleNotANumber);
Expand Down
6 changes: 6 additions & 0 deletions include/geos/geom/Coordinate.inl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ Coordinate::isNull() const
return (std::isnan(x) && std::isnan(y) && std::isnan(z));
}

INLINE bool
Coordinate::isValid() const
{
return std::isfinite(x) && std::isfinite(y);
}

INLINE
Coordinate::Coordinate() : x(0.0), y(0.0), z(DoubleNotANumber)
{}
Expand Down
11 changes: 7 additions & 4 deletions include/geos/geom/LinearRing.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,25 @@ namespace geom { // geos::geom
* closed and simple.
*
* In other words, the first and last coordinate in the ring must be equal,
* and the interior of the ring must not self-intersect. Either orientation
* and the ring must not self-intersect. Either orientation
* of the ring is allowed.
*
* A ring must have either 0 or 4 or more points. The first and last points
* A ring must have either 0 or 3 or more points. The first and last points
* must be equal (in 2D). If these conditions are not met, the constructors
* throw an {@link geos::util::IllegalArgumentException}
* A ring with 3 points is invalid, because it is collapsed
* and thus has a self-intersection. It is allowed to be constructed
* so that it can be represented, and repaired if needed.
*/
class GEOS_DLL LinearRing : public LineString {

public:

/** \brief
* The minimum number of vertices allowed in a valid non-empty ring (= 4).
* The minimum number of vertices allowed in a valid non-empty ring.
* Empty rings with 0 vertices are also valid.
*/
static const unsigned int MINIMUM_VALID_SIZE = 4;
static const unsigned int MINIMUM_VALID_SIZE = 3;

LinearRing(const LinearRing& lr);

Expand Down
Loading