void QgsRelationReferenceWidget::highlightFeature( QgsFeature f, CanvasExtent canvasExtent ) { if ( !mCanvas ) return; if ( !f.isValid() ) { f = referencedFeature(); if ( !f.isValid() ) return; } if ( !f.hasGeometry() ) { return; } QgsGeometry geom = f.geometry(); // scale or pan if ( canvasExtent == Scale ) { QgsRectangle featBBox = geom.boundingBox(); featBBox = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, featBBox ); QgsRectangle extent = mCanvas->extent(); if ( !extent.contains( featBBox ) ) { extent.combineExtentWith( featBBox ); extent.scale( 1.1 ); mCanvas->setExtent( extent ); mCanvas->refresh(); } } else if ( canvasExtent == Pan ) { QgsGeometry centroid = geom.centroid(); QgsPointXY center = centroid.asPoint(); center = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, center ); mCanvas->zoomByFactor( 1.0, ¢er ); // refresh is done in this method } // highlight deleteHighlight(); mHighlight = new QgsHighlight( mCanvas, f, mReferencedLayer ); QgsIdentifyMenu::styleHighlight( mHighlight ); mHighlight->show(); QTimer *timer = new QTimer( this ); timer->setSingleShot( true ); connect( timer, &QTimer::timeout, this, &QgsRelationReferenceWidget::deleteHighlight ); timer->start( 3000 ); }
void QgsAdvancedDigitizingCanvasItem::paint( QPainter *painter ) { if ( !mAdvancedDigitizingDockWidget->cadEnabled() ) return; QgsRectangle mapRect = mMapCanvas->extent(); if ( rect() != mapRect ) setRect( mapRect ); int nPoints = mAdvancedDigitizingDockWidget->pointsCount(); if ( !nPoints ) return; bool previousPointExist, penulPointExist; const QgsPointXY curPoint = mAdvancedDigitizingDockWidget->currentPoint(); const QgsPointXY prevPoint = mAdvancedDigitizingDockWidget->previousPoint( &previousPointExist ); const QgsPointXY penulPoint = mAdvancedDigitizingDockWidget->penultimatePoint( &penulPointExist ); const bool snappedToVertex = mAdvancedDigitizingDockWidget->snappedToVertex(); const QList<QgsPointXY> snappedSegment = mAdvancedDigitizingDockWidget->snappedSegment(); const bool hasSnappedSegment = snappedSegment.count() == 2; const bool curPointExist = mapRect.contains( curPoint ); const double mupp = mMapCanvas->getCoordinateTransform()->mapUnitsPerPixel(); if ( mupp == 0 ) return; QPointF curPointPix, prevPointPix, penulPointPix, snapSegmentPix1, snapSegmentPix2; if ( curPointExist ) { curPointPix = toCanvasCoordinates( curPoint ); } if ( previousPointExist ) { prevPointPix = toCanvasCoordinates( prevPoint ); } if ( penulPointExist ) { penulPointPix = toCanvasCoordinates( penulPoint ); } if ( hasSnappedSegment ) { snapSegmentPix1 = toCanvasCoordinates( snappedSegment[0] ); snapSegmentPix2 = toCanvasCoordinates( snappedSegment[1] ); } painter->setRenderHint( QPainter::Antialiasing ); painter->setCompositionMode( QPainter::CompositionMode_Difference ); // Draw point snap if ( curPointExist && snappedToVertex ) { painter->setPen( mSnapPen ); painter->drawEllipse( curPointPix, 10, 10 ); } // Draw segment snap if ( hasSnappedSegment && !snappedToVertex ) { painter->setPen( mSnapPen ); painter->drawLine( snapSegmentPix1, snapSegmentPix2 ); if ( curPointExist ) { painter->setPen( mSnapLinePen ); painter->drawLine( snapSegmentPix1, curPointPix ); } } // Draw segment par/per input if ( mAdvancedDigitizingDockWidget->additionalConstraint() != QgsAdvancedDigitizingDockWidget::NoConstraint && hasSnappedSegment ) { painter->setPen( mConstruction2Pen ); painter->drawLine( snapSegmentPix1, snapSegmentPix2 ); } // Draw angle if ( nPoints > 1 ) { double a0, a; if ( mAdvancedDigitizingDockWidget->constraintAngle()->relative() && nPoints > 2 ) { a0 = std::atan2( -( prevPoint.y() - penulPoint.y() ), prevPoint.x() - penulPoint.x() ); } else { a0 = 0; } if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() ) { a = a0 - mAdvancedDigitizingDockWidget->constraintAngle()->value() * M_PI / 180; } else { a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() ); } painter->setPen( mConstruction2Pen ); painter->drawArc( QRectF( prevPointPix.x() - 20, prevPointPix.y() - 20, 40, 40 ), static_cast<int>( 16 * -a0 * 180 / M_PI ), static_cast<int>( 16 * ( a0 - a ) * 180 / M_PI ) ); painter->drawLine( prevPointPix, prevPointPix + 60 * QPointF( std::cos( a0 ), std::sin( a0 ) ) ); if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() ) { painter->setPen( mLockedPen ); double d = std::max( boundingRect().width(), boundingRect().height() ); painter->drawLine( prevPointPix - d * QPointF( std::cos( a ), std::sin( a ) ), prevPointPix + d * QPointF( std::cos( a ), std::sin( a ) ) ); } } // Draw distance if ( nPoints > 1 && mAdvancedDigitizingDockWidget->constraintDistance()->isLocked() ) { painter->setPen( mLockedPen ); double r = mAdvancedDigitizingDockWidget->constraintDistance()->value() / mupp; painter->drawEllipse( prevPointPix, r, r ); } // Draw x if ( mAdvancedDigitizingDockWidget->constraintX()->isLocked() ) { double x = 0.0; bool draw = true; painter->setPen( mLockedPen ); if ( mAdvancedDigitizingDockWidget->constraintX()->relative() ) { if ( nPoints > 1 ) { x = mAdvancedDigitizingDockWidget->constraintX()->value() / mupp + prevPointPix.x(); } else { draw = false; } } else { x = toCanvasCoordinates( QgsPointXY( mAdvancedDigitizingDockWidget->constraintX()->value(), 0 ) ).x(); } if ( draw ) { painter->drawLine( QPointF( x, 0 ), QPointF( x, boundingRect().height() ) ); } } // Draw y if ( mAdvancedDigitizingDockWidget->constraintY()->isLocked() ) { double y = 0.0; bool draw = true; painter->setPen( mLockedPen ); if ( mAdvancedDigitizingDockWidget->constraintY()->relative() ) { if ( nPoints > 1 ) { // y is reversed! y = -mAdvancedDigitizingDockWidget->constraintY()->value() / mupp + prevPointPix.y(); } else { draw = false; } } else { y = toCanvasCoordinates( QgsPointXY( 0, mAdvancedDigitizingDockWidget->constraintY()->value() ) ).y(); } if ( draw ) { painter->drawLine( QPointF( 0, y ), QPointF( boundingRect().width(), y ) ); } } // Draw constr if ( mAdvancedDigitizingDockWidget->additionalConstraint() == QgsAdvancedDigitizingDockWidget::NoConstraint ) { if ( curPointExist && previousPointExist ) { painter->setPen( mConstruction2Pen ); painter->drawLine( prevPointPix, curPointPix ); } if ( previousPointExist && penulPointExist ) { painter->setPen( mConstruction1Pen ); painter->drawLine( penulPointPix, prevPointPix ); } } if ( curPointExist ) { painter->setPen( mCursorPen ); painter->drawLine( curPointPix + QPointF( -5, -5 ), curPointPix + QPointF( +5, +5 ) ); painter->drawLine( curPointPix + QPointF( -5, +5 ), curPointPix + QPointF( +5, -5 ) ); } QgsPointLocator::Match match = mAdvancedDigitizingDockWidget->mapPointMatch(); if ( match.isValid() ) { mSnapIndicator->setMatch( match ); mSnapIndicator->setVisible( true ); } else mSnapIndicator->setVisible( false ); }
QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTextFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsDelimitedTextFeatureSource>( source, ownSource, request ) , mTestGeometryExact( false ) { // Determine mode to use based on request... QgsDebugMsg( "Setting up QgsDelimitedTextIterator" ); // Does the layer have geometry - will revise later to determine if we actually need to // load it. bool hasGeometry = mSource->mGeomRep != QgsDelimitedTextProvider::GeomNone; // Does the layer have an explicit or implicit subset (implicit subset is if we have geometry which can // be invalid) mTestSubset = mSource->mSubsetExpression; mTestGeometry = false; mMode = FileScan; if ( request.filterType() == QgsFeatureRequest::FilterFid ) { QgsDebugMsg( "Configuring for returning single id" ); mFeatureIds.append( request.filterFid() ); mMode = FeatureIds; mTestSubset = false; } // If have geometry and testing geometry then evaluate options... // If we don't have geometry then all records pass geometry filter. // CC: 2013-05-09 // Not sure about intended relationship between filtering on geometry and // requesting no geometry? Have preserved current logic of ignoring spatial filter // if not requesting geometry. else if ( request.filterType() == QgsFeatureRequest::FilterRect && hasGeometry ) { QgsDebugMsg( "Configuring for rectangle select" ); mTestGeometry = true; // Exact intersection test only applies for WKT geometries mTestGeometryExact = mRequest.flags() & QgsFeatureRequest::ExactIntersect && mSource->mGeomRep == QgsDelimitedTextProvider::GeomAsWkt; QgsRectangle rect = request.filterRect(); // If request doesn't overlap extents, then nothing to return if ( ! rect.intersects( mSource->mExtent ) ) { QgsDebugMsg( "Rectangle outside layer extents - no features to return" ); mMode = FeatureIds; } // If the request extents include the entire layer, then revert to // a file scan else if ( rect.contains( mSource->mExtent ) ) { QgsDebugMsg( "Rectangle contains layer extents - bypass spatial filter" ); mTestGeometry = false; } // If we have a spatial index then use it. The spatial index already accounts // for the subset. Also means we don't have to test geometries unless doing exact // intersection else if ( mSource->mUseSpatialIndex ) { mFeatureIds = mSource->mSpatialIndex->intersects( rect ); // Sort for efficient sequential retrieval qSort( mFeatureIds.begin(), mFeatureIds.end() ); QgsDebugMsg( QString( "Layer has spatial index - selected %1 features from index" ).arg( mFeatureIds.size() ) ); mMode = FeatureIds; mTestSubset = false; mTestGeometry = mTestGeometryExact; } } // If we have a subset index then use it.. if ( mMode == FileScan && mSource->mUseSubsetIndex ) { QgsDebugMsg( QString( "Layer has subset index - use %1 items from subset index" ).arg( mSource->mSubsetIndex.size() ) ); mTestSubset = false; mMode = SubsetIndex; } // Otherwise just have to scan the file if ( mMode == FileScan ) { QgsDebugMsg( "File will be scanned for desired features" ); } // If the layer has geometry, do we really need to load it? // We need it if it is asked for explicitly in the request, // if we are testing geometry (ie spatial filter), or // if testing the subset expression. if ( hasGeometry && ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) || mTestGeometry || ( mTestSubset && mSource->mSubsetExpression->needsGeometry() ) ) ) { mLoadGeometry = true; } else { QgsDebugMsgLevel( "Feature geometries not required", 4 ); mLoadGeometry = false; } QgsDebugMsg( QString( "Iterator is scanning file: " ) + ( mMode == FileScan ? "Yes" : "No" ) ); QgsDebugMsg( QString( "Iterator is loading geometries: " ) + ( mLoadGeometry ? "Yes" : "No" ) ); QgsDebugMsg( QString( "Iterator is testing geometries: " ) + ( mTestGeometry ? "Yes" : "No" ) ); QgsDebugMsg( QString( "Iterator is testing subset: " ) + ( mTestSubset ? "Yes" : "No" ) ); rewind(); }
bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, bool triggerRepaint ) { mMessages.clear(); if ( error->status() >= QgsGeometryCheckError::StatusFixed ) { return true; } #if 0 QTextStream( stdout ) << "Fixing " << error->description() << ": " << error->layerId() << ":" << error->featureId() << " @[" << error->vidx().part << ", " << error->vidx().ring << ", " << error->vidx().vertex << "](" << error->location().x() << ", " << error->location().y() << ") = " << error->value().toString() << endl; #endif QgsGeometryCheck::Changes changes; QgsRectangle recheckArea = error->affectedAreaBBox(); error->check()->fixError( error, method, mMergeAttributeIndices, changes ); #if 0 QTextStream( stdout ) << " * Status: " << error->resolutionMessage() << endl; static QVector<QString> strChangeWhat = { "ChangeFeature", "ChangePart", "ChangeRing", "ChangeNode" }; static QVector<QString> strChangeType = { "ChangeAdded", "ChangeRemoved", "ChangeChanged" }; for ( const QString &layerId : changes.keys() ) { for ( const QgsFeatureId &fid : changes[layerId].keys() ) { for ( const QgsGeometryCheck::Change &change : changes[layerId][fid] ) { QTextStream( stdout ) << " * Change: " << layerId << ":" << fid << " :: " << strChangeWhat[change.what] << ":" << strChangeType[change.type] << ":(" << change.vidx.part << "," << change.vidx.ring << "," << change.vidx.vertex << ")" << endl; } } } #endif emit errorUpdated( error, true ); if ( error->status() != QgsGeometryCheckError::StatusFixed ) { return false; } // If nothing was changed, stop here if ( changes.isEmpty() ) { return true; } // Determine what to recheck // - Collect all features which were changed, get affected area QMap<QString, QSet<QgsFeatureId>> recheckFeatures; for ( auto it = changes.constBegin(); it != changes.constEnd(); ++it ) { const QMap<QgsFeatureId, QList<QgsGeometryCheck::Change>> &layerChanges = it.value(); QgsFeaturePool *featurePool = mContext->featurePools[it.key()]; QgsCoordinateTransform t( featurePool->getLayer()->crs(), mContext->mapCrs, QgsProject::instance() ); for ( auto layerChangeIt = layerChanges.constBegin(); layerChangeIt != layerChanges.constEnd(); ++layerChangeIt ) { bool removed = false; for ( const QgsGeometryCheck::Change &change : layerChangeIt.value() ) { if ( change.what == QgsGeometryCheck::ChangeFeature && change.type == QgsGeometryCheck::ChangeRemoved ) { removed = true; break; } } if ( !removed ) { QgsFeature f; if ( featurePool->get( layerChangeIt.key(), f ) ) { recheckFeatures[it.key()].insert( layerChangeIt.key() ); recheckArea.combineExtentWith( t.transformBoundingBox( f.geometry().boundingBox() ) ); } } } } // - Determine extent to recheck for gaps for ( QgsGeometryCheckError *err : qgis::as_const( mCheckErrors ) ) { if ( err->check()->getCheckType() == QgsGeometryCheck::LayerCheck ) { if ( err->affectedAreaBBox().intersects( recheckArea ) ) { recheckArea.combineExtentWith( err->affectedAreaBBox() ); } } } recheckArea.grow( 10 * mContext->tolerance ); QMap<QString, QgsFeatureIds> recheckAreaFeatures; for ( const QString &layerId : mContext->featurePools.keys() ) { QgsFeaturePool *featurePool = mContext->featurePools[layerId]; QgsCoordinateTransform t( mContext->mapCrs, featurePool->getLayer()->crs(), QgsProject::instance() ); recheckAreaFeatures[layerId] = featurePool->getIntersects( t.transform( recheckArea ) ); } // Recheck feature / changed area to detect new errors QList<QgsGeometryCheckError *> recheckErrors; for ( const QgsGeometryCheck *check : qgis::as_const( mChecks ) ) { if ( check->getCheckType() == QgsGeometryCheck::LayerCheck ) { if ( !recheckAreaFeatures.isEmpty() ) { check->collectErrors( recheckErrors, mMessages, nullptr, recheckAreaFeatures ); } } else { if ( !recheckFeatures.isEmpty() ) { check->collectErrors( recheckErrors, mMessages, nullptr, recheckFeatures ); } } } // Go through error list, update other errors of the checked feature for ( QgsGeometryCheckError *err : qgis::as_const( mCheckErrors ) ) { if ( err == error || err->status() == QgsGeometryCheckError::StatusObsolete ) { continue; } QgsGeometryCheckError::Status oldStatus = err->status(); bool handled = err->handleChanges( changes ); // Check if this error now matches one found when rechecking the feature/area QgsGeometryCheckError *matchErr = nullptr; int nMatch = 0; for ( QgsGeometryCheckError *recheckErr : qgis::as_const( recheckErrors ) ) { if ( recheckErr->isEqual( err ) || recheckErr->closeMatch( err ) ) { ++nMatch; matchErr = recheckErr; } } // If just one close match was found, take it if ( nMatch == 1 && matchErr ) { err->update( matchErr ); emit errorUpdated( err, err->status() != oldStatus ); recheckErrors.removeAll( matchErr ); delete matchErr; continue; } // If no match is found and the error is not fixed or obsolete, set it to obsolete if... if ( err->status() < QgsGeometryCheckError::StatusFixed && ( // changes weren't handled !handled || // or if it is a FeatureNodeCheck or FeatureCheck error whose feature was rechecked ( err->check()->getCheckType() <= QgsGeometryCheck::FeatureCheck && recheckFeatures[err->layerId()].contains( err->featureId() ) ) || // or if it is a LayerCheck error within the rechecked area ( err->check()->getCheckType() == QgsGeometryCheck::LayerCheck && recheckArea.contains( err->affectedAreaBBox() ) ) ) ) { err->setObsolete(); emit errorUpdated( err, err->status() != oldStatus ); } } // Add new errors for ( QgsGeometryCheckError *recheckErr : qgis::as_const( recheckErrors ) ) { emit errorAdded( recheckErr ); mCheckErrors.append( recheckErr ); } if ( triggerRepaint ) { for ( const QString &layerId : changes.keys() ) { mContext->featurePools[layerId]->getLayer()->triggerRepaint(); } } return true; }