ErrorList topolTest::checkPolygonContainsPoint( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( tolerance ); Q_UNUSED( isExtent ); int i = 0; ErrorList errorList; if ( layer1->geometryType() != QgsWkbTypes::PolygonGeometry ) { return errorList; } if ( layer2->geometryType() != QgsWkbTypes::PointGeometry ) { return errorList; } QgsSpatialIndex *index = mLayerIndexes[layer2->id()]; QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.constEnd(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature &f = mFeatureMap2[*cit].feature; QgsGeometry g2 = f.geometry(); if ( g2.isNull() || !_canExportToGeos( g2 ) ) { QgsMessageLog::logMessage( tr( "Second geometry missing or GEOS import failed." ), tr( "Topology plugin" ) ); continue; } if ( g1.contains( g2 ) ) { touched = true; break; } } if ( !touched ) { QList<FeatureLayer> fls; fls << *it << *it; //bb.scale(10); TopolErrorPolygonContainsPoint *err = new TopolErrorPolygonContainsPoint( bb, g1, fls ); errorList << err; } } return errorList; }
static void assignAnchors( QgsSpatialIndex &index, QVector<AnchorPoint> &pnts, double thresh ) { double thresh2 = thresh * thresh; int nanchors = 0, ntosnap = 0; for ( int point = 0; point < pnts.count(); ++point ) { if ( pnts[point].anchor >= 0 ) continue; pnts[point].anchor = -2; // make it anchor nanchors++; // Find points in threshold double x = pnts[point].x, y = pnts[point].y; QgsRectangle rect( x - thresh, y - thresh, x + thresh, y + thresh ); const QList<QgsFeatureId> ids = index.intersects( rect ); for ( QgsFeatureId pointb : ids ) { if ( pointb == point ) continue; double dx = pnts[pointb].x - pnts[point].x; double dy = pnts[pointb].y - pnts[point].y; double dist2 = dx * dx + dy * dy; if ( dist2 > thresh2 ) continue; // outside threshold if ( pnts[pointb].anchor == -1 ) { // doesn't have an anchor yet pnts[pointb].anchor = point; ntosnap++; } else if ( pnts[pointb].anchor >= 0 ) { // check distance to previously assigned anchor double dx2 = pnts[pnts[pointb].anchor].x - pnts[pointb].x; double dy2 = pnts[pnts[pointb].anchor].y - pnts[pointb].y; double dist2_a = dx2 * dx2 + dy2 * dy2; if ( dist2 < dist2_a ) pnts[pointb].anchor = point; // replace old anchor } } } }
static bool snapPoint( QgsPoint *pt, QgsSpatialIndex &index, QVector<AnchorPoint> &pnts ) { // Find point ( should always find one point ) QList<QgsFeatureId> fids = index.intersects( QgsRectangle( pt->x(), pt->y(), pt->x(), pt->y() ) ); Q_ASSERT( fids.count() == 1 ); int spoint = fids[0]; int anchor = pnts[spoint].anchor; if ( anchor >= 0 ) { // to be snapped pt->setX( pnts[anchor].x ); pt->setY( pnts[anchor].y ); return true; } return false; }
bool QgsTransectSample::otherTransectWithinDistance( QgsGeometry* geom, double minDistLayerUnit, double minDistance, QgsSpatialIndex& sIndex, const QMap< QgsFeatureId, QgsGeometry* >& lineFeatureMap, QgsDistanceArea& da ) { if ( !geom ) { return false; } QgsGeometry* buffer = geom->buffer( minDistLayerUnit, 8 ); if ( !buffer ) { return false; } QgsRectangle rect = buffer->boundingBox(); QList<QgsFeatureId> lineIdList = sIndex.intersects( rect ); QList<QgsFeatureId>::const_iterator lineIdIt = lineIdList.constBegin(); for ( ; lineIdIt != lineIdList.constEnd(); ++lineIdIt ) { const QMap< QgsFeatureId, QgsGeometry* >::const_iterator idMapIt = lineFeatureMap.find( *lineIdIt ); if ( idMapIt != lineFeatureMap.constEnd() ) { double dist = 0; QgsPoint pt1, pt2; closestSegmentPoints( *geom, *( idMapIt.value() ), dist, pt1, pt2 ); dist = da.measureLine( pt1, pt2 ); //convert degrees to meters if necessary if ( dist < minDistance ) { delete buffer; return true; } } } delete buffer; return false; }
static void buildSnapIndex( QgsFeatureIterator &fi, QgsSpatialIndex &index, QVector<AnchorPoint> &pnts, QgsFeedback *feedback, int &count, int totalCount ) { QgsFeature f; int pntId = 0; while ( fi.nextFeature( f ) ) { if ( feedback->isCanceled() ) break; QgsGeometry g = f.geometry(); for ( auto it = g.vertices_begin(); it != g.vertices_end(); ++it ) { QgsPoint pt = *it; QgsRectangle rect( pt.x(), pt.y(), pt.x(), pt.y() ); QList<QgsFeatureId> ids = index.intersects( rect ); if ( ids.isEmpty() ) { // add to tree and to structure index.insertFeature( pntId, pt.boundingBox() ); AnchorPoint xp; xp.x = pt.x(); xp.y = pt.y(); xp.anchor = -1; pnts.append( xp ); pntId++; } } ++count; feedback->setProgress( 100. * count / totalCount ); } }
void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback ) { int count = 0; int totalCount = source.featureCount(); if ( totalCount == 0 ) return; // nothing to do here QgsFeatureId newFid = -1; QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::geometryType( QgsWkbTypes::multiType( source.wkbType() ) ); QgsFeatureRequest requestOnlyGeoms; requestOnlyGeoms.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureRequest requestOnlyAttrs; requestOnlyAttrs.setFlags( QgsFeatureRequest::NoGeometry ); QgsFeatureRequest requestOnlyIds; requestOnlyIds.setFlags( QgsFeatureRequest::NoGeometry ); requestOnlyIds.setSubsetOfAttributes( QgsAttributeList() ); // make a set of used feature IDs so that we do not try to reuse them for newly added features QgsFeature f; QSet<QgsFeatureId> fids; QgsFeatureIterator it = source.getFeatures( requestOnlyIds ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; fids.insert( f.id() ); } QHash<QgsFeatureId, QgsGeometry> geometries; QgsSpatialIndex index; QHash<QgsFeatureId, QList<QgsFeatureId> > intersectingIds; // which features overlap a particular area // resolve intersections it = source.getFeatures( requestOnlyGeoms ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; QgsFeatureId fid1 = f.id(); QgsGeometry g1 = f.geometry(); std::unique_ptr< QgsGeometryEngine > g1engine; geometries.insert( fid1, g1 ); index.insertFeature( f ); QgsRectangle bbox( f.geometry().boundingBox() ); const QList<QgsFeatureId> ids = index.intersects( bbox ); for ( QgsFeatureId fid2 : ids ) { if ( fid1 == fid2 ) continue; if ( !g1engine ) { // use prepared geometries for faster intersection tests g1engine.reset( QgsGeometry::createGeometryEngine( g1.constGet() ) ); g1engine->prepareGeometry(); } QgsGeometry g2 = geometries.value( fid2 ); if ( !g1engine->intersects( g2.constGet() ) ) continue; QgsGeometry geomIntersection = g1.intersection( g2 ); if ( !sanitizeIntersectionResult( geomIntersection, geometryType ) ) continue; // // add intersection geometry // // figure out new fid while ( fids.contains( newFid ) ) --newFid; fids.insert( newFid ); geometries.insert( newFid, geomIntersection ); QgsFeature fx( newFid ); fx.setGeometry( geomIntersection ); index.insertFeature( fx ); // figure out which feature IDs belong to this intersection. Some of the IDs can be of the newly // created geometries - in such case we need to retrieve original IDs QList<QgsFeatureId> lst; if ( intersectingIds.contains( fid1 ) ) lst << intersectingIds.value( fid1 ); else lst << fid1; if ( intersectingIds.contains( fid2 ) ) lst << intersectingIds.value( fid2 ); else lst << fid2; intersectingIds.insert( newFid, lst ); // // update f1 // QgsGeometry g12 = g1.difference( g2 ); index.deleteFeature( f ); geometries.remove( fid1 ); if ( sanitizeDifferenceResult( g12 ) ) { geometries.insert( fid1, g12 ); QgsFeature f1x( fid1 ); f1x.setGeometry( g12 ); index.insertFeature( f1x ); } // // update f2 // QgsGeometry g21 = g2.difference( g1 ); QgsFeature f2old( fid2 ); f2old.setGeometry( g2 ); index.deleteFeature( f2old ); geometries.remove( fid2 ); if ( sanitizeDifferenceResult( g21 ) ) { geometries.insert( fid2, g21 ); QgsFeature f2x( fid2 ); f2x.setGeometry( g21 ); index.insertFeature( f2x ); } // update our temporary copy of the geometry to what is left from it g1 = g12; g1engine.reset(); } ++count; feedback->setProgress( count / ( double ) totalCount * 100. ); } // release some memory of structures we don't need anymore fids.clear(); index = QgsSpatialIndex(); // load attributes QHash<QgsFeatureId, QgsAttributes> attributesHash; it = source.getFeatures( requestOnlyAttrs ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; attributesHash.insert( f.id(), f.attributes() ); } // store stuff in the sink for ( auto i = geometries.constBegin(); i != geometries.constEnd(); ++i ) { if ( feedback->isCanceled() ) return; QgsFeature outFeature( i.key() ); outFeature.setGeometry( i.value() ); if ( intersectingIds.contains( i.key() ) ) { const QList<QgsFeatureId> ids = intersectingIds.value( i.key() ); for ( QgsFeatureId id : ids ) { outFeature.setAttributes( attributesHash.value( id ) ); sink.addFeature( outFeature, QgsFeatureSink::FastInsert ); } } else { outFeature.setAttributes( attributesHash.value( i.key() ) ); sink.addFeature( outFeature, QgsFeatureSink::FastInsert ); } } }
ErrorList topolTest::checkPointCoveredByLineEnds( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { int i = 0; ErrorList errorList; if ( layer1->geometryType() != QgsWkbTypes::PointGeometry ) { return errorList; } if ( layer2->geometryType() != QgsWkbTypes::LineGeometry ) { return errorList; } QgsSpatialIndex *index = mLayerIndexes[layer2->id()]; QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.constBegin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.constEnd(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature &f = mFeatureMap2[*cit].feature; QgsGeometry g2 = f.geometry(); if ( g2.isNull() || !_canExportToGeos( g2 ) ) { QgsMessageLog::logMessage( tr( "Second geometry missing or GEOS import failed." ), tr( "Topology plugin" ) ); continue; } QgsPolylineXY g2Line = g2.asPolyline(); QgsGeometry startPoint = QgsGeometry::fromPointXY( g2Line.at( 0 ) ); QgsGeometry endPoint = QgsGeometry::fromPointXY( g2Line.last() ); touched = g1.intersects( startPoint ) || g1.intersects( endPoint ); if ( touched ) { break; } } if ( !touched ) { QgsGeometry conflictGeom = g1; if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } } QList<FeatureLayer> fls; fls << *it << *it; //bb.scale(10); TopolErrorPointNotCoveredByLineEnds *err = new TopolErrorPointNotCoveredByLineEnds( bb, conflictGeom, fls ); errorList << err; } } return errorList; }
ErrorList topolTest::checkOverlapWithLayer( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { int i = 0; ErrorList errorList; bool skipItself = layer1 == layer2; QgsSpatialIndex *index = mLayerIndexes[layer2->id()]; QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); QList<FeatureLayer>::iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature &f = mFeatureMap2[*cit].feature; QgsGeometry g2 = f.geometry(); // skip itself, when invoked with the same layer if ( skipItself && f.id() == it->feature.id() ) continue; if ( g2.isNull() ) { QgsMessageLog::logMessage( tr( "Second geometry missing." ), tr( "Topology plugin" ) ); continue; } if ( g1.overlaps( g2 ) ) { QgsRectangle r = bb; QgsRectangle r2 = g2.boundingBox(); r.combineExtentWith( r2 ); QgsGeometry conflictGeom = g1.intersection( g2 ); // could this for some reason return NULL? if ( conflictGeom.isNull() ) { continue; } if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } if ( canvasExtentPoly.crosses( conflictGeom ) ) { conflictGeom = conflictGeom.intersection( canvasExtentPoly ); } } //c = new QgsGeometry; QList<FeatureLayer> fls; FeatureLayer fl; fl.feature = f; fl.layer = layer2; fls << *it << fl; TopolErrorIntersection *err = new TopolErrorIntersection( r, conflictGeom, fls ); errorList << err; } } } return errorList; }
ErrorList topolTest::checkPointCoveredBySegment( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { int i = 0; ErrorList errorList; if ( layer1->geometryType() != QgsWkbTypes::PointGeometry ) { return errorList; } if ( layer2->geometryType() == QgsWkbTypes::PointGeometry ) { return errorList; } QgsSpatialIndex *index = mLayerIndexes[layer2->id()]; QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature &f = mFeatureMap2[*cit].feature; QgsGeometry g2 = f.geometry(); if ( g2.isNull() ) { QgsMessageLog::logMessage( tr( "Invalid geometry in covering test." ), tr( "Topology plugin" ) ); continue; } // test if point touches other geometry if ( g1.touches( g2 ) ) { touched = true; break; } } if ( !touched ) { QgsGeometry conflictGeom = QgsGeometry( g1 ); if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } } QList<FeatureLayer> fls; fls << *it << *it; //bb.scale(10); TopolErrorCovered *err = new TopolErrorCovered( bb, conflictGeom, fls ); errorList << err; } } return errorList; }
ErrorList topolTest::checkOverlaps( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( layer2 ); int i = 0; ErrorList errorList; // could be enabled for lines and points too // so duplicate rule may be removed? if ( layer1->geometryType() != QgsWkbTypes::PolygonGeometry ) { return errorList; } QList<QgsFeatureId> *duplicateIds = new QList<QgsFeatureId>(); QgsSpatialIndex *index = mLayerIndexes[layer1->id()]; if ( !index ) { qDebug() << "no index present"; delete duplicateIds; return errorList; } QMap<QgsFeatureId, FeatureLayer>::const_iterator it; for ( it = mFeatureMap2.constBegin(); it != mFeatureMap2.constEnd(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); QgsFeatureId currentId = it->feature.id(); if ( duplicateIds->contains( currentId ) ) { //is already a duplicate geometry..skip.. continue; } if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); if ( !g1.isGeosValid() ) { qDebug() << "invalid geometry(g1) found..skipping.." << it->feature.id(); continue; } QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); bool duplicate = false; QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); for ( ; cit != crossingIdsEnd; ++cit ) { duplicate = false; // skip itself if ( mFeatureMap2[*cit].feature.id() == it->feature.id() ) continue; QgsGeometry g2 = mFeatureMap2[*cit].feature.geometry(); if ( g2.isNull() ) { QgsMessageLog::logMessage( tr( "Invalid second geometry in overlaps test." ), tr( "Topology plugin" ) ); continue; } if ( !_canExportToGeos( g2 ) ) { QgsMessageLog::logMessage( tr( "Failed to import second geometry into GEOS in overlaps test." ), tr( "Topology plugin" ) ); continue; } if ( !g2.isGeosValid() ) { QgsMessageLog::logMessage( tr( "Skipping invalid second geometry of feature %1 in overlaps test." ).arg( it->feature.id() ), tr( "Topology plugin" ) ); continue; } qDebug() << "checking overlap for" << it->feature.id(); if ( g1.overlaps( g2 ) ) { duplicate = true; duplicateIds->append( mFeatureMap2[*cit].feature.id() ); } if ( duplicate ) { QList<FeatureLayer> fls; fls << *it << *it; QgsGeometry conflictGeom = g1.intersection( g2 ); if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } if ( canvasExtentPoly.crosses( conflictGeom ) ) { conflictGeom = conflictGeom.intersection( canvasExtentPoly ); } } TopolErrorOverlaps *err = new TopolErrorOverlaps( bb, conflictGeom, fls ); errorList << err; } } } delete duplicateIds; return errorList; }
ErrorList topolTest::checkDuplicates( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( layer2 ); //TODO: multilines - check all separate pieces int i = 0; ErrorList errorList; QList<QgsFeatureId> duplicateIds; QgsSpatialIndex *index = mLayerIndexes[layer1->id()]; QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); QMap<QgsFeatureId, FeatureLayer>::const_iterator it; for ( it = mFeatureMap2.constBegin(); it != mFeatureMap2.constEnd(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); QgsFeatureId currentId = it->feature.id(); if ( duplicateIds.contains( currentId ) ) { //is already a duplicate geometry..skip.. continue; } if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.constBegin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.constEnd(); bool duplicate = false; for ( ; cit != crossingIdsEnd; ++cit ) { duplicate = false; // skip itself if ( mFeatureMap2[*cit].feature.id() == it->feature.id() ) continue; QgsGeometry g2 = mFeatureMap2[*cit].feature.geometry(); if ( g2.isNull() ) { QgsMessageLog::logMessage( tr( "Invalid second geometry in duplicate geometry test." ), tr( "Topology plugin" ) ); continue; } if ( !_canExportToGeos( g2 ) ) { QgsMessageLog::logMessage( tr( "Failed to import second geometry into GEOS in duplicate geometry test." ), tr( "Topology plugin" ) ); continue; } if ( g1.isGeosEqual( g2 ) ) { duplicate = true; duplicateIds.append( mFeatureMap2[*cit].feature.id() ); } if ( duplicate ) { QList<FeatureLayer> fls; fls << *it << *it; QgsGeometry conflict( g1 ); if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflict ) ) { continue; } if ( canvasExtentPoly.crosses( conflict ) ) { conflict = conflict.intersection( canvasExtentPoly ); } } TopolErrorDuplicates *err = new TopolErrorDuplicates( bb, conflict, fls ); errorList << err; } } } return errorList; }
ErrorList topolTest::checkDuplicates( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( tolerance ); Q_UNUSED( layer2 ); //TODO: multilines - check all separate pieces int i = 0; ErrorList errorList; QList<QgsFeatureId> duplicateIds; QgsSpatialIndex* index = mLayerIndexes[layer1->id()]; QgsGeometry* canvasExtentPoly = QgsGeometry::fromWkt( theQgsInterface->mapCanvas()->extent().asWktPolygon() ); QMap<QgsFeatureId, FeatureLayer>::Iterator it; QMap<QgsFeatureId, FeatureLayer>::ConstIterator FeatureListEnd = mFeatureMap2.end(); for ( it = mFeatureMap2.begin(); it != FeatureListEnd; ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); QgsFeatureId currentId = it->feature.id(); if ( duplicateIds.contains( currentId ) ) { //is already a duplicate geometry..skip.. continue; } if ( testCancelled() ) break; QgsGeometry* g1 = it->feature.geometry(); QgsRectangle bb = g1->boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::Iterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); bool duplicate = false; for ( ; cit != crossingIdsEnd; ++cit ) { duplicate = false; // skip itself if ( mFeatureMap2[*cit].feature.id() == it->feature.id() ) continue; const QgsGeometry* g2 = mFeatureMap2[*cit].feature.constGeometry(); if ( !g2 ) { QgsMessageLog::logMessage( tr( "Invalid second geometry in duplicate geometry test." ), tr( "Topology plugin" ) ); continue; } if ( !g2->asGeos() ) { QgsMessageLog::logMessage( tr( "Failed to import second geometry into GEOS in duplicate geometry test." ), tr( "Topology plugin" ) ); continue; } if ( g1->equals( g2 ) ) { duplicate = true; duplicateIds.append( mFeatureMap2[*cit].feature.id() ); } if ( duplicate ) { QList<FeatureLayer> fls; fls << *it << *it; QScopedPointer<QgsGeometry> conflict( new QgsGeometry( *g1 ) ); if ( isExtent ) { if ( canvasExtentPoly->disjoint( conflict.data() ) ) { continue; } if ( canvasExtentPoly->crosses( conflict.data() ) ) { conflict.reset( conflict->intersection( canvasExtentPoly ) ); } } TopolErrorDuplicates* err = new TopolErrorDuplicates( bb, conflict.take(), fls ); errorList << err; } } } delete canvasExtentPoly; return errorList; }
ErrorList topolTest::checkCloseFeature( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent ) { Q_UNUSED( isExtent ); ErrorList errorList; QgsSpatialIndex* index = 0; bool badG1 = false, badG2 = false; bool skipItself = layer1 == layer2; int i = 0; QList<FeatureLayer>::Iterator it; QList<FeatureLayer>::ConstIterator FeatureListEnd = mFeatureList1.end(); for ( it = mFeatureList1.begin(); it != FeatureListEnd; ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCancelled() ) break; QgsGeometry* g1 = it->feature.geometry(); if ( !g1 || !g1->asGeos() ) { badG1 = true; continue; } QgsRectangle bb = g1->boundingBox(); // increase bounding box by tolerance QgsRectangle frame( bb.xMinimum() - tolerance, bb.yMinimum() - tolerance, bb.xMaximum() + tolerance, bb.yMaximum() + tolerance ); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( frame ); QList<QgsFeatureId>::Iterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature& f = mFeatureMap2[*cit].feature; QgsGeometry* g2 = f.geometry(); // skip itself, when invoked with the same layer if ( skipItself && f.id() == it->feature.id() ) continue; if ( !g2 || !g2->asGeos() ) { badG2 = true; continue; } if ( g1->distance( *g2 ) < tolerance ) { QgsRectangle r = g2->boundingBox(); r.combineExtentWith( &bb ); QList<FeatureLayer> fls; FeatureLayer fl; fl.feature = f; fl.layer = layer2; fls << *it << fl; QgsGeometry* conflict = new QgsGeometry( *g2 ); TopolErrorClose* err = new TopolErrorClose( r, conflict, fls ); //TopolErrorClose* err = new TopolErrorClose(r, g2, fls); errorList << err; } } } if ( badG2 ) QgsMessageLog::logMessage( tr( "Invalid second geometry." ), tr( "Topology plugin" ) ); if ( badG1 ) QgsMessageLog::logMessage( tr( "Invalid first geometry." ), tr( "Topology plugin" ) ); return errorList; }
ErrorList topolTest::checkPointInPolygon( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( tolerance ); int i = 0; ErrorList errorList; if ( layer1->geometryType() != QGis::Point ) { return errorList; } if ( layer2->geometryType() != QGis::Polygon ) { return errorList; } QgsSpatialIndex* index = mLayerIndexes[layer2->id()]; QgsGeometry* canvasExtentPoly = QgsGeometry::fromWkt( theQgsInterface->mapCanvas()->extent().asWktPolygon() ); QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCancelled() ) break; QgsGeometry* g1 = it->feature.geometry(); QgsRectangle bb = g1->boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::Iterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature& f = mFeatureMap2[*cit].feature; const QgsGeometry* g2 = f.constGeometry(); if ( !g2 || !g2->asGeos() ) { QgsMessageLog::logMessage( tr( "Second geometry missing or GEOS import failed." ), tr( "Topology plugin" ) ); continue; } if ( g2->contains( g1 ) ) { touched = true; break; } } if ( !touched ) { QgsGeometry* conflictGeom = new QgsGeometry( *g1 ); if ( isExtent ) { if ( canvasExtentPoly->disjoint( conflictGeom ) ) { delete conflictGeom; continue; } } QList<FeatureLayer> fls; fls << *it << *it; //bb.scale(10); TopolErrorPointNotInPolygon* err = new TopolErrorPointNotInPolygon( bb, conflictGeom, fls ); errorList << err; } } delete canvasExtentPoly; return errorList; }
static bool snapLineString( QgsLineString *linestring, QgsSpatialIndex &index, QVector<AnchorPoint> &pnts, double thresh ) { QVector<QgsPoint> newPoints; QVector<int> anchors; // indexes of anchors for vertices double thresh2 = thresh * thresh; double minDistX, minDistY; // coordinates of the closest point on the segment line bool changed = false; // snap vertices for ( int v = 0; v < linestring->numPoints(); v++ ) { double x = linestring->xAt( v ); double y = linestring->yAt( v ); QgsRectangle rect( x, y, x, y ); // Find point ( should always find one point ) QList<QgsFeatureId> fids = index.intersects( rect ); Q_ASSERT( fids.count() == 1 ); int spoint = fids.first(); int anchor = pnts[spoint].anchor; if ( anchor >= 0 ) { // to be snapped linestring->setXAt( v, pnts[anchor].x ); linestring->setYAt( v, pnts[anchor].y ); anchors.append( anchor ); // point on new location changed = true; } else { anchors.append( spoint ); // old point } } // Snap all segments to anchors in threshold for ( int v = 0; v < linestring->numPoints() - 1; v++ ) { double x1 = linestring->xAt( v ); double x2 = linestring->xAt( v + 1 ); double y1 = linestring->yAt( v ); double y2 = linestring->yAt( v + 1 ); newPoints << linestring->pointN( v ); // Box double xmin = x1, xmax = x2, ymin = y1, ymax = y2; if ( xmin > xmax ) std::swap( xmin, xmax ); if ( ymin > ymax ) std::swap( ymin, ymax ); QgsRectangle rect( xmin - thresh, ymin - thresh, xmax + thresh, ymax + thresh ); // Find points const QList<QgsFeatureId> fids = index.intersects( rect ); QVector<AnchorAlongSegment> newVerticesAlongSegment; // Snap to anchor in threshold different from end points for ( QgsFeatureId fid : fids ) { int spoint = fid; if ( spoint == anchors[v] || spoint == anchors[v + 1] ) continue; // end point if ( pnts[spoint].anchor >= 0 ) continue; // point is not anchor // Check the distance double dist2 = QgsGeometryUtils::sqrDistToLine( pnts[spoint].x, pnts[spoint].y, x1, y1, x2, y2, minDistX, minDistY, 0 ); // skip points that are behind segment's endpoints or extremely close to them double dx1 = minDistX - x1, dx2 = minDistX - x2; double dy1 = minDistY - y1, dy2 = minDistY - y2; bool isOnSegment = !qgsDoubleNear( dx1 * dx1 + dy1 * dy1, 0 ) && !qgsDoubleNear( dx2 * dx2 + dy2 * dy2, 0 ); if ( isOnSegment && dist2 <= thresh2 ) { // an anchor is in the threshold AnchorAlongSegment item; item.anchor = spoint; item.along = QgsPointXY( x1, y1 ).distance( minDistX, minDistY ); newVerticesAlongSegment << item; } } if ( !newVerticesAlongSegment.isEmpty() ) { // sort by distance along the segment std::sort( newVerticesAlongSegment.begin(), newVerticesAlongSegment.end(), []( const AnchorAlongSegment & p1, const AnchorAlongSegment & p2 ) { return ( p1.along < p2.along ? -1 : ( p1.along > p2.along ) ); } ); // insert new vertices for ( int i = 0; i < newVerticesAlongSegment.count(); i++ ) { int anchor = newVerticesAlongSegment[i].anchor; newPoints << QgsPoint( pnts[anchor].x, pnts[anchor].y, 0 ); } changed = true; } } // append end point newPoints << linestring->pointN( linestring->numPoints() - 1 ); // replace linestring's points if ( changed ) linestring->setPoints( newPoints ); return changed; }
QVariantMap QgsSplitWithLinesAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !source ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); std::unique_ptr< QgsFeatureSource > linesSource( parameterAsSource( parameters, QStringLiteral( "LINES" ), context ) ); if ( !linesSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "LINES" ) ) ); bool sameLayer = parameters.value( QStringLiteral( "INPUT" ) ) == parameters.value( QStringLiteral( "LINES" ) ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), QgsWkbTypes::multiType( source->wkbType() ), source->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); QgsSpatialIndex spatialIndex; QMap< QgsFeatureId, QgsGeometry > splitGeoms; QgsFeatureRequest request; request.setSubsetOfAttributes( QgsAttributeList() ); request.setDestinationCrs( source->sourceCrs(), context.transformContext() ); QgsFeatureIterator splitLines = linesSource->getFeatures( request ); QgsFeature aSplitFeature; while ( splitLines.nextFeature( aSplitFeature ) ) { if ( feedback->isCanceled() ) { break; } splitGeoms.insert( aSplitFeature.id(), aSplitFeature.geometry() ); spatialIndex.addFeature( aSplitFeature ); } QgsFeature outFeat; QgsFeatureIterator features = source->getFeatures(); double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1; int i = 0; QgsFeature inFeatureA; while ( features.nextFeature( inFeatureA ) ) { i++; if ( feedback->isCanceled() ) { break; } if ( !inFeatureA.hasGeometry() ) { sink->addFeature( inFeatureA, QgsFeatureSink::FastInsert ); continue; } QgsGeometry inGeom = inFeatureA.geometry(); outFeat.setAttributes( inFeatureA.attributes() ); QVector< QgsGeometry > inGeoms = inGeom.asGeometryCollection(); const QgsFeatureIds lines = spatialIndex.intersects( inGeom.boundingBox() ).toSet(); if ( !lines.empty() ) // has intersection of bounding boxes { QVector< QgsGeometry > splittingLines; // use prepared geometries for faster intersection tests std::unique_ptr< QgsGeometryEngine > engine; for ( QgsFeatureId line : lines ) { // check if trying to self-intersect if ( sameLayer && inFeatureA.id() == line ) continue; QgsGeometry splitGeom = splitGeoms.value( line ); if ( !engine ) { engine.reset( QgsGeometry::createGeometryEngine( inGeom.constGet() ) ); engine->prepareGeometry(); } if ( engine->intersects( splitGeom.constGet() ) ) { QVector< QgsGeometry > splitGeomParts = splitGeom.asGeometryCollection(); splittingLines.append( splitGeomParts ); } } if ( !splittingLines.empty() ) { for ( const QgsGeometry &splitGeom : qgis::as_const( splittingLines ) ) { QVector<QgsPointXY> splitterPList; QVector< QgsGeometry > outGeoms; // use prepared geometries for faster intersection tests std::unique_ptr< QgsGeometryEngine > splitGeomEngine( QgsGeometry::createGeometryEngine( splitGeom.constGet() ) ); splitGeomEngine->prepareGeometry(); while ( !inGeoms.empty() ) { if ( feedback->isCanceled() ) { break; } QgsGeometry inGeom = inGeoms.takeFirst(); if ( !inGeom ) continue; if ( splitGeomEngine->intersects( inGeom.constGet() ) ) { QgsGeometry before = inGeom; if ( splitterPList.empty() ) { const QgsCoordinateSequence sequence = splitGeom.constGet()->coordinateSequence(); for ( const QgsRingSequence &part : sequence ) { for ( const QgsPointSequence &ring : part ) { for ( const QgsPoint &pt : ring ) { splitterPList << QgsPointXY( pt ); } } } } QVector< QgsGeometry > newGeometries; QVector<QgsPointXY> topologyTestPoints; QgsGeometry::OperationResult result = inGeom.splitGeometry( splitterPList, newGeometries, false, topologyTestPoints ); // splitGeometry: If there are several intersections // between geometry and splitLine, only the first one is considered. if ( result == QgsGeometry::Success ) // split occurred { if ( inGeom.isGeosEqual( before ) ) { // bug in splitGeometry: sometimes it returns 0 but // the geometry is unchanged outGeoms.append( inGeom ); } else { inGeoms.append( inGeom ); inGeoms.append( newGeometries ); } } else { outGeoms.append( inGeom ); } } else { outGeoms.append( inGeom ); } } inGeoms = outGeoms; } } } QVector< QgsGeometry > parts; for ( const QgsGeometry &aGeom : qgis::as_const( inGeoms ) ) { if ( feedback->isCanceled() ) { break; } bool passed = true; if ( QgsWkbTypes::geometryType( aGeom.wkbType() ) == QgsWkbTypes::LineGeometry ) { int numPoints = aGeom.constGet()->nCoordinates(); if ( numPoints <= 2 ) { if ( numPoints == 2 ) passed = !static_cast< const QgsCurve * >( aGeom.constGet() )->isClosed(); // tests if vertex 0 = vertex 1 else passed = false; // sometimes splitting results in lines of zero length } } if ( passed ) parts.append( aGeom ); } for ( const QgsGeometry &g : parts ) { outFeat.setGeometry( g ); sink->addFeature( outFeat, QgsFeatureSink::FastInsert ); } feedback->setProgress( i * step ); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
void QgsPointDisplacementRenderer::createDisplacementGroups( QgsVectorLayer* vlayer, const QgsRectangle& viewExtent ) { if ( !vlayer || ( vlayer->wkbType() != QGis::WKBPoint && vlayer->wkbType() != QGis::WKBPoint25D ) ) { return; } mDisplacementGroups.clear(); mDisplacementIds.clear(); //use a spatial index to check if there is already a point at a position QgsSpatialIndex spatialIndex; //attributes QgsAttributeList attList; QList<QString> attributeStrings = usedAttributes(); QList<QString>::const_iterator attStringIt = attributeStrings.constBegin(); for ( ; attStringIt != attributeStrings.constEnd(); ++attStringIt ) { attList.push_back( vlayer->fieldNameIndex( *attStringIt ) ); } QgsFeature f; QList<QgsFeatureId> intersectList; //Because the new vector api does not allow querying features by id within a nextFeature loop, default constructed QgsFeature() is //inserted first and the real features are created in a second loop QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( viewExtent ).setSubsetOfAttributes( attList ) ); while ( fit.nextFeature( f ) ) { intersectList.clear(); //check, if there is already a point at that position if ( f.geometry() ) { intersectList = spatialIndex.intersects( searchRect( f.geometry()->asPoint() ) ); if ( intersectList.empty() ) { spatialIndex.insertFeature( f ); } else { //go through all the displacement group maps and search an entry where the id equals the result of the spatial search QgsFeatureId existingEntry = intersectList.at( 0 ); bool found = false; QList< QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin(); for ( ; it != mDisplacementGroups.end(); ++it ) { if ( it->size() > 0 && it->contains( existingEntry ) ) { found = true; QgsFeature feature; it->insert( f.id(), QgsFeature() ); mDisplacementIds.insert( f.id() ); break; } } if ( !found )//insert the already existing feature and the new one into a map { QMap<QgsFeatureId, QgsFeature> newMap; newMap.insert( existingEntry, QgsFeature() ); mDisplacementIds.insert( existingEntry ); newMap.insert( f.id(), QgsFeature() ); mDisplacementIds.insert( f.id() ); mDisplacementGroups.push_back( newMap ); } } } } //insert the real features into mDisplacementGroups QList< QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin(); for ( ; it != mDisplacementGroups.end(); ++it ) { QMap<QgsFeatureId, QgsFeature>::iterator mapIt = it->begin(); for ( ; mapIt != it->end(); ++mapIt ) { QgsFeature fet; vlayer->getFeatures( QgsFeatureRequest().setFilterFid( mapIt.key() ) ).nextFeature( fet ); mapIt.value() = fet; } } }
void QgsPointDisplacementRenderer::createDisplacementGroups( QgsVectorLayer* vlayer, const QgsRectangle& viewExtent ) { if ( !vlayer || ( vlayer->wkbType() != QGis::WKBPoint && vlayer->wkbType() != QGis::WKBPoint25D ) ) { return; } mDisplacementGroups.clear(); mDisplacementIds.clear(); //use a spatial index to check if there is already a point at a position QgsSpatialIndex spatialIndex; //attributes QgsAttributeList attList; QList<QString> attributeStrings = usedAttributes(); QList<QString>::const_iterator attStringIt = attributeStrings.constBegin(); for ( ; attStringIt != attributeStrings.constEnd(); ++attStringIt ) { attList.push_back( vlayer->fieldNameIndex( *attStringIt ) ); } QgsFeature f; QList<int> intersectList; vlayer->select( attList, viewExtent, true, false ); while ( vlayer->nextFeature( f ) ) { intersectList.clear(); //check, if there is already a point at that position if ( f.geometry() ) { intersectList = spatialIndex.intersects( searchRect( f.geometry()->asPoint() ) ); if ( intersectList.empty() ) { spatialIndex.insertFeature( f ); } else { //go through all the displacement group maps and search an entry where the id equals the result of the spatial search int existingEntry = intersectList.at( 0 ); bool found = false; QList<QMap<int, QgsFeature> >::iterator it = mDisplacementGroups.begin(); for ( ; it != mDisplacementGroups.end(); ++it ) { if ( it->size() > 0 && it->contains( existingEntry ) ) { found = true; QgsFeature feature; it->insert( f.id(), f ); mDisplacementIds.insert( f.id() ); break; } } if ( !found )//insert the already existing feature and the new one into a map { QMap<int, QgsFeature> newMap; QgsFeature existingFeature; vlayer->featureAtId( existingEntry, existingFeature ); newMap.insert( existingEntry, existingFeature ); mDisplacementIds.insert( existingEntry ); newMap.insert( f.id(), f ); mDisplacementIds.insert( f.id() ); mDisplacementGroups.push_back( newMap ); } } } } //refresh the selection because the vector layer is going to step through all features now vlayer->select( attList, viewExtent, true, false ); }