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 ) ); } } } }
bool QgsGeometryCollectionV2::fromWkb( const unsigned char * wkb ) { if ( !wkb ) { return false; } QgsConstWkbPtr wkbPtr( wkb + 1 ); //type wkbPtr >> mWkbType; int nGeometries = 0; wkbPtr >> nGeometries; QList<QgsAbstractGeometryV2*> geometryList; for ( int i = 0; i < nGeometries; ++i ) { QgsAbstractGeometryV2* geom = QgsGeometryFactory::geomFromWkb( wkbPtr ); if ( geom ) { geometryList.append( geom ); wkbPtr += geom->wkbSize(); } } mGeometries.resize( geometryList.size() ); for ( int i = 0; i < geometryList.size(); ++i ) { mGeometries[i] = geometryList.at( i ); } return true; }
QgsAbstractGeometryV2* QgsGeometryFactory::geomFromWkb( QgsConstWkbPtr wkbPtr ) { if ( !wkbPtr ) return nullptr; //find out type (bytes 2-5) QgsWKBTypes::Type type = wkbPtr.readHeader(); wkbPtr -= 1 + sizeof( int ); QgsAbstractGeometryV2* geom = nullptr; geom = geomFromWkbType( type ); if ( geom ) { try { geom->fromWkb( wkbPtr ); } catch ( const QgsWkbException &e ) { Q_UNUSED( e ); QgsDebugMsg( "WKB exception: " + e.what() ); delete geom; geom = nullptr; } } return geom; }
void QgsGeometryDuplicateNodesCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const { const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; Q_FOREACH ( 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 ) ); if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() ) { errors.append( new QgsGeometryCheckError( this, featureid, pj, QgsVertexId( iPart, iRing, jVert ) ) ); } } } } } }
void QgsMapToolNodeTool::canvasMoveEvent( QMouseEvent * e ) { if ( !mSelectedFeature || e->buttons() == Qt::NoButton ) return; QgsVectorLayer* vlayer = mSelectedFeature->vlayer(); Q_ASSERT( vlayer ); mSelectAnother = false; if ( mMoving ) { if ( mMoveRubberBands.empty() ) { QgsGeometryRubberBand* rb = new QgsGeometryRubberBand( mCanvas, mSelectedFeature->geometry()->type() ); rb->setOutlineColor( Qt::blue ); rb->setBrushStyle( Qt::NoBrush ); rb->setOutlineWidth( 2 ); QgsAbstractGeometryV2* rbGeom = mSelectedFeature->geometry()->geometry()->clone(); if ( mCanvas->mapSettings().layerTransform( vlayer ) ) rbGeom->transform( *mCanvas->mapSettings().layerTransform( vlayer ) ); rb->setGeometry( rbGeom ); mMoveRubberBands.insert( mSelectedFeature->featureId(), rb ); foreach ( const QgsVertexEntry* vertexEntry, mSelectedFeature->vertexMap() ) { if ( vertexEntry->isSelected() ) mMoveVertices[mSelectedFeature->featureId()].append( qMakePair( vertexEntry->vertexId(), toMapCoordinates( vlayer, vertexEntry->point() ) ) ); } if ( QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ) ) { createTopologyRubberBands(); } } else {
QgsAbstractGeometryV2* QgsGeometryImport::geomFromWkb( const unsigned char* wkb ) { if ( !wkb ) { return 0; } //find out type (bytes 2-5) int type; memcpy( &type, wkb + 1, sizeof( int ) ); QgsAbstractGeometryV2* geom = 0; geom = geomFromWkbType( QgsWKBTypes::Type( type ) ); #if 0 type = QgsWKBTypes::flatType( QgsWKBTypes::Type( type ) ); switch ( type ) { case QgsWKBTypes::Point: geom = new QgsPointV2(); break; case QgsWKBTypes::LineString: geom = new QgsLineStringV2(); break; case QgsWKBTypes::CircularString: geom = new QgsCircularStringV2(); break; case QgsWKBTypes::CompoundCurve: geom = new QgsCompoundCurveV2(); break; case QgsWKBTypes::Polygon: geom = new QgsPolygonV2(); break; case QgsWKBTypes::CurvePolygon: geom = new QgsCurvePolygonV2(); break; case QgsWKBTypes::MultiLineString: geom = new QgsMultiLineStringV2(); break; case QgsWKBTypes::MultiPolygon: geom = new QgsMultiPolygonV2(); break; case QgsWKBTypes::MultiPoint: geom = new QgsMultiPointV2(); break; default: geom = 0; } #endif if ( geom ) { geom->fromWkb( wkb ); } return geom; }
void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const { const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; Q_FOREACH ( 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::tolerance() ); QgsFeatureIds ids = mFeaturePool->getIntersects( feature.geometry()->boundingBox() ); Q_FOREACH ( 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 QgsGeometryUtils::adjacentVertices( const QgsAbstractGeometryV2& geom, const QgsVertexId& atVertex, QgsVertexId& beforeVertex, QgsVertexId& afterVertex ) { bool polygonType = ( geom.dimension() == 2 ); QList< QList< QList< QgsPointV2 > > > coords; geom.coordinateSequence( coords ); //get feature if ( coords.size() <= atVertex.part ) { return; //error, no such feature } const QList< QList< QgsPointV2 > >& part = coords.at( atVertex.part ); //get ring if ( part.size() <= atVertex.ring ) { return; //error, no such ring } const QList< QgsPointV2 >& ring = part.at( atVertex.ring ); if ( ring.size() <= atVertex.vertex ) { return; } //vertex in the middle if ( atVertex.vertex > 0 && atVertex.vertex < ring.size() - 1 ) { beforeVertex.part = atVertex.part; beforeVertex.ring = atVertex.ring; beforeVertex.vertex = atVertex.vertex - 1; afterVertex.part = atVertex.part; afterVertex.ring = atVertex.ring; afterVertex.vertex = atVertex.vertex + 1; } else if ( atVertex.vertex == 0 ) { afterVertex.part = atVertex.part; afterVertex.ring = atVertex.ring; afterVertex.vertex = atVertex.vertex + 1; if ( polygonType && ring.size() > 3 ) { beforeVertex.part = atVertex.part; beforeVertex.ring = atVertex.ring; beforeVertex.vertex = ring.size() - 2; } else { beforeVertex = QgsVertexId(); //before vertex invalid } } else if ( atVertex.vertex == ring.size() - 1 ) { beforeVertex.part = atVertex.part; beforeVertex.ring = atVertex.ring; beforeVertex.vertex = atVertex.vertex - 1; if ( polygonType ) { afterVertex.part = atVertex.part; afterVertex.ring = atVertex.ring; afterVertex.vertex = 1; } else { afterVertex = QgsVertexId(); //after vertex invalid } } }
void QgsMapToolNodeTool::createTopologyRubberBands() { QgsVectorLayer* vlayer = mSelectedFeature->vlayer(); Q_FOREACH ( const QgsVertexEntry* vertexEntry, mSelectedFeature->vertexMap() ) { if ( !vertexEntry->isSelected() ) { continue; } // Snap vertex QMultiMap<double, QgsSnappingResult> snapResults; vlayer->snapWithContext( vertexEntry->pointV1(), ZERO_TOLERANCE, snapResults, QgsSnapper::SnapToVertex ); Q_FOREACH ( const QgsSnappingResult& snapResult, snapResults ) { // Get geometry of snapped feature QgsFeatureId snapFeatureId = snapResult.snappedAtGeometry; QgsFeature feature; if ( !vlayer->getFeatures( QgsFeatureRequest( snapFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( feature ) ) { continue; } // Get VertexId of snapped vertex QgsVertexId vid; if ( !feature.constGeometry()->vertexIdFromVertexNr( snapResult.snappedVertexNr, vid ) ) { continue; } // Add rubberband if not already added if ( !mMoveRubberBands.contains( snapFeatureId ) ) { QgsGeometryRubberBand* rb = new QgsGeometryRubberBand( mCanvas, feature.constGeometry()->type() ); QSettings settings; QColor color( settings.value( "/qgis/digitizing/line_color_red", 255 ).toInt(), settings.value( "/qgis/digitizing/line_color_green", 0 ).toInt(), settings.value( "/qgis/digitizing/line_color_blue", 0 ).toInt() ); double myAlpha = settings.value( "/qgis/digitizing/line_color_alpha", 30 ).toInt() / 255.0 ; color.setAlphaF( myAlpha ); rb->setOutlineColor( color ); rb->setBrushStyle( Qt::NoBrush ); rb->setOutlineWidth( settings.value( "/qgis/digitizing/line_width", 1 ).toInt() ); QgsAbstractGeometryV2* rbGeom = feature.constGeometry()->geometry()->clone(); if ( mCanvas->mapSettings().layerTransform( vlayer ) ) rbGeom->transform( *mCanvas->mapSettings().layerTransform( vlayer ) ); rb->setGeometry( rbGeom ); mMoveRubberBands.insert( snapFeatureId, rb ); } // Add to list of vertices to be moved mMoveVertices[snapFeatureId].append( qMakePair( vid, toMapCoordinates( vlayer, feature.constGeometry()->geometry()->vertexAt( vid ) ) ) ); } }
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(); 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" ) ); } }
double QgsGeometryUtils::distanceToVertex( const QgsAbstractGeometryV2 &geom, const QgsVertexId &id ) { double currentDist = 0; QgsVertexId vertexId; QgsPointV2 vertex; QgsPointV2 previousVertex; bool first = true; while ( geom.nextVertex( vertexId, vertex ) ) { if ( !first ) { currentDist += sqrt( QgsGeometryUtils::sqrDistance2D( previousVertex, vertex ) ); } previousVertex = vertex; first = false; if ( vertexId == id ) { //found target vertex return currentDist; } } //could not find target vertex return -1; }
QgsPointV2 QgsGeometryUtils::closestVertex( const QgsAbstractGeometryV2& geom, const QgsPointV2& pt, QgsVertexId& id ) { double minDist = std::numeric_limits<double>::max(); double currentDist = 0; QgsPointV2 minDistPoint; QgsVertexId vertexId; QgsPointV2 vertex; while ( geom.nextVertex( vertexId, vertex ) ) { currentDist = QgsGeometryUtils::sqrDistance2D( pt, vertex ); // The <= is on purpose: for geometries with closing vertices, this ensures // that the closing vertex is retuned. For the node tool, the rubberband // of the closing vertex is above the opening vertex, hence with the <= // situations where the covered opening vertex rubberband is selected are // avoided. if ( currentDist <= minDist ) { minDist = currentDist; minDistPoint = vertex; id.part = vertexId.part; id.ring = vertexId.ring; id.vertex = vertexId.vertex; } } return minDistPoint; }
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; } }
QgsAbstractGeometryV2 *QgsGeometryCheckError::geometry() { QgsFeature f; if ( mCheck->getFeaturePool()->get( featureId(), f ) && f.geometry() ) { QgsAbstractGeometryV2* geom = f.geometry()->geometry(); return mVidx.part >= 0 ? QgsGeomUtils::getGeomPart( geom, mVidx.part )->clone() : geom->clone(); } return nullptr; }
QgsAbstractGeometryV2* QgsGeometryFactory::geomFromWkb( const unsigned char* wkb ) { if ( !wkb ) { return 0; } //find out type (bytes 2-5) int type; memcpy( &type, wkb + 1, sizeof( int ) ); QgsAbstractGeometryV2* geom = 0; geom = geomFromWkbType( QgsWKBTypes::Type( type ) ); if ( geom ) { geom->fromWkb( wkb ); } return geom; }
void QgsGeometryTypeCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const { const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; Q_FOREACH ( QgsFeatureId featureid, featureIds ) { if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); QgsFeature feature; if ( !mFeaturePool->get( featureid, feature ) ) { continue; } QgsAbstractGeometryV2* geom = feature.geometry()->geometry(); QgsWKBTypes::Type type = QgsWKBTypes::flatType( geom->wkbType() ); if (( mAllowedTypes & ( 1 << type ) ) == 0 ) { errors.append( new QgsGeometryTypeCheckError( this, featureid, geom->centroid(), type ) ); } } }
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(); 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" ) ); } }
void QgsGeometryRubberBand::paint( QPainter* painter ) { if ( !mGeometry || !painter ) { return; } painter->save(); painter->translate( -pos() ); if ( mGeometryType == QGis::Polygon ) { painter->setBrush( mBrush ); } else { painter->setBrush( Qt::NoBrush ); } painter->setPen( mPen ); QgsAbstractGeometryV2* paintGeom = mGeometry->clone(); paintGeom->transform( mMapCanvas->getCoordinateTransform()->transform() ); paintGeom->draw( *painter ); //draw vertices QgsVertexId vertexId; QgsPointV2 vertex; while ( paintGeom->nextVertex( vertexId, vertex ) ) { drawVertex( painter, vertex.x(), vertex.y() ); } delete paintGeom; painter->restore(); }
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 ); }
void QgsGeometrySegmentLengthCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const { const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; Q_FOREACH ( 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 QgsGeometrySelfIntersectionCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const { const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; Q_FOREACH ( 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 ) { Q_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 QgsMapToolIdentify::closestVertexAttributes( const QgsAbstractGeometryV2& geometry, QgsVertexId vId, QgsMapLayer *layer, QMap< QString, QString >& derivedAttributes ) { QString str = QLocale::system().toString( vId.vertex + 1 ); derivedAttributes.insert( tr( "Closest vertex number" ), str ); QgsPointV2 closestPoint = geometry.vertexAt( vId ); QgsPoint closestPointMapCoords = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPoint( closestPoint.x(), closestPoint.y() ) ); str = QLocale::system().toString( closestPointMapCoords.x(), 'g', 10 ); derivedAttributes.insert( "Closest vertex X", str ); str = QLocale::system().toString( closestPointMapCoords.y(), 'g', 10 ); derivedAttributes.insert( "Closest vertex Y", str ); if ( closestPoint.is3D() ) { str = QLocale::system().toString( closestPoint.z(), 'g', 10 ); derivedAttributes.insert( "Closest vertex Z", str ); } if ( closestPoint.isMeasure() ) { str = QLocale::system().toString( closestPoint.m(), 'g', 10 ); derivedAttributes.insert( "Closest vertex M", str ); } }
void QgsGeometryCheckerResultTab::highlightErrors( bool current ) { qDeleteAll( mCurrentRubberBands ); mCurrentRubberBands.clear(); QList<QTableWidgetItem*> items; QList<QgsPoint> errorPositions; QgsRectangle totextent; if ( current ) { items.append( ui.tableWidgetErrors->currentItem() ); } else { items.append( ui.tableWidgetErrors->selectedItems() ); } foreach ( QTableWidgetItem* item, items ) { QgsGeometryCheckError* error = ui.tableWidgetErrors->item( item->row(), 0 )->data( Qt::UserRole ).value<QgsGeometryCheckError*>(); QgsAbstractGeometryV2* geometry = error->geometry(); if ( ui.checkBoxHighlight->isChecked() && geometry ) { QgsRubberBand* featureRubberBand = new QgsRubberBand( mIface->mapCanvas() ); QgsGeometry geom( geometry ); featureRubberBand->addGeometry( &geom, mFeaturePool->getLayer() ); featureRubberBand->setWidth( 5 ); featureRubberBand->setColor( Qt::yellow ); mCurrentRubberBands.append( featureRubberBand ); } else { // QgsGeometry above takes ownership of geometry and deletes it when it goes out of scope delete geometry; geometry = 0; } if ( ui.radioButtonError->isChecked() || current || error->status() == QgsGeometryCheckError::StatusFixed ) { QgsRubberBand* pointRubberBand = new QgsRubberBand( mIface->mapCanvas(), QGis::Point ); QgsPoint pos = mIface->mapCanvas()->mapSettings().layerToMapCoordinates( mFeaturePool->getLayer(), QgsPoint( error->location().x(), error->location().y() ) ); pointRubberBand->addPoint( pos ); pointRubberBand->setWidth( 20 ); pointRubberBand->setColor( Qt::red ); mCurrentRubberBands.append( pointRubberBand ); errorPositions.append( pos ); } else if ( ui.radioButtonFeature->isChecked() && geometry ) { QgsRectangle geomextent = mIface->mapCanvas()->mapSettings().layerExtentToOutputExtent( mFeaturePool->getLayer(), geometry->boundingBox() ); if ( totextent.isEmpty() ) { totextent = geomextent; } else { totextent.unionRect( geomextent ); } } }
void QgsGeometryTypeCheck::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 QgsWKBTypes::Type type = QgsWKBTypes::flatType( geom->wkbType() ); if (( mAllowedTypes & ( 1 << type ) ) != 0 ) { error->setObsolete(); return; } // Fix with selected method if ( method == NoChange ) { error->setFixed( method ); } else if ( method == Convert ) { // Check if corresponding single type is allowed if ( QgsWKBTypes::isMultiType( type ) && (( 1 << QgsWKBTypes::singleType( type ) ) & mAllowedTypes ) != 0 ) { // Explode multi-type feature into single-type features for ( int iPart = 1, nParts = geom->partCount(); iPart < nParts; ++iPart ) { QgsFeature newFeature; newFeature.setAttributes( feature.attributes() ); newFeature.setGeometry( new QgsGeometry( QgsGeomUtils::getGeomPart( geom, iPart )->clone() ) ); mFeaturePool->addFeature( newFeature ); changes[newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) ); } // Recycle feature for part 0 feature.setGeometry( new QgsGeometry( QgsGeomUtils::getGeomPart( geom, 0 ) ) ); changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } // Check if corresponding multi type is allowed else if ( QgsWKBTypes::isSingleType( type ) && (( 1 << QgsWKBTypes::multiType( type ) ) & mAllowedTypes ) != 0 ) { QgsGeometryCollectionV2* geomCollection = nullptr; switch ( QgsWKBTypes::multiType( type ) ) { case QgsWKBTypes::MultiPoint: { geomCollection = new QgsMultiPointV2(); break; } case QgsWKBTypes::MultiLineString: { geomCollection = new QgsMultiLineStringV2(); break; } case QgsWKBTypes::MultiPolygon: { geomCollection = new QgsMultiPolygonV2(); break; } case QgsWKBTypes::MultiCurve: { geomCollection = new QgsMultiCurveV2(); break; } case QgsWKBTypes::MultiSurface: { geomCollection = new QgsMultiSurfaceV2(); break; } default: break; } if ( !geomCollection ) { error->setFixFailed( tr( "Unknown geometry type" ) ); } else { geomCollection->addGeometry( geom->clone() ); feature.setGeometry( new QgsGeometry( geomCollection ) ); mFeaturePool->updateFeature( feature ); changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } } // Delete feature else { mFeaturePool->deleteFeature( feature ); changes[error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) ); } error->setFixed( method ); } else if ( method == Delete ) { mFeaturePool->deleteFeature( feature ); error->setFixed( method ); changes[error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) ); } 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::tolerance() ); QgsAbstractGeometryV2* combinedGeom = geomEngine->combine( *QgsGeomUtils::getGeomPart( geom, partIdx ), &errMsg ); delete geomEngine; if ( !combinedGeom || combinedGeom->isEmpty() ) { return false; } replaceFeatureGeometryPart( mergeFeature, mergePartIdx, combinedGeom, changes ); return true; }
QgsAbstractGeometryV2* QgsGeometryImport::geomFromWkt( const QString& text ) { QgsAbstractGeometryV2* geom = 0; if ( text.startsWith( "Point", Qt::CaseInsensitive ) ) { geom = new QgsPointV2(); } else if ( text.startsWith( "LineString", Qt::CaseInsensitive ) ) { geom = new QgsLineStringV2(); } else if ( text .startsWith( "CircularString", Qt::CaseInsensitive ) ) { geom = new QgsCircularStringV2(); } else if ( text.startsWith( "CompoundCurve" , Qt::CaseInsensitive ) ) { geom = new QgsCompoundCurveV2(); } else if ( text.startsWith( "Polygon", Qt::CaseInsensitive ) ) { geom = new QgsPolygonV2(); } else if ( text.startsWith( "CurvePolygon", Qt::CaseInsensitive ) ) { geom = new QgsCurvePolygonV2(); } else if ( text.startsWith( "MultiPoint", Qt::CaseInsensitive ) ) { geom = new QgsMultiPointV2(); } else if ( text.startsWith( "MultiCurve", Qt::CaseInsensitive ) ) { geom = new QgsMultiCurveV2(); } else if ( text.startsWith( "MultiLineString", Qt::CaseInsensitive ) ) { geom = new QgsMultiLineStringV2(); } else if ( text.startsWith( "MultiSurface", Qt::CaseInsensitive ) ) { geom = new QgsMultiSurfaceV2(); } else if ( text.startsWith( "MultiPolygon", Qt::CaseInsensitive ) ) { geom = new QgsMultiPolygonV2(); } else if ( text.startsWith( "GeometryCollection", Qt::CaseInsensitive ) ) { geom = new QgsGeometryCollectionV2(); } if ( geom ) { if ( !geom->fromWkt( text ) ) { delete geom; return 0; } } return geom; }
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" ) ); } }
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::tolerance() ); // 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 = nullptr; 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::reducedTolerance() ); QgsAbstractGeometryV2* diff1 = geomEngine->difference( *interPart, &errMsg ); delete geomEngine; if ( !diff1 || diff1->isEmpty() ) { delete diff1; diff1 = nullptr; } else { QgsGeomUtils::filter1DTypes( diff1 ); } QgsGeometryEngine* otherGeomEngine = QgsGeomUtils::createGeomEngine( otherFeature.geometry()->geometry(), QgsGeometryCheckPrecision::reducedTolerance() ); QgsAbstractGeometryV2* diff2 = otherGeomEngine->difference( *interPart, &errMsg ); delete otherGeomEngine; if ( !diff2 || diff2->isEmpty() ) { delete diff2; diff2 = nullptr; } 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; }