double QgsTolerance::computeMapUnitPerPixel( QgsMapLayer* layer, const QgsMapSettings& mapSettings ) { if ( ! mapSettings.hasCrsTransformEnabled() ) { // if the on-the-fly projections are not enabled, layer units pre pixel are the same as map units per pixel return mapSettings.mapUnitsPerPixel(); } // the layer is projected. Find out how many pixels are in one map unit - either horizontal and vertical direction // this check might not work correctly in some cases // (on a large area the pixels projected around "0,0" can have different properties from the actual point) QgsPoint p1 = toLayerCoordinates( layer, mapSettings, QPoint( 0, 1 ) ); QgsPoint p2 = toLayerCoordinates( layer, mapSettings, QPoint( 0, 2 ) ); QgsPoint p3 = toLayerCoordinates( layer, mapSettings, QPoint( 1, 0 ) ); QgsPoint p4 = toLayerCoordinates( layer, mapSettings, QPoint( 2, 0 ) ); double x = p1.sqrDist( p2 ); double y = p3.sqrDist( p4 ); if ( x > y ) { return sqrt( x ); } else { return sqrt( y ); } }
void QgsMapToolOffsetCurve::canvasMoveEvent( QgsMapMouseEvent* e ) { delete mSnapVertexMarker; mSnapVertexMarker = nullptr; if ( !mOriginalGeometry || !mRubberBand ) { return; } QgsVectorLayer* layer = currentVectorLayer(); if ( !layer ) { return; } mGeometryModified = true; //get offset from current position rectangular to feature QgsPoint layerCoords = toLayerCoordinates( layer, e->pos() ); //snap cursor to background layers QgsPointLocator::Match m = mCanvas->snappingUtils()->snapToMap( e->pos() ); if ( m.isValid() ) { if (( m.layer() && m.layer()->id() != mSourceLayerId ) || m.featureId() != mModifiedFeature ) { layerCoords = toLayerCoordinates( layer, m.point() ); mSnapVertexMarker = new QgsVertexMarker( mCanvas ); mSnapVertexMarker->setIconType( QgsVertexMarker::ICON_CROSS ); mSnapVertexMarker->setColor( Qt::green ); mSnapVertexMarker->setPenWidth( 1 ); mSnapVertexMarker->setCenter( m.point() ); } } QgsPoint minDistPoint; int beforeVertex; double leftOf; double offset = sqrt( mOriginalGeometry->closestSegmentWithContext( layerCoords, minDistPoint, beforeVertex, &leftOf ) ); if ( offset == 0.0 ) { return; } if ( mDistanceWidget ) { // this will also set the rubber band mDistanceWidget->setValue( leftOf < 0 ? offset : -offset ); mDistanceWidget->setFocus( Qt::TabFocusReason ); } else { //create offset geometry using geos setOffsetForRubberBand( leftOf < 0 ? offset : -offset ); } }
int QgsMapToolCapture::nextPoint( const QgsPoint &mapPoint, QgsPoint &layerPoint ) { QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { QgsDebugMsg( QStringLiteral( "no vector layer" ) ); return 1; } try { QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) ) layerPoint.addZValue( defaultZValue() ); if ( QgsWkbTypes::hasM( vlayer->wkbType() ) ) layerPoint.addMValue( 0.0 ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ) QgsDebugMsg( QStringLiteral( "transformation to layer coordinate failed" ) ); return 2; } return 0; }
void QgsGrassEditTool::canvasMoveEvent( QMouseEvent * event ) { QgsPoint point = toLayerCoordinates( e->layer(), event->pos() ); mouseMove( point ); e->statusBar()->showMessage( e->mCanvasPrompt ); }
bool QgsMapToolIdentify::identifyRasterLayer( QgsRasterLayer *layer, int x, int y ) { bool res = true; if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( dprovider && ( dprovider->capabilities() & QgsRasterDataProvider::Identify ) == 0 ) return false; QMap< QString, QString > attributes, derivedAttributes; QgsPoint idPoint = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); idPoint = toLayerCoordinates( layer, idPoint ); QString type; if ( layer->providerType() == "wms" ) { type = tr( "WMS layer" ); //if WMS layer does not cover the view origin, //we need to map the view pixel coordinates //to WMS layer pixel coordinates QgsRectangle viewExtent = mCanvas->extent(); QgsRectangle layerExtent = layer->extent(); double mapUnitsPerPixel = mCanvas->mapUnitsPerPixel(); if ( mapUnitsPerPixel > 0 && viewExtent.intersects( layerExtent ) ) { double xMinView = viewExtent.xMinimum(); double yMaxView = viewExtent.yMaximum(); double xMinLayer = layerExtent.xMinimum(); double yMaxLayer = layerExtent.yMaximum(); idPoint.set( xMinView < xMinLayer ? floor( x - ( xMinLayer - xMinView ) / mapUnitsPerPixel ) : x, yMaxView > yMaxLayer ? floor( y - ( yMaxView - yMaxLayer ) / mapUnitsPerPixel ) : y ); attributes.insert( tr( "Feature info" ), layer->identifyAsHtml( idPoint ) ); } else { res = false; } } else { type = tr( "Raster" ); res = layer->extent().contains( idPoint ) && layer->identify( idPoint, attributes ); } if ( res ) { derivedAttributes.insert( tr( "(clicked coordinate)" ), idPoint.toString() ); results()->addFeature( layer, type, attributes, derivedAttributes ); } return res; }
void QgsMapToolRotateFeature::canvasReleaseEvent( QMouseEvent * e ) { Q_UNUSED( e ); if ( !mRubberBand ) { return; } QgsVectorLayer* vlayer = currentVectorLayer(); if ( !vlayer ) { return; } //calculations for affine transformation double angle = -1 * mRotation * ( PI / 180 ); QgsPoint anchorPoint = toLayerCoordinates( vlayer, mStartPointMapCoords ); double a = cos( angle ); double b = -1 * sin( angle ); double c = anchorPoint.x() - cos( angle ) * anchorPoint.x() + sin( angle ) * anchorPoint.y(); double d = sin( angle ); double ee = cos( angle ); double f = anchorPoint.y() - sin( angle ) * anchorPoint.x() - cos( angle ) * anchorPoint.y(); vlayer->beginEditCommand( tr( "Features Rotated" ) ); int start; if ( vlayer->geometryType() == 2 ) { start = 1; } else { start = 0; } int i = 0; foreach ( QgsFeatureId id, mRotatedFeatures ) { QgsFeature feat; vlayer->getFeatures( QgsFeatureRequest().setFilterFid( id ) ).nextFeature( feat ); QgsGeometry* geom = feat.geometry(); i = start; QgsPoint vertex = geom->vertexAt( i ); while ( vertex != QgsPoint( 0, 0 ) ) { double newX = a * vertex.x() + b * vertex.y() + c; double newY = d * vertex.x() + ee * vertex.y() + f; vlayer->moveVertex( newX, newY, id, i ); i = i + 1; vertex = geom->vertexAt( i ); } }
void QgsMapToolRotateFeature::applyRotation( double rotation ) { mRotation = rotation; mRotationActive = false; QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer ) { deleteRubberband(); notifyNotVectorLayer(); return; } //calculations for affine transformation double angle = -1 * mRotation * ( M_PI / 180 ); QgsPointXY anchorPoint = toLayerCoordinates( vlayer, mStartPointMapCoords ); double a = std::cos( angle ); double b = -1 * std::sin( angle ); double c = anchorPoint.x() - std::cos( angle ) * anchorPoint.x() + std::sin( angle ) * anchorPoint.y(); double d = std::sin( angle ); double ee = std::cos( angle ); double f = anchorPoint.y() - std::sin( angle ) * anchorPoint.x() - std::cos( angle ) * anchorPoint.y(); vlayer->beginEditCommand( tr( "Features Rotated" ) ); int start; if ( vlayer->geometryType() == 2 ) { start = 1; } else { start = 0; } int i = 0; Q_FOREACH ( QgsFeatureId id, mRotatedFeatures ) { QgsFeature feat; vlayer->getFeatures( QgsFeatureRequest().setFilterFid( id ) ).nextFeature( feat ); QgsGeometry geom = feat.geometry(); i = start; QgsPointXY vertex = geom.vertexAt( i ); while ( vertex != QgsPointXY( 0, 0 ) ) { double newX = a * vertex.x() + b * vertex.y() + c; double newY = d * vertex.x() + ee * vertex.y() + f; vlayer->moveVertex( newX, newY, id, i ); i = i + 1; vertex = geom.vertexAt( i ); } }
double QgsTolerance::computeMapUnitPerPixel( QgsMapLayer *layer, const QgsMapSettings &mapSettings ) { // the layer is projected. Find out how many pixels are in one map unit - either horizontal and vertical direction // this check might not work correctly in some cases // (on a large area the pixels projected around "0,0" can have different properties from the actual point) QgsPointXY p1 = toLayerCoordinates( layer, mapSettings, QPoint( 0, 1 ) ); QgsPointXY p2 = toLayerCoordinates( layer, mapSettings, QPoint( 0, 2 ) ); QgsPointXY p3 = toLayerCoordinates( layer, mapSettings, QPoint( 1, 0 ) ); QgsPointXY p4 = toLayerCoordinates( layer, mapSettings, QPoint( 2, 0 ) ); double x = p1.sqrDist( p2 ); double y = p3.sqrDist( p4 ); if ( x > y ) { return std::sqrt( x ); } else { return std::sqrt( y ); } }
void QgsGrassEditTool::canvasPressEvent( QMouseEvent * event ) { QgsPoint point = toLayerCoordinates( e->layer(), event->pos() ); mouseClick( point, event->button() ); // Set last click e->mLastPoint = point; e->statusBar()->showMessage( e->mCanvasPrompt ); QgsDebugMsg( QString( "n_points = %1" ).arg( e->mEditPoints->n_points ) ); }
int QgsMapToolCapture::nextPoint( const QPoint &p, QgsPoint &layerPoint, QgsPoint &mapPoint ) { QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { QgsDebugMsg( "no vector layer" ); return 1; } QgsPoint digitisedPoint; try { digitisedPoint = toLayerCoordinates( vlayer, p ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( "transformation to layer coordinate failed" ); return 2; } QList<QgsSnappingResult> snapResults; if ( mSnapper.snapToBackgroundLayers( p, snapResults ) == 0 ) { mapPoint = snapPointFromResults( snapResults, p ); try { layerPoint = toLayerCoordinates( vlayer, mapPoint ); //transform snapped point back to layer crs } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( "transformation to layer coordinate failed" ); return 2; } } return 0; }
QgsGeometry QgsMapToolDeleteRing::ringUnderPoint( const QgsPoint &p, QgsFeatureId &fid, int &partNum, int &ringNum ) { //There is no clean way to find if we are inside the ring of a feature, //so we iterate over all the features visible in the canvas //If several rings are found at this position, the smallest one is chosen, //in order to be able to delete a ring inside another ring QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( toLayerCoordinates( vlayer, mCanvas->extent() ) ) ); QgsFeature f; QgsGeometry g; QgsGeometry ringGeom; QgsMultiPolygon pol; QgsPolygon tempPol; QgsGeometry tempGeom; double area = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { g = f.geometry(); if ( g.isNull() ) continue; if ( g.wkbType() == QgsWkbTypes::Polygon || g.wkbType() == QgsWkbTypes::Polygon25D ) { pol = QgsMultiPolygon() << g.asPolygon(); } else { pol = g.asMultiPolygon(); } for ( int i = 0; i < pol.size() ; ++i ) { //for each part if ( pol[i].size() > 1 ) { for ( int j = 1; j < pol[i].size(); ++j ) { tempPol = QgsPolygon() << pol[i][j]; tempGeom = QgsGeometry::fromPolygon( tempPol ); if ( tempGeom.area() < area && tempGeom.contains( &p ) ) { fid = f.id(); partNum = i; ringNum = j; area = tempGeom.area(); ringGeom = tempGeom; } } } } } return ringGeom; }
void QgsMapToolMoveFeature::canvasReleaseEvent( QgsMapMouseEvent* e ) { //QgsDebugMsg("entering."); if ( !mRubberBand ) { return; } QgsVectorLayer* vlayer = currentVectorLayer(); if ( !vlayer ) { return; } QgsPoint startPointLayerCoords = toLayerCoordinates(( QgsMapLayer* )vlayer, mStartPointMapCoords ); QgsPoint stopPointLayerCoords = toLayerCoordinates(( QgsMapLayer* )vlayer, e->pos() ); double dx = stopPointLayerCoords.x() - startPointLayerCoords.x(); double dy = stopPointLayerCoords.y() - startPointLayerCoords.y(); vlayer->beginEditCommand( tr( "Feature moved" ) ); Q_FOREACH ( QgsFeatureId id, mMovedFeatures ) { vlayer->translateFeature( id, dx, dy ); }
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; }
int QgsMapToolEdit::insertSegmentVerticesForSnap( const QList<QgsSnappingResult>& snapResults, QgsVectorLayer* editedLayer ) { QgsPoint layerPoint; if ( !editedLayer || !editedLayer->isEditable() ) { return 1; } //transform snaping coordinates to layer crs first QList<QgsSnappingResult> transformedSnapResults = snapResults; QList<QgsSnappingResult>::iterator it = transformedSnapResults.begin(); for ( ; it != transformedSnapResults.constEnd(); ++it ) { QgsPoint layerPoint = toLayerCoordinates( editedLayer, it->snappedVertex ); it->snappedVertex = layerPoint; } return editedLayer->insertSegmentVerticesForSnap( transformedSnapResults ); }
int QgsMapToolCapture::nextPoint( const QgsPoint& mapPoint, QgsPoint& layerPoint ) { QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { QgsDebugMsg( "no vector layer" ); return 1; } try { layerPoint = toLayerCoordinates( vlayer, mapPoint ); //transform snapped point back to layer crs } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( "transformation to layer coordinate failed" ); return 2; } return 0; }
/** * Selection routine called by the mouse release event * @param thePoint = QgsPoint representing the x, y coordinates of the mouse release event */ void eVisEventIdTool::select( QgsPoint thePoint ) { if ( 0 == mCanvas ) return; QgsVectorLayer* myLayer = ( QgsVectorLayer* )mCanvas->currentLayer( ); // create the search rectangle. this was modeled after the QgsMapIdentifyTool in core QGIS application double searchWidth = mCanvas->extent( ).width( ) * (( double )QGis::DEFAULT_IDENTIFY_RADIUS / 100.0 ); QgsRectangle myRectangle; myRectangle.setXMinimum( thePoint.x( ) - searchWidth ); myRectangle.setXMaximum( thePoint.x( ) + searchWidth ); myRectangle.setYMinimum( thePoint.y( ) - searchWidth ); myRectangle.setYMaximum( thePoint.y( ) + searchWidth ); //Transform rectange to map coordinates myRectangle = toLayerCoordinates( myLayer, myRectangle ); //Rather than add to the current selection, clear all selected features myLayer->removeSelection( false ); //select features QgsFeatureIterator fit = myLayer->getFeatures( QgsFeatureRequest().setFilterRect( myRectangle ).setFlags( QgsFeatureRequest::ExactIntersect ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; QgsFeatureIds newSelectedFeatures; while ( fit.nextFeature( f ) ) { newSelectedFeatures.insert( f.id() ); } myLayer->setSelectedFeatures( newSelectedFeatures ); //Launch a new event browser to view selected features mBrowser = new eVisGenericEventBrowserGui( mCanvas, mCanvas, NULL ); mBrowser->setAttribute( Qt::WA_DeleteOnClose ); }
double QgsMapToolOffsetCurve::calculateOffset( QgsPointXY mapPoint ) { double offset = 0.0; if ( mLayer ) { //get offset from current position rectangular to feature QgsPointXY layerCoords = toLayerCoordinates( mLayer, mapPoint ); QgsPointXY minDistPoint; int beforeVertex; int leftOf = 0; offset = std::sqrt( mManipulatedGeometry.closestSegmentWithContext( layerCoords, minDistPoint, beforeVertex, &leftOf ) ); if ( QgsWkbTypes::geometryType( mManipulatedGeometry.wkbType() ) == QgsWkbTypes::LineGeometry ) { offset = leftOf < 0 ? offset : -offset; } else { offset = mManipulatedGeometry.contains( &layerCoords ) ? -offset : offset; } } return offset; }
/** * Selection routine called by the mouse release event * @param point = QgsPointXY representing the x, y coordinates of the mouse release event */ void eVisEventIdTool::select( const QgsPointXY &point ) { if ( !mCanvas ) return; QgsVectorLayer *myLayer = ( QgsVectorLayer * )mCanvas->currentLayer(); // create the search rectangle. this was modeled after the QgsMapIdentifyTool in core QGIS application double searchWidth = QgsMapTool::searchRadiusMU( mCanvas ); QgsRectangle myRectangle; myRectangle.setXMinimum( point.x() - searchWidth ); myRectangle.setXMaximum( point.x() + searchWidth ); myRectangle.setYMinimum( point.y() - searchWidth ); myRectangle.setYMaximum( point.y() + searchWidth ); //Transform rectangle to map coordinates myRectangle = toLayerCoordinates( myLayer, myRectangle ); //select features QgsFeatureIterator fit = myLayer->getFeatures( QgsFeatureRequest().setFilterRect( myRectangle ).setFlags( QgsFeatureRequest::ExactIntersect ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; QgsFeatureIds newSelectedFeatures; while ( fit.nextFeature( f ) ) { newSelectedFeatures.insert( f.id() ); } myLayer->selectByIds( newSelectedFeatures ); //Launch a new event browser to view selected features mBrowser = new eVisGenericEventBrowserGui( mCanvas, mCanvas, nullptr ); mBrowser->setAttribute( Qt::WA_DeleteOnClose ); }
QgsGeometry* QgsMapToolDeletePart::partUnderPoint( QPoint point, int& fid, int& partNum ) { QgsFeature f; QgsGeometry* geomPart = new QgsGeometry(); switch ( vlayer->geometryType() ) { case QGis::Point: case QGis::Line: { if ( mSnapper.snapToCurrentLayer( point, mRecentSnappingResults, QgsSnapper::SnapToVertexAndSegment ) == 0 ) { if ( mRecentSnappingResults.length() > 0 ) { QgsSnappingResult sr = mRecentSnappingResults.first(); int snapVertex = sr.snappedVertexNr; if ( snapVertex == -1 ) snapVertex = sr.beforeVertexNr; vlayer->getFeatures( QgsFeatureRequest().setFilterFid( sr.snappedAtGeometry ) ).nextFeature( f ); QgsGeometry* g = f.geometry(); if ( !g->isMultipart() ) return geomPart; if ( g->wkbType() == QGis::WKBMultiPoint || g->wkbType() == QGis::WKBMultiPoint25D ) { fid = sr.snappedAtGeometry; partNum = snapVertex; return QgsGeometry::fromPoint( sr.snappedVertex ); } if ( g->wkbType() == QGis::WKBMultiLineString || g->wkbType() == QGis::WKBMultiLineString25D ) { QgsMultiPolyline mline = g->asMultiPolyline(); for ( int part = 0; part < mline.count(); part++ ) { if ( snapVertex < mline[part].count() ) { fid = sr.snappedAtGeometry; partNum = part; return QgsGeometry::fromPolyline( mline[part] ); } snapVertex -= mline[part].count(); } } } } break; } case QGis::Polygon: { QgsPoint layerCoords = toLayerCoordinates( vlayer, point ); double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() ); QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius, layerCoords.x() + searchRadius, layerCoords.y() + searchRadius ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ) ); fit.nextFeature( f ); QgsGeometry* g = f.geometry(); if ( !g ) return geomPart; if ( !g->isMultipart() ) return geomPart; QgsMultiPolygon mpolygon = g->asMultiPolygon(); for ( int part = 0; part < mpolygon.count(); part++ ) // go through the polygons { const QgsPolygon& polygon = mpolygon[part]; QgsGeometry* partGeo = QgsGeometry::fromPolygon( polygon ); if ( partGeo->contains( &layerCoords ) ) { fid = f.id(); partNum = part; return partGeo; } } break; } default: { break; } } return geomPart; }
bool QgsMapToolFeatureAction::doAction( QgsVectorLayer *layer, int x, int y ) { if ( !layer ) return false; QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); QgsFeatureList featList; // toLayerCoordinates will throw an exception for an 'invalid' point. // 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. try { // create the search rectangle double searchRadius = searchRadiusMU( mCanvas ); QgsRectangle r; r.setXMinimum( point.x() - searchRadius ); r.setXMaximum( point.x() + searchRadius ); r.setYMinimum( point.y() - searchRadius ); r.setYMaximum( point.y() + searchRadius ); r = toLayerCoordinates( layer, r ); QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature f; while ( fit.nextFeature( f ) ) featList << QgsFeature( f ); } catch ( QgsCsException & cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and proceed with no features found QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) ); } if ( featList.size() == 0 ) return false; Q_FOREACH ( const QgsFeature& feat, featList ) { if ( layer->actions()->defaultAction() >= 0 ) { // define custom substitutions: layer id and clicked coords QMap<QString, QVariant> substitutionMap; substitutionMap.insert( "$layerid", layer->id() ); point = toLayerCoordinates( layer, point ); substitutionMap.insert( "$clickx", point.x() ); substitutionMap.insert( "$clicky", point.y() ); int actionIdx = layer->actions()->defaultAction(); layer->actions()->doAction( actionIdx, feat, &substitutionMap ); } else { QgsMapLayerAction* mapLayerAction = QgsMapLayerActionRegistry::instance()->defaultActionForLayer( layer ); if ( mapLayerAction ) { mapLayerAction->triggerForFeature( layer, &feat ); } } } return true; }
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 QgsMapToolAddFeature::canvasReleaseEvent( QMouseEvent * e ) { QgsDebugMsg( "entered." ); QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { notifyNotVectorLayer(); return; } QGis::WkbType layerWKBType = vlayer->wkbType(); QgsVectorDataProvider* provider = vlayer->dataProvider(); if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) ) { QMessageBox::information( 0, tr( "Layer cannot be added to" ), tr( "The data provider for this layer does not support the addition of features." ) ); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } // POINT CAPTURING if ( mode() == CapturePoint ) { //check we only use this tool for point/multipoint layers if ( vlayer->geometryType() != QGis::Point ) { QMessageBox::information( 0, tr( "Wrong editing tool" ), tr( "Cannot apply the 'capture point' tool on this vector layer" ) ); return; } QgsPoint idPoint; //point in map coordinates QList<QgsSnappingResult> snapResults; QgsPoint savePoint; //point in layer coordinates if ( mSnapper.snapToBackgroundLayers( e->pos(), snapResults ) == 0 ) { idPoint = snapPointFromResults( snapResults, e->pos() ); try { savePoint = toLayerCoordinates( vlayer, idPoint ); QgsDebugMsg( "savePoint = " + savePoint.toString() ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QMessageBox::information( 0, tr( "Coordinate transform error" ), tr( "Cannot transform the point to the layers coordinate system" ) ); 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 = new QgsFeature( 0, "WKBPoint" ); 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 ); } f->setGeometry( g ); vlayer->beginEditCommand( tr( "Feature added" ) ); if ( addFeature( vlayer, f ) ) { vlayer->endEditCommand(); } else { delete f; vlayer->destroyEditCommand(); } mCanvas->refresh(); } } else if ( mode() == CaptureLine || mode() == CapturePolygon ) { //check we only use the line tool for line/multiline layers if ( mode() == CaptureLine && vlayer->geometryType() != QGis::Line ) { QMessageBox::information( 0, tr( "Wrong editing tool" ), tr( "Cannot apply the 'capture line' tool on this vector layer" ) ); return; } //check we only use the polygon tool for polygon/multipolygon layers if ( mode() == CapturePolygon && vlayer->geometryType() != QGis::Polygon ) { QMessageBox::information( 0, tr( "Wrong editing tool" ), tr( "Cannot apply the 'capture polygon' tool on this vector layer" ) ); return; } //add point to list and to rubber band int error = addVertex( e->pos() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation QMessageBox::information( 0, tr( "Coordinate transform error" ), tr( "Cannot transform the point to the layers coordinate system" ) ); return; } if ( e->button() == Qt::LeftButton ) { startCapturing(); } else if ( e->button() == Qt::RightButton ) { // End of string //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; } //create QgsFeature with wkb representation QgsFeature* f = new QgsFeature( 0, "WKBLineString" ); QgsGeometry *g; if ( mode() == CaptureLine ) { if ( layerWKBType == QGis::WKBLineString || layerWKBType == QGis::WKBLineString25D ) { g = QgsGeometry::fromPolyline( points().toVector() ); } else if ( layerWKBType == QGis::WKBMultiLineString || layerWKBType == QGis::WKBMultiLineString25D ) { g = QgsGeometry::fromMultiPolyline( QgsMultiPolyline() << points().toVector() ); } else { QMessageBox::critical( 0, tr( "Error" ), tr( "Cannot add feature. Unknown WKB type" ) ); stopCapturing(); return; //unknown wkbtype } f->setGeometry( g ); } else // polygon { if ( layerWKBType == QGis::WKBPolygon || layerWKBType == QGis::WKBPolygon25D ) { g = QgsGeometry::fromPolygon( QgsPolygon() << points().toVector() ); } else if ( layerWKBType == QGis::WKBMultiPolygon || layerWKBType == QGis::WKBMultiPolygon25D ) { g = QgsGeometry::fromMultiPolygon( QgsMultiPolygon() << ( QgsPolygon() << points().toVector() ) ); } else { QMessageBox::critical( 0, tr( "Error" ), tr( "Cannot add feature. Unknown WKB type" ) ); stopCapturing(); return; //unknown wkbtype } if ( !g ) { stopCapturing(); delete f; return; // invalid geometry; one possibility is from duplicate points } f->setGeometry( g ); 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... QMessageBox::critical( 0, tr( "Error" ), tr( "The feature could not be added because removing the polygon intersections would change the geometry type" ) ); delete f; stopCapturing(); return; } #endif else if ( avoidIntersectionsReturn == 3 ) { QMessageBox::critical( 0, tr( "Error" ), tr( "An error was reported during intersection removal" ) ); } } vlayer->beginEditCommand( tr( "Feature added" ) ); if ( addFeature( vlayer, f ) ) { //add points to other features to keep topology up-to-date int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); if ( topologicalEditing ) { vlayer->addTopologicalPoints( f->geometry() ); } vlayer->endEditCommand(); } else { delete f; vlayer->destroyEditCommand(); } stopCapturing(); } } }
QgsPoint QgsMapTool::toLayerCoordinates( QgsMapLayer* layer, const QPoint& point ) { QgsPoint pt = toMapCoordinates( point ); return toLayerCoordinates( layer, pt ); }
QgsGeometry* QgsMapToolDeletePart::partUnderPoint( QPoint point, QgsFeatureId& fid, int& partNum ) { QgsFeature f; QgsGeometry* geomPart = new QgsGeometry(); switch ( vlayer->geometryType() ) { case QGis::Point: case QGis::Line: { QgsPointLocator::Match match = mCanvas->snappingUtils()->snapToCurrentLayer( point, QgsPointLocator::Vertex | QgsPointLocator::Edge ); if ( !match.isValid() ) return geomPart; int snapVertex = match.vertexIndex(); vlayer->getFeatures( QgsFeatureRequest().setFilterFid( match.featureId() ) ).nextFeature( f ); const QgsGeometry* g = f.constGeometry(); if ( !g->isMultipart() ) { fid = match.featureId(); delete geomPart; return QgsGeometry::fromPoint( match.point() ); } if ( g->wkbType() == QGis::WKBMultiPoint || g->wkbType() == QGis::WKBMultiPoint25D ) { fid = match.featureId(); partNum = snapVertex; delete geomPart; return QgsGeometry::fromPoint( match.point() ); } if ( g->wkbType() == QGis::WKBMultiLineString || g->wkbType() == QGis::WKBMultiLineString25D ) { QgsMultiPolyline mline = g->asMultiPolyline(); for ( int part = 0; part < mline.count(); part++ ) { if ( snapVertex < mline[part].count() ) { fid = match.featureId(); partNum = part; delete geomPart; return QgsGeometry::fromPolyline( mline[part] ); } snapVertex -= mline[part].count(); } } break; } case QGis::Polygon: { QgsPoint layerCoords = toLayerCoordinates( vlayer, point ); double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() ); QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius, layerCoords.x() + searchRadius, layerCoords.y() + searchRadius ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ) ); fit.nextFeature( f ); const QgsGeometry* g = f.constGeometry(); if ( !g ) return geomPart; if ( !g->isMultipart() ) { fid = f.id(); return geomPart; } QgsMultiPolygon mpolygon = g->asMultiPolygon(); for ( int part = 0; part < mpolygon.count(); part++ ) // go through the polygons { const QgsPolygon& polygon = mpolygon[part]; QgsGeometry* partGeo = QgsGeometry::fromPolygon( polygon ); if ( partGeo->contains( &layerCoords ) ) { fid = f.id(); partNum = part; delete geomPart; return partGeo; } delete partGeo; } break; } default: { break; } } return geomPart; }
bool QgsMapToolIdentify::identifyVectorLayer( QList<IdentifyResult> *results, QgsVectorLayer *layer, const QgsPoint& point ) { if ( !layer || !layer->hasGeometryType() ) return false; if ( layer->hasScaleBasedVisibility() && ( layer->minimumScale() > mCanvas->mapSettings().scale() || layer->maximumScale() <= mCanvas->mapSettings().scale() ) ) { QgsDebugMsg( "Out of scale limits" ); return false; } QApplication::setOverrideCursor( Qt::WaitCursor ); QMap< QString, QString > commonDerivedAttributes; commonDerivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); int featureCount = 0; QgsFeatureList featureList; // toLayerCoordinates will throw an exception for an 'invalid' point. // 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. try { // create the search rectangle double searchRadius = searchRadiusMU( mCanvas ); QgsRectangle r; r.setXMinimum( point.x() - searchRadius ); r.setXMaximum( point.x() + searchRadius ); r.setYMinimum( point.y() - searchRadius ); r.setYMaximum( point.y() + searchRadius ); r = toLayerCoordinates( layer, r ); QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature f; while ( fit.nextFeature( f ) ) featureList << QgsFeature( f ); } catch ( QgsCsException & cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and proceed with no features found QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) ); } QgsFeatureList::iterator f_it = featureList.begin(); bool filter = false; QgsRenderContext context( QgsRenderContext::fromMapSettings( mCanvas->mapSettings() ) ); context.expressionContext() << QgsExpressionContextUtils::layerScope( layer ); QgsFeatureRendererV2* renderer = layer->rendererV2(); if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { // setup scale for scale dependent visibility (rule based) renderer->startRender( context, layer->fields() ); filter = renderer->capabilities() & QgsFeatureRendererV2::Filter; } for ( ; f_it != featureList.end(); ++f_it ) { QMap< QString, QString > derivedAttributes = commonDerivedAttributes; QgsFeatureId fid = f_it->id(); context.expressionContext().setFeature( *f_it ); if ( filter && !renderer->willRenderFeature( *f_it, context ) ) continue; featureCount++; derivedAttributes.unite( featureDerivedAttributes( &( *f_it ), layer, toLayerCoordinates( layer, point ) ) ); derivedAttributes.insert( tr( "feature id" ), fid < 0 ? tr( "new feature" ) : FID_TO_STRING( fid ) ); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), *f_it, derivedAttributes ) ); } if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { renderer->stopRender( context ); } QgsDebugMsg( "Feature count on identify: " + QString::number( featureCount ) ); QApplication::restoreOverrideCursor(); return featureCount > 0; }
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, bool doContains, bool doDifference, bool singleSelect ) { if ( selectGeometry->type() != QGis::Polygon ) { return; } QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( vlayer == nullptr ) { return; } // 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 ( canvas->mapSettings().hasCrsTransformEnabled() ) //{ // try // { // QgsCoordinateTransform ct( canvas->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__ ) ); // LOG_INFO( "CRS Exception\nSelection extends beyond layer's coordinate system" ); // return; // } //} QgsGeometry selectGeomTrans; try{ selectGeomTrans = toLayerCoordinates( canvas, selectGeometry, vlayer ); } catch ( QgsCsException & ) { return; } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsDebugMsg( "Selection layer: " + vlayer->name() ); QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() ); QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) ); QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); QgsFeatureRendererV2* r = vlayer->rendererV2(); if ( r ) r->startRender( context, vlayer->pendingFields() ); QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes(), vlayer->pendingFields() ); else request.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator fit = vlayer->getFeatures( request ); QgsFeatureIds newSelectedFeatures; QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { #if (VERSION_INT >= 21601) context.expressionContext().setFeature( f ); //taken from QGIS 2.16.1 // make sure to only use features that are visible if ( r && !r->willRenderFeature( f, context ) ) #else if ( r && !r->willRenderFeature( f ) ) #endif continue; QgsGeometry* g = f.geometry(); if ( doContains ) { if ( !selectGeomTrans.contains( g ) ) continue; } else { if ( !selectGeomTrans.intersects( g ) ) continue; } if ( singleSelect ) { foundSingleFeature = true; double distance = g->distance( selectGeomTrans ); if ( distance <= closestFeatureDist ) { closestFeatureDist = distance; closestFeatureId = f.id(); } } else { newSelectedFeatures.insert( f.id() ); } } if ( singleSelect && foundSingleFeature ) { newSelectedFeatures.insert( closestFeatureId ); } if ( r ) r->stopRender( context ); QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) ); if ( doDifference ) { QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds(); QgsFeatureIds selectedFeatures; QgsFeatureIds deselectedFeatures; QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd(); while ( i != newSelectedFeatures.constBegin() ) { --i; if ( layerSelectedFeatures.contains( *i ) ) { deselectedFeatures.insert( *i ); } else { selectedFeatures.insert( *i ); } } vlayer->modifySelection( selectedFeatures, deselectedFeatures ); } else { SelectFeatures( vlayer, newSelectedFeatures ); // vlayer->setSelectedFeatures( newSelectedFeatures ); } QApplication::restoreOverrideCursor(); }
bool QgsMapToolFeatureAction::doAction( QgsVectorLayer *layer, int x, int y ) { if ( !layer ) return false; QgsPointXY point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); QgsRectangle r; // create the search rectangle double searchRadius = searchRadiusMU( mCanvas ); r.setXMinimum( point.x() - searchRadius ); r.setXMaximum( point.x() + searchRadius ); r.setYMinimum( point.y() - searchRadius ); r.setYMaximum( point.y() + searchRadius ); // toLayerCoordinates will throw an exception for an 'invalid' point. // 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. try { r = toLayerCoordinates( layer, r ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and proceed with no features found QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) ); } QgsAction defaultAction = layer->actions()->defaultAction( QStringLiteral( "Canvas" ) ); QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature feat; while ( fit.nextFeature( feat ) ) { if ( defaultAction.isValid() ) { // define custom substitutions: layer id and clicked coords QgsExpressionContext context; context << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope( QgsProject::instance() ) << QgsExpressionContextUtils::mapSettingsScope( mCanvas->mapSettings() ); QgsExpressionContextScope *actionScope = new QgsExpressionContextScope(); actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "click_x" ), point.x(), true ) ); actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "click_y" ), point.y(), true ) ); actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "action_scope" ), QStringLiteral( "Canvas" ), true ) ); context << actionScope; defaultAction.run( layer, feat, context ); } else { QgsMapLayerAction *mapLayerAction = QgsGui::mapLayerActionRegistry()->defaultActionForLayer( layer ); if ( mapLayerAction ) { mapLayerAction->triggerForFeature( layer, &feat ); } } } return true; }
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(); } } }
bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPoint point, const QgsRectangle& viewExtent, double mapUnitsPerPixel ) { QgsDebugMsg( "point = " + point.toString() ); if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( !dprovider ) return false; int capabilities = dprovider->capabilities(); if ( !( capabilities & QgsRasterDataProvider::Identify ) ) return false; QgsPoint pointInCanvasCrs = point; try { point = toLayerCoordinates( layer, point ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) ); return false; } QgsDebugMsg( QString( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) ); if ( !layer->extent().contains( point ) ) return false; QMap< QString, QString > attributes, derivedAttributes; QgsRaster::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( "identify/format" ).toString() ); // check if the format is really supported otherwise use first supported format if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) ) { if ( capabilities & QgsRasterInterface::IdentifyFeature ) format = QgsRaster::IdentifyFormatFeature; else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRaster::IdentifyFormatValue; else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRaster::IdentifyFormatHtml; else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRaster::IdentifyFormatText; else return false; } QgsRasterIdentifyResult identifyResult; // We can only use current map canvas context (extent, width, height) if layer is not reprojected, if ( mCanvas->hasCrsTransformEnabled() && dprovider->crs() != mCanvas->mapSettings().destinationCrs() ) { // To get some reasonable response for point/line WMS vector layers we must // use a context with approximately a resolution in layer CRS units // corresponding to current map canvas resolution (for examplei UMN Mapserver // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel // + TOLERANCE (layer param) for feature selection) // QgsRectangle r; r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. ); r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. ); r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. ); r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. ); r = toLayerCoordinates( layer, r ); // will be a bit larger // Mapserver (6.0.3, for example) does not work with 1x1 pixel box // but that is fixed (the rect is enlarged) in the WMS provider identifyResult = dprovider->identify( point, format, r, 1, 1 ); } else { // It would be nice to use the same extent and size which was used for drawing, // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw() // is doing some tricks with extent and size to allign raster to output which // would be difficult to replicate here. // Note: cutting the extent may result in slightly different x and y resolutions // and thus shifted point calculated back in QGIS WMS (using average resolution) //viewExtent = dprovider->extent().intersect( &viewExtent ); // Width and height are calculated from not projected extent and we hope that // are similar to source width and height used to reproject layer for drawing. // TODO: may be very dangerous, because it may result in different resolutions // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution. int width = qRound( viewExtent.width() / mapUnitsPerPixel ); int height = qRound( viewExtent.height() / mapUnitsPerPixel ); QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) ); identifyResult = dprovider->identify( point, format, viewExtent, width, height ); } derivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); if ( identifyResult.isValid() ) { QMap<int, QVariant> values = identifyResult.results(); QgsGeometry geometry; if ( format == QgsRaster::IdentifyFormatValue ) { Q_FOREACH ( int bandNo, values.keys() ) { QString valueString; if ( values.value( bandNo ).isNull() ) { valueString = tr( "no data" ); } else { double value = values.value( bandNo ).toDouble(); valueString = QgsRasterBlock::printValue( value ); } attributes.insert( dprovider->generateBandName( bandNo ), valueString ); } QString label = layer->name(); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); }
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 ); }