mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Merge pull request #2302 from manisandro/geometry_plugins
Geometry Checker and Geometry Snapper plugins
This commit is contained in:
commit
8cec2c935f
@ -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 \
|
||||
|
@ -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}")
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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 );
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
@ -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. */
|
||||
|
@ -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 )
|
||||
|
@ -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. */
|
||||
|
@ -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}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -36,7 +36,7 @@ QgsCircularStringV2::~QgsCircularStringV2()
|
||||
|
||||
}
|
||||
|
||||
QgsAbstractGeometryV2 *QgsCircularStringV2::clone() const
|
||||
QgsCircularStringV2 *QgsCircularStringV2::clone() const
|
||||
{
|
||||
return new QgsCircularStringV2( *this );
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -56,7 +56,7 @@ QgsCompoundCurveV2& QgsCompoundCurveV2::operator=( const QgsCompoundCurveV2 & cu
|
||||
return *this;
|
||||
}
|
||||
|
||||
QgsAbstractGeometryV2 *QgsCompoundCurveV2::clone() const
|
||||
QgsCompoundCurveV2 *QgsCompoundCurveV2::clone() const
|
||||
{
|
||||
return new QgsCompoundCurveV2( *this );
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -134,7 +134,7 @@ void QgsGeometry::removeWkbGeos()
|
||||
}
|
||||
}
|
||||
|
||||
const QgsAbstractGeometryV2* QgsGeometry::geometry() const
|
||||
QgsAbstractGeometryV2* QgsGeometry::geometry() const
|
||||
{
|
||||
if ( !d )
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -33,7 +33,7 @@ QgsLineStringV2::QgsLineStringV2(): QgsCurveV2()
|
||||
QgsLineStringV2::~QgsLineStringV2()
|
||||
{}
|
||||
|
||||
QgsAbstractGeometryV2 *QgsLineStringV2::clone() const
|
||||
QgsLineStringV2 *QgsLineStringV2::clone() const
|
||||
{
|
||||
return new QgsLineStringV2( *this );
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "qgslinestringv2.h"
|
||||
#include "qgswkbptr.h"
|
||||
|
||||
QgsAbstractGeometryV2* QgsPolygonV2::clone() const
|
||||
QgsPolygonV2* QgsPolygonV2::clone() const
|
||||
{
|
||||
return new QgsPolygonV2( *this );
|
||||
}
|
||||
|
@ -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 );
|
||||
|
@ -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;
|
||||
};
|
||||
|
60
src/core/geosextra/geos_c_extra.cpp
Normal file
60
src/core/geosextra/geos_c_extra.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
40
src/core/geosextra/geos_c_extra.h
Normal file
40
src/core/geosextra/geos_c_extra.h
Normal 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
|
61
src/core/geosextra/geos_extra/geos_c_extra.cpp
Normal file
61
src/core/geosextra/geos_extra/geos_c_extra.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
40
src/core/geosextra/geos_extra/geos_c_extra.h
Normal file
40
src/core/geosextra/geos_extra/geos_c_extra.h
Normal 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
|
@ -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() )
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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. */
|
||||
|
@ -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)
|
||||
|
106
src/plugins/geometry_checker/CMakeLists.txt
Normal file
106
src/plugins/geometry_checker/CMakeLists.txt
Normal 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})
|
||||
|
132
src/plugins/geometry_checker/checks/qgsgeometryanglecheck.cpp
Normal file
132
src/plugins/geometry_checker/checks/qgsgeometryanglecheck.cpp
Normal 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;
|
||||
}
|
30
src/plugins/geometry_checker/checks/qgsgeometryanglecheck.h
Normal file
30
src/plugins/geometry_checker/checks/qgsgeometryanglecheck.h
Normal 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
|
216
src/plugins/geometry_checker/checks/qgsgeometryareacheck.cpp
Normal file
216
src/plugins/geometry_checker/checks/qgsgeometryareacheck.cpp
Normal 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;
|
||||
}
|
37
src/plugins/geometry_checker/checks/qgsgeometryareacheck.h
Normal file
37
src/plugins/geometry_checker/checks/qgsgeometryareacheck.h
Normal 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
|
208
src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp
Normal file
208
src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp
Normal 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 );
|
||||
}
|
||||
}
|
153
src/plugins/geometry_checker/checks/qgsgeometrycheck.h
Normal file
153
src/plugins/geometry_checker/checks/qgsgeometrycheck.h
Normal 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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
209
src/plugins/geometry_checker/checks/qgsgeometrygapcheck.cpp
Normal file
209
src/plugins/geometry_checker/checks/qgsgeometrygapcheck.cpp
Normal 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;
|
||||
}
|
96
src/plugins/geometry_checker/checks/qgsgeometrygapcheck.h
Normal file
96
src/plugins/geometry_checker/checks/qgsgeometrygapcheck.h
Normal 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
|
73
src/plugins/geometry_checker/checks/qgsgeometryholecheck.cpp
Normal file
73
src/plugins/geometry_checker/checks/qgsgeometryholecheck.cpp
Normal 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;
|
||||
}
|
29
src/plugins/geometry_checker/checks/qgsgeometryholecheck.h
Normal file
29
src/plugins/geometry_checker/checks/qgsgeometryholecheck.h
Normal 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
|
@ -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;
|
||||
}
|
@ -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
|
192
src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp
Normal file
192
src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp
Normal 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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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
Loading…
x
Reference in New Issue
Block a user