308 lines
8.8 KiB
C++
308 lines
8.8 KiB
C++
/**********************************************************************
|
|
*
|
|
* GEOS - Geometry Engine Open Source
|
|
* http://geos.osgeo.org
|
|
*
|
|
* Copyright (C) 2021 Paul Ramsey <pramsey@cleverelephant.ca>
|
|
* Copyright (C) 2021 Martin Davis
|
|
*
|
|
* 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.
|
|
*
|
|
**********************************************************************/
|
|
|
|
#pragma once
|
|
|
|
#include <geos/export.h>
|
|
|
|
#include <geos/operation/valid/PolygonTopologyAnalyzer.h>
|
|
#include <geos/operation/valid/TopologyValidationError.h>
|
|
#include <geos/util.h>
|
|
|
|
|
|
// Forward declarations
|
|
namespace geos {
|
|
namespace geom {
|
|
class CoordinateXY;
|
|
class Geometry;
|
|
class Point;
|
|
class MultiPoint;
|
|
class LineString;
|
|
class LinearRing;
|
|
class Polygon;
|
|
class MultiPolygon;
|
|
class GeometryCollection;
|
|
}
|
|
namespace algorithm {
|
|
namespace locate {
|
|
class IndexedPointInAreaLocator;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
namespace geos { // geos.
|
|
namespace operation { // geos.operation
|
|
namespace valid { // geos.operation.valid
|
|
|
|
/**
|
|
* Implements the algorithms required to compute the <code>isValid()</code> method
|
|
* for Geometry.
|
|
* See the documentation for the various geometry types for a specification of validity.
|
|
*
|
|
* @version 1.7
|
|
*/
|
|
class GEOS_DLL IsValidOp {
|
|
|
|
private:
|
|
|
|
static constexpr int MIN_SIZE_LINESTRING = 2;
|
|
static constexpr int MIN_SIZE_RING = 4;
|
|
|
|
/**
|
|
* The geometry being validated
|
|
*/
|
|
const geom::Geometry* inputGeometry;
|
|
/**
|
|
* If the following condition is true GEOS will validate
|
|
* inverted shells and exverted holes (the ESRI SDE model)
|
|
*/
|
|
bool isInvertedRingValid = false;
|
|
std::unique_ptr<TopologyValidationError> validErr;
|
|
|
|
bool hasInvalidError()
|
|
{
|
|
return validErr != nullptr;
|
|
}
|
|
|
|
void logInvalid(int code, const geom::CoordinateXY& pt);
|
|
|
|
bool isValidGeometry(const geom::Geometry* g);
|
|
|
|
/**
|
|
* Tests validity of a Point.
|
|
*/
|
|
bool isValid(const geom::Point* g);
|
|
|
|
/**
|
|
* Tests validity of a MultiPoint.
|
|
*/
|
|
bool isValid(const geom::MultiPoint* g);
|
|
|
|
/**
|
|
* Tests validity of a LineString.
|
|
* Almost anything goes for linestrings!
|
|
*/
|
|
bool isValid(const geom::LineString* g);
|
|
|
|
/**
|
|
* Tests validity of a LinearRing.
|
|
*/
|
|
bool isValid(const geom::LinearRing* g);
|
|
|
|
/**
|
|
* Tests the validity of a polygon.
|
|
* Sets the validErr flag.
|
|
*/
|
|
bool isValid(const geom::Polygon* g);
|
|
|
|
/**
|
|
* Tests validity of a MultiPolygon.
|
|
*
|
|
* @param g
|
|
* @return
|
|
*/
|
|
bool isValid(const geom::MultiPolygon* g);
|
|
|
|
/**
|
|
* Tests validity of a GeometryCollection.
|
|
*
|
|
* @param gc
|
|
* @return
|
|
*/
|
|
bool isValid(const geom::GeometryCollection* gc);
|
|
|
|
void checkCoordinatesValid(const geom::CoordinateSequence* coords);
|
|
void checkCoordinatesValid(const geom::Polygon* poly);
|
|
void checkRingClosed(const geom::LinearRing* ring);
|
|
void checkRingsClosed(const geom::Polygon* poly);
|
|
void checkRingsPointSize(const geom::Polygon* poly);
|
|
void checkRingPointSize(const geom::LinearRing* ring);
|
|
|
|
/**
|
|
* Check the number of non-repeated points is at least a given size.
|
|
*
|
|
* @param line
|
|
* @param minSize
|
|
*/
|
|
void checkTooFewPoints(const geom::LineString* line, std::size_t minSize);
|
|
|
|
/**
|
|
* Test if the number of non-repeated points in a line
|
|
* is at least a given minimum size.
|
|
*
|
|
* @param line the line to test
|
|
* @param minSize the minimum line size
|
|
* @return true if the line has the required number of non-repeated points
|
|
*/
|
|
bool isNonRepeatedSizeAtLeast(const geom::LineString* line, std::size_t minSize);
|
|
|
|
void checkAreaIntersections(PolygonTopologyAnalyzer& areaAnalyzer);
|
|
|
|
/**
|
|
* Check whether a ring self-intersects (except at its endpoints).
|
|
*
|
|
* @param ring the linear ring to check
|
|
*/
|
|
void checkRingSimple(const geom::LinearRing* ring);
|
|
|
|
|
|
/**
|
|
* Tests that each hole is inside the polygon shell.
|
|
* This routine assumes that the holes have previously been tested
|
|
* to ensure that all vertices lie on the shell or on the same side of it
|
|
* (i.e. that the hole rings do not cross the shell ring).
|
|
* Given this, a simple point-in-polygon test of a single point in the hole can be used,
|
|
* provided the point is chosen such that it does not lie on the shell.
|
|
*
|
|
* @param poly the polygon to be tested for hole inclusion
|
|
*/
|
|
void checkHolesInShell(const geom::Polygon* poly);
|
|
|
|
/**
|
|
* Checks if a polygon hole lies inside its shell
|
|
* and if not returns a point indicating this.
|
|
* The hole is known to be wholly inside or outside the shell,
|
|
* so it suffices to find a single point which is interior or exterior,
|
|
* or check the edge topology at a point on the boundary of the shell.
|
|
*
|
|
* @param hole the hole to test
|
|
* @param shell the polygon shell to test against
|
|
* @return a hole point outside the shell, or null if it is inside
|
|
*/
|
|
const CoordinateXY* findHoleOutsideShellPoint(
|
|
const geom::LinearRing* hole,
|
|
const geom::LinearRing* shell);
|
|
|
|
/**
|
|
* Checks if any polygon hole is nested inside another.
|
|
* Assumes that holes do not cross (overlap),
|
|
* This is checked earlier.
|
|
*
|
|
* @param poly the polygon with holes to test
|
|
*/
|
|
void checkHolesNotNested(const geom::Polygon* poly);
|
|
|
|
/**
|
|
* Checks that no element polygon is in the interior of another element polygon.
|
|
*
|
|
* Preconditions:
|
|
*
|
|
* * shells do not partially overlap
|
|
* * shells do not touch along an edge
|
|
* * no duplicate rings exist
|
|
*
|
|
* These have been confirmed by the PolygonTopologyAnalyzer.
|
|
*/
|
|
void checkShellsNotNested(const geom::MultiPolygon* mp);
|
|
|
|
void checkInteriorConnected(PolygonTopologyAnalyzer& areaAnalyzer);
|
|
|
|
|
|
public:
|
|
|
|
/**
|
|
* Creates a new validator for a geometry.
|
|
*
|
|
* @param p_inputGeometry the geometry to validate
|
|
*/
|
|
IsValidOp(const geom::Geometry* p_inputGeometry)
|
|
: inputGeometry(p_inputGeometry)
|
|
, validErr(nullptr)
|
|
{};
|
|
|
|
/**
|
|
* Sets whether polygons using <b>Self-Touching Rings</b> to form
|
|
* holes are reported as valid.
|
|
* If this flag is set, the following Self-Touching conditions
|
|
* are treated as being valid:
|
|
*
|
|
* * the shell ring self-touches to create a hole touching the shell
|
|
* * a hole ring self-touches to create two holes touching at a point
|
|
*
|
|
* The default (following the OGC SFS standard)
|
|
* is that this condition is not valid (false).
|
|
*
|
|
* Self-Touching Rings which disconnect the
|
|
* the polygon interior are still considered to be invalid
|
|
* (these are <b>invalid</b> under the SFS, and many other
|
|
* spatial models as well).
|
|
* This includes:
|
|
*
|
|
* * exverted ("bow-tie") shells which self-touch at a single point
|
|
* * inverted shells with the inversion touching the shell at another point
|
|
* * exverted holes with exversion touching the hole at another point
|
|
* * inverted ("C-shaped") holes which self-touch at a single point causing an island to be formed
|
|
* * inverted shells or exverted holes which form part of a chain of touching rings
|
|
* (which disconnect the interior)
|
|
*
|
|
* @param p_isValid states whether geometry with this condition is valid
|
|
*/
|
|
void setSelfTouchingRingFormingHoleValid(bool p_isValid)
|
|
{
|
|
isInvertedRingValid = p_isValid;
|
|
};
|
|
|
|
/**
|
|
* Tests whether a Geometry is valid.
|
|
* @param geom the Geometry to test
|
|
* @return true if the geometry is valid
|
|
*/
|
|
static bool isValid(const geom::Geometry* geom)
|
|
{
|
|
IsValidOp ivo(geom);
|
|
return ivo.isValid();
|
|
};
|
|
|
|
static bool isValid(const geom::CoordinateXY& coord)
|
|
{
|
|
return isValid(&coord);
|
|
}
|
|
|
|
/**
|
|
* Tests the validity of the input geometry.
|
|
*
|
|
* @return true if the geometry is valid
|
|
*/
|
|
bool isValid();
|
|
|
|
/**
|
|
* Checks whether a coordinate is valid for processing.
|
|
* Coordinates are valid if their x and y ordinates are in the
|
|
* range of the floating point representation.
|
|
*
|
|
* @param coord the coordinate to validate
|
|
* @return <code>true</code> if the coordinate is valid
|
|
*/
|
|
static bool isValid(const geom::CoordinateXY* coord);
|
|
|
|
/**
|
|
* Computes the validity of the geometry,
|
|
* and if not valid returns the validation error for the geometry,
|
|
* or null if the geometry is valid.
|
|
*
|
|
* @return the validation error, if the geometry is invalid
|
|
* or null if the geometry is valid
|
|
*/
|
|
const TopologyValidationError* getValidationError();
|
|
|
|
|
|
};
|
|
|
|
|
|
} // namespace geos.operation.valid
|
|
} // namespace geos.operation
|
|
} // namespace geos
|