Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add GeometryFixer class, and associated changes.
Expose in CAPI via GEOSMakeValidWithParams() which
takes a parameters object GEOSMakeValidParams*.
Default remains same but can be easily switched in
GEOSMakeValid().
  • Loading branch information
pramsey committed Apr 23, 2021
1 parent 200ff64 commit 5156001
Show file tree
Hide file tree
Showing 23 changed files with 1,620 additions and 41 deletions.
2 changes: 2 additions & 0 deletions NEWS
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: GEOSMakeValidWithParams 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
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
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
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
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
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
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

0 comments on commit 5156001

Please sign in to comment.