Merge pull request #2302 from manisandro/geometry_plugins

Geometry Checker and Geometry Snapper plugins
This commit is contained in:
mhugent 2015-09-25 02:39:50 +02:00
commit 8cec2c935f
144 changed files with 10474 additions and 329 deletions

View File

@ -8,7 +8,7 @@ sudo apt-get update -qq
sudo apt-get install --force-yes --no-install-recommends --no-install-suggests \
bison cmake cmake-data doxygen flex git graphviz \
grass-dev grass7-dev libexpat1-dev libfcgi-dev \
libgdal1-dev libgeos-dev libgsl0-dev libpq-dev \
libgdal1-dev libgeos-dev libgeos++-dev libgsl0-dev libpq-dev \
libproj-dev libqscintilla2-dev libqt4-dev \
libqt4-opengl-dev libqt4-sql-sqlite libqtwebkit-dev \
libqwt-dev libspatialindex-dev libspatialite-dev \

View File

@ -23,11 +23,16 @@ IF(WIN32)
IF (MINGW)
FIND_PATH(GEOS_INCLUDE_DIR geos_c.h /usr/local/include /usr/include c:/msys/local/include)
FIND_LIBRARY(GEOS_LIBRARY NAMES geos_c PATHS /usr/local/lib /usr/lib c:/msys/local/lib)
FIND_LIBRARY(GEOS_CPP_LIBRARY NAMES geos PATHS /usr/local/lib /usr/lib c:/msys/local/lib)
ENDIF (MINGW)
IF (MSVC)
FIND_PATH(GEOS_INCLUDE_DIR geos_c.h $ENV{LIB_DIR}/include $ENV{INCLUDE})
FIND_LIBRARY(GEOS_LIBRARY NAMES geos geos_c_i geos_c PATHS
FIND_LIBRARY(GEOS_LIBRARY NAMES geos_c_i geos_c PATHS
"$ENV{LIB_DIR}/lib"
$ENV{LIB}
)
FIND_LIBRARY(GEOS_CPP_LIBRARY NAMES geos PATHS
"$ENV{LIB_DIR}/lib"
$ENV{LIB}
)
@ -67,6 +72,7 @@ ELSE(WIN32)
IF(CYGWIN)
FIND_LIBRARY(GEOS_LIBRARY NAMES geos_c PATHS /usr/lib /usr/local/lib)
FIND_LIBRARY(GEOS_CPP_LIBRARY NAMES geos PATHS /usr/lib /usr/local/lib)
ENDIF(CYGWIN)
IF (NOT GEOS_INCLUDE_DIR OR NOT GEOS_LIBRARY OR NOT GEOS_CONFIG)
@ -132,12 +138,16 @@ ELSE(WIN32)
#MESSAGE("DBG GEOS_CONFIG_LIBS=${GEOS_CONFIG_LIBS}")
#MESSAGE("DBG GEOS_LIB_NAME_WITH_PREFIX=${GEOS_LIB_NAME_WITH_PREFIX}")
SET(GEOS_LIB_NAME_WITH_PREFIX -lgeos_c CACHE STRING INTERNAL)
SET(GEOS_CPP_LIB_NAME_WITH_PREFIX -lgeos CACHE STRING INTERNAL)
## remove prefix -l because we need the pure name
IF (GEOS_LIB_NAME_WITH_PREFIX)
STRING(REGEX REPLACE "[-][l]" "" GEOS_LIB_NAME ${GEOS_LIB_NAME_WITH_PREFIX} )
ENDIF (GEOS_LIB_NAME_WITH_PREFIX)
IF (GEOS_CPP_LIB_NAME_WITH_PREFIX)
STRING(REGEX REPLACE "[-][l]" "" GEOS_CPP_LIB_NAME ${GEOS_CPP_LIB_NAME_WITH_PREFIX} )
ENDIF (GEOS_CPP_LIB_NAME_WITH_PREFIX)
#MESSAGE("DBG GEOS_LIB_NAME=${GEOS_LIB_NAME}")
IF (APPLE)
@ -146,9 +156,11 @@ ELSE(WIN32)
# while still preserving user setting if given
# ***FIXME*** need to improve framework check so below not needed
SET(GEOS_LIBRARY ${GEOS_LINK_DIRECTORIES}/lib${GEOS_LIB_NAME}.dylib CACHE STRING INTERNAL FORCE)
SET(GEOS_CPP_LIBRARY ${GEOS_LINK_DIRECTORIES}/lib${GEOS_CPP_LIB_NAME}.dylib CACHE STRING INTERNAL FORCE)
ENDIF (NOT GEOS_LIBRARY)
ELSE (APPLE)
SET(GEOS_LIBRARY ${GEOS_LINK_DIRECTORIES}/lib${GEOS_LIB_NAME}.so CACHE STRING INTERNAL)
SET(GEOS_CPP_LIBRARY ${GEOS_LINK_DIRECTORIES}/lib${GEOS_CPP_LIB_NAME}.so CACHE STRING INTERNAL)
ENDIF (APPLE)
#MESSAGE("DBG GEOS_LIBRARY=${GEOS_LIBRARY}")

View File

@ -11,9 +11,16 @@ struct QgsVertexId
};
QgsVertexId();
QgsVertexId( int _part, int _ring, int _vertex, VertexType _type );
QgsVertexId( int _part, int _ring, int _vertex, VertexType _type);
bool isValid() const;
bool operator==( const QgsVertexId& other ) const;
bool operator!=( const QgsVertexId& other ) const;
bool partEqual( const QgsVertexId& o ) const;
bool ringEqual( const QgsVertexId& o ) const;
bool vertexEqual( const QgsVertexId& o ) const;
bool isValid(const QgsAbstractGeometryV2* geom) const;
int part;
int ring;
int vertex;
@ -101,7 +108,7 @@ class QgsAbstractGeometryV2
virtual void coordinateSequence( QList< QList< QList< QgsPointV2 > > >& coord /Out/ ) const = 0;
int nCoordinates() const;
QgsPointV2 vertexAt( const QgsVertexId& id ) const;
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const = 0;
virtual double closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const = 0;
//low-level editing
@ -113,6 +120,9 @@ class QgsAbstractGeometryV2
virtual double length() const;
virtual double area() const;
/** Returns the centroid of the geometry*/
virtual QgsPointV2 centroid() const;
virtual bool hasCurvedSegments() const;
/** Returns a geometry without curves. Caller takes ownership*/
virtual QgsAbstractGeometryV2* segmentize() const /Factory/;
@ -120,4 +130,8 @@ class QgsAbstractGeometryV2
/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@return rotation in radians, clockwise from north*/
virtual double vertexAngle( const QgsVertexId& vertex ) const = 0;
virtual int vertexCount(int part = 0, int ring = 0) const = 0;
virtual int ringCount(int part = 0) const = 0;
virtual int partCount() const = 0;
};

View File

@ -10,7 +10,7 @@ class QgsCircularStringV2: public QgsCurveV2
virtual QString geometryType() const;
virtual int dimension() const;
virtual QgsAbstractGeometryV2* clone() const;
virtual QgsCircularStringV2* clone() const;
virtual void clear();
virtual QgsRectangle calculateBoundingBox() const;

View File

@ -12,7 +12,7 @@ class QgsCompoundCurveV2: public QgsCurveV2
virtual QString geometryType() const;
virtual int dimension() const;
virtual QgsAbstractGeometryV2* clone() const;
virtual QgsCompoundCurveV2* clone() const;
virtual void clear();
virtual QgsRectangle calculateBoundingBox() const;

View File

@ -12,7 +12,7 @@ class QgsCurvePolygonV2: public QgsSurfaceV2
virtual QString geometryType() const;
virtual int dimension() const;
virtual QgsAbstractGeometryV2* clone() const;
virtual QgsCurvePolygonV2* clone() const;
void clear();
@ -30,14 +30,13 @@ class QgsCurvePolygonV2: public QgsSurfaceV2
//surface interface
virtual double area() const;
virtual double length() const;
QgsPointV2 centroid() const;
QgsPointV2 pointOnSurface() const;
QgsPolygonV2* surfaceToPolygon() const;
//curve polygon interface
int numInteriorRings() const;
const QgsCurveV2* exteriorRing() const;
const QgsCurveV2* interiorRing( int i ) const;
QgsCurveV2* exteriorRing() const;
QgsCurveV2* interiorRing( int i ) const;
virtual QgsPolygonV2* toPolygon() const;
/** Sets exterior ring (takes ownership)*/
@ -67,4 +66,8 @@ class QgsCurvePolygonV2: public QgsSurfaceV2
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const;
virtual int vertexCount(int part = 0, int ring = 0) const;
virtual int ringCount(int part = 0) const;
virtual int partCount() const;
};

View File

@ -26,4 +26,9 @@ class QgsCurveV2: public QgsAbstractGeometryV2
/** Returns a geometry without curves. Caller takes ownership*/
QgsAbstractGeometryV2* segmentize() const /Factory/;
virtual int vertexCount(int part = 0, int ring = 0) const;
virtual int ringCount(int part = 0) const;
virtual int partCount() const;
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const;
};

View File

@ -56,7 +56,7 @@ class QgsGeometry
* @note added in QGIS 2.10
* @see setGeometry
*/
const QgsAbstractGeometryV2* geometry() const;
QgsAbstractGeometryV2* geometry() const;
/** Sets the underlying geometry store. Ownership of geometry is transferred.
* @note added in QGIS 2.10

View File

@ -10,6 +10,8 @@ class QgsGeometryCollectionV2: public QgsAbstractGeometryV2
//QgsGeometryCollectionV2& operator=( const QgsGeometryCollectionV2& c );
virtual ~QgsGeometryCollectionV2();
virtual QgsGeometryCollectionV2* clone() const;
int numGeometries() const;
//const QgsAbstractGeometryV2* geometryN( int n ) const;
QgsAbstractGeometryV2* geometryN( int n );
@ -63,4 +65,9 @@ class QgsGeometryCollectionV2: public QgsAbstractGeometryV2
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const;
virtual int vertexCount(int part = 0, int ring = 0) const;
virtual int ringCount(int part = 0) const;
virtual int partCount() const;
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const;
};

View File

@ -11,31 +11,32 @@ class QgsGeometryEngine
virtual void geometryChanged() = 0;
virtual void prepareGeometry() = 0;
virtual QgsAbstractGeometryV2* intersection( const QgsAbstractGeometryV2& geom ) const = 0;
virtual QgsAbstractGeometryV2* difference( const QgsAbstractGeometryV2& geom ) const = 0;
virtual QgsAbstractGeometryV2* combine( const QgsAbstractGeometryV2& geom ) const = 0;
virtual QgsAbstractGeometryV2* combine( const QList< const QgsAbstractGeometryV2* > ) const = 0;
virtual QgsAbstractGeometryV2* symDifference( const QgsAbstractGeometryV2& geom ) const = 0;
virtual QgsAbstractGeometryV2* buffer( double distance, int segments ) const = 0;
virtual QgsAbstractGeometryV2* buffer( double distance, int segments, int endCapStyle, int joinStyle, double mitreLimit ) const = 0;
virtual QgsAbstractGeometryV2* simplify( double tolerance ) const = 0;
virtual QgsAbstractGeometryV2* interpolate( double distance ) const = 0;
virtual bool centroid( QgsPointV2& pt ) const = 0;
virtual bool pointOnSurface( QgsPointV2& pt ) const = 0;
virtual QgsAbstractGeometryV2* convexHull() const = 0;
virtual double distance( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool intersects( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool touches( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool crosses( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool within( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool overlaps( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool contains( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool disjoint( const QgsAbstractGeometryV2& geom ) const = 0;
virtual double area() const = 0;
virtual double length() const = 0;
virtual bool isValid() const = 0;
virtual bool isEqual( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool isEmpty() const = 0;
virtual QgsAbstractGeometryV2* intersection( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* difference( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* combine( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* combine( const QList< const QgsAbstractGeometryV2* >, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* symDifference( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* buffer( double distance, int segments, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* buffer( double distance, int segments, int endCapStyle, int joinStyle, double mitreLimit, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* simplify( double tolerance, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* interpolate( double distance, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* envelope( QString* errorMsg = 0 ) const = 0;
virtual bool centroid( QgsPointV2& pt, QString* errorMsg = 0 ) const = 0;
virtual bool pointOnSurface( QgsPointV2& pt, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* convexHull( QString* errorMsg = 0 ) const = 0;
virtual double distance( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool intersects( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool touches( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool crosses( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool within( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool overlaps( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool contains( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool disjoint( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual double area( QString* errorMsg = 0 ) const = 0;
virtual double length( QString* errorMsg = 0 ) const = 0;
virtual bool isValid( QString* errorMsg = 0 ) const = 0;
virtual bool isEqual( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool isEmpty( QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* offsetCurve( double distance, int segments, int joinStyle, double mitreLimit ) const = 0;
virtual QgsAbstractGeometryV2* offsetCurve( double distance, int segments, int joinStyle, double mitreLimit, QString* errorMsg = 0 ) const = 0;
};

View File

@ -10,7 +10,7 @@ class QgsLineStringV2: public QgsCurveV2
virtual QString geometryType() const;
virtual int dimension() const;
virtual QgsAbstractGeometryV2* clone() const;
virtual QgsLineStringV2* clone() const;
virtual void clear();
virtual bool fromWkb( const unsigned char* wkb );

View File

@ -6,7 +6,7 @@ class QgsMultiCurveV2: public QgsGeometryCollectionV2
public:
virtual QString geometryType() const;
QgsAbstractGeometryV2* clone() const;
virtual QgsMultiCurveV2* clone() const;
bool fromWkt( const QString& wkt );

View File

@ -6,7 +6,7 @@ class QgsMultiLineStringV2: public QgsMultiCurveV2
public:
virtual QString geometryType() const;
QgsAbstractGeometryV2* clone() const;
virtual QgsMultiLineStringV2* clone() const;
bool fromWkt( const QString& wkt );

View File

@ -5,7 +5,7 @@ class QgsMultiPointV2: public QgsGeometryCollectionV2
%End
public:
virtual QString geometryType() const;
QgsAbstractGeometryV2* clone() const;
virtual QgsMultiPointV2* clone() const;
bool fromWkt( const QString& wkt );

View File

@ -5,7 +5,7 @@ class QgsMultiPolygonV2: public QgsMultiSurfaceV2
%End
public:
virtual QString geometryType() const;
QgsAbstractGeometryV2* clone() const;
virtual QgsMultiPolygonV2* clone() const;
bool fromWkt( const QString& wkt );

View File

@ -5,7 +5,7 @@ class QgsMultiSurfaceV2: public QgsGeometryCollectionV2
%End
public:
virtual QString geometryType() const;
QgsAbstractGeometryV2* clone() const;
virtual QgsMultiSurfaceV2* clone() const;
bool fromWkt( const QString& wkt );

View File

@ -6,12 +6,13 @@ class QgsPointV2: public QgsAbstractGeometryV2
public:
QgsPointV2( double x = 0.0, double y = 0.0 );
QgsPointV2( const QgsPoint& p );
QgsPointV2( QgsWKBTypes::Type type, double x = 0.0, double y = 0.0, double z = 0.0, double m = 0.0 );
bool operator==( const QgsPointV2& pt ) const;
bool operator!=( const QgsPointV2& pt ) const;
virtual QgsAbstractGeometryV2* clone() const;
virtual QgsPointV2* clone() const;
void clear();
double x() const;
@ -60,4 +61,9 @@ class QgsPointV2: public QgsAbstractGeometryV2
@param vertex the vertex id
@return 0.0*/
double vertexAngle( const QgsVertexId& vertex ) const;
virtual int vertexCount(int part = 0, int ring = 0) const;
virtual int ringCount(int part = 0) const;
virtual int partCount() const;
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const;
};

View File

@ -6,7 +6,7 @@ class QgsPolygonV2: public QgsCurvePolygonV2
public:
virtual QString geometryType() const;
virtual QgsAbstractGeometryV2* clone() const;
virtual QgsPolygonV2* clone() const;
virtual bool fromWkb( const unsigned char* wkb );
// inherited: bool fromWkt( const QString &wkt );

View File

@ -5,7 +5,6 @@ class QgsSurfaceV2: public QgsAbstractGeometryV2
%End
public:
virtual QgsPointV2 centroid() const = 0;
virtual QgsPointV2 pointOnSurface() const = 0;
virtual QgsPolygonV2* surfaceToPolygon() const = 0;
};

View File

@ -139,6 +139,13 @@ class QgsMapSettings
const QgsMapToPixel& mapToPixel() const;
/** Computes an *estimated* conversion factor between layer and map units: layerUnits * layerToMapUnits = mapUnits
* @param theLayer The layer
* @param referenceExtent A reference extent based on which to perform the computation. If not specified, the layer extent is used
* @note added in QGIS 2.12
*/
double layerToMapUnits( QgsMapLayer* theLayer, const QgsRectangle& referenceExtent = QgsRectangle() ) const;
/**
* @brief transform bounding box from layer's CRS to output CRS
* @see layerToMapCoordinates( QgsMapLayer* theLayer, QgsRectangle rect ) if you want to transform a rectangle

View File

@ -58,6 +58,10 @@ class QgsRectangle
//! Scale the rectangle around its center point
void scale( double scaleFactor, const QgsPoint *c = 0 );
void scale( double scaleFactor, double centerX, double centerY );
//! Grow the rectangle by the specified amount
void grow( double delta );
/** Updates the rectangle to include the specified point */
void include( const QgsPoint& p );
/** Get rectangle enlarged by buffer.
* @note added in 2.1 */
QgsRectangle buffer( double width );

View File

@ -252,7 +252,7 @@ class QgisInterface : QObject
virtual void showLayerProperties( QgsMapLayer *l ) = 0;
/** Open attribute table dialog */
virtual void showAttributeTable( QgsVectorLayer *l ) = 0;
virtual QDialog* showAttributeTable( QgsVectorLayer *l, const QString& filterExpression = QString() ) = 0;
/** Add window to Window menu. The action title is the window title
* and the action should raise, unminimize and activate the window. */

View File

@ -429,13 +429,16 @@ void QgisAppInterface::showLayerProperties( QgsMapLayer *l )
}
}
void QgisAppInterface::showAttributeTable( QgsVectorLayer *l )
QDialog* QgisAppInterface::showAttributeTable( QgsVectorLayer *l, const QString& filterExpression )
{
if ( l )
{
QgsAttributeTableDialog *dialog = new QgsAttributeTableDialog( l );
dialog->setFilterExpression( filterExpression );
dialog->show();
return dialog;
}
return 0;
}
void QgisAppInterface::addWindow( QAction *action )

View File

@ -267,7 +267,7 @@ class APP_EXPORT QgisAppInterface : public QgisInterface
/** Show layer attribute dialog for layer
* @param l layer to show attribute table for
*/
virtual void showAttributeTable( QgsVectorLayer *l ) override;
virtual QDialog* showAttributeTable( QgsVectorLayer *l, const QString& filterExpression = QString() ) override;
/** Add window to Window menu. The action title is the window title
* and the action should raise, unminimize and activate the window. */

View File

@ -3,6 +3,8 @@
SET(QGIS_CORE_SRCS
geosextra/geos_c_extra.cpp
gps/qgsgpsconnection.cpp
gps/qgsgpsconnectionregistry.cpp
gps/qgsnmeaconnection.cpp
@ -735,6 +737,12 @@ SET(QGIS_CORE_HDRS
geometry/qgspointv2.h
)
IF(GEOS_CPP_LIBRARY)
SET(QGIS_CORE_SRCS ${QGIS_CORE_SRCS} geosextra/geos_c_extra.cpp)
SET(QGIS_CORE_HDRS ${QGIS_CORE_HDRS} geosextra/geos_c_extra.h)
ADD_DEFINITIONS("-DHAVE_GEOS_CPP")
ENDIF(GEOS_CPP_LIBRARY)
IF (QT_MOBILITY_LOCATION_FOUND OR Qt5Positioning_FOUND)
SET(QGIS_CORE_HDRS ${QGIS_CORE_HDRS}
gps/qgsqtlocationconnection.h
@ -847,6 +855,7 @@ TARGET_LINK_LIBRARIES(qgis_core
${PROJ_LIBRARY}
${GEOS_LIBRARY}
${GEOS_CPP_LIBRARY}
${GDAL_LIBRARY}
${SPATIALINDEX_LIBRARY}
${EXPAT_LIBRARY}

View File

@ -129,28 +129,6 @@ QgsRectangle QgsAbstractGeometryV2::calculateBoundingBox() const
return QgsRectangle( xmin, ymin, xmax, ymax );
}
QgsPointV2 QgsAbstractGeometryV2::vertexAt( const QgsVertexId& id ) const
{
QList< QList< QList< QgsPointV2 > > > coordinates;
coordinateSequence( coordinates );
if ( id.part >= coordinates.size() )
{
return QgsPointV2();
}
const QList< QList< QgsPointV2 > >& part = coordinates.at( id.part );
if ( id.ring >= part.size() )
{
return QgsPointV2();
}
const QList< QgsPointV2 >& ring = part.at( id.ring );
if ( id.vertex >= ring.size() )
{
return QgsPointV2();
}
return ring.at( id.vertex );
}
int QgsAbstractGeometryV2::nCoordinates() const
{
QList< QList< QList< QgsPointV2 > > > coordinates;
@ -204,6 +182,55 @@ bool QgsAbstractGeometryV2::readWkbHeader( QgsConstWkbPtr& wkbPtr, QgsWKBTypes::
return true;
}
QgsPointV2 QgsAbstractGeometryV2::centroid() const
{
// http://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
// Pick the first ring of first part for the moment
int n = vertexCount( 0, 0 );
if ( n == 1 )
{
return vertexAt( QgsVertexId( 0, 0, 0 ) );
}
double A = 0.;
double Cx = 0.;
double Cy = 0.;
int i = 0, j = 1;
if ( vertexAt( QgsVertexId( 0, 0, 0 ) ) != vertexAt( QgsVertexId( 0, 0, n - 1 ) ) )
{
i = n - 1;
j = 0;
}
for ( ; j < n; i = j++ )
{
QgsPointV2 vi = vertexAt( QgsVertexId( 0, 0, i ) );
QgsPointV2 vj = vertexAt( QgsVertexId( 0, 0, j ) );
double d = vi.x() * vj.y() - vj.x() * vi.y();
A += d;
Cx += ( vi.x() + vj.x() ) * d;
Cy += ( vi.y() + vj.y() ) * d;
}
if ( A < 1E-12 )
{
Cx = Cy = 0.;
for ( int i = 0; i < n - 1; ++i )
{
QgsPointV2 vi = vertexAt( QgsVertexId( 0, 0, i ) );
Cx += vi.x();
Cy += vi.y();
}
return QgsPointV2( Cx / ( n - 1 ), Cy / ( n - 1 ) );
}
else
{
return QgsPointV2( Cx / ( 3. * A ), Cy / ( 3. * A ) );
}
return QgsPointV2();
}
bool QgsAbstractGeometryV2::isEmpty() const
{
QgsVertexId vId; QgsPointV2 vertex;

View File

@ -28,45 +28,10 @@ class QgsMultiCurveV2;
class QgsMultiPointV2;
class QgsPointV2;
class QgsConstWkbPtr;
struct QgsVertexId;
class QgsWkbPtr;
class QPainter;
/** \ingroup core
* \class QgsVertexId
* \brief Utility class for identifying a unique vertex within a geometry.
* \note added in QGIS 2.10
* \note this API is not considered stable and may change for 2.12
*/
struct CORE_EXPORT QgsVertexId
{
enum VertexType
{
SegmentVertex = 1, //start / endpoint of a segment
CurveVertex
};
QgsVertexId(): part( - 1 ), ring( -1 ), vertex( -1 ), type( SegmentVertex ) {}
QgsVertexId( int _part, int _ring, int _vertex, VertexType _type = SegmentVertex )
: part( _part ), ring( _ring ), vertex( _vertex ), type( _type ) {}
/** Returns true if the vertex id is valid
*/
bool isValid() const { return part >= 0 && ring >= 0 && vertex >= 0; }
bool operator==( const QgsVertexId& other )
{
return part == other.part && ring == other.ring && vertex == other.vertex;
}
bool operator!=( const QgsVertexId& other )
{
return part != other.part || ring != other.ring || vertex != other.vertex;
}
int part;
int ring;
int vertex;
VertexType type;
};
/** \ingroup core
* \class QgsAbstractGeometryV2
@ -254,7 +219,7 @@ class CORE_EXPORT QgsAbstractGeometryV2
/** Returns the point corresponding to a specified vertex id
*/
QgsPointV2 vertexAt( const QgsVertexId& id ) const;
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const = 0;
/** Searches for the closest segment of the geometry to a given point.
* @param pt Specifies the point for search
@ -304,6 +269,9 @@ class CORE_EXPORT QgsAbstractGeometryV2
*/
virtual double area() const { return 0.0; }
/** Returns the centroid of the geometry */
virtual QgsPointV2 centroid() const;
/** Returns true if the geometry is empty
*/
bool isEmpty() const;
@ -322,6 +290,10 @@ class CORE_EXPORT QgsAbstractGeometryV2
@return rotation in radians, clockwise from north*/
virtual double vertexAngle( const QgsVertexId& vertex ) const = 0;
virtual int vertexCount( int part = 0, int ring = 0 ) const = 0;
virtual int ringCount( int part = 0 ) const = 0;
virtual int partCount() const = 0;
protected:
QgsWKBTypes::Type mWkbType;
mutable QgsRectangle mBoundingBox;
@ -341,4 +313,58 @@ class CORE_EXPORT QgsAbstractGeometryV2
};
/** \ingroup core
* \class QgsVertexId
* \brief Utility class for identifying a unique vertex within a geometry.
* \note added in QGIS 2.10
*/
struct CORE_EXPORT QgsVertexId
{
enum VertexType
{
SegmentVertex = 1, //start / endpoint of a segment
CurveVertex
};
QgsVertexId( int _part = -1, int _ring = -1, int _vertex = -1, VertexType _type = SegmentVertex )
: part( _part ), ring( _ring ), vertex( _vertex ), type( _type ) {}
/** Returns true if the vertex id is valid
*/
bool isValid() const { return part >= 0 && ring >= 0 && vertex >= 0; }
bool operator==( const QgsVertexId& other ) const
{
return part == other.part && ring == other.ring && vertex == other.vertex;
}
bool operator!=( const QgsVertexId& other ) const
{
return part != other.part || ring != other.ring || vertex != other.vertex;
}
bool partEqual( const QgsVertexId& o ) const
{
return part >= 0 && o.part == part;
}
bool ringEqual( const QgsVertexId& o ) const
{
return partEqual( o ) && ( ring >= 0 && o.ring == ring );
}
bool vertexEqual( const QgsVertexId& o ) const
{
return ringEqual( o ) && ( vertex >= 0 && o.ring == ring );
}
bool isValid( const QgsAbstractGeometryV2* geom ) const
{
return ( part >= 0 && part < geom->partCount() ) &&
( ring < geom->ringCount( part ) ) &&
( vertex < 0 || vertex < geom->vertexCount( part, ring ) );
}
int part;
int ring;
int vertex;
VertexType type;
};
#endif //QGSABSTRACTGEOMETRYV2

View File

@ -36,7 +36,7 @@ QgsCircularStringV2::~QgsCircularStringV2()
}
QgsAbstractGeometryV2 *QgsCircularStringV2::clone() const
QgsCircularStringV2 *QgsCircularStringV2::clone() const
{
return new QgsCircularStringV2( *this );
}

View File

@ -35,7 +35,7 @@ class CORE_EXPORT QgsCircularStringV2: public QgsCurveV2
virtual QString geometryType() const override { return "CircularString"; }
virtual int dimension() const override { return 1; }
virtual QgsAbstractGeometryV2* clone() const override;
virtual QgsCircularStringV2* clone() const override;
virtual void clear() override;
virtual QgsRectangle calculateBoundingBox() const override;

View File

@ -56,7 +56,7 @@ QgsCompoundCurveV2& QgsCompoundCurveV2::operator=( const QgsCompoundCurveV2 & cu
return *this;
}
QgsAbstractGeometryV2 *QgsCompoundCurveV2::clone() const
QgsCompoundCurveV2 *QgsCompoundCurveV2::clone() const
{
return new QgsCompoundCurveV2( *this );
}

View File

@ -36,7 +36,7 @@ class CORE_EXPORT QgsCompoundCurveV2: public QgsCurveV2
virtual QString geometryType() const override { return "CompoundCurve"; }
virtual int dimension() const override { return 1; }
virtual QgsAbstractGeometryV2* clone() const override;
virtual QgsCompoundCurveV2* clone() const override;
virtual void clear() override;
virtual QgsRectangle calculateBoundingBox() const override;

View File

@ -67,7 +67,7 @@ QgsCurvePolygonV2& QgsCurvePolygonV2::operator=( const QgsCurvePolygonV2 & p )
return *this;
}
QgsAbstractGeometryV2* QgsCurvePolygonV2::clone() const
QgsCurvePolygonV2* QgsCurvePolygonV2::clone() const
{
return new QgsCurvePolygonV2( *this );
}
@ -348,11 +348,6 @@ double QgsCurvePolygonV2::length() const
return length;
}
QgsPointV2 QgsCurvePolygonV2::centroid() const
{
return QgsPointV2( 0, 0 );
}
QgsPointV2 QgsCurvePolygonV2::pointOnSurface() const
{
return QgsPointV2( 0, 0 );
@ -396,12 +391,12 @@ int QgsCurvePolygonV2::numInteriorRings() const
return mInteriorRings.size();
}
const QgsCurveV2* QgsCurvePolygonV2::exteriorRing() const
QgsCurveV2* QgsCurvePolygonV2::exteriorRing() const
{
return mExteriorRing;
}
const QgsCurveV2* QgsCurvePolygonV2::interiorRing( int i ) const
QgsCurveV2* QgsCurvePolygonV2::interiorRing( int i ) const
{
if ( i >= mInteriorRings.size() )
{
@ -692,3 +687,13 @@ double QgsCurvePolygonV2::vertexAngle( const QgsVertexId& vertex ) const
QgsCurveV2* ring = vertex.ring == 0 ? mExteriorRing : mInteriorRings[vertex.ring - 1];
return ring->vertexAngle( vertex );
}
int QgsCurvePolygonV2::vertexCount( int /*part*/, int ring ) const
{
return ring == 0 ? mExteriorRing->vertexCount() : mInteriorRings[ring - 1]->vertexCount();
}
QgsPointV2 QgsCurvePolygonV2::vertexAt( const QgsVertexId& id ) const
{
return id.ring == 0 ? mExteriorRing->vertexAt( id ) : mInteriorRings[id.ring - 1]->vertexAt( id );
}

View File

@ -38,7 +38,7 @@ class CORE_EXPORT QgsCurvePolygonV2: public QgsSurfaceV2
virtual QString geometryType() const override { return "CurvePolygon"; }
virtual int dimension() const override { return 2; }
virtual QgsAbstractGeometryV2* clone() const override;
virtual QgsCurvePolygonV2* clone() const override;
void clear() override;
@ -56,14 +56,13 @@ class CORE_EXPORT QgsCurvePolygonV2: public QgsSurfaceV2
//surface interface
virtual double area() const override;
virtual double length() const override;
QgsPointV2 centroid() const override;
QgsPointV2 pointOnSurface() const override;
QgsPolygonV2* surfaceToPolygon() const override;
//curve polygon interface
int numInteriorRings() const;
const QgsCurveV2* exteriorRing() const;
const QgsCurveV2* interiorRing( int i ) const;
QgsCurveV2* exteriorRing() const;
QgsCurveV2* interiorRing( int i ) const;
virtual QgsPolygonV2* toPolygon() const;
/** Sets exterior ring (takes ownership)*/
@ -99,6 +98,11 @@ class CORE_EXPORT QgsCurvePolygonV2: public QgsSurfaceV2
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const override;
virtual int vertexCount( int /*part*/ = 0, int ring = 0 ) const override;
virtual int ringCount( int /*part*/ = 0 ) const override { return ( mExteriorRing != 0 ) + mInteriorRings.size(); }
virtual int partCount() const override { return ringCount() > 0; }
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const override;
protected:
QgsCurveV2* mExteriorRing;

View File

@ -85,3 +85,11 @@ QgsAbstractGeometryV2* QgsCurveV2::segmentize() const
{
return curveToLine();
}
QgsPointV2 QgsCurveV2::vertexAt( const QgsVertexId& id ) const
{
QgsPointV2 v;
QgsVertexId::VertexType type;
pointAt( id.vertex, v, type );
return v;
}

View File

@ -90,6 +90,11 @@ class CORE_EXPORT QgsCurveV2: public QgsAbstractGeometryV2
virtual bool pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const = 0;
QgsAbstractGeometryV2* segmentize() const override;
virtual int vertexCount( int /*part*/ = 0, int /*ring*/ = 0 ) const override { return numPoints(); }
virtual int ringCount( int /*part*/ = 0 ) const override { return numPoints() > 0; }
virtual int partCount() const override { return numPoints() > 0; }
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const override;
};
#endif // QGSCURVEV2_H

View File

@ -134,7 +134,7 @@ void QgsGeometry::removeWkbGeos()
}
}
const QgsAbstractGeometryV2* QgsGeometry::geometry() const
QgsAbstractGeometryV2* QgsGeometry::geometry() const
{
if ( !d )
{

View File

@ -23,6 +23,7 @@ email : morb at ozemail dot com dot au
#include "qgis.h"
#include <geos_c.h>
#include <climits>
#if defined(GEOS_VERSION_MAJOR) && (GEOS_VERSION_MAJOR<3)
#define GEOSGeometry struct GEOSGeom_t
@ -99,7 +100,7 @@ class CORE_EXPORT QgsGeometry
* @note added in QGIS 2.10
* @see setGeometry
*/
const QgsAbstractGeometryV2* geometry() const;
QgsAbstractGeometryV2* geometry() const;
/** Sets the underlying geometry store. Ownership of geometry is transferred.
* @note added in QGIS 2.10
@ -684,7 +685,7 @@ class CORE_EXPORT QgsGeometry
* points are equal within the specified tolerance
* @note added in QGIS 2.9
*/
static bool compare( const QgsPolyline& p1, const QgsPolyline& p2, double epsilon = 4 * DBL_EPSILON );
static bool compare( const QgsPolyline& p1, const QgsPolyline& p2, double epsilon = 4 * std::numeric_limits<double>::epsilon() );
/** Compares two polygons for equality within a specified tolerance.
* @param p1 first polygon
@ -694,7 +695,7 @@ class CORE_EXPORT QgsGeometry
* number of points and all points are equal within the specified tolerance
* @note added in QGIS 2.9
*/
static bool compare( const QgsPolygon& p1, const QgsPolygon& p2, double epsilon = 4 * DBL_EPSILON );
static bool compare( const QgsPolygon& p1, const QgsPolygon& p2, double epsilon = 4 * std::numeric_limits<double>::epsilon() );
/** Compares two multipolygons for equality within a specified tolerance.
* @param p1 first multipolygon
@ -705,7 +706,7 @@ class CORE_EXPORT QgsGeometry
* tolerance
* @note added in QGIS 2.9
*/
static bool compare( const QgsMultiPolygon& p1, const QgsMultiPolygon& p2, double epsilon = 4 * DBL_EPSILON );
static bool compare( const QgsMultiPolygon& p1, const QgsMultiPolygon& p2, double epsilon = 4 * std::numeric_limits<double>::epsilon() );
/** Smooths a geometry by rounding off corners using the Chaikin algorithm. This operation
* roughly doubles the number of vertices in a geometry.

View File

@ -59,7 +59,7 @@ QgsGeometryCollectionV2::~QgsGeometryCollectionV2()
clear();
}
QgsAbstractGeometryV2* QgsGeometryCollectionV2::clone() const
QgsGeometryCollectionV2 *QgsGeometryCollectionV2::clone() const
{
return new QgsGeometryCollectionV2( *this );
}
@ -122,6 +122,7 @@ bool QgsGeometryCollectionV2::removeGeometry( int nr )
{
return false;
}
delete mGeometries[nr];
mGeometries.remove( nr );
return true;
}

View File

@ -17,6 +17,7 @@ email : marco.hugentobler at sourcepole dot com
#define QGSGEOMETRYCOLLECTIONV2_H
#include "qgsabstractgeometryv2.h"
#include "qgspointv2.h"
#include <QVector>
/** \ingroup core
@ -33,7 +34,7 @@ class CORE_EXPORT QgsGeometryCollectionV2: public QgsAbstractGeometryV2
QgsGeometryCollectionV2& operator=( const QgsGeometryCollectionV2& c );
virtual ~QgsGeometryCollectionV2();
virtual QgsAbstractGeometryV2* clone() const override;
virtual QgsGeometryCollectionV2* clone() const override;
/** Returns the number of geometries within the collection.
*/
@ -113,6 +114,11 @@ class CORE_EXPORT QgsGeometryCollectionV2: public QgsAbstractGeometryV2
@return rotation in radians, clockwise from north*/
double vertexAngle( const QgsVertexId& vertex ) const override;
virtual int vertexCount( int part = 0, int ring = 0 ) const override { return mGeometries[part]->vertexCount( 0, ring ); }
virtual int ringCount( int part = 0 ) const override { return mGeometries[part]->ringCount(); }
virtual int partCount() const override { return mGeometries.size(); }
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const override { return mGeometries[id.part]->vertexAt( id ); }
protected:
QVector< QgsAbstractGeometryV2* > mGeometries;

View File

@ -134,7 +134,7 @@ int QgsGeometryEditUtils::addPart( QgsAbstractGeometryV2* geom, QgsAbstractGeome
int i;
int n = geomCollection->numGeometries();
for ( i = 0; i < parts->numGeometries() && geomCollection->addGeometry( parts->geometryN( i ) ); i++ )
for ( i = 0; i < parts->numGeometries() && geomCollection->addGeometry( parts->geometryN( i )->clone() ); i++ )
;
added = i == parts->numGeometries();
@ -145,11 +145,6 @@ int QgsGeometryEditUtils::addPart( QgsAbstractGeometryV2* geom, QgsAbstractGeome
delete part; return 2;
}
while ( parts->numGeometries() > 0 )
{
parts->removeGeometry( 0 );
}
delete part;
}
else

View File

@ -23,7 +23,6 @@ email : marco.hugentobler at sourcepole dot com
class QgsAbstractGeometryV2;
/** \ingroup core
* \class QgsGeometryEngine
* \brief Contains geometry relation and modification algorithms.
@ -39,45 +38,47 @@ class CORE_EXPORT QgsGeometryEngine
virtual void geometryChanged() = 0;
virtual void prepareGeometry() = 0;
virtual QgsAbstractGeometryV2* intersection( const QgsAbstractGeometryV2& geom ) const = 0;
virtual QgsAbstractGeometryV2* difference( const QgsAbstractGeometryV2& geom ) const = 0;
virtual QgsAbstractGeometryV2* combine( const QgsAbstractGeometryV2& geom ) const = 0;
virtual QgsAbstractGeometryV2* combine( const QList< const QgsAbstractGeometryV2* > ) const = 0;
virtual QgsAbstractGeometryV2* symDifference( const QgsAbstractGeometryV2& geom ) const = 0;
virtual QgsAbstractGeometryV2* buffer( double distance, int segments ) const = 0;
virtual QgsAbstractGeometryV2* buffer( double distance, int segments, int endCapStyle, int joinStyle, double mitreLimit ) const = 0;
virtual QgsAbstractGeometryV2* simplify( double tolerance ) const = 0;
virtual QgsAbstractGeometryV2* interpolate( double distance ) const = 0;
virtual bool centroid( QgsPointV2& pt ) const = 0;
virtual bool pointOnSurface( QgsPointV2& pt ) const = 0;
virtual QgsAbstractGeometryV2* convexHull() const = 0;
virtual double distance( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool intersects( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool touches( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool crosses( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool within( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool overlaps( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool contains( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool disjoint( const QgsAbstractGeometryV2& geom ) const = 0;
virtual double area() const = 0;
virtual double length() const = 0;
virtual bool isValid() const = 0;
virtual bool isEqual( const QgsAbstractGeometryV2& geom ) const = 0;
virtual bool isEmpty() const = 0;
virtual QgsAbstractGeometryV2* intersection( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* difference( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* combine( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* combine( const QList< const QgsAbstractGeometryV2* >, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* symDifference( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* buffer( double distance, int segments, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* buffer( double distance, int segments, int endCapStyle, int joinStyle, double mitreLimit, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* simplify( double tolerance, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* interpolate( double distance, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* envelope( QString* errorMsg = 0 ) const = 0;
virtual bool centroid( QgsPointV2& pt, QString* errorMsg = 0 ) const = 0;
virtual bool pointOnSurface( QgsPointV2& pt, QString* errorMsg = 0 ) const = 0;
virtual QgsAbstractGeometryV2* convexHull( QString* errorMsg = 0 ) const = 0;
virtual double distance( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool intersects( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool touches( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool crosses( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool within( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool overlaps( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool contains( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool disjoint( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual double area( QString* errorMsg = 0 ) const = 0;
virtual double length( QString* errorMsg = 0 ) const = 0;
virtual bool isValid( QString* errorMsg = 0 ) const = 0;
virtual bool isEqual( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;
virtual bool isEmpty( QString* errorMsg ) const = 0;
virtual int splitGeometry( const QgsLineStringV2& splitLine,
QList<QgsAbstractGeometryV2*>& newGeometries,
bool topological,
QList<QgsPointV2> &topologyTestPoints ) const
QList<QgsPointV2> &topologyTestPoints, QString* errorMsg = 0 ) const
{
Q_UNUSED( splitLine );
Q_UNUSED( newGeometries );
Q_UNUSED( topological );
Q_UNUSED( topologyTestPoints );
Q_UNUSED( errorMsg );
return 2;
} //= 0;
}
virtual QgsAbstractGeometryV2* offsetCurve( double distance, int segments, int joinStyle, double mitreLimit ) const = 0;
virtual QgsAbstractGeometryV2* offsetCurve( double distance, int segments, int joinStyle, double mitreLimit, QString* errorMsg = 0 ) const = 0;
protected:
const QgsAbstractGeometryV2* mGeometry;

View File

@ -145,6 +145,89 @@ double QgsGeometryUtils::sqrDistToLine( double ptX, double ptY, double x1, doubl
return dist;
}
bool QgsGeometryUtils::lineIntersection( const QgsPointV2& p1, const QgsVector& v, const QgsPointV2& q1, const QgsVector& w, QgsPointV2& inter )
{
double d = v.y() * w.x() - v.x() * w.y();
if ( d == 0 )
return false;
double dx = q1.x() - p1.x();
double dy = q1.y() - p1.y();
double k = ( dy * w.x() - dx * w.y() ) / d;
inter = QgsPointV2( p1.x() + v.x() * k, p1.y() + v.y() * k );
return true;
}
bool QgsGeometryUtils::segmentIntersection( const QgsPointV2 &p1, const QgsPointV2 &p2, const QgsPointV2 &q1, const QgsPointV2 &q2, QgsPointV2 &inter, double tolerance )
{
QgsVector v( p2.x() - p1.x(), p2.y() - p1.y() );
QgsVector w( q2.x() - q1.x(), q2.y() - q1.y() );
double vl = v.length();
double wl = w.length();
if ( qFuzzyIsNull( vl ) || qFuzzyIsNull( wl ) )
{
return false;
}
v = v / vl;
w = w / wl;
if ( !QgsGeometryUtils::lineIntersection( p1, v, q1, w, inter ) )
return false;
double lambdav = QgsVector( inter.x() - p1.x(), inter.y() - p1.y() ) * v;
if ( lambdav < 0. + tolerance || lambdav > vl - tolerance )
return false;
double lambdaw = QgsVector( inter.x() - q1.x(), inter.y() - q1.y() ) * w;
if ( lambdaw < 0. + tolerance || lambdaw >= wl - tolerance )
return false;
return true;
}
QList<QgsGeometryUtils::SelfIntersection> QgsGeometryUtils::getSelfIntersections( const QgsAbstractGeometryV2 *geom, int part, int ring, double tolerance )
{
QList<SelfIntersection> intersections;
int n = geom->vertexCount( part, ring );
bool isClosed = geom->vertexAt( QgsVertexId( part, ring, 0 ) ) == geom->vertexAt( QgsVertexId( part, ring, n - 1 ) );
// Check every pair of segments for intersections
for ( int i = 0, j = 1; j < n; i = j++ )
{
QgsPointV2 pi = geom->vertexAt( QgsVertexId( part, ring, i ) );
QgsPointV2 pj = geom->vertexAt( QgsVertexId( part, ring, j ) );
if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < tolerance * tolerance ) continue;
// Don't test neighboring edges
int start = j + 1;
int end = i == 0 && isClosed ? n - 1 : n;
for ( int k = start, l = start + 1; l < end; k = l++ )
{
QgsPointV2 pk = geom->vertexAt( QgsVertexId( part, ring, k ) );
QgsPointV2 pl = geom->vertexAt( QgsVertexId( part, ring, l ) );
QgsPointV2 inter;
if ( !QgsGeometryUtils::segmentIntersection( pi, pj, pk, pl, inter, tolerance ) ) continue;
SelfIntersection s;
s.segment1 = i;
s.segment2 = k;
if ( s.segment1 > s.segment2 )
{
qSwap( s.segment1, s.segment2 );
}
s.point = inter;
intersections.append( s );
}
}
return intersections;
}
double QgsGeometryUtils::leftOfLine( double x, double y, double x1, double y1, double x2, double y2 )
{
double f1 = x - x1;

View File

@ -45,6 +45,62 @@ class CORE_EXPORT QgsGeometryUtils
*/
static double sqrDistToLine( double ptX, double ptY, double x1, double y1, double x2, double y2, double& minDistX, double& minDistY, double epsilon );
/**
* @brief Compute the intersection between two lines
* @param p1 Point on the first line
* @param v Direction vector of the first line
* @param q1 Point on the second line
* @param w Direction vector of the second line
* @param inter Output parameter, the intersection point
* @return Whether the lines intersect
*/
static bool lineIntersection( const QgsPointV2& p1, const QgsVector& v, const QgsPointV2& q1, const QgsVector& w, QgsPointV2& inter );
/**
* @brief Compute the intersection between two segments
* @param p1 First segment start point
* @param p2 First segment end point
* @param q1 Second segment start point
* @param q2 Second segment end point
* @param inter Output parameter, the intersection point
* @param tolerance The tolerance to use
* @return Whether the segments intersect
*/
static bool segmentIntersection( const QgsPointV2 &p1, const QgsPointV2 &p2, const QgsPointV2 &q1, const QgsPointV2 &q2, QgsPointV2& inter, double tolerance );
/**
* @brief Project the point on a segment
* @param p The point
* @param s1 The segment start point
* @param s2 The segment end point
* @return The projection of the point on the segment
*/
static QgsPointV2 projPointOnSegment( const QgsPointV2& p, const QgsPointV2& s1, const QgsPointV2& s2 )
{
double nx = s2.y() - s1.y();
double ny = -( s2.x() - s1.x() );
double t = ( p.x() * ny - p.y() * nx - s1.x() * ny + s1.y() * nx ) / (( s2.x() - s1.x() ) * ny - ( s2.y() - s1.y() ) * nx );
return t < 0. ? s1 : t > 1. ? s2 : QgsPointV2( s1.x() + ( s2.x() - s1.x() ) * t, s1.y() + ( s2.y() - s1.y() ) * t );
}
struct SelfIntersection
{
int segment1;
int segment2;
QgsPointV2 point;
};
/**
* @brief Find self intersections in a polyline
* @param geom The geometry to check
* @param part The part of the geometry to check
* @param ring The ring of the geometry part to check
* @param tolerance The tolerance to use
* @return The list of self intersections
* @note added in QGIS 2.12
*/
static QList<SelfIntersection> getSelfIntersections( const QgsAbstractGeometryV2* geom, int part, int ring, double tolerance );
/** Returns < 0 if point(x/y) is left of the line x1,y1 -> x2,y2*/
static double leftOfLine( double x, double y, double x1, double y1, double x2, double y2 );

View File

@ -25,7 +25,9 @@ email : marco.hugentobler at sourcepole dot com
#include "qgsmultipolygonv2.h"
#include "qgslogger.h"
#include "qgspolygonv2.h"
#include <limits>
#include <cstdio>
#include <QtCore/qmath.h>
#define DEFAULT_QUADRANT_SEGMENTS 8
@ -36,9 +38,16 @@ email : marco.hugentobler at sourcepole dot com
return r; \
}
/// @cond
QString GEOSException::lastMsg;
/// @endcond
#define CATCH_GEOS_WITH_ERRMSG(r) \
catch (GEOSException &e) \
{ \
QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr("GEOS") ); \
if ( errorMsg ) \
{ \
*errorMsg = e.what(); \
} \
return r; \
}
static void throwGEOSException( const char *fmt, ... )
{
@ -88,15 +97,55 @@ class GEOSInit
static GEOSInit geosinit;
QgsGeos::QgsGeos( const QgsAbstractGeometryV2* geometry ): QgsGeometryEngine( geometry ), mGeos( 0 ), mGeosPrepared( 0 )
class GEOSGeomScopedPtr
{
public:
GEOSGeomScopedPtr( GEOSGeometry* geom = 0 ) : mGeom( geom ) {}
~GEOSGeomScopedPtr() { GEOSGeom_destroy_r( geosinit.ctxt, mGeom ); }
GEOSGeometry* get() const { return mGeom; }
operator bool() const { return mGeom != 0; }
void reset( GEOSGeometry* geom )
{
GEOSGeom_destroy_r( geosinit.ctxt, mGeom );
mGeom = geom;
}
private:
GEOSGeometry* mGeom;
};
QgsGeos::QgsGeos( const QgsAbstractGeometryV2* geometry, int precision ): QgsGeometryEngine( geometry ), mGeos( 0 ), mGeosPrepared( 0 )
{
#ifdef HAVE_GEOS_CPP
double prec = qPow( 10, -precision );
mPrecisionModel = GEOSPrecisionModel_createFixed( 1.f / prec );
mPrecisionReducer = GEOSGeometryPrecisionReducer_create( mPrecisionModel );
#else
Q_UNUSED( precision )
#endif
cacheGeos();
}
QgsGeos::~QgsGeos()
{
#ifdef HAVE_GEOS_CPP
GEOSGeom_destroy_r( geosinit.ctxt, mGeos );
GEOSPreparedGeom_destroy_r( geosinit.ctxt, mGeosPrepared );
GEOSGeometryPrecisionReducer_destroy( mPrecisionReducer );
GEOSPrecisionModel_destroy( mPrecisionModel );
#endif
}
inline GEOSGeometry* QgsGeos::getReducedGeometry( GEOSGeometry* geom ) const
{
#ifdef HAVE_GEOS_CPP
//reduce precision
GEOSGeometry* reduced = GEOSGeometryPrecisionReducer_reduce( mPrecisionReducer, geom );
GEOSGeom_destroy_r( geosinit.ctxt, geom );
return reduced;
#else
return geom;
#endif
}
void QgsGeos::geometryChanged()
@ -125,32 +174,41 @@ void QgsGeos::cacheGeos() const
return;
}
mGeos = asGeos( mGeometry );
GEOSGeometry* g = asGeos( mGeometry );
#ifdef HAVE_GEOS_CPP
if ( g )
{
mGeos = GEOSGeometryPrecisionReducer_reduce( mPrecisionReducer, g );
GEOSGeom_destroy_r( geosinit.ctxt, g );
}
#else
mGeos = g;
#endif
}
QgsAbstractGeometryV2* QgsGeos::intersection( const QgsAbstractGeometryV2& geom ) const
QgsAbstractGeometryV2* QgsGeos::intersection( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return overlay( geom, INTERSECTION );
return overlay( geom, INTERSECTION, errorMsg );
}
QgsAbstractGeometryV2* QgsGeos::difference( const QgsAbstractGeometryV2& geom ) const
QgsAbstractGeometryV2* QgsGeos::difference( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return overlay( geom, DIFFERENCE );
return overlay( geom, DIFFERENCE, errorMsg );
}
QgsAbstractGeometryV2* QgsGeos::combine( const QgsAbstractGeometryV2& geom ) const
QgsAbstractGeometryV2* QgsGeos::combine( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return overlay( geom, UNION );
return overlay( geom, UNION, errorMsg );
}
QgsAbstractGeometryV2* QgsGeos::combine( const QList< const QgsAbstractGeometryV2* > geomList ) const
QgsAbstractGeometryV2* QgsGeos::combine( const QList< const QgsAbstractGeometryV2* > geomList, QString* errorMsg ) const
{
QVector< GEOSGeometry* > geosGeometries;
geosGeometries.resize( geomList.size() );
for ( int i = 0; i < geomList.size(); ++i )
{
geosGeometries[i] = asGeos( geomList.at( i ) );
geosGeometries[i] = getReducedGeometry( asGeos( geomList.at( i ) ) );
}
GEOSGeometry* geomUnion = 0;
@ -160,19 +218,19 @@ QgsAbstractGeometryV2* QgsGeos::combine( const QList< const QgsAbstractGeometryV
geomUnion = GEOSUnaryUnion_r( geosinit.ctxt, geomCollection );
GEOSGeom_destroy_r( geosinit.ctxt, geomCollection );
}
CATCH_GEOS( 0 )
CATCH_GEOS_WITH_ERRMSG( 0 )
QgsAbstractGeometryV2* result = fromGeos( geomUnion );
GEOSGeom_destroy_r( geosinit.ctxt, geomUnion );
return result;
}
QgsAbstractGeometryV2* QgsGeos::symDifference( const QgsAbstractGeometryV2& geom ) const
QgsAbstractGeometryV2* QgsGeos::symDifference( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return overlay( geom, SYMDIFFERENCE );
return overlay( geom, SYMDIFFERENCE, errorMsg );
}
double QgsGeos::distance( const QgsAbstractGeometryV2& geom ) const
double QgsGeos::distance( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
double distance = -1.0;
if ( !mGeos )
@ -190,47 +248,47 @@ double QgsGeos::distance( const QgsAbstractGeometryV2& geom ) const
{
GEOSDistance_r( geosinit.ctxt, mGeos, otherGeosGeom, &distance );
}
CATCH_GEOS( -1.0 )
CATCH_GEOS_WITH_ERRMSG( -1.0 )
return distance;
}
bool QgsGeos::intersects( const QgsAbstractGeometryV2& geom ) const
bool QgsGeos::intersects( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return relation( geom, INTERSECTS );
return relation( geom, INTERSECTS, errorMsg );
}
bool QgsGeos::touches( const QgsAbstractGeometryV2& geom ) const
bool QgsGeos::touches( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return relation( geom, TOUCHES );
return relation( geom, TOUCHES, errorMsg );
}
bool QgsGeos::crosses( const QgsAbstractGeometryV2& geom ) const
bool QgsGeos::crosses( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return relation( geom, CROSSES );
return relation( geom, CROSSES, errorMsg );
}
bool QgsGeos::within( const QgsAbstractGeometryV2& geom ) const
bool QgsGeos::within( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return relation( geom, WITHIN );
return relation( geom, WITHIN, errorMsg );
}
bool QgsGeos::overlaps( const QgsAbstractGeometryV2& geom ) const
bool QgsGeos::overlaps( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return relation( geom, OVERLAPS );
return relation( geom, OVERLAPS, errorMsg );
}
bool QgsGeos::contains( const QgsAbstractGeometryV2& geom ) const
bool QgsGeos::contains( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return relation( geom, CONTAINS );
return relation( geom, CONTAINS, errorMsg );
}
bool QgsGeos::disjoint( const QgsAbstractGeometryV2& geom ) const
bool QgsGeos::disjoint( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
return relation( geom, DISJOINT );
return relation( geom, DISJOINT, errorMsg );
}
double QgsGeos::area() const
double QgsGeos::area( QString* errorMsg ) const
{
double area = -1.0;
if ( !mGeos )
@ -243,11 +301,11 @@ double QgsGeos::area() const
if ( GEOSArea_r( geosinit.ctxt, mGeos, &area ) != 1 )
return -1.0;
}
CATCH_GEOS( -1.0 );
CATCH_GEOS_WITH_ERRMSG( -1.0 );
return area;
}
double QgsGeos::length() const
double QgsGeos::length( QString* errorMsg ) const
{
double length = -1.0;
if ( !mGeos )
@ -259,14 +317,15 @@ double QgsGeos::length() const
if ( GEOSLength_r( geosinit.ctxt, mGeos, &length ) != 1 )
return -1.0;
}
CATCH_GEOS( -1.0 )
CATCH_GEOS_WITH_ERRMSG( -1.0 )
return length;
}
int QgsGeos::splitGeometry( const QgsLineStringV2& splitLine,
QList<QgsAbstractGeometryV2*>&newGeometries,
bool topological,
QList<QgsPointV2> &topologyTestPoints ) const
QList<QgsPointV2> &topologyTestPoints,
QString* errorMsg ) const
{
int returnCode = 0;
@ -337,14 +396,14 @@ int QgsGeos::splitGeometry( const QgsLineStringV2& splitLine,
return 1;
}
}
CATCH_GEOS( 2 )
CATCH_GEOS_WITH_ERRMSG( 2 )
return returnCode;
}
int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry* splitLine, QList<QgsPointV2>& testPoints ) const
int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry* splitLine, QList<QgsPointV2>& testPoints, QString* errorMsg ) const
{
//Find out the intersection points between splitLineGeos and this geometry.
//These points need to be tested for topological correctness by the calling function
@ -398,7 +457,7 @@ int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry* splitLine, QList<Qg
}
GEOSGeom_destroy_r( geosinit.ctxt, intersectionGeom );
}
CATCH_GEOS( 1 )
CATCH_GEOS_WITH_ERRMSG( 1 )
return 0;
}
@ -997,14 +1056,14 @@ GEOSGeometry* QgsGeos::asGeos( const QgsAbstractGeometryV2* geom )
return 0;
}
QgsAbstractGeometryV2* QgsGeos::overlay( const QgsAbstractGeometryV2& geom, Overlay op ) const
QgsAbstractGeometryV2* QgsGeos::overlay( const QgsAbstractGeometryV2& geom, Overlay op, QString* errorMsg ) const
{
if ( !mGeos )
{
return 0;
}
GEOSGeometry* geosGeom = asGeos( &geom );
GEOSGeomScopedPtr geosGeom = getReducedGeometry( asGeos( &geom ) );
if ( !geosGeom )
{
return 0;
@ -1012,53 +1071,51 @@ QgsAbstractGeometryV2* QgsGeos::overlay( const QgsAbstractGeometryV2& geom, Over
try
{
GEOSGeometry* opGeom = 0;
GEOSGeomScopedPtr opGeom;
switch ( op )
{
case INTERSECTION:
opGeom = GEOSIntersection_r( geosinit.ctxt, mGeos, geosGeom );
opGeom.reset( GEOSIntersection_r( geosinit.ctxt, mGeos, geosGeom.get() ) );
break;
case DIFFERENCE:
opGeom = GEOSDifference_r( geosinit.ctxt, mGeos, geosGeom );
opGeom.reset( GEOSDifference_r( geosinit.ctxt, mGeos, geosGeom.get() ) );
break;
case UNION:
opGeom = GEOSUnion_r( geosinit.ctxt, mGeos, geosGeom );
opGeom.reset( GEOSUnion_r( geosinit.ctxt, mGeos, geosGeom.get() ) );
break;
case SYMDIFFERENCE:
opGeom = GEOSSymDifference_r( geosinit.ctxt, mGeos, geosGeom );
opGeom.reset( GEOSSymDifference_r( geosinit.ctxt, mGeos, geosGeom.get() ) );
break;
default: //unknown op
GEOSGeom_destroy_r( geosinit.ctxt, geosGeom );
return 0;
}
QgsAbstractGeometryV2* opResult = fromGeos( opGeom );
GEOSGeom_destroy_r( geosinit.ctxt, opGeom );
GEOSGeom_destroy_r( geosinit.ctxt, geosGeom );
QgsAbstractGeometryV2* opResult = fromGeos( opGeom.get() );
return opResult;
}
catch ( GEOSException &e )
{
Q_UNUSED( e );
GEOSGeom_destroy_r( geosinit.ctxt, geosGeom );
if ( errorMsg )
{
*errorMsg = e.what();
}
return 0;
}
}
bool QgsGeos::relation( const QgsAbstractGeometryV2& geom, Relation r ) const
bool QgsGeos::relation( const QgsAbstractGeometryV2& geom, Relation r, QString* errorMsg ) const
{
if ( !mGeos )
{
return false;
}
GEOSGeometry* geosGeom = asGeos( &geom );
GEOSGeomScopedPtr geosGeom = getReducedGeometry( asGeos( &geom ) );
if ( !geosGeom )
{
return false;
}
bool result = false;
try
{
if ( mGeosPrepared ) //use faster version with prepared geometry
@ -1066,74 +1123,72 @@ bool QgsGeos::relation( const QgsAbstractGeometryV2& geom, Relation r ) const
switch ( r )
{
case INTERSECTS:
result = ( GEOSPreparedIntersects_r( geosinit.ctxt, mGeosPrepared, geosGeom ) == 1 );
result = ( GEOSPreparedIntersects_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
break;
case TOUCHES:
result = ( GEOSPreparedTouches_r( geosinit.ctxt, mGeosPrepared, geosGeom ) == 1 );
result = ( GEOSPreparedTouches_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
break;
case CROSSES:
result = ( GEOSPreparedCrosses_r( geosinit.ctxt, mGeosPrepared, geosGeom ) == 1 );
result = ( GEOSPreparedCrosses_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
break;
case WITHIN:
result = ( GEOSPreparedWithin_r( geosinit.ctxt, mGeosPrepared, geosGeom ) == 1 );
result = ( GEOSPreparedWithin_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
break;
case CONTAINS:
result = ( GEOSPreparedContains_r( geosinit.ctxt, mGeosPrepared, geosGeom ) == 1 );
result = ( GEOSPreparedContains_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
break;
case DISJOINT:
result = ( GEOSPreparedDisjoint_r( geosinit.ctxt, mGeosPrepared, geosGeom ) == 1 );
result = ( GEOSPreparedDisjoint_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
break;
case OVERLAPS:
result = ( GEOSPreparedOverlaps_r( geosinit.ctxt, mGeosPrepared, geosGeom ) == 1 );
result = ( GEOSPreparedOverlaps_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
break;
default:
GEOSGeom_destroy_r( geosinit.ctxt, geosGeom );
return false;
}
GEOSGeom_destroy_r( geosinit.ctxt, geosGeom );
return result;
}
switch ( r )
{
case INTERSECTS:
result = ( GEOSIntersects_r( geosinit.ctxt, mGeos, geosGeom ) == 1 );
result = ( GEOSIntersects_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
break;
case TOUCHES:
result = ( GEOSTouches_r( geosinit.ctxt, mGeos, geosGeom ) == 1 );
result = ( GEOSTouches_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
break;
case CROSSES:
result = ( GEOSCrosses_r( geosinit.ctxt, mGeos, geosGeom ) == 1 );
result = ( GEOSCrosses_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
break;
case WITHIN:
result = ( GEOSWithin_r( geosinit.ctxt, mGeos, geosGeom ) == 1 );
result = ( GEOSWithin_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
break;
case CONTAINS:
result = ( GEOSContains_r( geosinit.ctxt, mGeos, geosGeom ) == 1 );
result = ( GEOSContains_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
break;
case DISJOINT:
result = ( GEOSDisjoint_r( geosinit.ctxt, mGeos, geosGeom ) == 1 );
result = ( GEOSDisjoint_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
break;
case OVERLAPS:
result = ( GEOSOverlaps_r( geosinit.ctxt, mGeos, geosGeom ) == 1 );
result = ( GEOSOverlaps_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
break;
default:
GEOSGeom_destroy_r( geosinit.ctxt, geosGeom );
return false;
}
}
catch ( GEOSException &e )
{
Q_UNUSED( e );
GEOSGeom_destroy_r( geosinit.ctxt, geosGeom );
if ( errorMsg )
{
*errorMsg = e.what();
}
return 0;
}
GEOSGeom_destroy_r( geosinit.ctxt, geosGeom );
return result;
}
QgsAbstractGeometryV2* QgsGeos::buffer( double distance, int segments ) const
QgsAbstractGeometryV2* QgsGeos::buffer( double distance, int segments, QString* errorMsg ) const
{
if ( !mGeos )
{
@ -1145,11 +1200,11 @@ QgsAbstractGeometryV2* QgsGeos::buffer( double distance, int segments ) const
{
geos = GEOSBuffer_r( geosinit.ctxt, mGeos, distance, segments );
}
CATCH_GEOS( 0 );
CATCH_GEOS_WITH_ERRMSG( 0 );
return fromGeos( geos );
}
QgsAbstractGeometryV2 *QgsGeos::buffer( double distance, int segments, int endCapStyle, int joinStyle, double mitreLimit ) const
QgsAbstractGeometryV2 *QgsGeos::buffer( double distance, int segments, int endCapStyle, int joinStyle, double mitreLimit, QString* errorMsg ) const
{
if ( !mGeos )
{
@ -1164,14 +1219,14 @@ QgsAbstractGeometryV2 *QgsGeos::buffer( double distance, int segments, int endCa
{
geos = GEOSBufferWithStyle_r( geosinit.ctxt, mGeos, distance, segments, endCapStyle, joinStyle, mitreLimit );
}
CATCH_GEOS( 0 );
CATCH_GEOS_WITH_ERRMSG( 0 );
return fromGeos( geos );
#else
return 0;
#endif //0
}
QgsAbstractGeometryV2* QgsGeos::simplify( double tolerance ) const
QgsAbstractGeometryV2* QgsGeos::simplify( double tolerance, QString* errorMsg ) const
{
if ( !mGeos )
{
@ -1182,11 +1237,11 @@ QgsAbstractGeometryV2* QgsGeos::simplify( double tolerance ) const
{
geos = GEOSTopologyPreserveSimplify_r( geosinit.ctxt, mGeos, tolerance );
}
CATCH_GEOS( 0 );
CATCH_GEOS_WITH_ERRMSG( 0 );
return fromGeos( geos );
}
QgsAbstractGeometryV2* QgsGeos::interpolate( double distance ) const
QgsAbstractGeometryV2* QgsGeos::interpolate( double distance, QString* errorMsg ) const
{
if ( !mGeos )
{
@ -1197,11 +1252,11 @@ QgsAbstractGeometryV2* QgsGeos::interpolate( double distance ) const
{
geos = GEOSInterpolate_r( geosinit.ctxt, mGeos, distance );
}
CATCH_GEOS( 0 );
CATCH_GEOS_WITH_ERRMSG( 0 );
return fromGeos( geos );
}
bool QgsGeos::centroid( QgsPointV2& pt ) const
bool QgsGeos::centroid( QgsPointV2& pt, QString* errorMsg ) const
{
if ( !mGeos )
{
@ -1213,7 +1268,7 @@ bool QgsGeos::centroid( QgsPointV2& pt ) const
{
geos = GEOSGetCentroid_r( geosinit.ctxt, mGeos );
}
CATCH_GEOS( false );
CATCH_GEOS_WITH_ERRMSG( false );
if ( !geos )
{
@ -1227,7 +1282,22 @@ bool QgsGeos::centroid( QgsPointV2& pt ) const
return true;
}
bool QgsGeos::pointOnSurface( QgsPointV2& pt ) const
QgsAbstractGeometryV2* QgsGeos::envelope( QString* errorMsg ) const
{
if ( !mGeos )
{
return 0;
}
GEOSGeometry* geos = 0;
try
{
geos = GEOSEnvelope_r( geosinit.ctxt, mGeos );
}
CATCH_GEOS_WITH_ERRMSG( 0 );
return fromGeos( geos );
}
bool QgsGeos::pointOnSurface( QgsPointV2& pt, QString* errorMsg ) const
{
if ( !mGeos )
{
@ -1239,7 +1309,7 @@ bool QgsGeos::pointOnSurface( QgsPointV2& pt ) const
{
geos = GEOSPointOnSurface_r( geosinit.ctxt, mGeos );
}
CATCH_GEOS( false );
CATCH_GEOS_WITH_ERRMSG( false );
if ( !geos )
{
@ -1256,7 +1326,7 @@ bool QgsGeos::pointOnSurface( QgsPointV2& pt ) const
return true;
}
QgsAbstractGeometryV2* QgsGeos::convexHull() const
QgsAbstractGeometryV2* QgsGeos::convexHull( QString* errorMsg ) const
{
if ( !mGeos )
{
@ -1270,10 +1340,10 @@ QgsAbstractGeometryV2* QgsGeos::convexHull() const
GEOSGeom_destroy_r( geosinit.ctxt, cHull );
return cHullGeom;
}
CATCH_GEOS( 0 );
CATCH_GEOS_WITH_ERRMSG( 0 );
}
bool QgsGeos::isValid() const
bool QgsGeos::isValid( QString* errorMsg ) const
{
if ( !mGeos )
{
@ -1284,10 +1354,10 @@ bool QgsGeos::isValid() const
{
return GEOSisValid_r( geosinit.ctxt, mGeos );
}
CATCH_GEOS( false );
CATCH_GEOS_WITH_ERRMSG( false );
}
bool QgsGeos::isEqual( const QgsAbstractGeometryV2& geom ) const
bool QgsGeos::isEqual( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
{
if ( !mGeos )
{
@ -1296,19 +1366,18 @@ bool QgsGeos::isEqual( const QgsAbstractGeometryV2& geom ) const
try
{
GEOSGeometry* geosGeom = asGeos( &geom );
GEOSGeomScopedPtr geosGeom = getReducedGeometry( asGeos( &geom ) );
if ( !geosGeom )
{
return false;
}
bool equal = GEOSEquals_r( geosinit.ctxt, mGeos, geosGeom );
GEOSGeom_destroy_r( geosinit.ctxt, geosGeom );
bool equal = GEOSEquals_r( geosinit.ctxt, mGeos, geosGeom.get() );
return equal;
}
CATCH_GEOS( false );
CATCH_GEOS_WITH_ERRMSG( false );
}
bool QgsGeos::isEmpty() const
bool QgsGeos::isEmpty( QString* errorMsg ) const
{
if ( !mGeos )
{
@ -1319,7 +1388,7 @@ bool QgsGeos::isEmpty() const
{
return GEOSisEmpty_r( geosinit.ctxt, mGeos );
}
CATCH_GEOS( false );
CATCH_GEOS_WITH_ERRMSG( false );
}
GEOSCoordSequence* QgsGeos::createCoordinateSequence( const QgsCurveV2* curve )
@ -1463,7 +1532,7 @@ GEOSGeometry* QgsGeos::createGeosPolygon( const QgsAbstractGeometryV2* poly )
return geosPolygon;
}
QgsAbstractGeometryV2* QgsGeos::offsetCurve( double distance, int segments, int joinStyle, double mitreLimit ) const
QgsAbstractGeometryV2* QgsGeos::offsetCurve( double distance, int segments, int joinStyle, double mitreLimit, QString* errorMsg ) const
{
if ( !mGeos )
return 0;
@ -1473,13 +1542,13 @@ QgsAbstractGeometryV2* QgsGeos::offsetCurve( double distance, int segments, int
{
offset = GEOSOffsetCurve_r( geosinit.ctxt, mGeos, distance, segments, joinStyle, mitreLimit );
}
CATCH_GEOS( 0 )
CATCH_GEOS_WITH_ERRMSG( 0 )
QgsAbstractGeometryV2* offsetGeom = fromGeos( offset );
GEOSGeom_destroy_r( geosinit.ctxt, offset );
return offsetGeom;
}
QgsAbstractGeometryV2* QgsGeos::reshapeGeometry( const QgsLineStringV2& reshapeWithLine, int* errorCode ) const
QgsAbstractGeometryV2* QgsGeos::reshapeGeometry( const QgsLineStringV2& reshapeWithLine, int* errorCode, QString* errorMsg ) const
{
if ( !mGeos || reshapeWithLine.numPoints() < 2 || mGeometry->dimension() == 0 )
{
@ -1581,7 +1650,7 @@ QgsAbstractGeometryV2* QgsGeos::reshapeGeometry( const QgsLineStringV2& reshapeW
return 0;
}
}
CATCH_GEOS( 0 )
CATCH_GEOS_WITH_ERRMSG( 0 )
}
return 0;
}
@ -1755,7 +1824,7 @@ GEOSGeometry* QgsGeos::reshapeLine( const GEOSGeometry* line, const GEOSGeometry
{
GEOSGeometry* maxGeom = 0; //the longest geometry in the probabla list
GEOSGeometry* currentGeom = 0;
double maxLength = -DBL_MAX;
double maxLength = -std::numeric_limits<double>::max();
double currentLength = 0;
for ( int i = 0; i < probableParts.size(); ++i )
{

View File

@ -19,6 +19,9 @@ email : marco.hugentobler at sourcepole dot com
#include "qgsgeometryengine.h"
#include "qgspointv2.h"
#include <geos_c.h>
#ifdef HAVE_GEOS_CPP
#include "geosextra/geos_c_extra.h"
#endif
class QgsLineStringV2;
class QgsPolygonV2;
@ -29,52 +32,55 @@ class QgsPolygonV2;
class CORE_EXPORT QgsGeos: public QgsGeometryEngine
{
public:
QgsGeos( const QgsAbstractGeometryV2* geometry );
QgsGeos( const QgsAbstractGeometryV2* geometry, int precision = 7 );
~QgsGeos();
/** Removes caches*/
void geometryChanged() override;
void prepareGeometry() override;
QgsAbstractGeometryV2* intersection( const QgsAbstractGeometryV2& geom ) const override;
QgsAbstractGeometryV2* difference( const QgsAbstractGeometryV2& geom ) const override;
QgsAbstractGeometryV2* combine( const QgsAbstractGeometryV2& geom ) const override;
QgsAbstractGeometryV2* combine( const QList< const QgsAbstractGeometryV2* > ) const override;
QgsAbstractGeometryV2* symDifference( const QgsAbstractGeometryV2& geom ) const override;
QgsAbstractGeometryV2* buffer( double distance, int segments ) const override;
QgsAbstractGeometryV2* buffer( double distance, int segments, int endCapStyle, int joinStyle, double mitreLimit ) const override;
QgsAbstractGeometryV2* simplify( double tolerance ) const override;
QgsAbstractGeometryV2* interpolate( double distance ) const override;
bool centroid( QgsPointV2& pt ) const override;
bool pointOnSurface( QgsPointV2& pt ) const override;
QgsAbstractGeometryV2* convexHull() const override;
double distance( const QgsAbstractGeometryV2& geom ) const override;
bool intersects( const QgsAbstractGeometryV2& geom ) const override;
bool touches( const QgsAbstractGeometryV2& geom ) const override;
bool crosses( const QgsAbstractGeometryV2& geom ) const override;
bool within( const QgsAbstractGeometryV2& geom ) const override;
bool overlaps( const QgsAbstractGeometryV2& geom ) const override;
bool contains( const QgsAbstractGeometryV2& geom ) const override;
bool disjoint( const QgsAbstractGeometryV2& geom ) const override;
double area() const override;
double length() const override;
bool isValid() const override;
bool isEqual( const QgsAbstractGeometryV2& geom ) const override;
bool isEmpty() const override;
QgsAbstractGeometryV2* intersection( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* difference( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* combine( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* combine( const QList< const QgsAbstractGeometryV2* >, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* symDifference( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* buffer( double distance, int segments, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* buffer( double distance, int segments, int endCapStyle, int joinStyle, double mitreLimit, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* simplify( double tolerance, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* interpolate( double distance, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* envelope( QString* errorMsg = 0 ) const override;
bool centroid( QgsPointV2& pt, QString* errorMsg = 0 ) const override;
bool pointOnSurface( QgsPointV2& pt, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* convexHull( QString* errorMsg = 0 ) const override;
double distance( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
bool intersects( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
bool touches( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
bool crosses( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
bool within( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
bool overlaps( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
bool contains( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
bool disjoint( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
double area( QString* errorMsg = 0 ) const override;
double length( QString* errorMsg = 0 ) const override;
bool isValid( QString* errorMsg = 0 ) const override;
bool isEqual( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
bool isEmpty( QString* errorMsg = 0 ) const override;
/** Splits this geometry according to a given line.
@param splitLine the line that splits the geometry
@param[out] newGeometries list of new geometries that have been created with the split
@param topological true if topological editing is enabled
@param[out] topologyTestPoints points that need to be tested for topological completeness in the dataset
@param[out] errorMsg error messages emitted, if any
@return 0 in case of success, 1 if geometry has not been split, error else*/
int splitGeometry( const QgsLineStringV2& splitLine,
QList<QgsAbstractGeometryV2*>& newGeometries,
bool topological,
QList<QgsPointV2> &topologyTestPoints ) const override;
QList<QgsPointV2> &topologyTestPoints,
QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* offsetCurve( double distance, int segments, int joinStyle, double mitreLimit ) const override;
QgsAbstractGeometryV2* reshapeGeometry( const QgsLineStringV2& reshapeWithLine, int* errorCode ) const;
QgsAbstractGeometryV2* offsetCurve( double distance, int segments, int joinStyle, double mitreLimit, QString* errorMsg = 0 ) const override;
QgsAbstractGeometryV2* reshapeGeometry( const QgsLineStringV2& reshapeWithLine, int* errorCode, QString* errorMsg = 0 ) const;
static QgsAbstractGeometryV2* fromGeos( const GEOSGeometry* geos );
static QgsPolygonV2* fromGeosPolygon( const GEOSGeometry* geos );
@ -86,6 +92,11 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
private:
mutable GEOSGeometry* mGeos;
const GEOSPreparedGeometry* mGeosPrepared;
#ifdef HAVE_GEOS_CPP
//precision reducer
GEOSPrecisionModel* mPrecisionModel;
GEOSGeometryPrecisionReducer* mPrecisionReducer;
#endif
enum Overlay
{
@ -108,8 +119,8 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
//geos util functions
void cacheGeos() const;
QgsAbstractGeometryV2* overlay( const QgsAbstractGeometryV2& geom, Overlay op ) const;
bool relation( const QgsAbstractGeometryV2& geom, Relation r ) const;
QgsAbstractGeometryV2* overlay( const QgsAbstractGeometryV2& geom, Overlay op, QString* errorMsg = 0 ) const;
bool relation( const QgsAbstractGeometryV2& geom, Relation r, QString* errorMsg = 0 ) const;
static GEOSCoordSequence* createCoordinateSequence( const QgsCurveV2* curve );
static QgsLineStringV2* sequenceToLinestring( const GEOSGeometry* geos, bool hasZ, bool hasM );
static int numberOfGeometries( GEOSGeometry* g );
@ -122,7 +133,7 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
static GEOSGeometry* createGeosPolygon( const QgsAbstractGeometryV2* poly );
//utils for geometry split
int topologicalTestPointsSplit( const GEOSGeometry* splitLine, QList<QgsPointV2>& testPoints ) const;
int topologicalTestPointsSplit( const GEOSGeometry* splitLine, QList<QgsPointV2>& testPoints, QString* errorMsg = 0 ) const;
GEOSGeometry* linePointDifference( GEOSGeometry* GEOSsplitPoint ) const;
int splitLinearGeometry( GEOSGeometry* splitLine, QList<QgsAbstractGeometryV2*>& newGeometries ) const;
int splitPolygonGeometry( GEOSGeometry* splitLine, QList<QgsAbstractGeometryV2*>& newGeometries ) const;
@ -133,6 +144,9 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
static int lineContainedInLine( const GEOSGeometry* line1, const GEOSGeometry* line2 );
static int pointContainedInLine( const GEOSGeometry* point, const GEOSGeometry* line );
static int geomDigits( const GEOSGeometry* geom );
private:
inline GEOSGeometry *getReducedGeometry( GEOSGeometry* geom ) const;
};
/// @cond
@ -142,14 +156,14 @@ class GEOSException
public:
GEOSException( QString theMsg )
{
if ( theMsg == "Unknown exception thrown" && lastMsg.isNull() )
if ( theMsg == "Unknown exception thrown" && lastMsg().isNull() )
{
msg = theMsg;
}
else
{
msg = theMsg;
lastMsg = msg;
lastMsg() = msg;
}
}
@ -161,8 +175,8 @@ class GEOSException
~GEOSException()
{
if ( lastMsg == msg )
lastMsg = QString::null;
if ( lastMsg() == msg )
lastMsg() = QString::null;
}
QString what()
@ -172,7 +186,7 @@ class GEOSException
private:
QString msg;
static QString lastMsg;
static QString& lastMsg() { static QString _lastMsg; return _lastMsg; }
};
/// @endcond

View File

@ -33,7 +33,7 @@ QgsLineStringV2::QgsLineStringV2(): QgsCurveV2()
QgsLineStringV2::~QgsLineStringV2()
{}
QgsAbstractGeometryV2 *QgsLineStringV2::clone() const
QgsLineStringV2 *QgsLineStringV2::clone() const
{
return new QgsLineStringV2( *this );
}

View File

@ -36,7 +36,7 @@ class CORE_EXPORT QgsLineStringV2: public QgsCurveV2
virtual QString geometryType() const override { return "LineString"; }
virtual int dimension() const override { return 1; }
virtual QgsAbstractGeometryV2* clone() const override;
virtual QgsLineStringV2* clone() const override;
virtual void clear() override;
virtual bool fromWkb( const unsigned char* wkb ) override;

View File

@ -21,7 +21,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgsgeometryutils.h"
#include "qgslinestringv2.h"
QgsAbstractGeometryV2 *QgsMultiCurveV2::clone() const
QgsMultiCurveV2 *QgsMultiCurveV2::clone() const
{
return new QgsMultiCurveV2( *this );
}

View File

@ -28,7 +28,7 @@ class CORE_EXPORT QgsMultiCurveV2: public QgsGeometryCollectionV2
{
public:
virtual QString geometryType() const override { return "MultiCurve"; }
QgsAbstractGeometryV2* clone() const override;
QgsMultiCurveV2* clone() const override;
bool fromWkt( const QString& wkt ) override;

View File

@ -21,7 +21,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgsgeometryutils.h"
#include "qgslinestringv2.h"
QgsAbstractGeometryV2* QgsMultiLineStringV2::clone() const
QgsMultiLineStringV2* QgsMultiLineStringV2::clone() const
{
return new QgsMultiLineStringV2( *this );
}

View File

@ -28,7 +28,7 @@ class CORE_EXPORT QgsMultiLineStringV2: public QgsMultiCurveV2
{
public:
virtual QString geometryType() const override { return "MultiLineString"; }
QgsAbstractGeometryV2* clone() const override;
QgsMultiLineStringV2* clone() const override;
bool fromWkt( const QString& wkt ) override;

View File

@ -19,7 +19,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgspointv2.h"
#include "qgswkbptr.h"
QgsAbstractGeometryV2 *QgsMultiPointV2::clone() const
QgsMultiPointV2 *QgsMultiPointV2::clone() const
{
return new QgsMultiPointV2( *this );
}

View File

@ -28,7 +28,7 @@ class CORE_EXPORT QgsMultiPointV2: public QgsGeometryCollectionV2
{
public:
virtual QString geometryType() const override { return "MultiPoint"; }
QgsAbstractGeometryV2* clone() const override;
QgsMultiPointV2* clone() const override;
bool fromWkt( const QString& wkt ) override;

View File

@ -21,7 +21,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgspolygonv2.h"
#include "qgscurvepolygonv2.h"
QgsAbstractGeometryV2 *QgsMultiPolygonV2::clone() const
QgsMultiPolygonV2 *QgsMultiPolygonV2::clone() const
{
return new QgsMultiPolygonV2( *this );
}

View File

@ -28,7 +28,7 @@ class CORE_EXPORT QgsMultiPolygonV2: public QgsMultiSurfaceV2
{
public:
virtual QString geometryType() const override { return "MultiPolygon"; }
QgsAbstractGeometryV2* clone() const override;
QgsMultiPolygonV2* clone() const override;
bool fromWkt( const QString& wkt ) override;

View File

@ -22,7 +22,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgspolygonv2.h"
#include "qgscurvepolygonv2.h"
QgsAbstractGeometryV2 *QgsMultiSurfaceV2::clone() const
QgsMultiSurfaceV2 *QgsMultiSurfaceV2::clone() const
{
return new QgsMultiSurfaceV2( *this );
}

View File

@ -28,7 +28,7 @@ class CORE_EXPORT QgsMultiSurfaceV2: public QgsGeometryCollectionV2
{
public:
virtual QString geometryType() const override { return "MultiSurface"; }
QgsAbstractGeometryV2* clone() const override;
QgsMultiSurfaceV2* clone() const override;
bool fromWkt( const QString& wkt ) override;

View File

@ -29,6 +29,11 @@ QgsPointV2::QgsPointV2( double x, double y ): QgsAbstractGeometryV2(), mX( x ),
mWkbType = QgsWKBTypes::Point;
}
QgsPointV2::QgsPointV2( const QgsPoint& p ): QgsAbstractGeometryV2(), mX( p.x() ), mY( p.y() ), mZ( 0.0 ), mM( 0.0 )
{
mWkbType = QgsWKBTypes::Point;
}
QgsPointV2::QgsPointV2( QgsWKBTypes::Type type, double x, double y, double z, double m ): mX( x ), mY( y ), mZ( z ), mM( m )
{
mWkbType = type;
@ -48,7 +53,7 @@ bool QgsPointV2::operator!=( const QgsPointV2& pt ) const
return !operator==( pt );
}
QgsAbstractGeometryV2* QgsPointV2::clone() const
QgsPointV2 *QgsPointV2::clone() const
{
return new QgsPointV2( *this );
}

View File

@ -30,12 +30,13 @@ class CORE_EXPORT QgsPointV2: public QgsAbstractGeometryV2
{
public:
QgsPointV2( double x = 0.0, double y = 0.0 );
QgsPointV2( const QgsPoint& p );
QgsPointV2( QgsWKBTypes::Type type, double x = 0.0, double y = 0.0, double z = 0.0, double m = 0.0 );
bool operator==( const QgsPointV2& pt ) const;
bool operator!=( const QgsPointV2& pt ) const;
virtual QgsAbstractGeometryV2* clone() const override;
virtual QgsPointV2* clone() const override;
void clear() override;
double x() const { return mX; }
@ -90,6 +91,11 @@ class CORE_EXPORT QgsPointV2: public QgsAbstractGeometryV2
@return 0.0*/
double vertexAngle( const QgsVertexId& vertex ) const override { Q_UNUSED( vertex ); return 0.0; }
virtual int vertexCount( int /*part*/ = 0, int /*ring*/ = 0 ) const override { return 1; }
virtual int ringCount( int /*part*/ = 0 ) const override { return 1; }
virtual int partCount() const override { return 1; }
virtual QgsPointV2 vertexAt( const QgsVertexId& /*id*/ ) const override { return *this; }
private:
double mX;
double mY;

View File

@ -21,7 +21,7 @@
#include "qgslinestringv2.h"
#include "qgswkbptr.h"
QgsAbstractGeometryV2* QgsPolygonV2::clone() const
QgsPolygonV2* QgsPolygonV2::clone() const
{
return new QgsPolygonV2( *this );
}

View File

@ -30,7 +30,7 @@ class CORE_EXPORT QgsPolygonV2: public QgsCurvePolygonV2
{
public:
virtual QString geometryType() const override { return "Polygon"; }
virtual QgsAbstractGeometryV2* clone() const override;
virtual QgsPolygonV2* clone() const override;
virtual bool fromWkb( const unsigned char* wkb ) override;
// inherited: bool fromWkt( const QString &wkt );

View File

@ -26,7 +26,6 @@ class QgsPolygonV2;
class CORE_EXPORT QgsSurfaceV2: public QgsAbstractGeometryV2
{
public:
virtual QgsPointV2 centroid() const = 0;
virtual QgsPointV2 pointOnSurface() const = 0;
virtual QgsPolygonV2* surfaceToPolygon() const = 0;
};

View File

@ -0,0 +1,60 @@
#include <geos/geom/Geometry.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/PrecisionModel.h>
#include <geos/index/strtree/STRtree.h>
#include <geos/geom/prep/PreparedGeometry.h>
#include <geos/precision/GeometryPrecisionReducer.h>
#include <geos/util/GEOSException.h>
#define GEOSGeometry geos::geom::Geometry
#define GEOSPreparedGeometry geos::geom::prep::PreparedGeometry
#define GEOSSTRtree geos::index::strtree::STRtree
#define GEOSCoordSequence geos::geom::CoordinateSequence
#define GEOSPrecisionModel geos::geom::PrecisionModel
#define GEOSPrecisionModelType geos::geom::PrecisionModel::Type
#define GEOSGeometryPrecisionReducer geos::precision::GeometryPrecisionReducer
typedef struct GEOSBufParams_t GEOSBufferParams;
#include "geos_c_extra.h"
extern "C"
{
GEOSPrecisionModel* GEOSPrecisionModel_create( GEOSPrecisionModelType type )
{
return new GEOSPrecisionModel( type );
}
GEOSPrecisionModel* GEOSPrecisionModel_createFixed( double scale )
{
return new GEOSPrecisionModel( scale );
}
void GEOSPrecisionModel_destroy( GEOSPrecisionModel* model )
{
delete model;
}
GEOSGeometryPrecisionReducer* GEOSGeometryPrecisionReducer_create( GEOSPrecisionModel* model )
{
return new GEOSGeometryPrecisionReducer( *model );
}
GEOSGeometry* GEOSGeometryPrecisionReducer_reduce( GEOSGeometryPrecisionReducer* reducer, const GEOSGeometry* geometry )
{
try
{
return reducer->reduce( *geometry ).release();
}
catch ( const geos::util::GEOSException& )
{
return 0;
}
}
void GEOSGeometryPrecisionReducer_destroy( GEOSGeometryPrecisionReducer* reducer )
{
delete reducer;
}
}

View File

@ -0,0 +1,40 @@
#ifndef GEOS_C_EXTRAS_H_INCLUDED
#define GEOS_C_EXTRAS_H_INCLUDED
#include <geos_c.h>
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef GEOSPrecisionModel
typedef struct GEOSPrecisionModel_t GEOSPrecisionModel;
typedef enum { GEOS_PRECISION_FIXED,
GEOS_PRECISION_FLOATING,
GEOS_PRECISION_FLOATING_SINGLE
} GEOSPrecisionModelType;
typedef struct GEOSGeometryPrecisionReducer_t GEOSGeometryPrecisionReducer;
#endif
CORE_EXPORT GEOSPrecisionModel* GEOSPrecisionModel_create( GEOSPrecisionModelType type );
CORE_EXPORT GEOSPrecisionModel* GEOSPrecisionModel_createFixed( double scale );
CORE_EXPORT void GEOSPrecisionModel_destroy( GEOSPrecisionModel* model );
// Does not take ownership of model, model needs to stay valid
CORE_EXPORT GEOSGeometryPrecisionReducer* GEOSGeometryPrecisionReducer_create( GEOSPrecisionModel* model );
CORE_EXPORT GEOSGeometry* GEOSGeometryPrecisionReducer_reduce( GEOSGeometryPrecisionReducer* reducer, const GEOSGeometry* geometry );
CORE_EXPORT void GEOSGeometryPrecisionReducer_destroy( GEOSGeometryPrecisionReducer* reducer );
#ifdef __cplusplus
}
#endif
#endif // GEOS_C_EXTRAS_H_INCLUDED

View File

@ -0,0 +1,61 @@
#include <geos/geom/Geometry.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/PrecisionModel.h>
#include <geos/index/strtree/STRtree.h>
#include <geos/geom/prep/PreparedGeometry.h>
#include <geos/precision/GeometryPrecisionReducer.h>
#include <geos/util/GEOSException.h>
#define GEOSGeometry geos::geom::Geometry
#define GEOSPreparedGeometry geos::geom::prep::PreparedGeometry
#define GEOSSTRtree geos::index::strtree::STRtree
#define GEOSCoordSequence geos::geom::CoordinateSequence
#define GEOSPrecisionModel geos::geom::PrecisionModel
#define GEOSPrecisionModelType geos::geom::PrecisionModel::Type
#define GEOSGeometryPrecisionReducer geos::precision::GeometryPrecisionReducer
typedef struct GEOSBufParams_t GEOSBufferParams;
#include "geos_c_extra.h"
extern "C"
{
GEOSPrecisionModel* GEOSPrecisionModel_create( GEOSPrecisionModelType type )
{
return new GEOSPrecisionModel( type );
}
GEOSPrecisionModel* GEOSPrecisionModel_createFixed( double scale )
{
return new GEOSPrecisionModel( scale );
}
void GEOSPrecisionModel_destroy( GEOSPrecisionModel* model )
{
delete model;
}
GEOSGeometryPrecisionReducer* GEOSGeometryPrecisionReducer_create( GEOSPrecisionModel* model )
{
return new GEOSGeometryPrecisionReducer( *model );
}
GEOSGeometry* GEOSGeometryPrecisionReducer_reduce( GEOSGeometryPrecisionReducer* reducer, GEOSGeometry* geometry )
{
try
{
std::auto_ptr<GEOSGeometry> ptr = reducer->reduce( *geometry );
return ptr.release();
}
catch ( const geos::util::GEOSException& )
{
return 0;
}
}
void GEOSGeometryPrecisionReducer_destroy( GEOSGeometryPrecisionReducer* reducer )
{
delete reducer;
}
}

View File

@ -0,0 +1,40 @@
#ifndef GEOS_C_EXTRAS_H_INCLUDED
#define GEOS_C_EXTRAS_H_INCLUDED
#include <geos_c.h>
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef GEOSPrecisionModel
typedef struct GEOSPrecisionModel_t GEOSPrecisionModel;
typedef enum { GEOS_PRECISION_FIXED,
GEOS_PRECISION_FLOATING,
GEOS_PRECISION_FLOATING_SINGLE
} GEOSPrecisionModelType;
typedef struct GEOSGeometryPrecisionReducer_t GEOSGeometryPrecisionReducer;
#endif
CORE_EXPORT GEOSPrecisionModel* GEOSPrecisionModel_create( GEOSPrecisionModelType type );
CORE_EXPORT GEOSPrecisionModel* GEOSPrecisionModel_createFixed( double scale );
CORE_EXPORT void GEOSPrecisionModel_destroy( GEOSPrecisionModel* model );
// Does not take ownership of model, model needs to stay valid
CORE_EXPORT GEOSGeometryPrecisionReducer* GEOSGeometryPrecisionReducer_create( GEOSPrecisionModel* model );
CORE_EXPORT GEOSGeometry* GEOSGeometryPrecisionReducer_reduce( GEOSGeometryPrecisionReducer* reducer, GEOSGeometry* geometry );
CORE_EXPORT void GEOSGeometryPrecisionReducer_destroy( GEOSGeometryPrecisionReducer* reducer );
#ifdef __cplusplus
}
#endif
#endif // GEOS_C_EXTRAS_H_INCLUDED

View File

@ -343,6 +343,19 @@ const QgsCoordinateTransform* QgsMapSettings::layerTransform( QgsMapLayer *layer
}
double QgsMapSettings::layerToMapUnits( QgsMapLayer *theLayer, const QgsRectangle& referenceExtent ) const
{
QgsRectangle extent = referenceExtent.isEmpty() ? theLayer->extent() : referenceExtent;
QgsPoint l1( extent.xMinimum(), extent.yMinimum() );
QgsPoint l2( extent.xMaximum(), extent.yMaximum() );
double distLayerUnits = std::sqrt( l1.sqrDist( l2 ) );
QgsPoint m1 = layerToMapCoordinates( theLayer, l1 );
QgsPoint m2 = layerToMapCoordinates( theLayer, l2 );
double distMapUnits = std::sqrt( m1.sqrDist( m2 ) );
return distMapUnits / distLayerUnits;
}
QgsRectangle QgsMapSettings::layerExtentToOutputExtent( QgsMapLayer* theLayer, QgsRectangle extent ) const
{
if ( hasCrsTransformEnabled() )

View File

@ -186,6 +186,13 @@ class CORE_EXPORT QgsMapSettings
const QgsMapToPixel& mapToPixel() const { return mMapToPixel; }
/** Computes an *estimated* conversion factor between layer and map units: layerUnits * layerToMapUnits = mapUnits
* @param theLayer The layer
* @param referenceExtent A reference extent based on which to perform the computation. If not specified, the layer extent is used
* @note added in QGIS 2.12
*/
double layerToMapUnits( QgsMapLayer* theLayer, const QgsRectangle& referenceExtent = QgsRectangle() ) const;
/**
* @brief transform bounding box from layer's CRS to output CRS
* @see layerToMapCoordinates( QgsMapLayer* theLayer, QgsRectangle rect ) if you want to transform a rectangle

View File

@ -126,6 +126,26 @@ void QgsRectangle::scale( double scaleFactor, double centerX, double centerY )
ymax = centerY + newHeight / 2.0;
}
void QgsRectangle::grow( double delta )
{
xmin -= delta;
xmax += delta;
ymin -= delta;
ymax += delta;
}
void QgsRectangle::include( const QgsPoint &p )
{
if ( p.x() < xMinimum() )
setXMinimum( p.x() );
else if ( p.x() > xMaximum() )
setXMaximum( p.x() );
if ( p.y() < yMinimum() )
setYMinimum( p.y() );
if ( p.y() > yMaximum() )
setYMaximum( p.y() );
}
QgsRectangle QgsRectangle::buffer( double width )
{
return QgsRectangle( xmin - width, ymin - width, xmax + width, ymax + width );

View File

@ -81,6 +81,10 @@ class CORE_EXPORT QgsRectangle
//! Scale the rectangle around its center point
void scale( double scaleFactor, const QgsPoint *c = 0 );
void scale( double scaleFactor, double centerX, double centerY );
//! Grow the rectangle by the specified amount
void grow( double delta );
/** Updates the rectangle to include the specified point */
void include( const QgsPoint& p );
/** Get rectangle enlarged by buffer.
* @note added in 2.1 */
QgsRectangle buffer( double width );

View File

@ -303,7 +303,7 @@ class GUI_EXPORT QgisInterface : public QObject
virtual void showLayerProperties( QgsMapLayer *l ) = 0;
/** Open attribute table dialog */
virtual void showAttributeTable( QgsVectorLayer *l ) = 0;
virtual QDialog* showAttributeTable( QgsVectorLayer *l, const QString& filterExpression = QString() ) = 0;
/** Add window to Window menu. The action title is the window title
* and the action should raise, unminimize and activate the window. */

View File

@ -19,6 +19,8 @@ ADD_SUBDIRECTORY(gps_importer)
ADD_SUBDIRECTORY(topology)
ADD_SUBDIRECTORY(offline_editing)
ADD_SUBDIRECTORY(heatmap)
ADD_SUBDIRECTORY(geometry_checker)
ADD_SUBDIRECTORY(geometry_snapper)
IF (POSTGRES_FOUND)
ADD_SUBDIRECTORY(spit)

View File

@ -0,0 +1,106 @@
########################################################
# Files
SET (geometrychecker_SRCS
qgsgeometrychecker.cpp
qgsgeometrycheckerplugin.cpp
qgsgeometrycheckfactory.cpp
checks/qgsgeometryanglecheck.cpp
checks/qgsgeometryareacheck.cpp
checks/qgsgeometrydegeneratepolygoncheck.cpp
checks/qgsgeometryduplicatecheck.cpp
checks/qgsgeometryduplicatenodescheck.cpp
checks/qgsgeometrycheck.cpp
checks/qgsgeometrygapcheck.cpp
checks/qgsgeometryholecheck.cpp
checks/qgsgeometrymultipartcheck.cpp
checks/qgsgeometrycontainedcheck.cpp
checks/qgsgeometryoverlapcheck.cpp
checks/qgsgeometrysegmentlengthcheck.cpp
checks/qgsgeometryselfintersectioncheck.cpp
checks/qgsgeometrytypecheck.cpp
ui/qgsgeometrycheckerdialog.cpp
ui/qgsgeometrycheckersetuptab.cpp
ui/qgsgeometrycheckerresulttab.cpp
ui/qgsgeometrycheckfixdialog.cpp
ui/qgsgeometrycheckerfixsummarydialog.cpp
utils/qgsfeaturepool.cpp
utils/qgsgeomutils.cpp
)
SET (geometrychecker_HDRS
checks/qgsgeometrycheck.h
qgsgeometrycheckerplugin.h
qgsgeometrycheckfactory.h
utils/qgsfeaturepool.h
utils/qgsgeomutils.h
)
SET (geometrychecker_MOC_HDRS
qgsgeometrychecker.h
checks/qgsgeometryanglecheck.h
checks/qgsgeometryareacheck.h
checks/qgsgeometrydegeneratepolygoncheck.h
checks/qgsgeometryduplicatecheck.h
checks/qgsgeometryduplicatenodescheck.h
checks/qgsgeometrygapcheck.h
checks/qgsgeometryholecheck.h
checks/qgsgeometrymultipartcheck.h
checks/qgsgeometrycontainedcheck.h
checks/qgsgeometryoverlapcheck.h
checks/qgsgeometrysegmentlengthcheck.h
checks/qgsgeometryselfintersectioncheck.h
checks/qgsgeometrysliverpolygoncheck.h
checks/qgsgeometrytypecheck.h
ui/qgsgeometrycheckerdialog.h
ui/qgsgeometrycheckersetuptab.h
ui/qgsgeometrycheckerresulttab.h
ui/qgsgeometrycheckfixdialog.h
ui/qgsgeometrycheckerfixsummarydialog.h
)
SET (geometrychecker_UIS
ui/qgsgeometrycheckersetuptab.ui
ui/qgsgeometrycheckerresulttab.ui
ui/qgsgeometrycheckerfixsummarydialog.ui
)
SET (geometrychecker_RCCS
pluginres.qrc
)
########################################################
# Build
QT4_WRAP_UI (geometrychecker_UIS_H ${geometrychecker_UIS})
QT4_WRAP_CPP (geometrychecker_MOC_SRCS ${geometrychecker_MOC_HDRS})
QT4_ADD_RESOURCES(geometrychecker_RCC_SRCS ${geometrychecker_RCCS})
ADD_LIBRARY (geometrycheckerplugin MODULE ${geometrychecker_HDRS} ${geometrychecker_SRCS} ${geometrychecker_MOC_SRCS} ${geometrychecker_RCC_SRCS} ${geometrychecker_UIS_H})
INCLUDE_DIRECTORIES(
${CMAKE_CURRENT_BINARY_DIR}
${GEOS_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
../../core
../../core/geometry
../../core/symbology-ng
../../gui
..
)
TARGET_LINK_LIBRARIES(geometrycheckerplugin
qgis_core
qgis_gui
)
########################################################
# Install
INSTALL(TARGETS geometrycheckerplugin
RUNTIME DESTINATION ${QGIS_PLUGIN_DIR}
LIBRARY DESTINATION ${QGIS_PLUGIN_DIR})

View File

@ -0,0 +1,132 @@
/***************************************************************************
* qgsgeometryanglecheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometryanglecheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
const QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
int nVerts = QgsGeomUtils::polyLineSize( geom, iPart, iRing );
// Less than three points, no angles to check
if ( nVerts < 3 )
{
continue;
}
for ( int iVert = 0; iVert < nVerts; ++iVert )
{
const QgsPointV2& p1 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert - 1 + nVerts ) % nVerts ) );
const QgsPointV2& p2 = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
const QgsPointV2& p3 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert + 1 ) % nVerts ) );
QgsVector v21, v23;
try
{
v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normal();
v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normal();
}
catch ( const QgsException& )
{
// Zero length vectors
continue;
}
double angle = std::acos( v21 * v23 ) / M_PI * 180.0;
if ( angle < mMinAngle )
{
errors.append( new QgsGeometryCheckError( this, featureid, p2, QgsVertexId( iPart, iRing, iVert ), angle ) );
}
}
}
}
}
}
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
}
QgsAbstractGeometryV2* geometry = feature.geometry()->geometry();
const QgsVertexId& vidx = error->vidx();
// Check if point still exists
if ( !vidx.isValid( geometry ) )
{
error->setObsolete();
return;
}
// Check if error still applies
int n = QgsGeomUtils::polyLineSize( geometry, vidx.part, vidx.ring );
const QgsPointV2& p1 = geometry->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + n ) % n ) );
const QgsPointV2& p2 = geometry->vertexAt( vidx );
const QgsPointV2& p3 = geometry->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) );
QgsVector v21, v23;
try
{
v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normal();
v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normal();
}
catch ( const QgsException& )
{
error->setObsolete();
return;
}
double angle = std::acos( v21 * v23 ) / M_PI * 180.0;
if ( angle >= mMinAngle )
{
error->setObsolete();
return;
}
// Fix error
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == DeleteNode )
{
if ( n <= 3 )
{
error->setFixFailed( tr( "Resulting geometry is degenerate" ) );
}
else
{
geometry->deleteVertex( vidx );
mFeaturePool->updateFeature( feature );
error->setFixed( method );
changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) );
}
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
const QStringList& QgsGeometryAngleCheck::getResolutionMethods() const
{
static QStringList methods = QStringList() << tr( "Delete node with small angle" ) << tr( "No action" );
return methods;
}

View File

@ -0,0 +1,30 @@
/***************************************************************************
* qgsgeometryanglecheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_ANGLE_CHECK_H
#define QGS_GEOMETRY_ANGLE_CHECK_H
#include "qgsgeometrycheck.h"
class QgsGeometryAngleCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometryAngleCheck( QgsFeaturePool* featurePool, double minAngle )
: QgsGeometryCheck( FeatureNodeCheck, featurePool ), mMinAngle( minAngle ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList& messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Minimal angle" ); }
QString errorName() const { return "QgsGeometryAngleCheck"; }
private:
enum ResolutionMethod { DeleteNode, NoChange };
double mMinAngle;
};
#endif // QGS_GEOMETRY_ANGLE_CHECK_H

View File

@ -0,0 +1,216 @@
/***************************************************************************
* qgsgeometryareacheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometryengine.h"
#include "qgsgeometrycollectionv2.h"
#include "qgsgeometryareacheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
if ( dynamic_cast<QgsGeometryCollectionV2*>( geom ) )
{
QgsGeometryCollectionV2* multiGeom = static_cast<QgsGeometryCollectionV2*>( geom );
for ( int i = 0, n = multiGeom->numGeometries(); i < n; ++i )
{
double value;
if ( checkThreshold( multiGeom->geometryN( i ), value ) )
{
errors.append( new QgsGeometryCheckError( this, featureid, multiGeom->geometryN( i )->centroid(), QgsVertexId( i ), value, QgsGeometryCheckError::ValueArea ) );
}
}
}
else
{
double value;
if ( checkThreshold( geom, value ) )
{
errors.append( new QgsGeometryCheckError( this, featureid, geom->centroid(), QgsVertexId( 0 ), value, QgsGeometryCheckError::ValueArea ) );
}
}
}
}
void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes &changes ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
const QgsVertexId& vidx = error->vidx();
// Check if polygon still exists
if ( !vidx.isValid( geom ) )
{
error->setObsolete();
return;
}
// Check if error still applies
if ( dynamic_cast<QgsGeometryCollectionV2*>( geom ) )
{
double value;
if ( !checkThreshold( static_cast<QgsGeometryCollectionV2*>( geom )->geometryN( vidx.part ), value ) )
{
error->setObsolete();
return;
}
}
else
{
double value;
if ( !checkThreshold( geom, value ) )
{
error->setObsolete();
return;
}
}
// Fix with selected method
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == Delete )
{
deleteFeatureGeometryPart( feature, vidx.part, changes );
error->setFixed( method );
}
else if ( method == MergeLongestEdge || method == MergeLargestArea || method == MergeIdenticalAttribute )
{
QString errMsg;
if ( mergeWithNeighbor( feature, vidx.part, method, mergeAttributeIndex, changes, errMsg ) )
{
error->setFixed( method );
}
else
{
error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
}
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
bool QgsGeometryAreaCheck::mergeWithNeighbor( QgsFeature& feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString& errMsg ) const
{
double maxVal = 0.;
QgsFeature mergeFeature;
int mergePartIdx = -1;
bool matchFound = false;
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
// Search for touching neighboring geometries
foreach ( const QgsFeatureId& testId, mFeaturePool->getIntersects( feature.geometry()->boundingBox() ) )
{
QgsFeature testFeature;
if ( !mFeaturePool->get( testId, testFeature ) )
{
continue;
}
QgsAbstractGeometryV2* testGeom = testFeature.geometry()->geometry();
for ( int testPartIdx = 0, nTestParts = testGeom->partCount(); testPartIdx < nTestParts; ++testPartIdx )
{
if ( testId == feature.id() && testPartIdx == partIdx )
{
continue;
}
double len = QgsGeomUtils::sharedEdgeLength( QgsGeomUtils::getGeomPart( geom, partIdx ), QgsGeomUtils::getGeomPart( testGeom, testPartIdx ), QgsGeometryCheckPrecision::reducedTolerance() );
if ( len > 0. )
{
if ( method == MergeLongestEdge || method == MergeLargestArea )
{
double val;
if ( method == MergeLongestEdge )
{
val = len;
}
else
{
if ( dynamic_cast<QgsGeometryCollectionV2*>( testGeom ) )
val = static_cast<QgsGeometryCollectionV2*>( testGeom )->geometryN( testPartIdx )->area();
else
val = testGeom->area();
}
if ( val > maxVal )
{
maxVal = val;
mergeFeature = testFeature;
mergePartIdx = testPartIdx;
}
}
else if ( method == MergeIdenticalAttribute )
{
if ( testFeature.attribute( mergeAttributeIndex ) == feature.attribute( mergeAttributeIndex ) )
{
mergeFeature = testFeature;
mergePartIdx = testPartIdx;
matchFound = true;
break;
}
}
}
}
if ( matchFound )
{
break;
}
}
if ( !matchFound && maxVal == 0. )
{
return method == MergeIdenticalAttribute ? true : false;
}
// Remove polygon from source geometry
deleteFeatureGeometryPart( feature, partIdx, changes );
if ( mergeFeature.id() == feature.id() && mergePartIdx > partIdx )
{
--mergePartIdx;
}
// Merge geometries
QgsAbstractGeometryV2* mergeGeom = mergeFeature.geometry()->geometry();
QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( QgsGeomUtils::getGeomPart( mergeGeom, mergePartIdx ), QgsGeometryCheckPrecision::precision() );
QgsAbstractGeometryV2* combinedGeom = geomEngine->combine( *QgsGeomUtils::getGeomPart( geom, partIdx ), &errMsg );
delete geomEngine;
if ( !combinedGeom || combinedGeom->isEmpty() )
{
return false;
}
replaceFeatureGeometryPart( mergeFeature, mergePartIdx, combinedGeom, changes );
return true;
}
const QStringList& QgsGeometryAreaCheck::getResolutionMethods() const
{
static QStringList methods = QStringList()
<< tr( "Merge with neighboring polygon with longest shared edge" )
<< tr( "Merge with neighboring polygon with largest area" )
<< tr( "Merge with neighboring polygon with identical attribute value, if any, or leave as is" )
<< tr( "Delete feature" )
<< tr( "No action" );
return methods;
}

View File

@ -0,0 +1,37 @@
/***************************************************************************
* qgsgeometryareacheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_AREA_CHECK_H
#define QGS_GEOMETRY_AREA_CHECK_H
#include "qgsgeometrycheck.h"
class QgsSurfaceV2;
class QgsGeometryAreaCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometryAreaCheck( QgsFeaturePool* featurePool, double threshold )
: QgsGeometryCheck( FeatureCheck, featurePool ), mThreshold( threshold ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList& messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Minimal area" ); }
QString errorName() const { return "QgsGeometryAreaCheck"; }
private:
enum ResolutionMethod { MergeLongestEdge, MergeLargestArea, MergeIdenticalAttribute, Delete, NoChange };
bool mergeWithNeighbor( QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes , QString &errMsg ) const;
virtual bool checkThreshold( const QgsAbstractGeometryV2* geom, double& value ) const { value = geom->area(); return value < mThreshold; }
protected:
double mThreshold;
};
#endif // QGS_GEOMETRY_AREA_CHECK_H

View File

@ -0,0 +1,208 @@
/***************************************************************************
* qgsgeometrycheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometrycollectionv2.h"
#include "qgscurvepolygonv2.h"
#include "qgsgeometrycheck.h"
#include "../utils/qgsfeaturepool.h"
QgsGeometryCheckPrecision::QgsGeometryCheckPrecision()
{
mPrecision = 10;
mReducedPrecision = 6;
}
QgsGeometryCheckPrecision* QgsGeometryCheckPrecision::get()
{
static QgsGeometryCheckPrecision instance;
return &instance;
}
void QgsGeometryCheckPrecision::setPrecision( int tolerance )
{
get()->mPrecision = tolerance;
get()->mReducedPrecision = tolerance / 2;
}
int QgsGeometryCheckPrecision::precision()
{
return get()->mPrecision;
}
int QgsGeometryCheckPrecision::reducedPrecision()
{
return get()->mReducedPrecision;
}
double QgsGeometryCheckPrecision::tolerance()
{
return qPow( 10, -get()->mPrecision );
}
double QgsGeometryCheckPrecision::reducedTolerance()
{
return qPow( 10, -get()->mReducedPrecision );
}
QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck* check,
const QgsFeatureId& featureId,
const QgsPointV2& errorLocation,
const QgsVertexId& vidx,
const QVariant& value , ValueType valueType )
: mCheck( check ),
mFeatureId( featureId ),
mErrorLocation( errorLocation ),
mVidx( vidx ),
mValue( value ),
mValueType( valueType ),
mStatus( StatusPending )
{}
QgsGeometryCheckError::~QgsGeometryCheckError()
{
}
QgsAbstractGeometryV2 *QgsGeometryCheckError::geometry()
{
QgsFeature f;
if ( mCheck->getFeaturePool()->get( featureId(), f ) && f.geometry() )
return f.geometry()->geometry()->clone();
return 0;
}
bool QgsGeometryCheckError::handleChanges( const QgsGeometryCheck::Changes& changes )
{
if ( status() == StatusObsolete )
{
return false;
}
foreach ( const QgsGeometryCheck::Change& change, changes.value( featureId() ) )
{
if ( change.what == QgsGeometryCheck::ChangeFeature )
{
if ( change.type == QgsGeometryCheck::ChangeRemoved )
{
return false;
}
else if ( change.type == QgsGeometryCheck::ChangeChanged )
{
// If the check is checking the feature at geometry nodes level, the
// error almost certainly invalid after a geometry change. In the other
// cases, it might likely still be valid.
return mCheck->getCheckType() == QgsGeometryCheck::FeatureNodeCheck ? false : true;
}
}
else if ( change.what == QgsGeometryCheck::ChangePart )
{
if ( change.type == QgsGeometryCheck::ChangeChanged || mVidx.part == change.vidx.part )
{
return false;
}
else if ( mVidx.part > change.vidx.part )
{
mVidx.part += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
}
}
else if ( change.what == QgsGeometryCheck::ChangeRing )
{
if ( mVidx.partEqual( change.vidx ) )
{
if ( change.type == QgsGeometryCheck::ChangeChanged || mVidx.ring == change.vidx.ring )
{
return false;
}
else if ( mVidx.ring > change.vidx.ring )
{
mVidx.ring += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
}
}
}
else if ( change.what == QgsGeometryCheck::ChangeNode )
{
if ( mVidx.ringEqual( change.vidx ) )
{
if ( mVidx.vertex == change.vidx.vertex )
{
return false;
}
else if ( mVidx.vertex > change.vidx.vertex )
{
mVidx.vertex += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
}
}
}
}
return true;
}
void QgsGeometryCheck::replaceFeatureGeometryPart( QgsFeature& feature, int partIdx, QgsAbstractGeometryV2* newPartGeom, Changes& changes ) const
{
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
if ( dynamic_cast<QgsGeometryCollectionV2*>( geom ) )
{
QgsGeometryCollectionV2* GeomCollection = static_cast<QgsGeometryCollectionV2*>( geom );
GeomCollection->removeGeometry( partIdx );
GeomCollection->addGeometry( newPartGeom );
changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved, QgsVertexId( partIdx ) ) );
changes[feature.id()].append( Change( ChangeFeature, ChangeAdded, QgsVertexId( GeomCollection->partCount() - 1 ) ) );
}
else
{
feature.setGeometry( new QgsGeometry( newPartGeom ) );
changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
}
mFeaturePool->updateFeature( feature );
}
void QgsGeometryCheck::deleteFeatureGeometryPart( QgsFeature &feature, int partIdx, Changes &changes ) const
{
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
if ( dynamic_cast<QgsGeometryCollectionV2*>( geom ) )
{
static_cast<QgsGeometryCollectionV2*>( geom )->removeGeometry( partIdx );
if ( static_cast<QgsGeometryCollectionV2*>( geom )->numGeometries() == 0 )
{
mFeaturePool->deleteFeature( feature );
changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) );
}
else
{
mFeaturePool->updateFeature( feature );
changes[feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( partIdx ) ) );
}
}
else
{
mFeaturePool->deleteFeature( feature );
changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) );
}
}
void QgsGeometryCheck::deleteFeatureGeometryRing( QgsFeature &feature, int partIdx, int ringIdx, Changes &changes ) const
{
QgsAbstractGeometryV2* partGeom = QgsGeomUtils::getGeomPart( feature.geometry()->geometry(), partIdx );
if ( dynamic_cast<QgsCurvePolygonV2*>( partGeom ) )
{
// If we delete the exterior ring of a polygon, it makes no sense to keep the interiors
if ( ringIdx == 0 )
{
deleteFeatureGeometryPart( feature, partIdx, changes );
}
else
{
static_cast<QgsCurvePolygonV2*>( partGeom )->removeInteriorRing( ringIdx - 1 );
mFeaturePool->updateFeature( feature );
changes[feature.id()].append( Change( ChangeRing, ChangeRemoved, QgsVertexId( partIdx, ringIdx ) ) );
}
}
// Other geometry types do not have rings, remove the entire part
else
{
deleteFeatureGeometryPart( feature, partIdx, changes );
}
}

View File

@ -0,0 +1,153 @@
/***************************************************************************
* qgsgeometrycheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_CHECK_H
#define QGS_GEOMETRY_CHECK_H
#include <limits>
#include <QStringList>
#include "qgsfeature.h"
#include "qgsvectorlayer.h"
#include "geometry/qgsgeometry.h"
#include "../utils/qgsgeomutils.h"
#include <geosextra/geos_c_extra.h>
#include <QApplication>
class QgsGeometryCheckError;
class QgsFeaturePool;
#define FEATUREID_NULL std::numeric_limits<QgsFeatureId>::min()
class QgsGeometryCheckPrecision
{
public:
static void setPrecision( int precision );
static int precision();
static int reducedPrecision();
static double tolerance();
static double reducedTolerance();
private:
QgsGeometryCheckPrecision();
static QgsGeometryCheckPrecision* get();
int mPrecision;
int mReducedPrecision;
};
class QgsGeometryCheck : public QObject
{
public:
enum ChangeWhat { ChangeFeature, ChangePart, ChangeRing, ChangeNode };
enum ChangeType { ChangeAdded, ChangeRemoved, ChangeChanged };
enum CheckType { FeatureNodeCheck, FeatureCheck, LayerCheck };
struct Change
{
Change() {}
Change( ChangeWhat _what, ChangeType _type, QgsVertexId _vidx = QgsVertexId() )
: what( _what ), type( _type ), vidx( _vidx ) {}
ChangeWhat what;
ChangeType type;
QgsVertexId vidx;
};
typedef QMap<QgsFeatureId, QList<Change> > Changes;
QgsGeometryCheck( CheckType checkType, QgsFeaturePool* featurePool ) : mCheckType( checkType ), mFeaturePool( featurePool ) {}
virtual ~QgsGeometryCheck() {}
virtual void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList& messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const = 0;
virtual void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const = 0;
virtual const QStringList& getResolutionMethods() const = 0;
virtual QString errorDescription() const = 0;
virtual QString errorName() const = 0;
CheckType getCheckType() const { return mCheckType; }
QgsFeaturePool* getFeaturePool() const { return mFeaturePool; }
protected:
const CheckType mCheckType;
QgsFeaturePool* mFeaturePool;
void replaceFeatureGeometryPart( QgsFeature& feature, int partIdx, QgsAbstractGeometryV2* newPartGeom, Changes& changes ) const;
void deleteFeatureGeometryPart( QgsFeature& feature, int partIdx, Changes& changes ) const;
void deleteFeatureGeometryRing( QgsFeature& feature, int partIdx, int ringIdx, Changes& changes ) const;
};
class QgsGeometryCheckError
{
public:
enum Status { StatusPending, StatusFixFailed, StatusFixed, StatusObsolete };
enum ValueType { ValueLength, ValueArea, ValueOther };
QgsGeometryCheckError( const QgsGeometryCheck* check,
const QgsFeatureId& featureId,
const QgsPointV2& errorLocation,
const QgsVertexId& vidx = QgsVertexId(),
const QVariant& value = QVariant(),
ValueType valueType = ValueOther );
virtual ~QgsGeometryCheckError();
const QgsGeometryCheck* check() const { return mCheck; }
const QgsFeatureId& featureId() const { return mFeatureId; }
virtual QgsAbstractGeometryV2* geometry();
virtual QgsRectangle affectedAreaBBox() { return geometry() ? geometry()->boundingBox() : QgsRectangle(); }
virtual QString description() const { return mCheck->errorDescription(); }
const QgsPointV2& location() const { return mErrorLocation; }
const QVariant& value() const { return mValue; }
ValueType valueType() const { return mValueType; }
const QgsVertexId& vidx() const { return mVidx; }
Status status() const { return mStatus; }
const QString& resolutionMessage() const { return mResolutionMessage; }
void setFixed( int method )
{
mStatus = StatusFixed;
mResolutionMessage = mCheck->getResolutionMethods()[method];
}
void setFixFailed( const QString& reason )
{
mStatus = StatusFixFailed;
mResolutionMessage = reason;
}
void setObsolete() { mStatus = StatusObsolete; }
virtual bool isEqual( QgsGeometryCheckError* other ) const
{
return other->check() == check() &&
other->featureId() == featureId() &&
other->vidx() == vidx();
}
virtual bool closeMatch( QgsGeometryCheckError* /*other*/ ) const
{
return false;
}
virtual void update( const QgsGeometryCheckError* other )
{
assert( mCheck == other->mCheck );
assert( mFeatureId == other->mFeatureId );
mErrorLocation = other->mErrorLocation;
mVidx = other->mVidx;
mValue = other->mValue;
}
virtual bool handleChanges( const QgsGeometryCheck::Changes& changes );
protected:
const QgsGeometryCheck* mCheck;
QgsFeatureId mFeatureId;
QgsPointV2 mErrorLocation;
QgsVertexId mVidx;
QVariant mValue;
ValueType mValueType;
Status mStatus;
QString mResolutionMessage;
private:
const QgsGeometryCheckError& operator=( const QgsGeometryCheckError& );
};
Q_DECLARE_METATYPE( QgsGeometryCheckError* )
#endif // QGS_GEOMETRY_CHECK_H

View File

@ -0,0 +1,100 @@
/***************************************************************************
* qgsgeometrycontainedcheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometryengine.h"
#include "qgsgeometrycontainedcheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryContainedCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( feature.geometry()->geometry(), QgsGeometryCheckPrecision::precision() );
QgsFeatureIds ids = mFeaturePool->getIntersects( feature.geometry()->geometry()->boundingBox() );
foreach ( const QgsFeatureId& otherid, ids )
{
if ( otherid == featureid )
{
continue;
}
QgsFeature otherFeature;
if ( !mFeaturePool->get( otherid, otherFeature ) )
{
continue;
}
QString errMsg;
if ( geomEngine->within( *otherFeature.geometry()->geometry(), &errMsg ) )
{
errors.append( new QgsGeometryContainedCheckError( this, featureid, feature.geometry()->geometry()->centroid(), otherid ) );
}
else if ( !errMsg.isEmpty() )
{
messages.append( tr( "Feature %1 within feature %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) );
}
}
delete geomEngine;
}
}
void QgsGeometryContainedCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
QgsGeometryContainedCheckError* coverError = static_cast<QgsGeometryContainedCheckError*>( error );
QgsFeature feature;
QgsFeature otherFeature;
if ( !mFeaturePool->get( error->featureId(), feature ) ||
!mFeaturePool->get( coverError->otherId(), otherFeature ) )
{
error->setObsolete();
return;
}
// Check if error still applies
QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( feature.geometry()->geometry(), QgsGeometryCheckPrecision::precision() );
if ( !geomEngine->within( *otherFeature.geometry()->geometry() ) )
{
delete geomEngine;
error->setObsolete();
return;
}
delete geomEngine;
// Fix error
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == Delete )
{
changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) );
mFeaturePool->deleteFeature( feature );
error->setFixed( method );
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
const QStringList& QgsGeometryContainedCheck::getResolutionMethods() const
{
static QStringList methods = QStringList()
<< tr( "Delete feature" )
<< tr( "No action" );
return methods;
}

View File

@ -0,0 +1,52 @@
/***************************************************************************
* qgsgeometrycontainedcheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_COVER_CHECK_H
#define QGS_GEOMETRY_COVER_CHECK_H
#include "qgsgeometrycheck.h"
class QgsGeometryContainedCheckError : public QgsGeometryCheckError
{
public:
QgsGeometryContainedCheckError( const QgsGeometryCheck* check,
const QgsFeatureId& featureId,
const QgsPointV2& errorLocation,
const QgsFeatureId& otherId
)
: QgsGeometryCheckError( check, featureId, errorLocation, QgsVertexId(), otherId, ValueOther ), mOtherId( otherId ) { }
const QgsFeatureId& otherId() const { return mOtherId; }
bool isEqual( QgsGeometryCheckError* other ) const
{
return other->check() == check() &&
other->featureId() == featureId() &&
static_cast<QgsGeometryContainedCheckError*>( other )->otherId() == otherId();
}
virtual QString description() const { return QApplication::translate( "QgsGeometryContainedCheckError", "Within %1" ).arg( otherId() ); }
private:
QgsFeatureId mOtherId;
};
class QgsGeometryContainedCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometryContainedCheck( QgsFeaturePool* featurePool ) : QgsGeometryCheck( FeatureCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList& messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Within" ); }
QString errorName() const { return "QgsGeometryContainedCheck"; }
private:
enum ResolutionMethod { Delete, NoChange };
};
#endif // QGS_GEOMETRY_COVER_CHECK_H

View File

@ -0,0 +1,81 @@
/***************************************************************************
* qgsgeometrydegeneratepolygoncheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometrydegeneratepolygoncheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryDegeneratePolygonCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
if ( QgsGeomUtils::polyLineSize( geom, iPart, iRing ) < 3 )
{
errors.append( new QgsGeometryCheckError( this, featureid, geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ), QgsVertexId( iPart, iRing ) ) );
}
}
}
}
}
void QgsGeometryDegeneratePolygonCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
const QgsVertexId& vidx = error->vidx();
// Check if ring still exists
if ( !vidx.isValid( geom ) )
{
error->setObsolete();
return;
}
// Check if error still applies
if ( QgsGeomUtils::polyLineSize( geom, vidx.part, vidx.ring ) >= 3 )
{
error->setObsolete();
return;
}
// Fix error
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == DeleteRing )
{
deleteFeatureGeometryRing( feature, vidx.part, vidx.ring, changes );
error->setFixed( method );
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
const QStringList& QgsGeometryDegeneratePolygonCheck::getResolutionMethods() const
{
static QStringList methods = QStringList() << tr( "Delete feature" ) << tr( "No action" );
return methods;
}

View File

@ -0,0 +1,30 @@
/***************************************************************************
* qgsgeometrydegeneratepolygoncheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_DEGENERATEPOLYGON_CHECK_H
#define QGS_GEOMETRY_DEGENERATEPOLYGON_CHECK_H
#include "qgsgeometrycheck.h"
class QgsGeometryDegeneratePolygonCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometryDegeneratePolygonCheck( QgsFeaturePool* featurePool )
: QgsGeometryCheck( FeatureNodeCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Polygon with less than three nodes" ); }
QString errorName() const { return "QgsGeometryDegeneratePolygonCheck"; }
private:
enum ResolutionMethod { DeleteRing, NoChange };
};
#endif // QGS_GEOMETRY_DEGENERATEPOLYGON_CHECK_H

View File

@ -0,0 +1,111 @@
/***************************************************************************
* qgsgeometryduplicatecheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometryengine.h"
#include "qgsgeometryduplicatecheck.h"
#include "qgsspatialindex.h"
#include "qgsgeometry.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryDuplicateCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( feature.geometry()->geometry(), QgsGeometryCheckPrecision::precision() );
QList<QgsFeatureId> duplicates;
QgsFeatureIds ids = mFeaturePool->getIntersects( feature.geometry()->geometry()->boundingBox() );
foreach ( const QgsFeatureId& id, ids )
{
// > : only report overlaps once
if ( id >= featureid )
{
continue;
}
QgsFeature testFeature;
if ( !mFeaturePool->get( id, testFeature ) )
{
continue;
}
QString errMsg;
QgsAbstractGeometryV2* diffGeom = geomEngine->symDifference( *testFeature.geometry()->geometry(), &errMsg );
if ( diffGeom && diffGeom->area() < QgsGeometryCheckPrecision::tolerance() )
{
duplicates.append( id );
}
else if ( !diffGeom )
{
messages.append( tr( "Duplicate check between features %1 and %2: %3" ).arg( feature.id() ).arg( testFeature.id() ).arg( errMsg ) );
}
delete diffGeom;
}
if ( !duplicates.isEmpty() )
{
qSort( duplicates );
errors.append( new QgsGeometryDuplicateCheckError( this, featureid, feature.geometry()->geometry()->centroid(), duplicates ) );
}
delete geomEngine;
}
}
void QgsGeometryDuplicateCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
}
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == RemoveDuplicates )
{
QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( feature.geometry()->geometry(), QgsGeometryCheckPrecision::precision() );
QgsGeometryDuplicateCheckError* duplicateError = static_cast<QgsGeometryDuplicateCheckError*>( error );
foreach ( const QgsFeatureId& id, duplicateError->duplicates() )
{
QgsFeature testFeature;
if ( !mFeaturePool->get( id, testFeature ) )
{
continue;
}
QgsAbstractGeometryV2* diffGeom = geomEngine->symDifference( *testFeature.geometry()->geometry() );
if ( diffGeom && diffGeom->area() < QgsGeometryCheckPrecision::tolerance() )
{
mFeaturePool->deleteFeature( testFeature );
changes[id].append( Change( ChangeFeature, ChangeRemoved ) );
}
delete diffGeom;
}
delete geomEngine;
error->setFixed( method );
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
const QStringList& QgsGeometryDuplicateCheck::getResolutionMethods() const
{
static QStringList methods = QStringList()
<< tr( "No action" )
<< tr( "Remove duplicates" );
return methods;
}

View File

@ -0,0 +1,62 @@
/***************************************************************************
* qgsgeometryduplicatecheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_DUPLICATE_CHECK_H
#define QGS_GEOMETRY_DUPLICATE_CHECK_H
#include "qgsgeometrycheck.h"
class QgsGeometryDuplicateCheckError : public QgsGeometryCheckError
{
public:
QgsGeometryDuplicateCheckError( const QgsGeometryCheck* check,
const QgsFeatureId& featureId,
const QgsPointV2& errorLocation,
const QList<QgsFeatureId>& duplicates )
: QgsGeometryCheckError( check, featureId, errorLocation, QgsVertexId(), duplicatesString( duplicates ) ), mDuplicates( duplicates ) { }
const QList<QgsFeatureId>& duplicates() const { return mDuplicates; }
bool isEqual( QgsGeometryCheckError* other ) const
{
return other->check() == check() &&
other->featureId() == featureId() &&
// static_cast: since other->checker() == checker is only true if the types are actually the same
static_cast<QgsGeometryDuplicateCheckError*>( other )->duplicates() == duplicates();
}
private:
QList<QgsFeatureId> mDuplicates;
static inline QString duplicatesString( const QList<QgsFeatureId>& duplicates )
{
QStringList str;
foreach ( QgsFeatureId id, duplicates )
{
str.append( QString::number( id ) );
}
return str.join( ", " );
}
};
class QgsGeometryDuplicateCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometryDuplicateCheck( QgsFeaturePool* featurePool )
: QgsGeometryCheck( FeatureCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Duplicate" ); }
QString errorName() const { return "QgsGeometryDuplicateCheck"; }
private:
enum ResolutionMethod { NoChange, RemoveDuplicates };
};
#endif // QGS_GEOMETRY_DUPLICATE_CHECK_H

View File

@ -0,0 +1,105 @@
/***************************************************************************
* qgsgeometryduplicatenodescheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometryduplicatenodescheck.h"
#include "qgsgeometryutils.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryDuplicateNodesCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
int nVerts = QgsGeomUtils::polyLineSize( geom, iPart, iRing );
if ( nVerts < 2 )
continue;
for ( int iVert = nVerts - 1, jVert = 0; jVert < nVerts; iVert = jVert++ )
{
QgsPointV2 pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
QgsPointV2 pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) );
QgsDebugMsg( QString( "(%1,%2) (%3,%4) d = %5, tol = %6" ).arg( pi.x(), 0, 'f', 6 ).arg( pi.y(), 0, 'f', 6 ).arg( pj.x(), 0, 'f', 6 ).arg( pj.y(), 0, 'f', 6 ).arg( QgsGeometryUtils::sqrDistance2D( pi, pj ) ).arg( QgsGeometryCheckPrecision::tolerance(), 0, 'f', 16 ) );
if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() )
{
QgsDebugMsg( "Duplicate" );
errors.append( new QgsGeometryCheckError( this, featureid, pj, QgsVertexId( iPart, iRing, jVert ) ) );
}
}
}
}
}
}
void QgsGeometryDuplicateNodesCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
const QgsVertexId& vidx = error->vidx();
// Check if point still exists
if ( !vidx.isValid( geom ) )
{
error->setObsolete();
return;
}
// Check if error still applies
int nVerts = QgsGeomUtils::polyLineSize( geom, vidx.part, vidx.ring );
QgsPointV2 pi = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + nVerts - 1 ) % nVerts ) );
QgsPointV2 pj = geom->vertexAt( error->vidx() );
if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) >= QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() )
{
error->setObsolete();
return;
}
// Fix error
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == RemoveDuplicates )
{
geom->deleteVertex( error->vidx() );
if ( QgsGeomUtils::polyLineSize( geom, vidx.part, vidx.ring ) < 3 )
{
error->setFixFailed( tr( "Resulting geometry is degenerate" ) );
}
else
{
mFeaturePool->updateFeature( feature );
error->setFixed( method );
changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, error->vidx() ) );
}
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
const QStringList& QgsGeometryDuplicateNodesCheck::getResolutionMethods() const
{
static QStringList methods = QStringList() << tr( "Delete duplicate node" ) << tr( "No action" );
return methods;
}

View File

@ -0,0 +1,30 @@
/***************************************************************************
* qgsgeometryduplicatenodescheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_DUPLICATENODES_CHECK_H
#define QGS_GEOMETRY_DUPLICATENODES_CHECK_H
#include "qgsgeometrycheck.h"
class QgsGeometryDuplicateNodesCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometryDuplicateNodesCheck( QgsFeaturePool* featurePool )
: QgsGeometryCheck( FeatureNodeCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Duplicate node" ); }
QString errorName() const { return "QgsGeometryDuplicateNodesCheck"; }
private:
enum ResolutionMethod { RemoveDuplicates, NoChange };
};
#endif // QGS_GEOMETRY_DUPLICATENODES_CHECK_H

View File

@ -0,0 +1,209 @@
/***************************************************************************
* qgsgeometrygapcheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometryengine.h"
#include "qgsgeometrygapcheck.h"
#include "qgsgeometrycollectionv2.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
assert( mFeaturePool->getLayer()->geometryType() == QGis::Polygon );
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
// Collect geometries, build spatial index
QList<const QgsAbstractGeometryV2*> geomList;
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& id, featureIds )
{
QgsFeature feature;
if ( mFeaturePool->get( id, feature ) )
{
geomList.append( feature.geometry()->geometry()->clone() );
}
}
if ( geomList.isEmpty() )
{
return;
}
QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( 0, QgsGeometryCheckPrecision::precision() );
// Create union of geometry
QString errMsg;
QgsAbstractGeometryV2* unionGeom = geomEngine->combine( geomList, &errMsg );
qDeleteAll( geomList );
delete geomEngine;
if ( !unionGeom )
{
messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
return;
}
// Get envelope of union
geomEngine = QgsGeomUtils::createGeomEngine( unionGeom, QgsGeometryCheckPrecision::precision() );
QgsAbstractGeometryV2* envelope = geomEngine->envelope( &errMsg );
delete geomEngine;
if ( !envelope )
{
messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
delete unionGeom;
return;
}
// Buffer envelope
geomEngine = QgsGeomUtils::createGeomEngine( envelope, QgsGeometryCheckPrecision::precision() );
QgsAbstractGeometryV2* bufEnvelope = geomEngine->buffer( 2, 0, GEOSBUF_CAP_SQUARE, GEOSBUF_JOIN_MITRE, 4. );
delete geomEngine;
delete envelope;
envelope = bufEnvelope;
// Compute difference between envelope and union to obtain gap polygons
geomEngine = QgsGeomUtils::createGeomEngine( envelope, QgsGeometryCheckPrecision::precision() );
QgsAbstractGeometryV2* diffGeom = geomEngine->difference( *unionGeom, &errMsg );
delete geomEngine;
if ( !diffGeom )
{
messages.append( tr( "Gap check: %1" ).arg( errMsg ) );
delete unionGeom;
delete diffGeom;
return;
}
// For each gap polygon which does not lie on the boundary, get neighboring polygons and add error
for ( int iPart = 0, nParts = diffGeom->partCount(); iPart < nParts; ++iPart )
{
QgsAbstractGeometryV2* geom = QgsGeomUtils::getGeomPart( diffGeom, iPart );
// Skip the gap between features and boundingbox
if ( geom->boundingBox() == envelope->boundingBox() )
{
continue;
}
// Skip gaps above threshold
if ( geom->area() > mThreshold || geom->area() < QgsGeometryCheckPrecision::reducedTolerance() )
{
continue;
}
// Get neighboring polygons
QgsFeatureIds neighboringIds;
QgsRectangle gapAreaBBox = geom->boundingBox();
QgsFeatureIds intersectIds = mFeaturePool->getIntersects( geom->boundingBox() );
foreach ( QgsFeatureId id, intersectIds )
{
QgsFeature feature;
if ( !mFeaturePool->get( id, feature ) )
{
continue;
}
QgsAbstractGeometryV2* geom2 = feature.geometry()->geometry();
if ( QgsGeomUtils::sharedEdgeLength( geom, geom2, QgsGeometryCheckPrecision::reducedTolerance() ) > 0 )
{
neighboringIds.insert( feature.id() );
gapAreaBBox.unionRect( geom2->boundingBox() );
}
}
if ( neighboringIds.isEmpty() )
{
delete geom;
continue;
}
// Add error
errors.append( new QgsGeometryGapCheckError( this, geom->clone(), neighboringIds, geom->area(), gapAreaBBox ) );
}
delete unionGeom;
delete envelope;
delete diffGeom;
}
void QgsGeometryGapCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == MergeLongestEdge )
{
QString errMsg;
if ( mergeWithNeighbor( static_cast<QgsGeometryGapCheckError*>( error ), changes, errMsg ) )
{
error->setFixed( method );
}
else
{
error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) );
}
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError* err, Changes &changes, QString& errMsg ) const
{
double maxVal = 0.;
QgsFeature mergeFeature;
int mergePartIdx;
QgsAbstractGeometryV2* errGeometry = QgsGeomUtils::getGeomPart( err->geometry(), 0 );
// Search for touching neighboring geometries
foreach ( const QgsFeatureId& testId, err->neighbors() )
{
QgsFeature testFeature;
if ( !mFeaturePool->get( testId, testFeature ) )
{
continue;
}
QgsAbstractGeometryV2* testGeom = testFeature.geometry()->geometry();
for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart )
{
double len = QgsGeomUtils::sharedEdgeLength( errGeometry, QgsGeomUtils::getGeomPart( testGeom, iPart ), QgsGeometryCheckPrecision::reducedTolerance() );
if ( len > maxVal )
{
maxVal = len;
mergeFeature = testFeature;
mergePartIdx = iPart;
}
}
}
if ( maxVal == 0. )
{
return false;
}
// Merge geometries
QgsAbstractGeometryV2* mergeGeom = mergeFeature.geometry()->geometry();
QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( errGeometry, QgsGeometryCheckPrecision::precision() );
QgsAbstractGeometryV2* combinedGeom = geomEngine->combine( *QgsGeomUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg );
delete geomEngine;
if ( !combinedGeom || combinedGeom->isEmpty() )
{
delete combinedGeom;
return false;
}
// Add merged polygon to destination geometry
replaceFeatureGeometryPart( mergeFeature, mergePartIdx, combinedGeom, changes );
return true;
}
const QStringList& QgsGeometryGapCheck::getResolutionMethods() const
{
static QStringList methods = QStringList() << tr( "Add gap area to neighboring polygon with longest shared edge" ) << tr( "No action" );
return methods;
}

View File

@ -0,0 +1,96 @@
/***************************************************************************
* qgsgeometrygapcheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_GAP_CHECK_H
#define QGS_GEOMETRY_GAP_CHECK_H
#include "qgsgeometrycheck.h"
class QgsGeometryGapCheckError : public QgsGeometryCheckError
{
public:
QgsGeometryGapCheckError( const QgsGeometryCheck* check,
QgsAbstractGeometryV2* geometry,
const QgsFeatureIds& neighbors,
double area,
const QgsRectangle& gapAreaBBox )
: QgsGeometryCheckError( check, FEATUREID_NULL, geometry->centroid(), QgsVertexId(), area, ValueArea )
, mNeighbors( neighbors )
, mGapAreaBBox( gapAreaBBox )
{
mGeometry = geometry;
}
~QgsGeometryGapCheckError()
{
delete mGeometry;
}
QgsAbstractGeometryV2* geometry() { return mGeometry->clone(); }
const QgsFeatureIds& neighbors() const { return mNeighbors; }
bool isEqual( QgsGeometryCheckError* other ) const
{
QgsGeometryGapCheckError* err = dynamic_cast<QgsGeometryGapCheckError*>( other );
return err && QgsGeomUtils::pointsFuzzyEqual( err->location(), location(), QgsGeometryCheckPrecision::reducedTolerance() ) && err->neighbors() == neighbors();
}
bool closeMatch( QgsGeometryCheckError *other ) const
{
QgsGeometryGapCheckError* err = dynamic_cast<QgsGeometryGapCheckError*>( other );
return err && err->neighbors() == neighbors();
}
void update( const QgsGeometryCheckError* other )
{
QgsGeometryCheckError::update( other );
// Static cast since this should only get called if isEqual == true
const QgsGeometryGapCheckError* err = static_cast<const QgsGeometryGapCheckError*>( other );
delete mGeometry;
mGeometry = err->mGeometry->clone();
mNeighbors = err->mNeighbors;
mGapAreaBBox = err->mGapAreaBBox;
}
bool handleChanges( const QgsGeometryCheck::Changes& /*changes*/ )
{
return true;
}
QgsRectangle affectedAreaBBox() const
{
return mGapAreaBBox;
}
private:
QgsFeatureIds mNeighbors;
QgsRectangle mGapAreaBBox;
QgsAbstractGeometryV2* mGeometry;
};
class QgsGeometryGapCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometryGapCheck( QgsFeaturePool* featurePool, double threshold )
: QgsGeometryCheck( LayerCheck, featurePool ), mThreshold( threshold ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Gap" ); }
QString errorName() const { return "QgsGeometryGapCheck"; }
private:
enum ResolutionMethod { MergeLongestEdge, NoChange };
double mThreshold;
bool mergeWithNeighbor( QgsGeometryGapCheckError *err, Changes &changes , QString &errMsg ) const;
};
#endif // QGS_GEOMETRY_GAP_CHECK_H

View File

@ -0,0 +1,73 @@
/***************************************************************************
* qgsgeometryholescheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometryholecheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryHoleCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
// Rings after the first one are interiors
for ( int iRing = 1, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
errors.append( new QgsGeometryCheckError( this, featureid, QgsGeomUtils::getGeomPart( geom, iPart )->centroid(), QgsVertexId( iPart, iRing ) ) );
}
}
}
}
void QgsGeometryHoleCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
const QgsVertexId& vidx = error->vidx();
// Check if ring still exists
if ( !vidx.isValid( geom ) )
{
error->setObsolete();
return;
}
// Fix error
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == RemoveHoles )
{
deleteFeatureGeometryRing( feature, vidx.part, vidx.ring, changes );
error->setFixed( method );
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
const QStringList& QgsGeometryHoleCheck::getResolutionMethods() const
{
static QStringList methods = QStringList() << tr( "Remove hole" ) << tr( "No action" );
return methods;
}

View File

@ -0,0 +1,29 @@
/***************************************************************************
* qgsgeometryholecheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_HOLE_CHECK_H
#define QGS_GEOMETRY_HOLE_CHECK_H
#include "qgsgeometrycheck.h"
class QgsGeometryHoleCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometryHoleCheck( QgsFeaturePool* featurePool )
: QgsGeometryCheck( FeatureCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Polygon with hole" ); }
QString errorName() const { return "QgsGeometryHoleCheck"; }
private:
enum ResolutionMethod { RemoveHoles, NoChange };
};
#endif // QGS_GEOMETRY_HOLE_CHECK_H

View File

@ -0,0 +1,80 @@
/***************************************************************************
* qgsgeometrymultipartcheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometrymultipartcheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryMultipartCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
QgsWKBTypes::Type type = geom->wkbType();
if ( geom->partCount() == 1 && QgsWKBTypes::isMultiType( type ) )
{
errors.append( new QgsGeometryCheckError( this, featureid, geom->centroid() ) );
}
}
}
void QgsGeometryMultipartCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
// Check if error still applies
if ( geom->partCount() > 1 || !QgsWKBTypes::isMultiType( geom->wkbType() ) )
{
error->setObsolete();
return;
}
// Fix error
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == ConvertToSingle )
{
feature.setGeometry( new QgsGeometry( QgsGeomUtils::getGeomPart( geom, 0 )->clone() ) );
mFeaturePool->updateFeature( feature );
error->setFixed( method );
changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
}
else if ( method == RemoveObject )
{
mFeaturePool->deleteFeature( feature );
error->setFixed( method );
changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) );
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
const QStringList& QgsGeometryMultipartCheck::getResolutionMethods() const
{
static QStringList methods = QStringList()
<< tr( "Convert to single part feature" )
<< tr( "Delete feature" )
<< tr( "No action" );
return methods;
}

View File

@ -0,0 +1,29 @@
/***************************************************************************
* qgsgeometrymultipartcheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_MULTIPART_CHECK_H
#define QGS_GEOMETRY_MULTIPART_CHECK_H
#include "qgsgeometrycheck.h"
class QgsGeometryMultipartCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometryMultipartCheck( QgsFeaturePool* featurePool )
: QgsGeometryCheck( FeatureCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Multipart object with only one feature" ); }
QString errorName() const { return "QgsGeometryMultipartCheck"; }
private:
enum ResolutionMethod { ConvertToSingle, RemoveObject, NoChange };
};
#endif // QGS_GEOMETRY_MULTIPART_CHECK_H

View File

@ -0,0 +1,192 @@
/***************************************************************************
* qgsgeometryoverlapcheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometryengine.h"
#include "qgsgeometryoverlapcheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::precision() );
QgsFeatureIds ids = mFeaturePool->getIntersects( feature.geometry()->boundingBox() );
foreach ( const QgsFeatureId& otherid, ids )
{
// >= : only report overlaps once
if ( otherid >= featureid )
{
continue;
}
QgsFeature otherFeature;
if ( !mFeaturePool->get( otherid, otherFeature ) )
{
continue;
}
QString errMsg;
if ( geomEngine->overlaps( *otherFeature.geometry()->geometry(), &errMsg ) )
{
QgsAbstractGeometryV2* interGeom = geomEngine->intersection( *otherFeature.geometry()->geometry() );
if ( interGeom && !interGeom->isEmpty() )
{
QgsGeomUtils::filter1DTypes( interGeom );
for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart )
{
double area = QgsGeomUtils::getGeomPart( interGeom, iPart )->area();
if ( area > QgsGeometryCheckPrecision::reducedTolerance() && area < mThreshold )
{
errors.append( new QgsGeometryOverlapCheckError( this, featureid, QgsGeomUtils::getGeomPart( interGeom, iPart )->centroid(), area, otherid ) );
}
}
}
else if ( !errMsg.isEmpty() )
{
messages.append( tr( "Overlap check between features %1 and %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) );
}
delete interGeom;
}
}
delete geomEngine;
}
}
void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
QString errMsg;
QgsGeometryOverlapCheckError* overlapError = static_cast<QgsGeometryOverlapCheckError*>( error );
QgsFeature feature;
QgsFeature otherFeature;
if ( !mFeaturePool->get( error->featureId(), feature ) ||
!mFeaturePool->get( overlapError->otherId(), otherFeature ) )
{
error->setObsolete();
return;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::precision() );
// Check if error still applies
if ( !geomEngine->overlaps( *otherFeature.geometry()->geometry() ) )
{
delete geomEngine;
error->setObsolete();
return;
}
QgsAbstractGeometryV2* interGeom = geomEngine->intersection( *otherFeature.geometry()->geometry(), &errMsg );
delete geomEngine;
if ( !interGeom )
{
error->setFixFailed( tr( "Failed to compute intersection between overlapping features: %1" ).arg( errMsg ) );
return;
}
// Search which overlap part this error parametrizes (using fuzzy-matching of the area and centroid...)
QgsAbstractGeometryV2* interPart = 0;
for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart )
{
QgsAbstractGeometryV2* part = QgsGeomUtils::getGeomPart( interGeom, iPart );
if ( qAbs( part->area() - overlapError->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance() &&
QgsGeomUtils::pointsFuzzyEqual( part->centroid(), overlapError->location(), QgsGeometryCheckPrecision::reducedTolerance() ) )
{
interPart = part;
break;
}
}
if ( !interPart || interPart->isEmpty() )
{
delete interGeom;
error->setObsolete();
return;
}
// Fix error
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == Subtract )
{
geomEngine = QgsGeomUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::reducedPrecision() );
QgsAbstractGeometryV2* diff1 = geomEngine->difference( *interPart, &errMsg );
delete geomEngine;
if ( !diff1 || diff1->isEmpty() )
{
delete diff1;
diff1 = 0;
}
else
{
QgsGeomUtils::filter1DTypes( diff1 );
}
QgsGeometryEngine* otherGeomEngine = QgsGeomUtils::createGeomEngine( otherFeature.geometry()->geometry(), QgsGeometryCheckPrecision::reducedPrecision() );
QgsAbstractGeometryV2* diff2 = otherGeomEngine->difference( *interPart, &errMsg );
delete otherGeomEngine;
if ( !diff2 || diff2->isEmpty() )
{
delete diff2;
diff2 = 0;
}
else
{
QgsGeomUtils::filter1DTypes( diff2 );
}
double shared1 = diff1 ? QgsGeomUtils::sharedEdgeLength( diff1, interPart, QgsGeometryCheckPrecision::reducedPrecision() ) : 0;
double shared2 = diff2 ? QgsGeomUtils::sharedEdgeLength( diff2, interPart, QgsGeometryCheckPrecision::reducedPrecision() ) : 0;
if ( shared1 == 0. || shared2 == 0. )
{
error->setFixFailed( tr( "Could not find shared edges between intersection and overlapping features" ) );
}
else
{
if ( shared1 < shared2 )
{
feature.setGeometry( new QgsGeometry( diff1 ) );
changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
mFeaturePool->updateFeature( feature );
delete diff2;
}
else
{
otherFeature.setGeometry( new QgsGeometry( diff2 ) );
changes[otherFeature.id()].append( Change( ChangeFeature, ChangeChanged ) );
mFeaturePool->updateFeature( otherFeature );
delete diff1;
}
error->setFixed( method );
}
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
delete interGeom;
}
const QStringList& QgsGeometryOverlapCheck::getResolutionMethods() const
{
static QStringList methods = QStringList()
<< tr( "Remove overlapping area from neighboring polygon with shortest shared edge" )
<< tr( "No action" );
return methods;
}

View File

@ -0,0 +1,63 @@
/***************************************************************************
* qgsgeometryoverlapcheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_OVERLAP_CHECK_H
#define QGS_GEOMETRY_OVERLAP_CHECK_H
#include "qgsgeometrycheck.h"
class QgsGeometryOverlapCheckError : public QgsGeometryCheckError
{
public:
QgsGeometryOverlapCheckError( const QgsGeometryCheck* check,
const QgsFeatureId& featureId,
const QgsPointV2& errorLocation,
const QVariant& value,
const QgsFeatureId& otherId )
: QgsGeometryCheckError( check, featureId, errorLocation, QgsVertexId(), value, ValueArea ), mOtherId( otherId ) { }
const QgsFeatureId& otherId() const { return mOtherId; }
bool isEqual( QgsGeometryCheckError* other ) const
{
QgsGeometryOverlapCheckError* err = dynamic_cast<QgsGeometryOverlapCheckError*>( other );
return err &&
other->featureId() == featureId() &&
err->otherId() == otherId() &&
QgsGeomUtils::pointsFuzzyEqual( location(), other->location(), QgsGeometryCheckPrecision::reducedTolerance() ) &&
qAbs( value().toDouble() - other->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance();
}
bool closeMatch( QgsGeometryCheckError *other ) const
{
QgsGeometryOverlapCheckError* err = dynamic_cast<QgsGeometryOverlapCheckError*>( other );
return err && other->featureId() == featureId() && err->otherId() == otherId();
}
virtual QString description() const { return QApplication::translate( "QgsGeometryTypeCheckError", "Overlap with %1" ).arg( otherId() ); }
private:
QgsFeatureId mOtherId;
};
class QgsGeometryOverlapCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometryOverlapCheck( QgsFeaturePool* featurePool, double threshold )
: QgsGeometryCheck( FeatureCheck, featurePool ), mThreshold( threshold ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Overlap" ); }
QString errorName() const { return "QgsGeometryOverlapCheck"; }
private:
enum ResolutionMethod { Subtract, NoChange };
double mThreshold;
};
#endif // QGS_GEOMETRY_OVERLAP_CHECK_H

View File

@ -0,0 +1,95 @@
/***************************************************************************
* qgsgeometrysegmentlengthcheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include <qmath.h>
#include "qgsgeometrysegmentlengthcheck.h"
#include "qgsgeometryutils.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometrySegmentLengthCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
int nVerts = QgsGeomUtils::polyLineSize( geom, iPart, iRing );
if ( nVerts < 2 )
{
continue;
}
for ( int iVert = 0, jVert = nVerts - 1; iVert < nVerts; jVert = iVert++ )
{
QgsPointV2 pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) );
QgsPointV2 pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) );
double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) );
if ( dist < mMinLength )
{
errors.append( new QgsGeometryCheckError( this, featureid, QgsPointV2( 0.5 * ( pi.x() + pj.x() ), 0.5 * ( pi.y() + pj.y() ) ), QgsVertexId( iPart, iRing, iVert ), dist, QgsGeometryCheckError::ValueLength ) );
}
}
}
}
}
}
void QgsGeometrySegmentLengthCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &/*changes*/ ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
const QgsVertexId& vidx = error->vidx();
// Check if point still exists
if ( !vidx.isValid( geom ) )
{
error->setObsolete();
return;
}
// Check if error still applies
int nVerts = QgsGeomUtils::polyLineSize( geom, vidx.part, vidx.ring );
QgsPointV2 pi = geom->vertexAt( error->vidx() );
QgsPointV2 pj = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + nVerts ) % nVerts ) );
double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) );
if ( dist >= mMinLength )
{
error->setObsolete();
return;
}
// Fix error
if ( method == NoChange )
{
error->setFixed( method );
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
const QStringList& QgsGeometrySegmentLengthCheck::getResolutionMethods() const
{
static QStringList methods = QStringList() << tr( "No action" );
return methods;
}

View File

@ -0,0 +1,30 @@
/***************************************************************************
* qgsgeometrysegmentlengthcheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_SEGMENTLENGTH_CHECK_H
#define QGS_GEOMETRY_SEGMENTLENGTH_CHECK_H
#include "qgsgeometrycheck.h"
class QgsGeometrySegmentLengthCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometrySegmentLengthCheck( QgsFeaturePool* featurePool, double minLength )
: QgsGeometryCheck( FeatureNodeCheck, featurePool ), mMinLength( minLength ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Minimal segment length" ); }
QString errorName() const { return "QgsGeometrySegmentLengthCheck"; }
private:
enum ResolutionMethod { NoChange };
double mMinLength;
};
#endif // QGS_GEOMETRY_SEGMENTLENGTH_CHECK_H

View File

@ -0,0 +1,315 @@
/***************************************************************************
* qgsgeometryselfintersectioncheck.cpp *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#include "qgsgeometryselfintersectioncheck.h"
#include "qgscurvepolygonv2.h"
#include "qgslinestringv2.h"
#include "qgsgeometryengine.h"
#include "qgsmultipolygonv2.h"
#include "qgsmulticurvev2.h"
#include "qgsgeometryutils.h"
#include "../utils/qgsfeaturepool.h"
bool QgsGeometrySelfIntersectionCheckError::isEqual( QgsGeometryCheckError* other ) const
{
return QgsGeometryCheckError::isEqual( other ) &&
other->featureId() == featureId() &&
other->vidx() == vidx() &&
static_cast<QgsGeometrySelfIntersectionCheckError*>( other )->intersection().segment1 == intersection().segment1 &&
static_cast<QgsGeometrySelfIntersectionCheckError*>( other )->intersection().segment2 == intersection().segment2;
}
bool QgsGeometrySelfIntersectionCheckError::handleChanges( const QgsGeometryCheck::Changes& changes )
{
if ( !QgsGeometryCheckError::handleChanges( changes ) )
{
return false;
}
foreach ( const QgsGeometryCheck::Change& change, changes.value( featureId() ) )
{
if ( change.vidx.vertex == mInter.segment1 ||
change.vidx.vertex == mInter.segment1 + 1 ||
change.vidx.vertex == mInter.segment2 ||
change.vidx.vertex == mInter.segment2 + 1 )
{
return false;
}
else if ( change.vidx.vertex >= 0 )
{
if ( change.vidx.vertex < mInter.segment1 )
{
mInter.segment1 += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
}
if ( change.vidx.vertex < mInter.segment2 )
{
mInter.segment2 += change.type == QgsGeometryCheck::ChangeAdded ? 1 : -1;
}
}
}
return true;
}
void QgsGeometrySelfIntersectionCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const
{
const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
foreach ( const QgsFeatureId& featureid, featureIds )
{
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QgsFeature feature;
if ( !mFeaturePool->get( featureid, feature ) )
{
continue;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart )
{
for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing )
{
foreach ( const QgsGeometryUtils::SelfIntersection& inter, QgsGeometryUtils::getSelfIntersections( geom, iPart, iRing, QgsGeometryCheckPrecision::tolerance() ) )
{
errors.append( new QgsGeometrySelfIntersectionCheckError( this, featureid, inter.point, QgsVertexId( iPart, iRing ), inter ) );
}
}
}
}
}
void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
{
QgsFeature feature;
if ( !mFeaturePool->get( error->featureId(), feature ) )
{
error->setObsolete();
return;
}
QgsAbstractGeometryV2* geom = feature.geometry()->geometry();
const QgsVertexId& vidx = error->vidx();
// Check if ring still exists
if ( !vidx.isValid( geom ) )
{
error->setObsolete();
return;
}
const QgsGeometryUtils::SelfIntersection& inter = static_cast<QgsGeometrySelfIntersectionCheckError*>( error )->intersection();
// Check if error still applies
bool ringIsClosed = false;
int nVerts = QgsGeomUtils::polyLineSize( geom, vidx.part, vidx.ring, &ringIsClosed );
if ( inter.segment1 >= nVerts || inter.segment2 >= nVerts )
{
error->setObsolete();
return;
}
QgsPointV2 p1 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, inter.segment1 ) );
QgsPointV2 q1 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, inter.segment2 ) );
QgsPointV2 p2 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( inter.segment1 + 1 ) % nVerts ) );
QgsPointV2 q2 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( inter.segment2 + 1 ) % nVerts ) );
QgsPointV2 s;
if ( !QgsGeometryUtils::segmentIntersection( p1, p2, q1, q2, s, QgsGeometryCheckPrecision::tolerance() ) )
{
error->setObsolete();
return;
}
// Fix with selected method
if ( method == NoChange )
{
error->setFixed( method );
}
else if ( method == ToMultiObject || method == ToSingleObjects )
{
// Extract rings
QList<QgsPointV2> ring1, ring2;
bool ring1EndsWithS = false;
bool ring2EndsWithS = false;
for ( int i = 0; i < nVerts; ++i )
{
if ( i <= inter.segment1 || i >= inter.segment2 + 1 )
{
ring1.append( geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, i ) ) );
ring1EndsWithS = false;
if ( i == inter.segment2 + 1 )
{
ring2.append( s );
ring2EndsWithS = true;
}
}
else
{
ring2.append( geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, i ) ) );
ring2EndsWithS = true;
if ( i == inter.segment1 + 1 )
{
ring1.append( s );
ring1EndsWithS = false;
}
}
}
if ( nVerts == inter.segment2 + 1 )
{
ring2.append( s );
ring2EndsWithS = true;
}
if ( ringIsClosed || ring1EndsWithS )
ring1.append( ring1.front() ); // Ensure ring is closed
if ( ringIsClosed || ring2EndsWithS )
ring2.append( ring2.front() ); // Ensure ring is closed
if ( ring1.size() < 3 + ( ringIsClosed || ring1EndsWithS ) || ring2.size() < 3 + ( ringIsClosed || ring2EndsWithS ) )
{
error->setFixFailed( tr( "Resulting geometry is degenerate" ) );
return;
}
QgsLineStringV2* ringGeom1 = new QgsLineStringV2();
ringGeom1->setPoints( ring1 );
QgsLineStringV2* ringGeom2 = new QgsLineStringV2();
ringGeom2->setPoints( ring2 );
QgsAbstractGeometryV2* part = QgsGeomUtils::getGeomPart( geom, vidx.part );
// If is a polygon...
if ( dynamic_cast<QgsCurvePolygonV2*>( part ) )
{
QgsCurvePolygonV2* poly = static_cast<QgsCurvePolygonV2*>( part );
// If ring is interior ring, create separate holes
if ( vidx.ring > 0 )
{
poly->removeInteriorRing( vidx.ring );
poly->addInteriorRing( ringGeom1 );
poly->addInteriorRing( ringGeom2 );
changes[feature.id()].append( Change( ChangeRing, ChangeRemoved, vidx ) );
changes[feature.id()].append( Change( ChangeRing, ChangeAdded, QgsVertexId( vidx.part, poly->ringCount() - 2 ) ) );
changes[feature.id()].append( Change( ChangeRing, ChangeAdded, QgsVertexId( vidx.part, poly->ringCount() - 1 ) ) );
mFeaturePool->updateFeature( feature );
}
else
{
// If ring is exterior, build two polygons, and reassign interiors as necessary
poly->setExteriorRing( ringGeom1 );
QgsCurvePolygonV2* poly2 = new QgsCurvePolygonV2();
poly2->setExteriorRing( ringGeom2 );
// Reassing interiors as necessary
QgsGeometryEngine* geomEnginePoly1 = QgsGeomUtils::createGeomEngine( poly, QgsGeometryCheckPrecision::precision() );
QgsGeometryEngine* geomEnginePoly2 = QgsGeomUtils::createGeomEngine( poly2, QgsGeometryCheckPrecision::precision() );
for ( int n = poly->numInteriorRings(), i = n - 1; i >= 0; --i )
{
if ( !geomEnginePoly1->contains( *poly->interiorRing( i ) ) )
{
if ( geomEnginePoly2->contains( *poly->interiorRing( i ) ) )
{
poly2->addInteriorRing( static_cast<QgsCurveV2*>( poly->interiorRing( i )->clone() ) );
// No point in adding ChangeAdded changes, since the entire poly2 is added anyways later on
}
poly->removeInteriorRing( i );
changes[feature.id()].append( Change( ChangeRing, ChangeRemoved, QgsVertexId( vidx.part, i ) ) );
}
}
delete geomEnginePoly1;
delete geomEnginePoly2;
if ( method == ToMultiObject )
{
// If is alreay a geometry collection, just add the new polygon.
if ( dynamic_cast<QgsGeometryCollectionV2*>( geom ) )
{
static_cast<QgsGeometryCollectionV2*>( geom )->addGeometry( poly2 );
changes[feature.id()].append( Change( ChangeRing, ChangeChanged, QgsVertexId( vidx.part, vidx.ring ) ) );
changes[feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geom->partCount() - 1 ) ) );
mFeaturePool->updateFeature( feature );
}
// Otherwise, create multipolygon
else
{
QgsMultiPolygonV2* multiPoly = new QgsMultiPolygonV2();
multiPoly->addGeometry( poly->clone() );
multiPoly->addGeometry( poly2 );
feature.setGeometry( new QgsGeometry( multiPoly ) );
mFeaturePool->updateFeature( feature );
changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
}
}
else // if ( method == ToSingleObjects )
{
QgsFeature newFeature;
newFeature.setAttributes( feature.attributes() );
newFeature.setGeometry( new QgsGeometry( poly2 ) );
mFeaturePool->updateFeature( feature );
mFeaturePool->addFeature( newFeature );
changes[feature.id()].append( Change( ChangeRing, ChangeChanged, QgsVertexId( vidx.part, vidx.ring ) ) );
changes[newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) );
}
}
}
else if ( dynamic_cast<QgsCurveV2*>( part ) )
{
if ( method == ToMultiObject )
{
if ( dynamic_cast<QgsGeometryCollectionV2*>( geom ) )
{
QgsGeometryCollectionV2* geomCollection = static_cast<QgsGeometryCollectionV2*>( geom );
geomCollection->removeGeometry( vidx.part );
geomCollection->addGeometry( ringGeom1 );
geomCollection->addGeometry( ringGeom2 );
mFeaturePool->updateFeature( feature );
changes[feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( vidx.part ) ) );
changes[feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 2 ) ) );
changes[feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 1 ) ) );
}
else
{
QgsMultiCurveV2* geomCollection = new QgsMultiCurveV2();
geomCollection->addGeometry( ringGeom1 );
geomCollection->addGeometry( ringGeom2 );
feature.setGeometry( new QgsGeometry( geomCollection ) );
mFeaturePool->updateFeature( feature );
changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) );
}
}
else // if(method == ToSingleObjects)
{
if ( dynamic_cast<QgsGeometryCollectionV2*>( geom ) )
{
QgsGeometryCollectionV2* geomCollection = static_cast<QgsGeometryCollectionV2*>( geom );
geomCollection->removeGeometry( vidx.part );
geomCollection->addGeometry( ringGeom1 );
mFeaturePool->updateFeature( feature );
changes[feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( vidx.part ) ) );
changes[feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 1 ) ) );
}
else
{
feature.setGeometry( new QgsGeometry( ringGeom1 ) );
mFeaturePool->updateFeature( feature );
changes[feature.id()].append( Change( ChangeFeature, ChangeChanged, QgsVertexId( vidx.part ) ) );
}
QgsFeature newFeature;
newFeature.setAttributes( feature.attributes() );
newFeature.setGeometry( new QgsGeometry( ringGeom2 ) );
mFeaturePool->addFeature( newFeature );
changes[newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) );
}
}
error->setFixed( method );
}
else
{
error->setFixFailed( tr( "Unknown method" ) );
}
}
const QStringList& QgsGeometrySelfIntersectionCheck::getResolutionMethods() const
{
static QStringList methods = QStringList()
<< tr( "Split feature into a multi-object feature" )
<< tr( "Split feature into multiple single-object features" )
<< tr( "No action" );
return methods;
}

View File

@ -0,0 +1,54 @@
/***************************************************************************
* qgsgeometryselfintersectioncheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_SELFINTERSECTION_CHECK_H
#define QGS_GEOMETRY_SELFINTERSECTION_CHECK_H
#include "qgsgeometryutils.h"
#include "qgsgeometrycheck.h"
class QgsGeometrySelfIntersectionCheckError : public QgsGeometryCheckError
{
public:
QgsGeometrySelfIntersectionCheckError( const QgsGeometryCheck* check,
const QgsFeatureId& featureId,
const QgsPointV2& errorLocation,
const QgsVertexId& vidx,
const QgsGeometryUtils::SelfIntersection& inter )
: QgsGeometryCheckError( check, featureId, errorLocation, vidx ), mInter( inter ) { }
const QgsGeometryUtils::SelfIntersection& intersection() const { return mInter; }
bool isEqual( QgsGeometryCheckError* other ) const;
bool handleChanges( const QgsGeometryCheck::Changes& changes );
void update( const QgsGeometrySelfIntersectionCheckError* other )
{
QgsGeometryCheckError::update( other );
// Static cast since this should only get called if isEqual == true
const QgsGeometrySelfIntersectionCheckError* err = static_cast<const QgsGeometrySelfIntersectionCheckError*>( other );
mInter.point = err->mInter.point;
}
private:
QgsGeometryUtils::SelfIntersection mInter;
};
class QgsGeometrySelfIntersectionCheck : public QgsGeometryCheck
{
Q_OBJECT
public:
QgsGeometrySelfIntersectionCheck( QgsFeaturePool* featurePool )
: QgsGeometryCheck( FeatureNodeCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter = 0, const QgsFeatureIds& ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError* error, int method, int mergeAttributeIndex, Changes& changes ) const;
const QStringList& getResolutionMethods() const;
QString errorDescription() const { return tr( "Self intersection" ); }
QString errorName() const { return "QgsGeometrySelfIntersectionCheck"; }
private:
enum ResolutionMethod { ToMultiObject, ToSingleObjects, NoChange };
};
#endif // QGS_GEOMETRY_SELFINTERSECTION_CHECK_H

View File

@ -0,0 +1,41 @@
/***************************************************************************
* qgsgeometrysliverpolygoncheck.h *
* ------------------- *
* copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
* email : smani@sourcepole.ch *
***************************************************************************/
#ifndef QGS_GEOMETRY_SLIVERPOLYGON_CHECK_H
#define QGS_GEOMETRY_SLIVERPOLYGON_CHECK_H
#include "qgsgeometryareacheck.h"
#include <qmath.h>
class QgsGeometrySliverPolygonCheck : public QgsGeometryAreaCheck
{
Q_OBJECT
public:
QgsGeometrySliverPolygonCheck( QgsFeaturePool* featurePool, double threshold, double maxArea = 0. )
: QgsGeometryAreaCheck( featurePool, threshold ), mMaxArea( maxArea ) {}
QString errorDescription() const { return tr( "Sliver polygon" ); }
QString errorName() const { return "QgsGeometrySliverPolygonCheck"; }
private:
double mMaxArea;
bool checkThreshold( const QgsAbstractGeometryV2 *geom, double &value ) const override
{
QgsRectangle bb = geom->boundingBox();
double maxDim = qMax( bb.width(), bb.height() );
double area = geom->area();
value = ( maxDim * maxDim ) / area;
if ( mMaxArea > 0. && area > mMaxArea )
{
return false;
}
return value > mThreshold;
}
};
#endif // QGS_GEOMETRY_SLIVERPOLYGON_CHECK_H

Some files were not shown because too many files have changed in this diff Show More