Skip to content

Commit

Permalink
Fixed issues with GisGeometry/Strategy classes. Addresses DOI-USGS#5612.
Browse files Browse the repository at this point in the history
* GisGeometry was throwing an exception when isValid() was called and the geometry was indeed invalid
* GisGeometry - added buffer() method
* Strategy was updated to better manage geometries
* Strategy - Added RepairInvalidGeometry and InvalidGeometryAction to allow better user control over invalid geometries
* Strategy - Apply buffer(0) algorithm when an invalid geometry is detected
* Strategy - Added more debug output
  • Loading branch information
KrisBecker authored and kledmundson committed Oct 8, 2024
1 parent 6e57958 commit cc8261f
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 9 deletions.
50 changes: 43 additions & 7 deletions isis/src/base/objs/GisGeometry/GisGeometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,15 +207,25 @@ namespace Isis {
*
* First determines if it contains a geometry and then validates with the
* GEOS toolkit.
*
* @return bool True if valid, false if invalid or non-existant
*/
*
* @return bool True if valid, false if invalid or non-existant
*
* @history 2018-07-29 Kris Becker - If the geometry is invalid, it throws an
* exception. Catch all exceptions an return proper
* status
*/
bool GisGeometry::isValid() const {
if (!isDefined()) {
return (false);
}

return (1 == GEOSisValid(this->m_geom));

int valid(0);
try {
valid = GEOSisValid(this->m_geom);
} catch (...) {
valid = 0;
}
return (1 == valid);
}


Expand Down Expand Up @@ -601,9 +611,35 @@ namespace Isis {
double ratio = inCommon->area() / this->area();
return (ratio);
}



/**
/**
* @brief Compute a buffer around an existing geometry
*
* Add a buffer around a geometry with the defined enlargement factor. It is
* common to use this as buffer(0) to fix geometries with self-intersections.
* These cases are often identified by calling isValid() and getting a false
* value returned.
*
* @author 2018-07-29 Kris Becker
*
* @param width With to enlarge or shrink polygon
* @param quadsegs Numnber of segments to define a circle on corners
*
* @return GisGeometry* Pointer to new GisGeometry with buffer applied
*/
GisGeometry *GisGeometry::buffer(const double width,const int quadsegs) const {
// If there is no geometry, return a null geometry
if ( !isDefined()) {
return (new GisGeometry());
}

// Create the buffer around the geom
GEOSGeometry *geom = GEOSBuffer(m_geom, width, quadsegs);
return (new GisGeometry(geom));
}

/**
* @brief Computes the envelope or bounding box of this geometry
*
* This method computes the envelope or bounding box of the geometry in this
Expand Down
6 changes: 6 additions & 0 deletions isis/src/base/objs/GisGeometry/GisGeometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ namespace Isis {
* ISIS. Fixes #2398.
* @history 2016-03-04 Kris Becker - Completed the documentation and implmented the equals()
* method.
* @history 2018-07-29 Kris Becker - Added buffer() method; isValid() was
* throwing an exception if the geometry was invalid
* (e.g., self-intersecting geometry), which is now
* trapped and a false condition is properly returned.
*
*/
class GisGeometry {

Expand Down Expand Up @@ -99,6 +104,7 @@ namespace Isis {

double intersectRatio(const GisGeometry &geom) const;

GisGeometry *buffer(const double width=0.0, const int quadsegs=16) const;
GisGeometry *envelope( ) const;
GisGeometry *convexHull( ) const;
GisGeometry *simplify(const double &tolerance) const;
Expand Down
73 changes: 71 additions & 2 deletions isis/src/base/objs/Strategy/Strategy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,18 @@ namespace Isis {

// Got a geometry.
if ( !geom.isEmpty() ) {


// Get decision keys
bool repairGeom = toBool(keys.get("RepairInvalidGeometry", "true"));
QString geomAction = keys.get("InvalidGeometryAction", "disable").toLower();
if ( !QString("disable error continue").contains(geomAction) ) {
if ( isDebug() ) {
cout << " Invalid value for InvalidGeometryAction (" << geomAction
<< ") - set to disable!\n";
}
geomAction = "disable";
}

// Process arguments Allows creation specialized geometry as well
if ( keys.exists("GisGeometryArgs") ) {
QStringList args = keys.allValues("GisGeometryArgs");
Expand All @@ -743,7 +754,65 @@ namespace Isis {
if ( !geom.isEmpty() ) {

QScopedPointer<GisGeometry> geosgeom(new GisGeometry(geom, GisGeometry::type(gisType)));
if ( geosgeom.isNull() || !geosgeom->isValid() ) return (false);
if ( geosgeom.isNull() ) {
if ( isDebug() ) {
cout << resource->name() << " geometry failed to construct\n";
}
if ("continue" == geomAction) return (false);
if ( "disable" == geomAction ) {
resource->discard();
return ( false );
}

// Throw an error
QString mess = resource->name() + " failed to construct geometry!";
throw IException(IException::Programmer, mess, _FILEINFO_);
}

// Check validity and take appropriate action
if ( !geosgeom->isValid() ) {

QString geomError = geosgeom->isValidReason();
if ( isDebug() ) {
cout << " Geometry error: " << geomError << "\n";
}

// Attempt repair if requested
if ( repairGeom ) {
if (isDebug()) {
cout << " " << resource->name() << " geometry is invalid..."
<< "attempting buffer(0) to fix it!\n";
}
geosgeom.reset( geosgeom->buffer(0) );
if ( isDebug() ) {
if (geosgeom.isNull() || !geosgeom->isValid() ) {
cout << " Geomtry could not be repaired!\n";
}
else {
cout << " Geometry was successfully repaired!\n";
}
}
}

// Now check state and take final action regarding a failed
// geometry

if (geosgeom.isNull() || !geosgeom->isValid() ) {
if ( isDebug() ) {
cout << " All efforts to convert geometry failed!\n";
}
if ("continue" == geomAction) return (false);
if ( "disable" == geomAction ) {
resource->discard();
return ( false );
}

// Throw an error
QString mess = resource->name() + " failed to construct geomtry - Error: " +
geomError;
throw IException(IException::Programmer, mess, _FILEINFO_);
}
}

npointsOrg = npoints = geosgeom->points();
QString gisTolerance = translateKeywordArgs("GisSimplifyTolerance",
Expand Down
7 changes: 7 additions & 0 deletions isis/src/base/objs/Strategy/Strategy.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ find files of those names at the top level of this repository. **/
#include <QString>

// SharedResource and ResourceList typedefs
#include "GisGeometry.h"
#include "Resource.h"
#include "PvlObject.h"

Expand Down Expand Up @@ -80,6 +81,12 @@ namespace Isis {
* replacements in reverse order fixes this issue.
* @history 2016-03-07 Tyler Wilson - Corrected documentation, and created a
* unit test to test most of this classe's methods.
* @history 2018-07-29 Kris Becker - Fixed errors in importGeometry() where an
* exception was being thrown when checking for valid
* Geometry. It now checks for this case and runs
* buffer(0) to attempt to fix the geometry. This is
* typically a self-intersection error that can be
* repaired with a buffer(0) operator.
*/

class Strategy {
Expand Down

0 comments on commit cc8261f

Please sign in to comment.