void QgsMapToolFeatureAction::canvasReleaseEvent( QMouseEvent *e ) { QgsMapLayer *layer = mCanvas->currentLayer(); if ( !layer || layer->type() != QgsMapLayer::VectorLayer ) { emit messageEmitted( tr( "To run an action, you must choose an active vector layer." ), QgsMessageBar::INFO ); return; } if ( !mCanvas->layers().contains( layer ) ) { // do not run actions on hidden layers return; } QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ); if ( vlayer->actions()->size() == 0 && QgsMapLayerActionRegistry::instance()->mapLayerActions( vlayer ).size() == 0 ) { emit messageEmitted( tr( "The active vector layer has no defined actions" ), QgsMessageBar::INFO ); return; } if ( !doAction( vlayer, e->x(), e->y() ) ) QgisApp::instance()->statusBar()->showMessage( tr( "No features at this position found." ) ); }
void QgsMapToolNodeTool::canvasPressEvent( QMouseEvent * e ) { QgsDebugCall; mClicked = true; mPressCoordinates = e->pos(); if ( !mSelectedFeature ) { QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) return; mSelectAnother = false; QgsPointLocator::Match m = mCanvas->snappingUtils()->snapToCurrentLayer( e->pos(), QgsPointLocator::Vertex | QgsPointLocator::Edge ); if ( !m.isValid() ) { emit messageEmitted( tr( "could not snap to a segment on the current layer." ) ); return; } // remove previous warning emit messageDiscarded(); mSelectedFeature = new QgsSelectedFeature( m.featureId(), vlayer, mCanvas ); connect( QgisApp::instance()->layerTreeView(), SIGNAL( currentLayerChanged( QgsMapLayer* ) ), this, SLOT( currentLayerChanged( QgsMapLayer* ) ) ); connect( mSelectedFeature, SIGNAL( destroyed() ), this, SLOT( selectedFeatureDestroyed() ) ); connect( vlayer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) ); mIsPoint = vlayer->geometryType() == QGis::Point; }
bool QgsMapToolAddPart::checkSelection() { //check if we operate on a vector layer QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return false; } //inform user at the begin of the digitizing action that the island tool only works if exactly one feature is selected int nSelectedFeatures = vlayer->selectedFeatureCount(); QString selectionErrorMsg; if ( nSelectedFeatures < 1 ) { selectionErrorMsg = tr( "No feature selected. Please select a feature with the selection tool or in the attribute table" ); } else if ( nSelectedFeatures > 1 ) { selectionErrorMsg = tr( "Several features are selected. Please select only one feature to which an part should be added." ); } if ( !selectionErrorMsg.isEmpty() ) { emit messageEmitted( tr( "Could not add part. %1" ).arg( selectionErrorMsg ), Qgis::Warning ); } return selectionErrorMsg.isEmpty(); }
void QgsMapToolNodeTool::canvasPressEvent( QMouseEvent * e ) { QgsDebugCall; mClicked = true; mPressCoordinates = e->pos(); QList<QgsSnappingResult> snapResults; if ( !mSelectedFeature ) { QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) return; mSelectAnother = false; mSnapper.snapToCurrentLayer( e->pos(), snapResults, QgsSnapper::SnapToVertexAndSegment, -1 ); if ( snapResults.size() < 1 ) { emit messageEmitted( tr( "could not snap to a segment on the current layer." ) ); return; } // remove previous warning emit messageDiscarded(); mSelectedFeature = new QgsSelectedFeature( snapResults[0].snappedAtGeometry, vlayer, mCanvas ); connect( QgisApp::instance()->layerTreeView(), SIGNAL( currentLayerChanged( QgsMapLayer* ) ), this, SLOT( currentLayerChanged( QgsMapLayer* ) ) ); connect( mSelectedFeature, SIGNAL( destroyed() ), this, SLOT( selectedFeatureDestroyed() ) ); connect( vlayer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) ); mIsPoint = vlayer->geometryType() == QGis::Point; }
void QgsMapToolDeletePart::canvasReleaseEvent( QgsMapMouseEvent* e ) { Q_UNUSED( e ); delete mRubberBand; mRubberBand = 0; if ( !vlayer || !vlayer->isEditable() ) { return; } if ( mPressedFid == -1 ) return; QgsFeature f; vlayer->getFeatures( QgsFeatureRequest().setFilterFid( mPressedFid ) ).nextFeature( f ); QgsGeometry* g = f.geometry(); if ( g->deletePart( mPressedPartNum ) ) { vlayer->beginEditCommand( tr( "Part of multipart feature deleted" ) ); vlayer->changeGeometry( f.id(), g ); vlayer->endEditCommand(); mCanvas->refresh(); } else { emit messageEmitted( tr( "Couldn't remove the selected part." ) ); } return; }
void QgsMapToolReshape::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) { //check if we operate on a vector layer //todo: move this to a function in parent class to avoid duplication QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::Warning ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { deleteTempRubberBand(); //find out bounding box of mCaptureList if ( size() < 1 ) { stopCapturing(); return; } reshape( vlayer ); stopCapturing(); } }
bool QgsMapToolShowHideLabels::selectedFeatures( QgsVectorLayer* vlayer, QgsFeatureIds& selectedFeatIds ) { // culled from QgsMapToolSelectUtils::setSelectFeatures() QgsGeometry* selectGeometry = mRubberBand->asGeometry(); // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. QgsGeometry selectGeomTrans( *selectGeometry ); if ( mCanvas->hasCrsTransformEnabled() ) { try { QgsCoordinateTransform ct( mCanvas->mapSettings().destinationCrs(), vlayer->crs() ); selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) ); emit messageEmitted( tr( "CRS Exception: selection extends beyond layer's coordinate system." ), QgsMessageBar::WARNING ); return false; } } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsDebugMsg( "Selection layer: " + vlayer->name() ); QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest() .setFilterRect( selectGeomTrans.boundingBox() ) .setFlags( QgsFeatureRequest::NoGeometry | QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; while ( fit.nextFeature( f ) ) { selectedFeatIds.insert( f.id() ); } QApplication::restoreOverrideCursor(); return true; }
void QgsMapToolOffsetCurve::setOffsetForRubberBand( double offset, bool leftSide ) { // need at least geos 3.3 for OffsetCurve tool #if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && \ ((GEOS_VERSION_MAJOR>3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3))) if ( !mRubberBand || !mOriginalGeometry ) { return; } QgsVectorLayer* sourceLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( mSourceLayerId ) ); if ( !sourceLayer ) { return; } QgsGeometry geomCopy( *mOriginalGeometry ); const GEOSGeometry* geosGeom = geomCopy.asGeos(); if ( geosGeom ) { QSettings s; int joinStyle = s.value( "/Qgis/digitizing/offset_join_style", 0 ).toInt(); int quadSegments = s.value( "/Qgis/digitizing/offset_quad_seg", 8 ).toInt(); double mitreLimit = s.value( "/Qgis/digitizing/offset_miter_limit", 5.0 ).toDouble(); GEOSGeometry* offsetGeom = GEOSOffsetCurve_r( QgsGeometry::getGEOSHandler(), geosGeom, leftSide ? offset : -offset, quadSegments, joinStyle, mitreLimit ); if ( !offsetGeom ) { deleteRubberBandAndGeometry(); deleteDistanceItem(); delete mSnapVertexMarker; mSnapVertexMarker = 0; mForceCopy = false; mGeometryModified = false; deleteDistanceItem(); emit messageEmitted( tr( "Creating offset geometry failed" ), QgsMessageBar::CRITICAL ); return; } if ( offsetGeom ) { mModifiedGeometry.fromGeos( offsetGeom ); mRubberBand->setToGeometry( &mModifiedGeometry, sourceLayer ); } } #else //GEOS_VERSION>=3.3 Q_UNUSED( offset ); Q_UNUSED( leftSide ); #endif //GEOS_VERSION>=3.3 }
void QgsMapToolDeleteRing::canvasPressEvent( QgsMapMouseEvent* e ) { delete mRubberBand; mRubberBand = 0; mPressedFid = -1; mPressedPartNum = -1; mPressedRingNum = -1; QgsMapLayer* currentLayer = mCanvas->currentLayer(); if ( !currentLayer ) return; vlayer = qobject_cast<QgsVectorLayer *>( currentLayer ); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( vlayer->geometryType() != QGis::Polygon ) { emit messageEmitted( tr( "Delete ring can only be used in a polygon layer." ) ); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } QgsPoint p = toLayerCoordinates( vlayer, e->pos() ); QgsGeometry* ringGeom = ringUnderPoint( p, mPressedFid, mPressedPartNum, mPressedRingNum ); if ( mPressedFid != -1 ) { QgsFeature f; vlayer->getFeatures( QgsFeatureRequest().setFilterFid( mPressedFid ) ).nextFeature( f ); mRubberBand = createRubberBand( vlayer->geometryType() ); mRubberBand->setToGeometry( ringGeom, vlayer ); mRubberBand->show(); } delete ringGeom; ringGeom = 0; }
void QgsMapToolOffsetCurve::updateGeometryAndRubberBand( double offset ) { if ( !mRubberBand || mOriginalGeometry.isNull() ) { return; } if ( !mLayer ) { return; } QgsGeometry offsetGeom; QgsSettings s; QgsGeometry::JoinStyle joinStyle = s.enumValue( QStringLiteral( "/qgis/digitizing/offset_join_style" ), QgsGeometry::JoinStyleRound ); int quadSegments = s.value( QStringLiteral( "/qgis/digitizing/offset_quad_seg" ), 8 ).toInt(); double miterLimit = s.value( QStringLiteral( "/qgis/digitizing/offset_miter_limit" ), 5.0 ).toDouble(); QgsGeometry::EndCapStyle capStyle = s.enumValue( QStringLiteral( "/qgis/digitizing/offset_cap_style" ), QgsGeometry::CapRound ); if ( QgsWkbTypes::geometryType( mOriginalGeometry.wkbType() ) == QgsWkbTypes::LineGeometry ) { offsetGeom = mManipulatedGeometry.offsetCurve( offset, quadSegments, joinStyle, miterLimit ); } else { offsetGeom = mManipulatedGeometry.buffer( offset, quadSegments, capStyle, joinStyle, miterLimit ); } if ( !offsetGeom ) { deleteRubberBandAndGeometry(); deleteUserInputWidget(); mLayer = nullptr; mGeometryModified = false; emit messageDiscarded(); emit messageEmitted( tr( "Creating offset geometry failed: %1" ).arg( offsetGeom.lastError() ), Qgis::Critical ); } else { mModifiedGeometry = offsetGeom; mRubberBand->setToGeometry( mModifiedGeometry, mLayer ); } }
void QgsMapToolAddPart::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) { //check if we operate on a vector layer QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } bool isGeometryEmpty = false; QgsFeatureList selectedFeatures = vlayer->selectedFeatures(); if ( !selectedFeatures.isEmpty() && selectedFeatures.at( 0 ).geometry().isNull() ) isGeometryEmpty = true; if ( !checkSelection() ) { stopCapturing(); return; } int errorCode = 0; switch ( mode() ) { case CapturePoint: { QgsPoint layerPoint; QgsPointXY mapPoint = e->mapPoint(); if ( nextPoint( QgsPoint( mapPoint ), layerPoint ) != 0 ) { QgsDebugMsg( "nextPoint failed" ); return; } vlayer->beginEditCommand( tr( "Part added" ) ); errorCode = vlayer->addPart( QgsPointSequence() << layerPoint ); } break; case CaptureLine: case CapturePolygon: { //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { QgsDebugMsg( "current layer is not a vector layer" ); return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Coordinate transform error. Cannot transform the point to the layers coordinate system" ), Qgis::Warning ); return; } startCapturing(); return; } else if ( e->button() != Qt::RightButton ) { deleteTempRubberBand(); return; } if ( !isCapturing() ) return; if ( mode() == CapturePolygon ) { closePolygon(); } //does compoundcurve contain circular strings? //does provider support circular strings? bool hasCurvedSegments = captureCurve()->hasCurvedSegments(); bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries; QgsCurve *curveToAdd = nullptr; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { curveToAdd = captureCurve()->clone(); } else { curveToAdd = captureCurve()->curveToLine(); } vlayer->beginEditCommand( tr( "Part added" ) ); if ( mode() == CapturePolygon ) { //avoid intersections QgsCurvePolygon *cp = new QgsCurvePolygon(); cp->setExteriorRing( curveToAdd ); QgsGeometry *geom = new QgsGeometry( cp ); geom->avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() ); const QgsCurvePolygon *cpGeom = qgsgeometry_cast<const QgsCurvePolygon *>( geom->constGet() ); if ( !cpGeom ) { stopCapturing(); delete geom; vlayer->destroyEditCommand(); return; } errorCode = vlayer->addPart( cpGeom->exteriorRing()->clone() ); delete geom; } else { errorCode = vlayer->addPart( curveToAdd ); } stopCapturing(); } break; default: Q_ASSERT( !"invalid capture mode" ); errorCode = 6; break; } QString errorMessage; switch ( errorCode ) { case 0: { // remove previous message emit messageDiscarded(); //add points to other features to keep topology up-to-date bool topologicalEditing = QgsProject::instance()->topologicalEditing(); if ( topologicalEditing ) { addTopologicalPoints( points() ); } vlayer->endEditCommand(); vlayer->triggerRepaint(); if ( ( !isGeometryEmpty ) && QgsWkbTypes::isSingleType( vlayer->wkbType() ) ) { emit messageEmitted( tr( "Add part: Feature geom is single part and you've added more than one" ), Qgis::Warning ); } return; } case 1: errorMessage = tr( "Selected feature is not multi part." ); break; case 2: errorMessage = tr( "New part's geometry is not valid." ); break; case 3: errorMessage = tr( "New polygon ring not disjoint with existing polygons." ); break; case 4: errorMessage = tr( "No feature selected. Please select a feature with the selection tool or in the attribute table" ); break; case 5: errorMessage = tr( "Several features are selected. Please select only one feature to which an island should be added." ); break; case 6: errorMessage = tr( "Selected geometry could not be found" ); break; } emit messageEmitted( errorMessage, Qgis::Warning ); vlayer->destroyEditCommand(); }
void QgsMapToolFillRing::cadCanvasReleaseEvent( QgsMapMouseEvent * e ) { //check if we operate on a vector layer QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { if ( !isCapturing() ) return; deleteTempRubberBand(); closePolygon(); vlayer->beginEditCommand( tr( "Ring added and filled" ) ); QList< QgsPoint > pointList = points(); QgsFeatureId modifiedFid; int addRingReturnCode = vlayer->addRing( pointList, &modifiedFid ); if ( addRingReturnCode != 0 ) { QString errorMessage; //todo: open message box to communicate errors if ( addRingReturnCode == 1 ) { errorMessage = tr( "a problem with geometry type occured" ); } else if ( addRingReturnCode == 2 ) { errorMessage = tr( "the inserted Ring is not closed" ); } else if ( addRingReturnCode == 3 ) { errorMessage = tr( "the inserted Ring is not a valid geometry" ); } else if ( addRingReturnCode == 4 ) { errorMessage = tr( "the inserted Ring crosses existing rings" ); } else if ( addRingReturnCode == 5 ) { errorMessage = tr( "the inserted Ring is not contained in a feature" ); } else { errorMessage = tr( "an unknown error occured" ); } emit messageEmitted( tr( "could not add ring since %1." ).arg( errorMessage ), QgsMessageBar::CRITICAL ); vlayer->destroyEditCommand(); } else { // find parent feature and get it attributes double xMin, xMax, yMin, yMax; QgsRectangle bBox; xMin = std::numeric_limits<double>::max(); xMax = -std::numeric_limits<double>::max(); yMin = std::numeric_limits<double>::max(); yMax = -std::numeric_limits<double>::max(); Q_FOREACH ( const QgsPoint& point, pointList ) { xMin = qMin( xMin, point.x() ); xMax = qMax( xMax, point.x() ); yMin = qMin( yMin, point.y() ); yMax = qMax( yMax, point.y() ); } bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin ); bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterFid( modifiedFid ) ); QgsFeature f; bool res = false; if ( fit.nextFeature( f ) ) { //create QgsFeature with wkb representation QgsFeature* ft = new QgsFeature( vlayer->fields(), 0 ); ft->setGeometry( QgsGeometry::fromPolygon( QgsPolygon() << pointList.toVector() ) ); ft->setAttributes( f.attributes() ); if ( QApplication::keyboardModifiers() == Qt::ControlModifier ) { res = vlayer->addFeature( *ft ); } else { QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, ft, false, NULL, true ); dialog->setIsAddDialog( true ); res = dialog->exec(); // will also add the feature } if ( res ) { vlayer->endEditCommand(); } else { delete ft; vlayer->destroyEditCommand(); } res = false; } } stopCapturing(); }
void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent* e ) { QgsVectorLayer* vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } QGis::WkbType layerWKBType = vlayer->wkbType(); QgsVectorDataProvider* provider = vlayer->dataProvider(); if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) ) { emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), QgsMessageBar::WARNING ); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } // POINT CAPTURING if ( mode() == CapturePoint ) { if ( e->button() != Qt::LeftButton ) return; //check we only use this tool for point/multipoint layers if ( vlayer->geometryType() != QGis::Point && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), QgsMessageBar::WARNING ); return; } QgsPoint savePoint; //point in layer coordinates try { savePoint = toLayerCoordinates( vlayer, e->mapPoint() ); QgsDebugMsg( "savePoint = " + savePoint.toString() ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } //only do the rest for provider with feature addition support //note that for the grass provider, this will return false since //grass provider has its own mechanism of feature addition if ( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) { QgsFeature f( vlayer->fields(), 0 ); QgsGeometry *g = 0; if ( layerWKBType == QGis::WKBPoint || layerWKBType == QGis::WKBPoint25D ) { g = QgsGeometry::fromPoint( savePoint ); } else if ( layerWKBType == QGis::WKBMultiPoint || layerWKBType == QGis::WKBMultiPoint25D ) { g = QgsGeometry::fromMultiPoint( QgsMultiPoint() << savePoint ); } else { // if layer supports more types (mCheckGeometryType is false) g = QgsGeometry::fromPoint( savePoint ); } f.setGeometry( g ); f.setValid( true ); addFeature( vlayer, &f, false ); mCanvas->refresh(); } } // LINE AND POLYGON CAPTURING else if ( mode() == CaptureLine || mode() == CapturePolygon ) { //check we only use the line tool for line/multiline layers if ( mode() == CaptureLine && vlayer->geometryType() != QGis::Line && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), QgsMessageBar::WARNING ); return; } //check we only use the polygon tool for polygon/multipolygon layers if ( mode() == CapturePolygon && vlayer->geometryType() != QGis::Polygon && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), QgsMessageBar::WARNING ); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { // End of string deleteTempRubberBand(); //lines: bail out if there are not at least two vertices if ( mode() == CaptureLine && size() < 2 ) { stopCapturing(); return; } //polygons: bail out if there are not at least two vertices if ( mode() == CapturePolygon && size() < 3 ) { stopCapturing(); return; } if ( mode() == CapturePolygon ) { closePolygon(); } //create QgsFeature with wkb representation QScopedPointer< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) ); //does compoundcurve contain circular strings? //does provider support circular strings? bool hasCurvedSegments = captureCurve()->hasCurvedSegments(); bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries; QgsCurveV2* curveToAdd = 0; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { curveToAdd = captureCurve()->clone(); } else { curveToAdd = captureCurve()->curveToLine(); } if ( mode() == CaptureLine ) { f->setGeometry( new QgsGeometry( curveToAdd ) ); } else { QgsCurvePolygonV2* poly = 0; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { poly = new QgsCurvePolygonV2(); } else { poly = new QgsPolygonV2(); } poly->setExteriorRing( curveToAdd ); f->setGeometry( new QgsGeometry( poly ) ); int avoidIntersectionsReturn = f->geometry()->avoidIntersections(); if ( avoidIntersectionsReturn == 1 ) { //not a polygon type. Impossible to get there } #if 0 else if ( avoidIntersectionsReturn == 2 ) //MH120131: disable this error message until there is a better way to cope with the single type / multi type problem { //bail out... emit messageEmitted( tr( "The feature could not be added because removing the polygon intersections would change the geometry type" ), QgsMessageBar::CRITICAL ); stopCapturing(); return; } #endif else if ( avoidIntersectionsReturn == 3 ) { emit messageEmitted( tr( "An error was reported during intersection removal" ), QgsMessageBar::CRITICAL ); } if ( !f->constGeometry()->asWkb() ) //avoid intersection might have removed the whole geometry { QString reason; if ( avoidIntersectionsReturn != 2 ) { reason = tr( "The feature cannot be added because it's geometry is empty" ); } else { reason = tr( "The feature cannot be added because it's geometry collapsed due to intersection avoidance" ); } emit messageEmitted( reason, QgsMessageBar::CRITICAL ); stopCapturing(); return; } } if ( addFeature( vlayer, f.data(), false ) ) { //add points to other features to keep topology up-to-date int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); //use always topological editing for avoidIntersection. //Otherwise, no way to guarantee the geometries don't have a small gap in between. QStringList intersectionLayers = QgsProject::instance()->readListEntry( "Digitizing", "/AvoidIntersectionsList" ); bool avoidIntersection = !intersectionLayers.isEmpty(); if ( avoidIntersection ) //try to add topological points also to background layers { QStringList::const_iterator lIt = intersectionLayers.constBegin(); for ( ; lIt != intersectionLayers.constEnd(); ++lIt ) { QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( *lIt ); QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( ml ); //can only add topological points if background layer is editable... if ( vl && vl->geometryType() == QGis::Polygon && vl->isEditable() ) { vl->addTopologicalPoints( f->constGeometry() ); } } } else if ( topologicalEditing ) { vlayer->addTopologicalPoints( f->constGeometry() ); } } stopCapturing(); } } }
void QgsMapToolRotateFeature::canvasReleaseEvent( QgsMapMouseEvent *e ) { if ( !mCanvas ) { return; } QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer ) { deleteRotationWidget(); deleteRubberband(); notifyNotVectorLayer(); return; } if ( e->button() == Qt::RightButton ) { cancel(); return; } // place anchor point on CTRL + click if ( e->modifiers() & Qt::ControlModifier ) { if ( !mAnchorPoint ) { return; } mAnchorPoint->setCenter( toMapCoordinates( e->pos() ) ); mStartPointMapCoords = toMapCoordinates( e->pos() ); mStPoint = e->pos(); return; } deleteRotationWidget(); // Initialize rotation if not yet active if ( !mRotationActive ) { mRotation = 0; mRotationOffset = 0; deleteRubberband(); mInitialPos = e->pos(); if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } QgsPointXY layerCoords = toLayerCoordinates( vlayer, e->pos() ); double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() ); QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius, layerCoords.x() + searchRadius, layerCoords.y() + searchRadius ); if ( vlayer->selectedFeatureCount() == 0 ) { QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setNoAttributes() ); //find the closest feature QgsGeometry pointGeometry = QgsGeometry::fromPointXY( layerCoords ); if ( pointGeometry.isNull() ) { return; } double minDistance = std::numeric_limits<double>::max(); QgsFeature cf; QgsFeature f; while ( fit.nextFeature( f ) ) { if ( f.hasGeometry() ) { double currentDistance = pointGeometry.distance( f.geometry() ); if ( currentDistance < minDistance ) { minDistance = currentDistance; cf = f; } } } if ( minDistance == std::numeric_limits<double>::max() ) { emit messageEmitted( tr( "Could not find a nearby feature in the current layer." ) ); return; } QgsRectangle bound = cf.geometry().boundingBox(); mStartPointMapCoords = toMapCoordinates( vlayer, bound.center() ); if ( !mAnchorPoint ) { mAnchorPoint = qgis::make_unique<QgsVertexMarker>( mCanvas ); } mAnchorPoint->setIconType( QgsVertexMarker::ICON_CROSS ); mAnchorPoint->setCenter( mStartPointMapCoords ); mStPoint = toCanvasCoordinates( mStartPointMapCoords ); mRotatedFeatures.clear(); mRotatedFeatures << cf.id(); //todo: take the closest feature, not the first one... mRubberBand = createRubberBand( vlayer->geometryType() ); mRubberBand->setToGeometry( cf.geometry(), vlayer ); } else { mRotatedFeatures = vlayer->selectedFeatureIds(); mRubberBand = createRubberBand( vlayer->geometryType() ); QgsFeature feat; QgsFeatureIterator it = vlayer->getSelectedFeatures(); while ( it.nextFeature( feat ) ) { mRubberBand->addGeometry( feat.geometry(), vlayer ); } } mRubberBand->show(); double XDistance = mInitialPos.x() - mAnchorPoint->x(); double YDistance = mInitialPos.y() - mAnchorPoint->y(); mRotationOffset = std::atan2( YDistance, XDistance ) * ( 180 / M_PI ); createRotationWidget(); if ( e->modifiers() & Qt::ShiftModifier ) { if ( mRotationWidget ) { mRotationWidget->setMagnet( 45 ); } } mRotationActive = true; return; } applyRotation( mRotation ); }
void QgsMapToolAddRing::canvasReleaseEvent( QMouseEvent * e ) { emit messageDiscarded(); //check if we operate on a vector layer QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->pos() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { if ( !isCapturing() ) return; deleteTempRubberBand(); closePolygon(); vlayer->beginEditCommand( tr( "Ring added" ) ); int addRingReturnCode = vlayer->addRing( points() ); if ( addRingReturnCode != 0 ) { QString errorMessage; //todo: open message box to communicate errors if ( addRingReturnCode == 1 ) { errorMessage = tr( "a problem with geometry type occured" ); } else if ( addRingReturnCode == 2 ) { errorMessage = tr( "the inserted ring is not closed" ); } else if ( addRingReturnCode == 3 ) { errorMessage = tr( "the inserted ring is not a valid geometry" ); } else if ( addRingReturnCode == 4 ) { errorMessage = tr( "the inserted ring crosses existing rings" ); } else if ( addRingReturnCode == 5 ) { errorMessage = tr( "the inserted ring is not contained in a feature" ); } else { errorMessage = tr( "an unknown error occured" ); } emit messageEmitted( tr( "could not add ring since %1." ).arg( errorMessage ), QgsMessageBar::CRITICAL ); vlayer->destroyEditCommand(); } else { vlayer->endEditCommand(); } stopCapturing(); } }
void QgsMapToolOffsetCurve::applyOffset( double offset, Qt::KeyboardModifiers modifiers ) { if ( !mLayer || offset == 0.0 ) { cancel(); notifyNotVectorLayer(); return; } updateGeometryAndRubberBand( offset ); // no modification if ( !mGeometryModified ) { mLayer->destroyEditCommand(); cancel(); return; } if ( mModifiedPart >= 0 ) { QgsGeometry geometry; int partIndex = 0; QgsWkbTypes::Type geomType = mOriginalGeometry.wkbType(); if ( QgsWkbTypes::geometryType( geomType ) == QgsWkbTypes::LineGeometry ) { QgsMultiPolylineXY newMultiLine; QgsMultiPolylineXY multiLine = mOriginalGeometry.asMultiPolyline(); QgsMultiPolylineXY::const_iterator it = multiLine.constBegin(); for ( ; it != multiLine.constEnd(); ++it ) { if ( partIndex == mModifiedPart ) { newMultiLine.append( mModifiedGeometry.asPolyline() ); } else { newMultiLine.append( *it ); } partIndex++; } geometry = QgsGeometry::fromMultiPolylineXY( newMultiLine ); } else { QgsMultiPolygonXY newMultiPoly; const QgsMultiPolygonXY multiPoly = mOriginalGeometry.asMultiPolygon(); QgsMultiPolygonXY::const_iterator multiPolyIt = multiPoly.constBegin(); for ( ; multiPolyIt != multiPoly.constEnd(); ++multiPolyIt ) { if ( partIndex == mModifiedPart ) { if ( mModifiedGeometry.isMultipart() ) { // not a ring if ( mModifiedRing <= 0 ) { // part became mulitpolygon, that means discard original rings from the part newMultiPoly += mModifiedGeometry.asMultiPolygon(); } else { // ring became multipolygon, oh boy! QgsPolygonXY newPoly; int ringIndex = 0; QgsPolygonXY::const_iterator polyIt = multiPolyIt->constBegin(); for ( ; polyIt != multiPolyIt->constEnd(); ++polyIt ) { if ( ringIndex == mModifiedRing ) { const QgsMultiPolygonXY ringParts = mModifiedGeometry.asMultiPolygon(); QgsPolygonXY newRings; QgsMultiPolygonXY::const_iterator ringIt = ringParts.constBegin(); for ( ; ringIt != ringParts.constEnd(); ++ringIt ) { // the different parts of the new rings cannot have rings themselves newRings.append( ringIt->at( 0 ) ); } newPoly += newRings; } else { newPoly.append( *polyIt ); } ringIndex++; } newMultiPoly.append( newPoly ); } } else { // original part had no ring if ( mModifiedRing == -1 ) { newMultiPoly.append( mModifiedGeometry.asPolygon() ); } else { QgsPolygonXY newPoly; int ringIndex = 0; QgsPolygonXY::const_iterator polyIt = multiPolyIt->constBegin(); for ( ; polyIt != multiPolyIt->constEnd(); ++polyIt ) { if ( ringIndex == mModifiedRing ) { newPoly.append( mModifiedGeometry.asPolygon().at( 0 ) ); } else { newPoly.append( *polyIt ); } ringIndex++; } newMultiPoly.append( newPoly ); } } } else { newMultiPoly.append( *multiPolyIt ); } partIndex++; } geometry = QgsGeometry::fromMultiPolygonXY( newMultiPoly ); } geometry.convertToMultiType(); mModifiedGeometry = geometry; } else if ( mModifiedRing >= 0 ) { // original geometry had some rings if ( mModifiedGeometry.isMultipart() ) { // not a ring if ( mModifiedRing == 0 ) { // polygon became mulitpolygon, that means discard original rings from the part // keep the modified geometry as is } else { QgsPolygonXY newPoly; const QgsPolygonXY poly = mOriginalGeometry.asPolygon(); // ring became multipolygon, oh boy! int ringIndex = 0; QgsPolygonXY::const_iterator polyIt = poly.constBegin(); for ( ; polyIt != poly.constEnd(); ++polyIt ) { if ( ringIndex == mModifiedRing ) { QgsMultiPolygonXY ringParts = mModifiedGeometry.asMultiPolygon(); QgsPolygonXY newRings; QgsMultiPolygonXY::const_iterator ringIt = ringParts.constBegin(); for ( ; ringIt != ringParts.constEnd(); ++ringIt ) { // the different parts of the new rings cannot have rings themselves newRings.append( ringIt->at( 0 ) ); } newPoly += newRings; } else { newPoly.append( *polyIt ); } ringIndex++; } mModifiedGeometry = QgsGeometry::fromPolygonXY( newPoly ); } } else { // simple case where modified geom is a polygon (not multi) QgsPolygonXY newPoly; const QgsPolygonXY poly = mOriginalGeometry.asPolygon(); int ringIndex = 0; QgsPolygonXY::const_iterator polyIt = poly.constBegin(); for ( ; polyIt != poly.constEnd(); ++polyIt ) { if ( ringIndex == mModifiedRing ) { newPoly.append( mModifiedGeometry.asPolygon().at( 0 ) ); } else { newPoly.append( *polyIt ); } ringIndex++; } mModifiedGeometry = QgsGeometry::fromPolygonXY( newPoly ); } } if ( !mModifiedGeometry.isGeosValid() ) { emit messageEmitted( tr( "Generated geometry is not valid." ), Qgis::Critical ); // no cancel, continue editing. return; } mLayer->beginEditCommand( tr( "Offset curve" ) ); bool editOk; if ( !mCtrlHeldOnFirstClick && !( modifiers & Qt::ControlModifier ) ) { editOk = mLayer->changeGeometry( mModifiedFeature, mModifiedGeometry ); } else { QgsFeature f; f.setGeometry( mModifiedGeometry ); //add empty values for all fields (allows inserting attribute values via the feature form in the same session) QgsAttributes attrs( mLayer->fields().count() ); const QgsFields &fields = mLayer->fields(); for ( int idx = 0; idx < fields.count(); ++idx ) { attrs[idx] = QVariant(); } f.setAttributes( attrs ); editOk = mLayer->addFeature( f ); } if ( editOk ) { mLayer->endEditCommand(); } else { mLayer->destroyEditCommand(); emit messageEmitted( QStringLiteral( "Could not apply offset" ), Qgis::Critical ); } deleteRubberBandAndGeometry(); deleteUserInputWidget(); mLayer->triggerRepaint(); mLayer = nullptr; }
void QgsMapToolDigitizeFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) { QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayer ); if ( !vlayer ) //if no given layer take the current from canvas vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } QgsWkbTypes::Type layerWKBType = vlayer->wkbType(); QgsVectorDataProvider *provider = vlayer->dataProvider(); if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) ) { emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), Qgis::Warning ); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } // POINT CAPTURING if ( mode() == CapturePoint ) { if ( e->button() != Qt::LeftButton ) return; //check we only use this tool for point/multipoint layers if ( vlayer->geometryType() != QgsWkbTypes::PointGeometry && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), Qgis::Warning ); return; } QgsPointXY savePoint; //point in layer coordinates try { QgsPoint fetchPoint; int res; res = fetchLayerPoint( e->mapPointMatch(), fetchPoint ); if ( res == 0 ) { savePoint = QgsPointXY( fetchPoint.x(), fetchPoint.y() ); } else { savePoint = toLayerCoordinates( vlayer, e->mapPoint() ); } QgsDebugMsg( "savePoint = " + savePoint.toString() ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::Warning ); return; } //only do the rest for provider with feature addition support //note that for the grass provider, this will return false since //grass provider has its own mechanism of feature addition if ( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) { QgsFeature f( vlayer->fields(), 0 ); QgsGeometry g; if ( layerWKBType == QgsWkbTypes::Point ) { g = QgsGeometry::fromPointXY( savePoint ); } else if ( !QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) ) { g = QgsGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), defaultZValue() ) ); } else if ( QgsWkbTypes::isMultiType( layerWKBType ) && !QgsWkbTypes::hasZ( layerWKBType ) ) { g = QgsGeometry::fromMultiPointXY( QgsMultiPointXY() << savePoint ); } else if ( QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) ) { QgsMultiPoint *mp = new QgsMultiPoint(); mp->addGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), defaultZValue() ) ); g = QgsGeometry( mp ); } else { // if layer supports more types (mCheckGeometryType is false) g = QgsGeometry::fromPointXY( savePoint ); } if ( QgsWkbTypes::hasM( layerWKBType ) ) { g.get()->addMValue(); } f.setGeometry( g ); f.setValid( true ); digitized( f ); // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points cadDockWidget()->clearPoints(); } } // LINE AND POLYGON CAPTURING else if ( mode() == CaptureLine || mode() == CapturePolygon ) { //check we only use the line tool for line/multiline layers if ( mode() == CaptureLine && vlayer->geometryType() != QgsWkbTypes::LineGeometry && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), Qgis::Warning ); return; } //check we only use the polygon tool for polygon/multipolygon layers if ( mode() == CapturePolygon && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry && mCheckGeometryType ) { emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), Qgis::Warning ); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::Warning ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { // End of string deleteTempRubberBand(); //lines: bail out if there are not at least two vertices if ( mode() == CaptureLine && size() < 2 ) { stopCapturing(); return; } //polygons: bail out if there are not at least two vertices if ( mode() == CapturePolygon && size() < 3 ) { stopCapturing(); return; } if ( mode() == CapturePolygon ) { closePolygon(); } //create QgsFeature with wkb representation std::unique_ptr< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) ); //does compoundcurve contain circular strings? //does provider support circular strings? bool hasCurvedSegments = captureCurve()->hasCurvedSegments(); bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries; QList<QgsPointLocator::Match> snappingMatchesList; QgsCurve *curveToAdd = nullptr; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { curveToAdd = captureCurve()->clone(); } else { curveToAdd = captureCurve()->curveToLine(); snappingMatchesList = snappingMatches(); } if ( mode() == CaptureLine ) { QgsGeometry g( curveToAdd ); f->setGeometry( g ); } else { QgsCurvePolygon *poly = nullptr; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { poly = new QgsCurvePolygon(); } else { poly = new QgsPolygon(); } poly->setExteriorRing( curveToAdd ); QgsGeometry g( poly ); f->setGeometry( g ); QgsGeometry featGeom = f->geometry(); int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() ); f->setGeometry( featGeom ); if ( avoidIntersectionsReturn == 1 ) { //not a polygon type. Impossible to get there } if ( f->geometry().isEmpty() ) //avoid intersection might have removed the whole geometry { emit messageEmitted( tr( "The feature cannot be added because it's geometry collapsed due to intersection avoidance" ), Qgis::Critical ); stopCapturing(); return; } } f->setValid( true ); digitized( *f ); stopCapturing(); } } }
void QgsMapToolEdit::notifyNotVectorLayer() { emit messageEmitted( tr( "No active vector layer" ) ); }
void QgsMapToolSimplify::canvasPressEvent( QMouseEvent * e ) { QgsVectorLayer * vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } QgsPoint layerCoords = mCanvas->getCoordinateTransform()->toMapPoint( e->pos().x(), e->pos().y() ); double r = QgsTolerance::vertexSearchRadius( vlayer, mCanvas->mapSettings() ); QgsRectangle selectRect = QgsRectangle( layerCoords.x() - r, layerCoords.y() - r, layerCoords.x() + r, layerCoords.y() + r ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsGeometry* geometry = QgsGeometry::fromPoint( layerCoords ); double minDistance = DBL_MAX; double currentDistance; mSelectedFeature.setValid( false ); QgsFeature f; while ( fit.nextFeature( f ) ) { currentDistance = geometry->distance( *( f.geometry() ) ); if ( currentDistance < minDistance ) { minDistance = currentDistance; mSelectedFeature = f; } } // delete previous rubberband (if any) removeRubberBand(); if ( mSelectedFeature.isValid() ) { if ( mSelectedFeature.geometry()->isMultipart() ) { emit messageEmitted( tr( "Multipart features are not supported for simplification." ), QgsMessageBar::CRITICAL ); return; } mRubberBand = new QgsRubberBand( mCanvas ); mRubberBand->setToGeometry( mSelectedFeature.geometry(), 0 ); mRubberBand->setColor( QColor( 255, 0, 0, 65 ) ); mRubberBand->setWidth( 2 ); mRubberBand->show(); //calculate boudaries for slidebar if ( calculateSliderBoudaries() ) { // show dialog as a non-modal window mSimplifyDialog->show(); } else { emit messageEmitted( tr( "This feature cannot be simplified. Check if feature has enough vertices to be simplified." ), QgsMessageBar::WARNING ); } } }
void QgsMapToolAddRing::cadCanvasReleaseEvent( QgsMapMouseEvent * e ) { emit messageDiscarded(); //check if we operate on a vector layer QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { if ( !isCapturing() ) return; deleteTempRubberBand(); closePolygon(); vlayer->beginEditCommand( tr( "Ring added" ) ); //does compoundcurve contain circular strings? //does provider support circular strings? bool hasCurvedSegments = captureCurve()->hasCurvedSegments(); bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries; QgsCurveV2* curveToAdd = 0; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { curveToAdd = dynamic_cast<QgsCurveV2*>( captureCurve()->clone() ); } else { curveToAdd = captureCurve()->curveToLine(); } int addRingReturnCode = vlayer->addRing( curveToAdd ); if ( addRingReturnCode != 0 ) { QString errorMessage; //todo: open message box to communicate errors if ( addRingReturnCode == 1 ) { errorMessage = tr( "a problem with geometry type occured" ); } else if ( addRingReturnCode == 2 ) { errorMessage = tr( "the inserted ring is not closed" ); } else if ( addRingReturnCode == 3 ) { errorMessage = tr( "the inserted ring is not a valid geometry" ); } else if ( addRingReturnCode == 4 ) { errorMessage = tr( "the inserted ring crosses existing rings" ); } else if ( addRingReturnCode == 5 ) { errorMessage = tr( "the inserted ring is not contained in a feature" ); } else { errorMessage = tr( "an unknown error occured" ); } emit messageEmitted( tr( "could not add ring since %1." ).arg( errorMessage ), QgsMessageBar::CRITICAL ); vlayer->destroyEditCommand(); } else { vlayer->endEditCommand(); } stopCapturing(); } }
void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent *e ) { mCtrlHeldOnFirstClick = false; if ( e->button() == Qt::RightButton ) { cancel(); return; } if ( mOriginalGeometry.isNull() ) { // first click, get feature to modify deleteRubberBandAndGeometry(); mGeometryModified = false; QgsPointLocator::Match match = mCanvas->snappingUtils()->snapToCurrentLayer( e->pos(), QgsPointLocator::Types( QgsPointLocator::Edge | QgsPointLocator::Area ) ); if ( ( match.hasEdge() || match.hasArea() ) && match.layer() ) { mLayer = match.layer(); QgsFeature fet; if ( match.layer()->getFeatures( QgsFeatureRequest( match.featureId() ) ).nextFeature( fet ) ) { mCtrlHeldOnFirstClick = ( e->modifiers() & Qt::ControlModifier ); //no geometry modification if ctrl is pressed prepareGeometry( match, fet ); mRubberBand = createRubberBand(); if ( mRubberBand ) { mRubberBand->setToGeometry( mManipulatedGeometry, match.layer() ); } mModifiedFeature = fet.id(); createUserInputWidget(); bool hasZ = QgsWkbTypes::hasZ( mLayer->wkbType() ); bool hasM = QgsWkbTypes::hasZ( mLayer->wkbType() ); if ( hasZ || hasM ) { emit messageEmitted( QStringLiteral( "layer %1 has %2%3%4 geometry. %2%3%4 values be set to 0 when using offset tool." ) .arg( mLayer->name() ) .arg( hasZ ? "Z" : "" ) .arg( hasZ && hasM ? "/" : "" ) .arg( hasM ? "M" : "" ) , Qgis::Warning ); } } } if ( mOriginalGeometry.isNull() ) { emit messageEmitted( tr( "Could not find a nearby feature in any vector layer." ) ); cancel(); notifyNotVectorLayer(); } } else { // second click - apply changes double offset = calculateOffset( e->snapPoint() ); applyOffset( offset, e->modifiers() ); } }
void QgsMapToolEdit::notifyNotEditableLayer() { emit messageEmitted( tr( "Layer not editable" ) ); }
void QgsMapToolAddPart::canvasReleaseEvent( QMouseEvent * e ) { //check if we operate on a vector layer QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } //inform user at the begin of the digitising action that the island tool only works if exactly one feature is selected int nSelectedFeatures = vlayer->selectedFeatureCount(); QString selectionErrorMsg; if ( nSelectedFeatures < 1 ) { selectionErrorMsg = tr( "No feature selected. Please select a feature with the selection tool or in the attribute table" ); } else if ( nSelectedFeatures > 1 ) { selectionErrorMsg = tr( "Several features are selected. Please select only one feature to which an part should be added." ); } if ( !selectionErrorMsg.isEmpty() ) { emit messageEmitted( tr( "Could not add part. %1" ).arg( selectionErrorMsg ), QgsMessageBar::WARNING ); stopCapturing(); return; } int errorCode; switch ( mode() ) { case CapturePoint: { QgsPoint layerPoint; QgsPoint mapPoint; if ( nextPoint( e->pos(), layerPoint, mapPoint ) != 0 ) { QgsDebugMsg( "nextPoint failed" ); return; } vlayer->beginEditCommand( tr( "Part added" ) ); errorCode = vlayer->addPart( QList<QgsPoint>() << layerPoint ); } break; case CaptureLine: case CapturePolygon: { //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->pos() ); if ( error == 1 ) { QgsDebugMsg( "current layer is not a vector layer" ); return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Coordinate transform error. Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } startCapturing(); return; } else if ( e->button() != Qt::RightButton ) { deleteTempRubberBand(); return; } if ( !isCapturing() ) return; // we are now going to finish the capturing if ( mode() == CapturePolygon ) { //close polygon closePolygon(); //avoid intersections QgsGeometry* geom = QgsGeometry::fromPolygon( QgsPolygon() << points().toVector() ); if ( geom ) { geom->avoidIntersections(); QgsPolygon poly = geom->asPolygon(); if ( poly.size() < 1 ) { stopCapturing(); delete geom; vlayer->destroyEditCommand(); return; } setPoints( geom->asPolygon()[0].toList() ); delete geom; } } vlayer->beginEditCommand( tr( "Part added" ) ); errorCode = vlayer->addPart( points() ); stopCapturing(); } break; default: Q_ASSERT( !"invalid capture mode" ); errorCode = 6; break; } QString errorMessage; switch ( errorCode ) { case 0: { // remove previous message emit messageDiscarded(); //add points to other features to keep topology up-to-date int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); if ( topologicalEditing ) { addTopologicalPoints( points() ); } vlayer->endEditCommand(); mCanvas->refresh(); return; } case 1: errorMessage = tr( "Selected feature is not multi part." ); break; case 2: errorMessage = tr( "New part's geometry is not valid." ); break; case 3: errorMessage = tr( "New polygon ring not disjoint with existing polygons." ); break; case 4: errorMessage = tr( "No feature selected. Please select a feature with the selection tool or in the attribute table" ); break; case 5: errorMessage = tr( "Several features are selected. Please select only one feature to which an island should be added." ); break; case 6: errorMessage = tr( "Selected geometry could not be found" ); break; } emit messageEmitted( errorMessage, QgsMessageBar::WARNING ); vlayer->destroyEditCommand(); }
void QgsMapToolRotatePointSymbols::noCompatibleSymbols() { emit messageEmitted( tr( "The selected point does not have a rotation attribute set." ), QgsMessageBar::CRITICAL ); }
void QgsMapToolMoveFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) { QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer || !vlayer->isEditable() ) { delete mRubberBand; mRubberBand = nullptr; mSnapIndicator->setMatch( QgsPointLocator::Match() ); cadDockWidget()->clear(); notifyNotEditableLayer(); return; } if ( !mRubberBand ) { //find first geometry under mouse cursor and store iterator to it QgsPointXY layerCoords = toLayerCoordinates( vlayer, e->mapPoint() ); double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() ); QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius, layerCoords.x() + searchRadius, layerCoords.y() + searchRadius ); if ( vlayer->selectedFeatureCount() == 0 ) { QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setNoAttributes() ); //find the closest feature QgsGeometry pointGeometry = QgsGeometry::fromPointXY( layerCoords ); if ( pointGeometry.isNull() ) { cadDockWidget()->clear(); return; } double minDistance = std::numeric_limits<double>::max(); QgsFeature cf; QgsFeature f; while ( fit.nextFeature( f ) ) { if ( f.hasGeometry() ) { double currentDistance = pointGeometry.distance( f.geometry() ); if ( currentDistance < minDistance ) { minDistance = currentDistance; cf = f; } } } if ( minDistance == std::numeric_limits<double>::max() ) { cadDockWidget()->clear(); return; } mMovedFeatures.clear(); mMovedFeatures << cf.id(); //todo: take the closest feature, not the first one... mRubberBand = createRubberBand( vlayer->geometryType() ); mRubberBand->setToGeometry( cf.geometry(), vlayer ); } else { mMovedFeatures = vlayer->selectedFeatureIds(); mRubberBand = createRubberBand( vlayer->geometryType() ); QgsFeature feat; QgsFeatureIterator it = vlayer->getSelectedFeatures( QgsFeatureRequest().setNoAttributes() ); bool allFeaturesInView = true; QgsRectangle viewRect = mCanvas->mapSettings().mapToLayerCoordinates( vlayer, mCanvas->extent() ); while ( it.nextFeature( feat ) ) { mRubberBand->addGeometry( feat.geometry(), vlayer ); if ( allFeaturesInView && !viewRect.intersects( feat.geometry().boundingBox() ) ) allFeaturesInView = false; } if ( !allFeaturesInView ) { // for extra safety to make sure we are not modifying geometries by accident int res = QMessageBox::warning( mCanvas, tr( "Move features" ), tr( "Some of the selected features are outside of the current map view. Would you still like to continue?" ), QMessageBox::Yes | QMessageBox::No ); if ( res != QMessageBox::Yes ) { mMovedFeatures.clear(); delete mRubberBand; mRubberBand = nullptr; mSnapIndicator->setMatch( QgsPointLocator::Match() ); return; } } } mStartPointMapCoords = e->mapPoint(); mRubberBand->show(); } else { // copy and move mode if ( e->button() != Qt::LeftButton ) { cadDockWidget()->clear(); delete mRubberBand; mRubberBand = nullptr; mSnapIndicator->setMatch( QgsPointLocator::Match() ); return; } QgsPointXY startPointLayerCoords = toLayerCoordinates( ( QgsMapLayer * )vlayer, mStartPointMapCoords ); QgsPointXY stopPointLayerCoords = toLayerCoordinates( ( QgsMapLayer * )vlayer, e->mapPoint() ); double dx = stopPointLayerCoords.x() - startPointLayerCoords.x(); double dy = stopPointLayerCoords.y() - startPointLayerCoords.y(); vlayer->beginEditCommand( mMode == Move ? tr( "Feature moved" ) : tr( "Feature copied and moved" ) ); switch ( mMode ) { case Move: Q_FOREACH ( QgsFeatureId id, mMovedFeatures ) { vlayer->translateFeature( id, dx, dy ); } delete mRubberBand; mRubberBand = nullptr; mSnapIndicator->setMatch( QgsPointLocator::Match() ); cadDockWidget()->clear(); break; case CopyMove: QgsFeatureRequest request; request.setFilterFids( mMovedFeatures ); QString *errorMsg = new QString(); if ( !QgisApp::instance()->vectorLayerTools()->copyMoveFeatures( vlayer, request, dx, dy, errorMsg ) ) { emit messageEmitted( *errorMsg, Qgis::Critical ); delete mRubberBand; mRubberBand = nullptr; mSnapIndicator->setMatch( QgsPointLocator::Match() ); } break; } vlayer->endEditCommand(); vlayer->triggerRepaint(); }
void QgsMapToolReshape::reshape( QgsVectorLayer *vlayer ) { QgsPointXY firstPoint = points().at( 0 ); QgsRectangle bbox( firstPoint.x(), firstPoint.y(), firstPoint.x(), firstPoint.y() ); for ( int i = 1; i < size(); ++i ) { bbox.combineExtentWith( points().at( i ).x(), points().at( i ).y() ); } QgsLineString reshapeLineString( points() ); if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) ) reshapeLineString.addZValue( defaultZValue() ); //query all the features that intersect bounding box of capture line QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; int reshapeReturn; bool reshapeDone = false; bool isBinding = isBindingLine( vlayer, bbox ); vlayer->beginEditCommand( tr( "Reshape" ) ); while ( fit.nextFeature( f ) ) { //query geometry //call geometry->reshape(mCaptureList) //register changed geometry in vector layer QgsGeometry geom = f.geometry(); if ( !geom.isNull() ) { // in case of a binding line, we just want to update the line from // the starting point and not both side if ( isBinding && !geom.asPolyline().contains( points().first() ) ) continue; reshapeReturn = geom.reshapeGeometry( reshapeLineString ); if ( reshapeReturn == 0 ) { //avoid intersections on polygon layers if ( vlayer->geometryType() == QgsWkbTypes::PolygonGeometry ) { //ignore all current layer features as they should be reshaped too QHash<QgsVectorLayer *, QSet<QgsFeatureId> > ignoreFeatures; ignoreFeatures.insert( vlayer, vlayer->allFeatureIds() ); if ( geom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers(), ignoreFeatures ) != 0 ) { emit messageEmitted( tr( "An error was reported during intersection removal" ), Qgis::Critical ); vlayer->destroyEditCommand(); stopCapturing(); return; } if ( geom.isEmpty() ) //intersection removal might have removed the whole geometry { emit messageEmitted( tr( "The feature cannot be reshaped because the resulting geometry is empty" ), Qgis::Critical ); vlayer->destroyEditCommand(); return; } } vlayer->changeGeometry( f.id(), geom ); reshapeDone = true; } } } if ( reshapeDone ) { vlayer->endEditCommand(); } else { vlayer->destroyEditCommand(); } }
void QgsMapToolReshape::cadCanvasReleaseEvent( QgsMapMouseEvent * e ) { //check if we operate on a vector layer //todo: move this to a function in parent class to avoid duplication QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { deleteTempRubberBand(); //find out bounding box of mCaptureList if ( size() < 1 ) { stopCapturing(); return; } QgsPoint firstPoint = points().at( 0 ); QgsRectangle bbox( firstPoint.x(), firstPoint.y(), firstPoint.x(), firstPoint.y() ); for ( int i = 1; i < size(); ++i ) { bbox.combineExtentWith( points().at( i ).x(), points().at( i ).y() ); } //query all the features that intersect bounding box of capture line QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; int reshapeReturn; bool reshapeDone = false; vlayer->beginEditCommand( tr( "Reshape" ) ); while ( fit.nextFeature( f ) ) { //query geometry //call geometry->reshape(mCaptureList) //register changed geometry in vector layer QgsGeometry* geom = f.geometry(); if ( geom ) { reshapeReturn = geom->reshapeGeometry( points() ); if ( reshapeReturn == 0 ) { //avoid intersections on polygon layers if ( vlayer->geometryType() == QGis::Polygon ) { //ignore all current layer features as they should be reshaped too QMap<QgsVectorLayer*, QSet<QgsFeatureId> > ignoreFeatures; ignoreFeatures.insert( vlayer, vlayer->allFeatureIds() ); if ( geom->avoidIntersections( ignoreFeatures ) != 0 ) { emit messageEmitted( tr( "An error was reported during intersection removal" ), QgsMessageBar::CRITICAL ); vlayer->destroyEditCommand(); stopCapturing(); return; } if ( geom->isGeosEmpty() ) //intersection removal might have removed the whole geometry { emit messageEmitted( tr( "The feature cannot be reshaped because the resulting geometry is empty" ), QgsMessageBar::CRITICAL ); vlayer->destroyEditCommand(); stopCapturing(); return; } } vlayer->changeGeometry( f.id(), geom ); reshapeDone = true; } } } if ( reshapeDone ) { vlayer->endEditCommand(); } else { vlayer->destroyEditCommand(); } stopCapturing(); } }