diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index e7fef3fabb9..bb8324713aa 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -92,6 +92,7 @@ SET(QGIS_APP_SRCS nodetool/qgsmaptoolnodetool.cpp nodetool/qgsselectedfeature.cpp nodetool/qgsvertexentry.cpp + nodetool/qgsnodeeditor.cpp qgsmeasuredialog.cpp qgsmeasuretool.cpp @@ -242,6 +243,7 @@ SET (QGIS_APP_MOC_HDRS nodetool/qgsmaptoolnodetool.h nodetool/qgsselectedfeature.h + nodetool/qgsnodeeditor.h qgsmeasuredialog.h qgsmeasuretool.h diff --git a/src/app/nodetool/qgsmaptoolnodetool.cpp b/src/app/nodetool/qgsmaptoolnodetool.cpp index ef0344c41d3..8868489f198 100644 --- a/src/app/nodetool/qgsmaptoolnodetool.cpp +++ b/src/app/nodetool/qgsmaptoolnodetool.cpp @@ -17,372 +17,68 @@ #include "nodetool/qgsselectedfeature.h" #include "nodetool/qgsvertexentry.h" +#include "nodetool/qgsnodeeditor.h" #include "qgisapp.h" #include "qgslayertreeview.h" #include "qgslogger.h" #include "qgsmapcanvas.h" #include "qgsproject.h" -#include "qgsrubberband.h" -#include "qgssnappingutils.h" +#include "qgsgeometryrubberband.h" #include "qgsvectorlayer.h" #include #include -//! Match filter that does not accept only one particular point -struct QgsExcludePointFilter : public QgsPointLocator::MatchFilter -{ - QgsExcludePointFilter( const QgsPoint& exclPoint ) : mExclPoint( exclPoint ) {} - bool acceptMatch( const QgsPointLocator::Match& match ) override { return match.point() != mExclPoint; } - QgsPoint mExclPoint; -}; - -//! Match filter that accepts only matches from a particular feature ID -struct QgsFeatureIdFilter : public QgsPointLocator::MatchFilter -{ - QgsFeatureIdFilter( const QgsFeatureId& fid ) : mFid( fid ) {} - bool acceptMatch( const QgsPointLocator::Match& match ) override { return match.featureId() == mFid; } - QgsFeatureId mFid; -}; - QgsMapToolNodeTool::QgsMapToolNodeTool( QgsMapCanvas* canvas ) : QgsMapToolEdit( canvas ) , mSelectedFeature( 0 ) - , mSelectionRectangle( false ) - , mMoving( true ) - , mClicked( false ) - , mCtrl( false ) - , mSelectAnother( false ) - , mAnother( 0 ) - , mSelectionRubberBand( 0 ) - , mRect( NULL ) + , mNodeEditor( 0 ) + , mRect( 0 ) , mIsPoint( false ) - , mDeselectOnRelease( -1 ) { - mToolName = tr( "Node tool" ); + mSnapper.setMapCanvas( canvas ); + mCadAllowed = true; + mSnapOnPress = true; } QgsMapToolNodeTool::~QgsMapToolNodeTool() { cleanTool(); - delete mRect; - delete mSelectionRubberBand; } -void QgsMapToolNodeTool::createMovingRubberBands() +void QgsMapToolNodeTool::canvasMapPressEvent( QgsMapMouseEvent* e ) { - int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); - - Q_ASSERT( mSelectedFeature ); - - QgsVectorLayer *vlayer = mSelectedFeature->vlayer(); - Q_ASSERT( vlayer ); - - QList &vertexMap = mSelectedFeature->vertexMap(); - QgsGeometry* geometry = mSelectedFeature->geometry(); - int beforeVertex, afterVertex; - int lastRubberBand = 0; - int vertex; - for ( int i = 0; i < vertexMap.size(); i++ ) + removeRubberBands(); + QgsVectorLayer *vlayer = qobject_cast( mCanvas->currentLayer() ); + if ( !vlayer ) { - // create rubberband - if ( vertexMap[i]->isSelected() && !vertexMap[i]->isInRubberBand() ) - { - geometry->adjacentVertices( i, beforeVertex, afterVertex ); - vertex = i; - while ( beforeVertex != -1 ) - { - // move forward NOTE: end if whole cycle is selected - if ( vertexMap[beforeVertex]->isSelected() && beforeVertex != i ) // and take care of cycles - { - vertex = beforeVertex; - geometry->adjacentVertices( vertex, beforeVertex, afterVertex ); - } - else - { - // break if cycle is found - break; - } - } - // we have first vertex of moving part - // create rubberband and set default paramaters - QgsRubberBand* rb = new QgsRubberBand( mCanvas, QGis::Line ); - rb->setWidth( 2 ); - rb->setColor( Qt::blue ); - int index = 0; - if ( beforeVertex != -1 ) // adding first point which is not moving - { - rb->addPoint( toMapCoordinates( vlayer, vertexMap[beforeVertex]->point() ), false ); - vertexMap[beforeVertex]->setRubberBandValues( true, lastRubberBand, index ); - index++; - } - while ( vertex != -1 && vertexMap[vertex]->isSelected() && !vertexMap[vertex]->isInRubberBand() ) - { - // topology rubberband creation if needed - if ( topologicalEditing ) - { - createTopologyRubberBands( vlayer, vertexMap, vertex ); - } - // adding point which will be moved - rb->addPoint( toMapCoordinates( vlayer, vertexMap[vertex]->point() ), false ); - // setting values about added vertex - vertexMap[vertex]->setRubberBandValues( true, lastRubberBand, index ); - index++; - geometry->adjacentVertices( vertex, beforeVertex, vertex ); - } - if ( vertex != -1 && !vertexMap[vertex]->isSelected() ) // add last point not moving if exists - { - rb->addPoint( toMapCoordinates( vlayer, vertexMap[vertex]->point() ), true ); - vertexMap[vertex]->setRubberBandValues( true, lastRubberBand, index ); - index++; - } - rb->show(); - mRubberBands.append( rb ); - lastRubberBand++; - } - } -} - -void QgsMapToolNodeTool::createTopologyRubberBands( QgsVectorLayer* vlayer, const QList &vertexMap, int vertex ) -{ - QMultiMap currentResultList; - QgsGeometry *geometry = mSelectedFeature->geometry(); - - // snap from current vertex - currentResultList.clear(); - vlayer->snapWithContext( vertexMap[vertex]->point(), ZERO_TOLERANCE, currentResultList, QgsSnapper::SnapToVertex ); - QMultiMap::iterator resultIt = currentResultList.begin(); - - for ( ; resultIt != currentResultList.end(); ++resultIt ) - { - // move all other - if ( mSelectedFeature->featureId() != resultIt.value().snappedAtGeometry ) - { - if ( mTopologyMovingVertexes.contains( resultIt.value().snappedAtGeometry ) ) - { - if ( mTopologyMovingVertexes[resultIt.value().snappedAtGeometry]->contains( resultIt.value().snappedVertexNr ) ) - { - // skip vertex already exists in some rubberband - continue; - } - } - QgsRubberBand* trb = new QgsRubberBand( mCanvas, QGis::Line ); - mTopologyRubberBand.append( trb ); - int rbId = mTopologyRubberBand.size() - 1; - trb->setWidth( 1 ); - trb->setColor( Qt::red ); - - int tVertex = resultIt.value().snappedVertexNr; - int tVertexBackup = -1, tVertexAfter = -1; - int tVertexFirst = tVertex; // vertex number to check for cycling - QgsFeature topolFeature; - - vlayer->getFeatures( QgsFeatureRequest().setFilterFid( resultIt.value().snappedAtGeometry ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( topolFeature ); - const QgsGeometry* topolGeometry = topolFeature.constGeometry(); - - while ( tVertex != -1 ) // looking for first vertex to rubberband - { - tVertexBackup = tVertex; - topolGeometry->adjacentVertices( tVertex, tVertex, tVertexAfter ); - if ( tVertex == -1 || tVertex == tVertexFirst ) - break; // check if this is not first vertex of the feature or cycling error - // if closest vertex is not from selected feature or is not selected end - double dist; - QgsPoint point = topolGeometry->vertexAt( tVertex ); - int at, before, after; - geometry->closestVertex( point, at, before, after, dist ); - if ( dist > ZERO_TOLERANCE || !vertexMap[at]->isSelected() ) // problem with double precision - { - break; // found first vertex - } - } - - int movingPointIndex = 0; - Vertexes* movingPoints = new Vertexes(); - Vertexes* addedPoints = 0; - if ( mTopologyMovingVertexes.contains( resultIt.value().snappedAtGeometry ) ) - { - addedPoints = mTopologyMovingVertexes[ resultIt.value().snappedAtGeometry ]; - } - else - { - addedPoints = new Vertexes(); - } - if ( tVertex == -1 ) // adding first point if needed - { - tVertex = tVertexBackup; - } - else - { - trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) ); - if ( tVertex == tVertexFirst ) // cycle first vertex need to be added also - { - movingPoints->insert( movingPointIndex ); - } - movingPointIndex = 1; - topolGeometry->adjacentVertices( tVertex, tVertexAfter, tVertex ); - } - - while ( tVertex != -1 ) - { - // if closest vertex is not from selected feature or is not selected end - double dist; - QgsPoint point = topolGeometry->vertexAt( tVertex ); - int at, before, after; - geometry->closestVertex( point, at, before, after, dist ); - // find first no matching vertex - if ( dist > ZERO_TOLERANCE || !vertexMap[at]->isSelected() ) // problem with double precision - { - trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) ); - break; // found first vertex - } - else // add moving point to rubberband - { - if ( addedPoints->contains( tVertex ) ) - break; // just preventing to circle - trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) ); - movingPoints->insert( movingPointIndex ); - movingPointIndex++; - addedPoints->insert( tVertex ); - } - topolGeometry->adjacentVertices( tVertex, tVertexAfter, tVertex ); - } - mTopologyMovingVertexes.insert( resultIt.value().snappedAtGeometry, addedPoints ); - mTopologyRubberBandVertexes.insert( rbId, movingPoints ); - } - } -} - -void QgsMapToolNodeTool::canvasMoveEvent( QMouseEvent * e ) -{ - if ( !mSelectedFeature || !mClicked ) return; - - QgsVectorLayer* vlayer = mSelectedFeature->vlayer(); - Q_ASSERT( vlayer ); - - mSelectAnother = false; - - if ( mMoving ) - { - // create rubberband, if none exists - if ( mRubberBands.empty() ) - { - if ( mIsPoint ) - { - QList &vertexMap = mSelectedFeature->vertexMap(); - for ( int i = 0; i < vertexMap.size(); i++ ) - { - if ( vertexMap[i]->isSelected() ) - { - QgsRubberBand* rb = createRubberBandMarker( vertexMap[i]->point(), vlayer ); - mRubberBands.append( rb ); - } - } - } - createMovingRubberBands(); - - mPosMapCoordBackup = toMapCoordinates( e->pos() ); - } - else - { - // move rubberband - QgsPoint posMapCoord, pressMapCoords; - QgsExcludePointFilter excludePointFilter( mClosestMapVertex ); - QgsPointLocator::Match match = mCanvas->snappingUtils()->snapToMap( e->pos(), &excludePointFilter ); - if ( match.isValid() ) - { - posMapCoord = match.point(); - pressMapCoords = mClosestMapVertex; - } - else - { - posMapCoord = toMapCoordinates( e->pos() ); - pressMapCoords = toMapCoordinates( mPressCoordinates ); - } - - QgsVector offset = posMapCoord - pressMapCoords; - - // handle points - if ( mIsPoint ) - { - for ( int i = 0; i < mRubberBands.size(); i++ ) - { - mRubberBands[i]->setTranslationOffset( offset.x(), offset.y() ); - } - return; - } - - // move points - QList &vertexMap = mSelectedFeature->vertexMap(); - for ( int i = 0; i < vertexMap.size(); i++ ) - { - if ( !vertexMap[i]->isSelected() ) - continue; - - QgsPoint p = toMapCoordinates( vlayer, vertexMap[i]->point() ) + offset; - - mRubberBands[vertexMap[i]->rubberBandNr()]->movePoint( vertexMap[i]->rubberBandIndex(), p ); - - if ( vertexMap[i]->rubberBandIndex() == 0 ) - { - mRubberBands[vertexMap[i]->rubberBandNr()]->movePoint( 0, p ); - } - } - - // topological editing - offset = posMapCoord - mPosMapCoordBackup; - for ( int i = 0; i < mTopologyRubberBand.size(); i++ ) - { - for ( int pointIndex = 0; pointIndex < mTopologyRubberBand[i]->numberOfVertices(); pointIndex++ ) - { - if ( mTopologyRubberBandVertexes[i]->contains( pointIndex ) ) - { - const QgsPoint* point = mTopologyRubberBand[i]->getPoint( 0, pointIndex ); - if ( point == 0 ) - { - break; - } - mTopologyRubberBand[i]->movePoint( pointIndex, *point + offset ); - } - } - } - - mPosMapCoordBackup = posMapCoord; - } } - else + + bool ctrlModifier = e->modifiers() & Qt::ControlModifier; + QList snapResults; + + QgsFeatureId bkFeatureId = 0; + if ( mSelectedFeature ) { - if ( !mSelectionRectangle ) - { - mSelectionRectangle = true; - mSelectionRubberBand = new QRubberBand( QRubberBand::Rectangle, mCanvas ); - mRect = new QRect(); - mRect->setTopLeft( mPressCoordinates ); - } - mRect->setBottomRight( e->pos() ); - QRect normalizedRect = mRect->normalized(); - mSelectionRubberBand->setGeometry( normalizedRect ); - mSelectionRubberBand->show(); + bkFeatureId = mSelectedFeature->featureId(); } -} -void QgsMapToolNodeTool::canvasPressEvent( QMouseEvent * e ) -{ - QgsDebugCall; + bool hasVertexSelection = mSelectedFeature && mSelectedFeature->hasSelection(); - mClicked = true; - mPressCoordinates = e->pos(); - if ( !mSelectedFeature ) + if ( !mSelectedFeature || !hasVertexSelection ) { + //try to select feature QgsVectorLayer *vlayer = qobject_cast( mCanvas->currentLayer() ); if ( !vlayer ) + { return; + } - mSelectAnother = false; - QgsPointLocator::Match m = mCanvas->snappingUtils()->snapToCurrentLayer( e->pos(), QgsPointLocator::Vertex | QgsPointLocator::Edge ); - if ( !m.isValid() ) + 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; @@ -391,89 +87,95 @@ void QgsMapToolNodeTool::canvasPressEvent( QMouseEvent * e ) // 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; + if ( !mSelectedFeature || snapResults[0].snappedAtGeometry != mSelectedFeature->featureId() ) + { + delete mSelectedFeature; + 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( mSelectedFeature, SIGNAL( lastVertexChanged( const QgsPointV2& ) ), this, SLOT( changeLastVertex( const QgsPointV2& ) ) ); + connect( vlayer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) ); + mIsPoint = vlayer->geometryType() == QGis::Point; + mNodeEditor = new QgsNodeEditor( vlayer, mSelectedFeature, mCanvas ); + QgisApp::instance()->addDockWidget( Qt::LeftDockWidgetArea, mNodeEditor ); + } + } + + //select or move vertices if selected feature has not been changed + QgsPoint layerPoint = toLayerCoordinates( vlayer, e->mapPoint() ); + if ( mSelectedFeature->featureId() == bkFeatureId ) + { + if ( mSelectedFeature->hasSelection() && !ctrlModifier ) //move vertices + { + QgsPoint targetCoords = layerPoint; + mSelectedFeature->moveSelectedVertexes( targetCoords - mClosestLayerVertex ); + mCanvas->refresh(); + mSelectedFeature->deselectAllVertexes(); + } + else //add vertex selection + { + int atVertex, beforeVertex, afterVertex; + double dist; + QgsPoint closestLayerVertex = mSelectedFeature->geometry()->closestVertex( layerPoint, atVertex, beforeVertex, afterVertex, dist ); + mSelectedFeature->selectVertex( atVertex ); + } + } +} + +void QgsMapToolNodeTool::canvasMapMoveEvent( QgsMapMouseEvent* e ) +{ + if ( !mSelectedFeature || !mSelectedFeature->hasSelection() ) + { + return; + } + + QgsVectorLayer* vlayer = mSelectedFeature->vlayer(); + if ( !vlayer ) + { + return; + } + + if ( mMoveRubberBands.empty() ) + { + QgsGeometryRubberBand* rb = new QgsGeometryRubberBand( mCanvas, mSelectedFeature->geometry()->type() ); + rb->setOutlineColor( Qt::blue ); + rb->setBrushStyle( Qt::NoBrush ); + rb->setOutlineWidth( 2 ); + QgsAbstractGeometryV2* rbGeom = mSelectedFeature->geometry()->geometry()->clone(); + if ( mCanvas->mapSettings().layerTransform( vlayer ) ) + rbGeom->transform( *mCanvas->mapSettings().layerTransform( vlayer ) ); + rb->setGeometry( rbGeom ); + mMoveRubberBands.insert( mSelectedFeature->featureId(), rb ); + foreach ( const QgsVertexEntry* vertexEntry, mSelectedFeature->vertexMap() ) + { + if ( vertexEntry->isSelected() ) + mMoveVertices[mSelectedFeature->featureId()].append( qMakePair( vertexEntry->vertexId(), toMapCoordinates( vlayer, vertexEntry->point() ) ) ); + } + if ( QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ) ) + { + createTopologyRubberBands(); + } } else { - // remove previous warning - emit messageDiscarded(); + // move rubberband + QList snapResults; + mSnapper.snapToBackgroundLayers( e->pos(), snapResults, QList() << mClosestLayerVertex ); - Q_ASSERT( mSelectedFeature->vlayer() ); + QgsPoint origPos = toMapCoordinates( vlayer, mClosestLayerVertex ); + QgsPoint curPos = snapPointFromResults( snapResults, e->pos() ); + double diffX = curPos.x() - origPos.x(); + double diffY = curPos.y() - origPos.y(); - // try to find a piece of currently selected geometry - QgsFeatureIdFilter filterFid( mSelectedFeature->featureId() ); - QgsPointLocator::Match mSel = mCanvas->snappingUtils()->snapToCurrentLayer( e->pos(), QgsPointLocator::Vertex | QgsPointLocator::Edge, &filterFid ); - - if ( mSel.hasVertex() ) + foreach ( const QgsFeatureId& fid, mMoveRubberBands.keys() ) { - // mouse pressed on a vertex: - // - if clicked on already selected vertex - deselect it - // - if clicked on vertex that is not selected - select it - // - if clicked with CTRL - invert selection state of the vertex - // - if pressed+dragging on already selected vertex - will move selected vertices - - mMoving = true; - mClosestMapVertex = mSel.point(); - int atVertex = mSel.vertexIndex(); - - if ( mSelectedFeature->isSelected( atVertex ) ) + typedef QPair MoveVertex; + foreach ( const MoveVertex& pair, mMoveVertices[fid] ) { - mDeselectOnRelease = atVertex; - } - else if ( mCtrl ) - { - mSelectedFeature->invertVertexSelection( atVertex ); - } - else - { - mSelectedFeature->deselectAllVertexes(); - mSelectedFeature->selectVertex( atVertex ); - } - } - else if ( mSel.hasEdge() ) - { - // mouse pressed on an edge: - // - if clicked - select just vertices of that edge - // - if clicked with CTRL - invert selection state of vertices of the edge - // - if pressed+dragging - will move vertices of the edge - - mMoving = true; - QgsPoint p1, p2; - mSel.edgePoints( p1, p2 ); - mClosestMapVertex = p1.sqrDist( mSel.point() ) < p2.sqrDist( mSel.point() ) ? p1 : p2; - - if ( !mCtrl ) - { - mSelectedFeature->deselectAllVertexes(); - mSelectedFeature->selectVertex( mSel.vertexIndex() + 1 ); - mSelectedFeature->selectVertex( mSel.vertexIndex() ); - } - else - { - mSelectedFeature->invertVertexSelection( mSel.vertexIndex() + 1 ); - mSelectedFeature->invertVertexSelection( mSel.vertexIndex() ); - } - - } - else - { - // nothing from the feature is acceptable: - // - if clicked - try to select a different feature. if nothing is around, at least deselect all vertices - - QgsPointLocator::Match m = mCanvas->snappingUtils()->snapToCurrentLayer( e->pos(), QgsPointLocator::Vertex | QgsPointLocator::Edge ); - if ( m.isValid() ) - { - // if this will be just a click, on release we will select this new feature - mAnother = m.featureId(); - mSelectAnother = true; - } - else if ( !mCtrl ) - { - mSelectedFeature->deselectAllVertexes(); + QgsPointV2 pos = pair.second; + pos.setX( pos.x() + diffX ); + pos.setY( pos.y() + diffY ); + mMoveRubberBands.value( fid )->moveVertex( pair.first, pos ); } } } @@ -498,160 +200,15 @@ void QgsMapToolNodeTool::editingToggled() cleanTool(); } -void QgsMapToolNodeTool::canvasReleaseEvent( QMouseEvent * e ) -{ - if ( !mSelectedFeature ) - return; - - removeRubberBands(); - - QgsVectorLayer *vlayer = mSelectedFeature->vlayer(); - Q_ASSERT( vlayer ); - - mClicked = false; - mSelectionRectangle = false; - - if ( mSelectionRubberBand ) - { - mSelectionRubberBand->close(); - delete mSelectionRubberBand; - mSelectionRubberBand = 0; - } - - if ( mRect ) - { - delete mRect; - mRect = 0; - } - - if ( mPressCoordinates == e->pos() ) - { - if ( mSelectAnother ) - { - // select another feature - mSelectedFeature->setSelectedFeature( mAnother, vlayer, mCanvas ); - mIsPoint = vlayer->geometryType() == QGis::Point; - mSelectAnother = false; - } - } - else if ( mMoving ) - { - mMoving = false; - QgsPoint releaseMapCoords, pressMapCoords; - - QgsExcludePointFilter excludePointFilter( mClosestMapVertex ); - QgsPointLocator::Match match = mCanvas->snappingUtils()->snapToMap( e->pos(), &excludePointFilter ); - - if ( match.isValid() ) - { - releaseMapCoords = match.point(); - pressMapCoords = mClosestMapVertex; - } - else - { - releaseMapCoords = toMapCoordinates( e->pos() ); - pressMapCoords = toMapCoordinates( mPressCoordinates ); - } - - QgsPoint releaseLayerCoords = toLayerCoordinates( vlayer, releaseMapCoords ); - QgsPoint pressLayerCoords = toLayerCoordinates( vlayer, pressMapCoords ); - - if ( match.isValid() ) - { - int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); - if ( topologicalEditing ) - { - addTopologicalPoints( QList() << releaseMapCoords ); - } - } - - mSelectedFeature->moveSelectedVertexes( releaseLayerCoords - pressLayerCoords ); - mCanvas->refresh(); - } - else // selecting vertexes by rubberband - { - // coordinates has to be coordinates from layer not canvas - QgsRectangle r( toLayerCoordinates( vlayer, mPressCoordinates ), - toLayerCoordinates( vlayer, e->pos() ) ); - - QList &vertexMap = mSelectedFeature->vertexMap(); - if ( !mCtrl ) - { - mSelectedFeature->deselectAllVertexes(); - } - - for ( int i = 0; i < vertexMap.size(); i++ ) - { - if ( r.contains( vertexMap[i]->point() ) ) - { - // inverting selection is enough because all were deselected if ctrl is not pressed - mSelectedFeature->invertVertexSelection( i, false ); - } - } - } - - mMoving = false; - - if ( mDeselectOnRelease != -1 ) - { - if ( mCtrl ) - { - mSelectedFeature->invertVertexSelection( mDeselectOnRelease ); - } - else - { - mSelectedFeature->deselectAllVertexes(); - mSelectedFeature->selectVertex( mDeselectOnRelease ); - } - - mDeselectOnRelease = -1; - } - - mExcludePoint.clear(); -} - void QgsMapToolNodeTool::deactivate() { cleanTool(); - delete mRect; - mRect = 0; - delete mSelectionRubberBand; - mSelectionRubberBand = 0; - mSelectAnother = false; - mCtrl = false; - mMoving = true; - mClicked = false; - QgsMapTool::deactivate(); } -void QgsMapToolNodeTool::removeRubberBands() -{ - // cleanup rubberbands and list - foreach ( QgsRubberBand *rb, mRubberBands ) - { - delete rb; - } - mRubberBands.clear(); - - foreach ( QgsRubberBand *rb, mTopologyRubberBand ) - { - delete rb; - } - mTopologyRubberBand.clear(); - - mTopologyMovingVertexes.clear(); - mTopologyRubberBandVertexes.clear(); - - // remove all data from selected feature (no change to rubberbands itself) - if ( mSelectedFeature ) - mSelectedFeature->cleanRubberBandsData(); -} - void QgsMapToolNodeTool::cleanTool( bool deleteSelectedFeature ) { removeRubberBands(); - if ( mSelectedFeature ) { QgsVectorLayer *vlayer = mSelectedFeature->vlayer(); @@ -664,6 +221,11 @@ void QgsMapToolNodeTool::cleanTool( bool deleteSelectedFeature ) if ( deleteSelectedFeature ) delete mSelectedFeature; mSelectedFeature = 0; } + if ( mNodeEditor ) + { + delete mNodeEditor; + mNodeEditor = 0; + } } void QgsMapToolNodeTool::canvasDoubleClickEvent( QMouseEvent * e ) @@ -677,13 +239,16 @@ void QgsMapToolNodeTool::canvasDoubleClickEvent( QMouseEvent * e ) int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); QMultiMap currentResultList; - mMoving = false; - QgsPointLocator::Match m = mCanvas->snappingUtils()->snapToCurrentLayer( e->pos(), QgsPointLocator::Edge ); - if ( !m.isValid() || m.featureId() != mSelectedFeature->featureId() ) + QList snapResults; + double tol = QgsTolerance::vertexSearchRadius( vlayer, mCanvas->mapSettings() ); + mSnapper.snapToCurrentLayer( e->pos(), snapResults, QgsSnapper::SnapToSegment, tol ); + if ( snapResults.size() < 1 || + snapResults.first().snappedAtGeometry != mSelectedFeature->featureId() || + snapResults.first().snappedVertexNr != -1 ) return; // some segment selected - QgsPoint layerCoords = toLayerCoordinates( vlayer, m.point() ); + QgsPoint layerCoords = toLayerCoordinates( vlayer, snapResults.first().snappedVertex ); if ( topologicalEditing ) { // snap from adding position to this vertex when topological editing is enabled @@ -694,7 +259,7 @@ void QgsMapToolNodeTool::canvasDoubleClickEvent( QMouseEvent * e ) vlayer->beginEditCommand( tr( "Inserted vertex" ) ); // add vertex - vlayer->insertVertex( layerCoords.x(), layerCoords.y(), mSelectedFeature->featureId(), m.vertexIndex() + 1 ); + vlayer->insertVertex( layerCoords.x(), layerCoords.y(), mSelectedFeature->featureId(), snapResults.first().afterVertexNr ); if ( topologicalEditing ) { @@ -716,12 +281,6 @@ void QgsMapToolNodeTool::canvasDoubleClickEvent( QMouseEvent * e ) void QgsMapToolNodeTool::keyPressEvent( QKeyEvent* e ) { - if ( e->key() == Qt::Key_Control ) - { - mCtrl = true; - return; - } - if ( mSelectedFeature && ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete ) ) { int firstSelectedIndex = firstSelectedVertex(); @@ -743,6 +302,7 @@ void QgsMapToolNodeTool::keyPressEvent( QKeyEvent* e ) mSelectedFeature->deselectAllVertexes(); safeSelectVertex( firstSelectedIndex - 1 ); + e->ignore(); } else if ( mSelectedFeature && ( e->key() == Qt::Key_Greater || e->key() == Qt::Key_Period ) ) { @@ -752,62 +312,125 @@ void QgsMapToolNodeTool::keyPressEvent( QKeyEvent* e ) mSelectedFeature->deselectAllVertexes(); safeSelectVertex( firstSelectedIndex + 1 ); + e->ignore(); } } -void QgsMapToolNodeTool::keyReleaseEvent( QKeyEvent* e ) -{ - if ( e->key() == Qt::Key_Control ) - { - mCtrl = false; - return; - } -} - -QgsRubberBand* QgsMapToolNodeTool::createRubberBandMarker( QgsPoint center, QgsVectorLayer* vlayer ) -{ - - // create rubberband marker for moving points - QgsRubberBand* marker = new QgsRubberBand( mCanvas, QGis::Point ); - marker->setColor( Qt::red ); - marker->setWidth( 2 ); - marker->setIcon( QgsRubberBand::ICON_FULL_BOX ); - marker->setIconSize( 8 ); - QgsPoint pom = toMapCoordinates( vlayer, center ); - marker->addPoint( pom ); - return marker; -} - -int QgsMapToolNodeTool::firstSelectedVertex() +int QgsMapToolNodeTool::firstSelectedVertex( ) { if ( mSelectedFeature ) { QList &vertexMap = mSelectedFeature->vertexMap(); - int vertexNr = 0; - - foreach ( QgsVertexEntry *entry, vertexMap ) + for ( int i = 0, n = vertexMap.size(); i < n; ++i ) { - if ( entry->isSelected() ) + if ( vertexMap[i]->isSelected() ) { - return vertexNr; + return i; } - vertexNr++; } } return -1; } -int QgsMapToolNodeTool::safeSelectVertex( int vertexNr ) +void QgsMapToolNodeTool::safeSelectVertex( int vertexNr ) { if ( mSelectedFeature ) { - QList &vertexMap = mSelectedFeature->vertexMap(); - - if ( vertexNr >= vertexMap.size() ) vertexNr -= vertexMap.size(); - if ( vertexNr < 0 ) vertexNr = vertexMap.size() - 1 + vertexNr; - - mSelectedFeature->selectVertex( vertexNr ); - return vertexNr; + int n = mSelectedFeature->vertexMap().size(); + mSelectedFeature->selectVertex(( vertexNr + n ) % n ); + } +} + +QgsPoint QgsMapToolNodeTool::snapPointFromResults( const QList& snapResults, const QPoint& screenCoords ) +{ + if ( snapResults.size() < 1 ) + { + return toMapCoordinates( screenCoords ); + } + else + { + return snapResults.constBegin()->snappedVertex; + } +} + +int QgsMapToolNodeTool::insertSegmentVerticesForSnap( const QList& snapResults, QgsVectorLayer* editedLayer ) +{ + QgsPoint layerPoint; + + if ( !editedLayer || !editedLayer->isEditable() ) + { + return 1; + } + + //transform snaping coordinates to layer crs first + QList transformedSnapResults = snapResults; + QList::iterator it = transformedSnapResults.begin(); + for ( ; it != transformedSnapResults.constEnd(); ++it ) + { + QgsPoint layerPoint = toLayerCoordinates( editedLayer, it->snappedVertex ); + it->snappedVertex = layerPoint; + } + + return editedLayer->insertSegmentVerticesForSnap( transformedSnapResults ); +} + +void QgsMapToolNodeTool::changeLastVertex( const QgsPointV2& pt ) +{ + mClosestLayerVertex.setX( pt.x() ); + mClosestLayerVertex.setY( pt.y() ); +} + +void QgsMapToolNodeTool::removeRubberBands() +{ + qDeleteAll( mMoveRubberBands ); + mMoveRubberBands.clear(); + mMoveVertices.clear(); +} + +void QgsMapToolNodeTool::createTopologyRubberBands() +{ + QgsVectorLayer* vlayer = mSelectedFeature->vlayer(); + + foreach ( const QgsVertexEntry* vertexEntry, mSelectedFeature->vertexMap() ) + { + if ( !vertexEntry->isSelected() ) + { + continue; + } + + // Snap vertex + QMultiMap snapResults; + vlayer->snapWithContext( vertexEntry->pointV1(), ZERO_TOLERANCE, snapResults, QgsSnapper::SnapToVertex ); + foreach ( const QgsSnappingResult& snapResult, snapResults.values() ) + { + // Get geometry of snapped feature + QgsFeatureId snapFeatureId = snapResult.snappedAtGeometry; + QgsFeature feature; + if ( !vlayer->getFeatures( QgsFeatureRequest( snapFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( feature ) ) + { + continue; + } + // Get VertexId of snapped vertex + QgsVertexId vid; + if ( !feature.constGeometry()->vertexIdFromVertexNr( snapResult.snappedVertexNr, vid ) ) + { + continue; + } + // Add rubberband if not already added + if ( !mMoveRubberBands.contains( snapFeatureId ) ) + { + QgsGeometryRubberBand* rb = new QgsGeometryRubberBand( mCanvas, feature.constGeometry()->type() ); + rb->setOutlineColor( Qt::blue ); + rb->setBrushStyle( Qt::NoBrush ); + rb->setOutlineWidth( 2 ); + QgsAbstractGeometryV2* rbGeom = feature.constGeometry()->geometry()->clone(); + if ( mCanvas->mapSettings().layerTransform( vlayer ) ) + rbGeom->transform( *mCanvas->mapSettings().layerTransform( vlayer ) ); + rb->setGeometry( rbGeom ); + mMoveRubberBands.insert( snapFeatureId, rb ); + } + // Add to list of vertices to be moved + mMoveVertices[snapFeatureId].append( qMakePair( vid, toMapCoordinates( vlayer, feature.constGeometry()->geometry()->vertexAt( vid ) ) ) ); + } } - return -1; } diff --git a/src/app/nodetool/qgsmaptoolnodetool.h b/src/app/nodetool/qgsmaptoolnodetool.h index 63f11120561..d76487865ee 100644 --- a/src/app/nodetool/qgsmaptoolnodetool.h +++ b/src/app/nodetool/qgsmaptoolnodetool.h @@ -16,22 +16,17 @@ #ifndef QGSMAPTOOLNODETOOL_H #define QGSMAPTOOLNODETOOL_H -#include "qgsfeature.h" #include "qgsmaptooledit.h" -#include "qgspoint.h" +#include "qgsmapcanvassnapper.h" class QRubberBand; -class QgsRubberBand; +class QgsGeometryRubberBand; class QgsVertexEntry; class QgsSelectedFeature; +class QgsNodeEditor; -/** - * Set representing set of vertex numbers - */ -typedef QSet Vertexes; - -/**A maptool to move/deletes/adds vertices of line or polygon features*/ +/** A maptool to move/deletes/adds vertices of line or polygon features*/ class QgsMapToolNodeTool: public QgsMapToolEdit { Q_OBJECT @@ -39,20 +34,18 @@ class QgsMapToolNodeTool: public QgsMapToolEdit QgsMapToolNodeTool( QgsMapCanvas* canvas ); virtual ~QgsMapToolNodeTool(); - void canvasMoveEvent( QMouseEvent * e ) override; + void canvasDoubleClickEvent( QMouseEvent * e ); - void canvasDoubleClickEvent( QMouseEvent * e ) override; + //! mouse press event in map coordinates (eventually filtered) to be redefined in subclass + void canvasMapPressEvent( QgsMapMouseEvent* e ) override; - void canvasPressEvent( QMouseEvent * e ) override; + //! mouse move event in map coordinates (eventually filtered) to be redefined in subclass + void canvasMapMoveEvent( QgsMapMouseEvent* e ) override; - void canvasReleaseEvent( QMouseEvent * e ) override; - - void keyPressEvent( QKeyEvent* e ) override; - - void keyReleaseEvent( QKeyEvent* e ) override; + void keyPressEvent( QKeyEvent* e ); //! called when map tool is being deactivated - void deactivate() override; + void deactivate(); public slots: void selectedFeatureDestroyed(); @@ -67,25 +60,24 @@ class QgsMapToolNodeTool: public QgsMapToolEdit */ void editingToggled(); + void changeLastVertex( const QgsPointV2& pt ); + private: /** * Deletes the rubber band pointers and clears mRubberBands */ void removeRubberBands(); + /** + * Creates rubber bands for ther features when topology editing is enabled + */ + void createTopologyRubberBands(); + /** * Disconnects signals and clears objects */ void cleanTool( bool deleteSelectedFeature = true ); - /** - * Creating rubber band marker for movin of point - * @param center coordinates of point to be moved - * @param vlayer vector layer on which we are working - * @return rubber band marker - */ - QgsRubberBand* createRubberBandMarker( QgsPoint center, QgsVectorLayer* vlayer ); - /** * Function to check if selected feature exists and is same with original one * stored in internal structures @@ -94,19 +86,6 @@ class QgsMapToolNodeTool: public QgsMapToolEdit */ bool checkCorrectnessOfFeature( QgsVectorLayer* vlayer ); - /** - * Creates rubberbands for moving points - */ - void createMovingRubberBands(); - - /** - * Creates rubber bands for ther features when topology editing is enabled - * @param vlayer vector layer for ehich rubber bands are created - * @param vertexMap map of vertexes - * @param vertex currently processed vertex - */ - void createTopologyRubberBands( QgsVectorLayer* vlayer, const QList &vertexMap, int vertex ); - /** * Returns the index of first selected vertex, -1 when all unselected */ @@ -115,64 +94,52 @@ class QgsMapToolNodeTool: public QgsMapToolEdit /** * Select the specified vertex bounded to current index range, returns the valid selected index */ - int safeSelectVertex( int vertexNr ); + void safeSelectVertex( int vertexNr ); - /** The position of the vertex to move (in map coordinates) to exclude later from snapping*/ - QList mExcludePoint; + /** Extracts a single snapping point from a set of snapping results. + This is useful for snapping operations that just require a position to snap to and not all the + snapping results. If the list is empty, the screen coordinates are transformed into map coordinates and returned + @param snapResults results collected from the snapping operation. + @return the snapped point in map coordinates*/ + QgsPoint snapPointFromResults( const QList& snapResults, const QPoint& screenCoords ); - /** rubber bands */ - QList mRubberBands; + /** Inserts vertices to the snapped segments of the editing layer. + This is useful for topological editing if snap to segment is enabled. + @param snapResults results collected from the snapping operation + @param editedLayer pointer to the editing layer + @return 0 in case of success*/ + int insertSegmentVerticesForSnap( const QList& snapResults, QgsVectorLayer* editedLayer ); - /** list of topology rubber bands */ - QList mTopologyRubberBand; + /** Snapper object that reads the settings from project and option + and applies it to the map canvas*/ + QgsMapCanvasSnapper mSnapper; - /** vertexes of rubberbands which are to be moved */ - QMap mTopologyMovingVertexes; - - /** vertexes of features with int id which were already added tu rubber bands */ - QMap mTopologyRubberBandVertexes; - - /** object containing selected feature and it's vertexes */ + /** Object containing selected feature and it's vertexes */ QgsSelectedFeature *mSelectedFeature; - /** flag if selection rectangle is active */ - bool mSelectionRectangle; + /** Dock widget which allows to edit vertices */ + QgsNodeEditor* mNodeEditor; - /** flag if moving of vertexes is occuring */ - bool mMoving; - - /** flag if click action is still in queue to be processed */ - bool mClicked; - - /** flag if crtl is pressed */ - bool mCtrl; - - /** flag if selection of another feature can occur */ - bool mSelectAnother; - - /** feature id of another feature where user clicked */ + /** Feature id of another feature where user clicked */ QgsFeatureId mAnother; - /** stored position of last press down action to count how much vertexes should be moved */ + /** Stored position of last press down action to count how much vertexes should be moved */ QPoint mPressCoordinates; - /** closest vertex to click in map coordinates */ - QgsPoint mClosestMapVertex; + /** Closest vertex to click in map coordinates */ + QgsPoint mClosestLayerVertex; - /** backup of map coordinates to be able to count change between moves */ - QgsPoint mPosMapCoordBackup; - - /** active rubberband for selecting vertexes */ - QRubberBand *mSelectionRubberBand; - - /** rectangle defining area for selecting vertexes */ + /** Rectangle defining area for selecting vertexes */ QRect* mRect; - /** flag to tell if edition points */ + /** Flag to tell if edition points */ bool mIsPoint; - /** vertex to deselect on release */ - int mDeselectOnRelease; + /** Rubber bands during node move */ + QMap mMoveRubberBands; + + /** Vertices of features to move */ + QMap > > mMoveVertices; }; #endif diff --git a/src/app/nodetool/qgsnodeeditor.cpp b/src/app/nodetool/qgsnodeeditor.cpp new file mode 100644 index 00000000000..a7c48e6a78b --- /dev/null +++ b/src/app/nodetool/qgsnodeeditor.cpp @@ -0,0 +1,238 @@ +/*************************************************************************** + qgsnodeeditor.cpp + ----------------- + begin : Tue Mar 24 2015 + copyright : (C) 2015 Sandro Mani / Sourcepole AG + email : smani@sourcepole.ch + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsnodeeditor.h" +#include "qgsmapcanvas.h" +#include "qgsselectedfeature.h" +#include "qgsvertexentry.h" +#include "qgsvectorlayer.h" +#include "qgsgeometryutils.h" + +#include +#include +#include +#include +#include +#include + +static const int MinRadiusRole = Qt::UserRole + 1; + + +class CoordinateItemDelegate : public QStyledItemDelegate +{ + public: + QString displayText( const QVariant & value, const QLocale & locale ) const + { + return locale.toString( value.toDouble(), 'f', 4 ); + } + + protected: + QWidget* createEditor( QWidget * parent, const QStyleOptionViewItem & /*option*/, const QModelIndex & index ) const + { + QLineEdit* lineEdit = new QLineEdit( parent ); + QDoubleValidator* validator = new QDoubleValidator(); + if ( !index.data( MinRadiusRole ).isNull() ) + validator->setBottom( index.data( MinRadiusRole ).toDouble() ); + lineEdit->setValidator( validator ); + return lineEdit; + } + void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const + { + QLineEdit* lineEdit = qobject_cast( editor ); + if ( lineEdit->hasAcceptableInput() ) + { + QStyledItemDelegate::setModelData( editor, model, index ); + } + } +}; + + +QgsNodeEditor::QgsNodeEditor( + QgsVectorLayer *layer, + QgsSelectedFeature *selectedFeature, + QgsMapCanvas *canvas ) +{ + setWindowTitle( tr( "Vertex editor" ) ); + setFeatures( features() ^ QDockWidget::DockWidgetClosable ); + + mLayer = layer; + mSelectedFeature = selectedFeature; + mCanvas = canvas; + + mTableWidget = new QTableWidget( 0, 6, this ); + mTableWidget->setHorizontalHeaderLabels( QStringList() << "id" << "x" << "y" << "z" << "m" << "r" ); + mTableWidget->setSelectionMode( QTableWidget::ExtendedSelection ); + mTableWidget->setSelectionBehavior( QTableWidget::SelectRows ); + mTableWidget->verticalHeader()->hide(); + mTableWidget->horizontalHeader()->setResizeMode( 1, QHeaderView::Stretch ); + mTableWidget->horizontalHeader()->setResizeMode( 2, QHeaderView::Stretch ); + mTableWidget->horizontalHeader()->setResizeMode( 3, QHeaderView::Stretch ); + mTableWidget->horizontalHeader()->setResizeMode( 4, QHeaderView::Stretch ); + mTableWidget->horizontalHeader()->setResizeMode( 5, QHeaderView::Stretch ); + mTableWidget->setItemDelegateForColumn( 1, new CoordinateItemDelegate() ); + mTableWidget->setItemDelegateForColumn( 2, new CoordinateItemDelegate() ); + mTableWidget->setItemDelegateForColumn( 3, new CoordinateItemDelegate() ); + mTableWidget->setItemDelegateForColumn( 4, new CoordinateItemDelegate() ); + mTableWidget->setItemDelegateForColumn( 5, new CoordinateItemDelegate() ); + + setWidget( mTableWidget ); + + connect( mSelectedFeature, SIGNAL( selectionChanged() ), this, SLOT( updateTableSelection() ) ); + connect( mSelectedFeature, SIGNAL( vertexMapChanged() ), this, SLOT( rebuildTable() ) ); + connect( mTableWidget, SIGNAL( itemSelectionChanged() ), this, SLOT( updateNodeSelection() ) ); + connect( mTableWidget, SIGNAL( cellChanged( int, int ) ), this, SLOT( tableValueChanged( int, int ) ) ); + + rebuildTable(); +} + +void QgsNodeEditor::rebuildTable() +{ + QFont curvePointFont = mTableWidget->font(); + curvePointFont.setItalic( true ); + + mTableWidget->blockSignals( true ); + mTableWidget->setRowCount( 0 ); + int row = 0; + bool hasR = false; + foreach ( const QgsVertexEntry* entry, mSelectedFeature->vertexMap() ) + { + mTableWidget->insertRow( row ); + + QTableWidgetItem* idItem = new QTableWidgetItem(); + idItem->setData( Qt::DisplayRole, row ); + idItem->setFlags( idItem->flags() ^ Qt::ItemIsEditable ); + mTableWidget->setItem( row, 0, idItem ); + + QTableWidgetItem* xItem = new QTableWidgetItem(); + xItem->setData( Qt::EditRole, entry->point().x() ); + mTableWidget->setItem( row, 1, xItem ); + + QTableWidgetItem* yItem = new QTableWidgetItem(); + yItem->setData( Qt::EditRole, entry->point().y() ); + mTableWidget->setItem( row, 2, yItem ); + + QTableWidgetItem* zItem = new QTableWidgetItem(); + zItem->setData( Qt::EditRole, entry->point().z() ); + mTableWidget->setItem( row, 3, zItem ); + + QTableWidgetItem* mItem = new QTableWidgetItem(); + mItem->setData( Qt::EditRole, entry->point().m() ); + mTableWidget->setItem( row, 4, mItem ); + + QTableWidgetItem* rItem = new QTableWidgetItem(); + mTableWidget->setItem( row, 5, rItem ); + + bool curvePoint = ( entry->vertexId().type == QgsVertexId::CurveVertex ); + if ( curvePoint ) + { + idItem->setFont( curvePointFont ); + xItem->setFont( curvePointFont ); + yItem->setFont( curvePointFont ); + zItem->setFont( curvePointFont ); + mItem->setFont( curvePointFont ); + rItem->setFont( curvePointFont ); + + const QgsPointV2& p1 = mSelectedFeature->vertexMap()[row - 1]->point(); + const QgsPointV2& p2 = mSelectedFeature->vertexMap()[row]->point(); + const QgsPointV2& p3 = mSelectedFeature->vertexMap()[row + 1]->point(); + + double r, cx, cy; + QgsGeometryUtils::circleCenterRadius( p1, p2, p3, r, cx, cy ); + rItem->setData( Qt::EditRole, r ); + + double x13 = p3.x() - p1.x(), y13 = p3.y() - p1.y(); + rItem->setData( MinRadiusRole, 0.5 * qSqrt( x13 * x13 + y13 * y13 ) ); + + hasR = true; + } + else + { + rItem->setFlags( rItem->flags() & ~( Qt::ItemIsSelectable | Qt::ItemIsEnabled ) ); + } + + ++row; + } + mTableWidget->setColumnHidden( 3, !mSelectedFeature->vertexMap()[0]->point().is3D() ); + mTableWidget->setColumnHidden( 4, !mSelectedFeature->vertexMap()[0]->point().isMeasure() ); + mTableWidget->setColumnHidden( 5, !hasR ); + mTableWidget->resizeColumnToContents( 0 ); + mTableWidget->blockSignals( false ); +} + +void QgsNodeEditor::tableValueChanged( int row, int col ) +{ + int nodeIdx = mTableWidget->item( row, 0 )->data( Qt::DisplayRole ).toInt(); + double x, y; + if ( col == 5 ) // radius modified + { + double r = mTableWidget->item( row, 5 )->data( Qt::EditRole ).toDouble(); + double x1 = mTableWidget->item( row - 1, 1 )->data( Qt::EditRole ).toDouble(); + double y1 = mTableWidget->item( row - 1, 2 )->data( Qt::EditRole ).toDouble(); + double x2 = mTableWidget->item( row , 1 )->data( Qt::EditRole ).toDouble(); + double y2 = mTableWidget->item( row , 2 )->data( Qt::EditRole ).toDouble(); + double x3 = mTableWidget->item( row + 1, 1 )->data( Qt::EditRole ).toDouble(); + double y3 = mTableWidget->item( row + 1, 2 )->data( Qt::EditRole ).toDouble(); + + QgsPointV2 result; + QgsGeometryUtils::segmentMidPoint( QgsPointV2( x1, y1 ), QgsPointV2( x3, y3 ), result, r, QgsPointV2( x2, y2 ) ); + x = result.x(); + y = result.y(); + } + else + { + x = mTableWidget->item( row, 1 )->data( Qt::EditRole ).toDouble(); + y = mTableWidget->item( row, 2 )->data( Qt::EditRole ).toDouble(); + } + double z = mTableWidget->item( row, 3 )->data( Qt::EditRole ).toDouble(); + double m = mTableWidget->item( row, 4 )->data( Qt::EditRole ).toDouble(); + QgsPointV2 p( QgsWKBTypes::PointZM, x, y, z, m ); + + mLayer->beginEditCommand( QObject::tr( "Moved vertices" ) ); + mLayer->moveVertex( p, mSelectedFeature->featureId(), nodeIdx ); + mLayer->endEditCommand(); + mCanvas->refresh(); +} + +void QgsNodeEditor::updateTableSelection() +{ + mTableWidget->blockSignals( true ); + mTableWidget->clearSelection(); + const QList& vertexMap = mSelectedFeature->vertexMap(); + for ( int i = 0, n = vertexMap.size(); i < n; ++i ) + { + if ( vertexMap[i]->isSelected() ) + { + mTableWidget->selectRow( i ); + } + } + mTableWidget->blockSignals( false ); +} + +void QgsNodeEditor::updateNodeSelection() +{ + disconnect( mSelectedFeature, SIGNAL( selectionChanged() ), this, SLOT( updateTableSelection() ) ); + + mSelectedFeature->deselectAllVertexes(); + foreach ( const QModelIndex& index, mTableWidget->selectionModel()->selectedRows() ) + { + int nodeIdx = mTableWidget->item( index.row(), 0 )->data( Qt::DisplayRole ).toInt(); + mSelectedFeature->selectVertex( nodeIdx ); + } + + connect( mSelectedFeature, SIGNAL( selectionChanged() ), this, SLOT( updateTableSelection() ) ); +} diff --git a/src/app/nodetool/qgsnodeeditor.h b/src/app/nodetool/qgsnodeeditor.h new file mode 100644 index 00000000000..7dc638efb75 --- /dev/null +++ b/src/app/nodetool/qgsnodeeditor.h @@ -0,0 +1,52 @@ +/*************************************************************************** + qgsnodeeditor.h + --------------- + begin : Tue Mar 24 2015 + copyright : (C) 2015 Sandro Mani / Sourcepole AG + email : smani@sourcepole.ch + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSNODEEDITOR_H +#define QGSNODEEDITOR_H + +#include + +class QgsMapCanvas; +class QgsRubberBand; +class QgsSelectedFeature; +class QgsVectorLayer; +class QTableWidget; + +/** A widget to select and edit the vertex coordinates of a geometry numerically*/ +class QgsNodeEditor : public QDockWidget +{ + Q_OBJECT + public: + QgsNodeEditor( QgsVectorLayer* layer, + QgsSelectedFeature* selectedFeature, + QgsMapCanvas* canvas ); + + public: + QgsVectorLayer* mLayer; + QgsSelectedFeature* mSelectedFeature; + QgsMapCanvas* mCanvas; + QTableWidget* mTableWidget; + + private slots: + void rebuildTable(); + void tableValueChanged( int row, int col ); + void updateTableSelection(); + void updateNodeSelection(); +}; + +#endif // QGSNODEEDITOR_H diff --git a/src/app/nodetool/qgsselectedfeature.cpp b/src/app/nodetool/qgsselectedfeature.cpp index ec8a0d1cf73..bb58b9367dd 100644 --- a/src/app/nodetool/qgsselectedfeature.cpp +++ b/src/app/nodetool/qgsselectedfeature.cpp @@ -16,6 +16,7 @@ #include "nodetool/qgsselectedfeature.h" #include "nodetool/qgsvertexentry.h" +#include "qgspointv2.h" #include #include #include @@ -32,7 +33,6 @@ QgsSelectedFeature::QgsSelectedFeature( QgsFeatureId featureId, : mFeatureId( featureId ) , mGeometry( 0 ) , mChangingGeometry( false ) - , mRubberBand( 0 ) , mValidator( 0 ) { QgsDebugCall; @@ -77,8 +77,10 @@ void QgsSelectedFeature::updateGeometry( QgsGeometry *geom ) if ( !geom ) { QgsFeature f; - mVlayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeatureId ) ).nextFeature( f ); - mGeometry = new QgsGeometry( *f.constGeometry() ); + if ( mVlayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeatureId ) ).nextFeature( f ) ) + { + mGeometry = new QgsGeometry( *f.constGeometry() ); + } } else { @@ -86,23 +88,12 @@ void QgsSelectedFeature::updateGeometry( QgsGeometry *geom ) } } -void QgsSelectedFeature::cleanRubberBandsData() -{ - for ( int i = 0; i < mVertexMap.size(); i++ ) - { - mVertexMap[i]->setRubberBandValues( false, 0, 0 ); - } -} - void QgsSelectedFeature::setSelectedFeature( QgsFeatureId featureId, QgsVectorLayer* vlayer, QgsMapCanvas* canvas ) { mFeatureId = featureId; mVlayer = vlayer; mCanvas = canvas; - delete mRubberBand; - mRubberBand = 0; - delete mGeometry; mGeometry = 0; @@ -262,18 +253,11 @@ void QgsSelectedFeature::deleteSelectedVertexes() { if ( mVertexMap[i]->isSelected() ) { - if ( mVertexMap[i]->equals() != -1 ) - { - // to avoid try to delete some vertex twice - mVertexMap[ mVertexMap[i]->equals()]->setSelected( false ); - nSelected--; - } - if ( topologicalEditing ) { // snap from current vertex currentResultList.clear(); - mVlayer->snapWithContext( mVertexMap[i]->point(), ZERO_TOLERANCE, currentResultList, QgsSnapper::SnapToVertex ); + mVlayer->snapWithContext( mVertexMap[i]->pointV1(), ZERO_TOLERANCE, currentResultList, QgsSnapper::SnapToVertex ); } // only last update should trigger the geometry update @@ -345,7 +329,7 @@ void QgsSelectedFeature::moveSelectedVertexes( const QgsVector &v ) { // snap from current vertex currentResultList.clear(); - mVlayer->snapWithContext( entry->point(), ZERO_TOLERANCE, currentResultList, QgsSnapper::SnapToVertex ); + mVlayer->snapWithContext( entry->pointV1(), ZERO_TOLERANCE, currentResultList, QgsSnapper::SnapToVertex ); } // only last update should trigger the geometry update @@ -353,8 +337,10 @@ void QgsSelectedFeature::moveSelectedVertexes( const QgsVector &v ) if ( --nUpdates == 0 ) endGeometryChange(); - QgsPoint p = entry->point() + v; - mVlayer->moveVertex( p.x(), p.y(), mFeatureId, i ); + QgsPointV2 p = entry->point(); + p.setX( p.x() + v.x() ); + p.setY( p.y() + v.y() ); + mVlayer->moveVertex( p, mFeatureId, i ); if ( topologicalEditing ) { @@ -364,8 +350,7 @@ void QgsSelectedFeature::moveSelectedVertexes( const QgsVector &v ) { // move all other if ( mFeatureId != resultIt.value().snappedAtGeometry ) - mVlayer->moveVertex( p.x(), p.y(), - resultIt.value().snappedAtGeometry, resultIt.value().snappedVertexNr ); + mVlayer->moveVertex( p, resultIt.value().snappedAtGeometry, resultIt.value().snappedVertexNr ); } } } @@ -386,6 +371,8 @@ void QgsSelectedFeature::replaceVertexMap() // validate the geometry validateGeometry(); + + emit vertexMapChanged(); } void QgsSelectedFeature::deleteVertexMap() @@ -409,100 +396,8 @@ QgsGeometry *QgsSelectedFeature::geometry() return mGeometry; } -void QgsSelectedFeature::createVertexMapPolygon() -{ - int y = 0; - QgsPolygon polygon = mGeometry->asPolygon(); - if ( !polygon.empty() ) - { - // polygon - for ( int i2 = 0; i2 < polygon.size(); i2++ ) - { - const QgsPolyline& poly = polygon[i2]; - for ( int i = 0; i < poly.size(); i++ ) - { - mVertexMap.insert( y + i, new QgsVertexEntry( mCanvas, mVlayer, poly[i], tr( "ring %1, vertex %2" ).arg( i2 ).arg( i ) ) ); - } - mVertexMap[y + poly.size() - 1 ]->setEqual( y ); - mVertexMap[y]->setEqual( y + poly.size() - 1 ); - y += poly.size(); - } - } - else // multipolygon - { - QgsMultiPolygon multiPolygon = mGeometry->asMultiPolygon(); - for ( int i2 = 0; i2 < multiPolygon.size(); i2++ ) - { - // iterating through polygons - const QgsPolygon& poly2 = multiPolygon[i2]; - for ( int i3 = 0; i3 < poly2.size(); i3++ ) - { - // iterating through polygon rings - const QgsPolyline& poly = poly2[i3]; - for ( int i = 0; i < poly.size(); i++ ) - { - mVertexMap.insert( y + i, new QgsVertexEntry( mCanvas, mVlayer, poly[i], tr( "polygon %1, ring %2, vertex %3" ).arg( i2 ).arg( i3 ).arg( i ) ) ); - } - mVertexMap[y + poly.size() - 1]->setEqual( y ); - mVertexMap[y]->setEqual( y + poly.size() - 1 ); - y += poly.size(); - } - } - } -} - -void QgsSelectedFeature::createVertexMapLine() -{ - Q_ASSERT( mGeometry ); - - if ( mGeometry->isMultipart() ) - { - int y = 0; - QgsMultiPolyline mLine = mGeometry->asMultiPolyline(); - for ( int i2 = 0; i2 < mLine.size(); i2++ ) - { - // iterating through polylines - QgsPolyline poly = mLine[i2]; - for ( int i = 0; i < poly.size(); i++ ) - { - mVertexMap.insert( y + i, new QgsVertexEntry( mCanvas, mVlayer, poly[i], tr( "polyline %1, vertex %2" ).arg( i2 ).arg( i ) ) ); - } - y += poly.size(); - } - } - else - { - QgsPolyline poly = mGeometry->asPolyline(); - for ( int i = 0; i < poly.size(); i++ ) - { - mVertexMap.insert( i, new QgsVertexEntry( mCanvas, mVlayer, poly[i], tr( "vertex %1" ).arg( i ) ) ); - } - } -} - -void QgsSelectedFeature::createVertexMapPoint() -{ - Q_ASSERT( mGeometry ); - - if ( mGeometry->isMultipart() ) - { - // multipoint - QgsMultiPoint poly = mGeometry->asMultiPoint(); - for ( int i = 0; i < poly.size(); i++ ) - { - mVertexMap.insert( i, new QgsVertexEntry( mCanvas, mVlayer, poly[i], tr( "point %1" ).arg( i ) ) ); - } - } - else - { - // single point - mVertexMap.insert( 1, new QgsVertexEntry( mCanvas, mVlayer, mGeometry->asPoint(), tr( "single point" ) ) ); - } -} - void QgsSelectedFeature::createVertexMap() { - QgsDebugCall; if ( !mGeometry ) { @@ -510,26 +405,22 @@ void QgsSelectedFeature::createVertexMap() updateGeometry( 0 ); } - Q_ASSERT( mGeometry ); - - // createvertexmap for correct geometry type - switch ( mGeometry->type() ) + if ( !mGeometry ) { - case QGis::Polygon: - createVertexMapPolygon(); - break; + return; + } - case QGis::Line: - createVertexMapLine(); - break; + const QgsAbstractGeometryV2* geom = mGeometry->geometry(); + if ( !geom ) + { + return; + } - case QGis::Point: - createVertexMapPoint(); - break; - - case QGis::UnknownGeometry: - case QGis::NoGeometry: - break; + QgsVertexId vertexId; + QgsPointV2 pt; + while ( geom->nextVertex( vertexId, pt ) ) + { + mVertexMap.append( new QgsVertexEntry( mCanvas, mVlayer, pt, vertexId, tr( "ring %1, vertex %2" ).arg( vertexId.ring ).arg( vertexId.vertex ) ) ); } } @@ -540,15 +431,9 @@ void QgsSelectedFeature::selectVertex( int vertexNr ) QgsVertexEntry *entry = mVertexMap[vertexNr]; entry->setSelected(); - entry->update(); - if ( entry->equals() != -1 ) - { - // select both vertexes if this is first/last vertex - entry = mVertexMap[ entry->equals()]; - entry->setSelected(); - entry->update(); - } + emit selectionChanged(); + emit lastVertexChanged( entry->point() ); } void QgsSelectedFeature::deselectVertex( int vertexNr ) @@ -558,14 +443,22 @@ void QgsSelectedFeature::deselectVertex( int vertexNr ) QgsVertexEntry *entry = mVertexMap[vertexNr]; entry->setSelected( false ); - entry->update(); + emit selectionChanged(); - if ( entry->equals() != -1 ) + //todo: take another selected vertex as 'lastVertexChanged' + QList::const_iterator vIt = mVertexMap.constBegin(); + for ( ; vIt != mVertexMap.constEnd(); ++vIt ) { - // deselect both vertexes if this is first/last vertex - entry = mVertexMap[ entry->equals()]; - entry->setSelected( false ); - entry->update(); + if (( *vIt )->isSelected() ) + { + emit lastVertexChanged(( *vIt )->point() ); + return; + } + } + + if ( vIt == mVertexMap.constEnd() ) + { + emit lastVertexChanged( QgsPointV2() ); //no selection anymore } } @@ -574,11 +467,12 @@ void QgsSelectedFeature::deselectAllVertexes() for ( int i = 0; i < mVertexMap.size(); i++ ) { mVertexMap[i]->setSelected( false ); - mVertexMap[i]->update(); } + emit selectionChanged(); + emit lastVertexChanged( QgsPointV2() ); } -void QgsSelectedFeature::invertVertexSelection( int vertexNr, bool invert ) +void QgsSelectedFeature::invertVertexSelection( int vertexNr ) { if ( vertexNr < 0 || vertexNr >= mVertexMap.size() ) return; @@ -588,24 +482,18 @@ void QgsSelectedFeature::invertVertexSelection( int vertexNr, bool invert ) bool selected = !entry->isSelected(); entry->setSelected( selected ); - entry->update(); - - if ( entry->equals() != -1 && invert ) + emit selectionChanged(); + if ( selected ) { - entry = mVertexMap[ entry->equals()]; - entry->setSelected( selected ); - entry->update(); + emit lastVertexChanged( entry->point() ); } } void QgsSelectedFeature::updateVertexMarkersPosition() { - // function for on-line updating vertex markers without refresh of canvas - for ( int i = 0; i < mVertexMap.size(); i++ ) + foreach ( QgsVertexEntry* vertexEntry, mVertexMap ) { - QgsVertexEntry *entry = mVertexMap[i]; - entry->setCenter( entry->point() ); - entry->update(); + vertexEntry->placeMarker(); } } @@ -623,3 +511,17 @@ QgsVectorLayer* QgsSelectedFeature::vlayer() { return mVlayer; } + +bool QgsSelectedFeature::hasSelection() const +{ + bool hasSelection = false; + QList::const_iterator vertexIt = mVertexMap.constBegin(); + for ( ; vertexIt != mVertexMap.constEnd(); ++vertexIt ) + { + if (( *vertexIt )->isSelected() ) + { + return true; + } + } + return hasSelection; +} diff --git a/src/app/nodetool/qgsselectedfeature.h b/src/app/nodetool/qgsselectedfeature.h index 5ce2803643c..8183c75b124 100644 --- a/src/app/nodetool/qgsselectedfeature.h +++ b/src/app/nodetool/qgsselectedfeature.h @@ -89,7 +89,7 @@ class QgsSelectedFeature: public QObject * @param vertexNr number of vertex which is to be inverted * @param invert flag if vertex selection should be inverted or not */ - void invertVertexSelection( int vertexNr, bool invert = true ); + void invertVertexSelection( int vertexNr ); /** * Tells if vertex is selected @@ -115,11 +115,6 @@ class QgsSelectedFeature: public QObject */ void replaceVertexMap(); - /** - * Clears data about vertexes if they are in rubber band for moving etc. - */ - void cleanRubberBandsData(); - /** * Get the layer of the selected feature * @return used vector layer @@ -134,6 +129,13 @@ class QgsSelectedFeature: public QObject void beginGeometryChange(); void endGeometryChange(); + bool hasSelection() const; + + signals: + void selectionChanged(); + void lastVertexChanged( const QgsPointV2& pt ); + void vertexMapChanged(); + public slots: /* * geometry validation found a problem @@ -186,21 +188,6 @@ class QgsSelectedFeature: public QObject */ void createVertexMap(); - /** - * Creates vertex map for polygon type feature - */ - void createVertexMapPolygon(); - - /** - * Creates vertex map for line type feature - */ - void createVertexMapLine(); - - /** - * Creates vertex map for ppint type feature - */ - void createVertexMapPoint(); - /** * Updates stored geometry to actual one loaded from layer * (or already available geometry) @@ -217,7 +204,6 @@ class QgsSelectedFeature: public QObject bool mFeatureSelected; bool mChangingGeometry; QgsVectorLayer* mVlayer; - QgsRubberBand* mRubberBand; QList mVertexMap; QgsMapCanvas* mCanvas; diff --git a/src/app/nodetool/qgsvertexentry.cpp b/src/app/nodetool/qgsvertexentry.cpp index 2aabffce434..eac02ca0f3a 100644 --- a/src/app/nodetool/qgsvertexentry.cpp +++ b/src/app/nodetool/qgsvertexentry.cpp @@ -16,12 +16,10 @@ #include "nodetool/qgsvertexentry.h" #include "qgsmaprenderer.h" -QgsVertexEntry::QgsVertexEntry( QgsMapCanvas *canvas, QgsMapLayer *layer, QgsPoint p, QString tooltip, QgsVertexMarker::IconType type, int penWidth ) +QgsVertexEntry::QgsVertexEntry( QgsMapCanvas *canvas, QgsMapLayer *layer, const QgsPointV2 &p, const QgsVertexId &vertexId, QString tooltip, QgsVertexMarker::IconType type, int penWidth ) : mSelected( false ) - , mEquals( -1 ) - , mInRubberBand( false ) - , mRubberBandNr( 0 ) - , mRubberBandIndex( 0 ) + , mPoint( p ) + , mVertexId( vertexId ) , mPenWidth( penWidth ) , mToolTip( tooltip ) , mType( type ) @@ -29,37 +27,39 @@ QgsVertexEntry::QgsVertexEntry( QgsMapCanvas *canvas, QgsMapLayer *layer, QgsPoi , mCanvas( canvas ) , mLayer( layer ) { - setCenter( p ); + placeMarker(); } QgsVertexEntry::~QgsVertexEntry() { - if ( mMarker ) - { - delete mMarker; - mMarker = 0; - } + delete mMarker; } -void QgsVertexEntry::setCenter( QgsPoint p ) +void QgsVertexEntry::placeMarker() { - mPoint = p; - p = mCanvas->mapSettings().layerToMapCoordinates( mLayer, p ); + QgsPoint pm = mCanvas->mapSettings().layerToMapCoordinates( mLayer, pointV1() ); - if ( mCanvas->extent().contains( p ) ) + if ( mCanvas->extent().contains( pm ) ) { if ( !mMarker ) { mMarker = new QgsVertexMarker( mCanvas ); - mMarker->setIconType( mType ); - mMarker->setColor( mSelected ? Qt::blue : Qt::red ); + QColor c = mSelected ? QColor( Qt::blue ) : QColor( Qt::red ); + if ( mVertexId.type == QgsVertexId::CurveVertex ) + { + mMarker->setIconType( QgsVertexMarker::ICON_CIRCLE ); + } + else + { + mMarker->setIconType( mType ); + } + mMarker->setColor( c ); mMarker->setPenWidth( mPenWidth ); - - if ( !mToolTip.isEmpty() ) - mMarker->setToolTip( mToolTip ); + mMarker->setToolTip( mToolTip ); } - mMarker->setCenter( p ); + mMarker->setCenter( pm ); + mMarker->update(); } else if ( mMarker ) { @@ -73,19 +73,8 @@ void QgsVertexEntry::setSelected( bool selected ) mSelected = selected; if ( mMarker ) { - mMarker->setColor( mSelected ? Qt::blue : Qt::red ); + QColor c = mSelected ? QColor( Qt::blue ) : QColor( Qt::red ); + mMarker->setColor( c ); + mMarker->update(); } } - -void QgsVertexEntry::setRubberBandValues( bool inRubberBand, int rubberBandNr, int indexInRubberBand ) -{ - mRubberBandIndex = indexInRubberBand; - mInRubberBand = inRubberBand; - mRubberBandNr = rubberBandNr; -} - -void QgsVertexEntry::update() -{ - if ( mMarker ) - mMarker->update(); -} diff --git a/src/app/nodetool/qgsvertexentry.h b/src/app/nodetool/qgsvertexentry.h index 2a1a7d9ac23..deefa225fe3 100644 --- a/src/app/nodetool/qgsvertexentry.h +++ b/src/app/nodetool/qgsvertexentry.h @@ -16,7 +16,7 @@ #ifndef QGSVERTEXENTRY_H #define QGSVERTEXENTRY_H -#include +#include #include #include #include @@ -24,11 +24,8 @@ class QgsVertexEntry { bool mSelected; - QgsPoint mPoint; - int mEquals; - bool mInRubberBand; - int mRubberBandNr; - int mRubberBandIndex; + QgsPointV2 mPoint; + QgsVertexId mVertexId; int mPenWidth; QString mToolTip; QgsVertexMarker::IconType mType; @@ -39,28 +36,21 @@ class QgsVertexEntry public: QgsVertexEntry( QgsMapCanvas *canvas, QgsMapLayer *layer, - QgsPoint p, + const QgsPointV2& p, + const QgsVertexId& vertexId, QString tooltip = QString::null, QgsVertexMarker::IconType type = QgsVertexMarker::ICON_BOX, int penWidth = 2 ); ~QgsVertexEntry(); - QgsPoint point() const { return mPoint; } - int equals() const { return mEquals; } + const QgsPointV2& point() const { return mPoint; } + QgsPoint pointV1() const { return QgsPoint( mPoint.x(), mPoint.y() ); } + const QgsVertexId& vertexId() const { return mVertexId; } bool isSelected() const { return mSelected; } - bool isInRubberBand() const { return mInRubberBand; } - void setCenter( QgsPoint p ); + void placeMarker(); - void setEqual( int index ) { mEquals = index; } void setSelected( bool selected = true ); - void setInRubberBand( bool inRubberBand = true ) { mInRubberBand = inRubberBand; } - - int rubberBandNr() const { return mRubberBandNr; } - int rubberBandIndex() { return mRubberBandIndex; } - - void setRubberBandValues( bool inRubberBand, int rubberBandNr, int indexInRubberBand ); - void update(); }; #endif diff --git a/src/app/qgsadvanceddigitizingdockwidget.cpp b/src/app/qgsadvanceddigitizingdockwidget.cpp index 64f57b911ea..bce9c06b4cf 100644 --- a/src/app/qgsadvanceddigitizingdockwidget.cpp +++ b/src/app/qgsadvanceddigitizingdockwidget.cpp @@ -853,8 +853,7 @@ bool QgsAdvancedDigitizingDockWidget::alignToSegment( QgsMapMouseEvent* e, CadCo bool QgsAdvancedDigitizingDockWidget::canvasPressEventFilter( QgsMapMouseEvent* e ) { - Q_UNUSED( e ); - + applyConstraints( e ); return mCadEnabled && mConstructionMode; } diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 61b0b23d191..9219bab3931 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -990,6 +990,14 @@ bool QgsVectorLayer::moveVertex( double x, double y, QgsFeatureId atFeatureId, i return utils.moveVertex( x, y, atFeatureId, atVertex ); } +bool QgsVectorLayer::moveVertex( const QgsPointV2& p, QgsFeatureId atFeatureId, int atVertex ) +{ + if ( !mEditBuffer || !mDataProvider ) + return false; + + QgsVectorLayerEditUtils utils( this ); + return utils.moveVertex( p, atFeatureId, atVertex ); +} bool QgsVectorLayer::deleteVertex( QgsFeatureId atFeatureId, int atVertex ) { @@ -2189,7 +2197,7 @@ bool QgsVectorLayer::deleteAttributes( QList attrs ) qSort( attrs.begin(), attrs.end(), qGreater() ); - Q_FOREACH ( int attr, attrs ) + Q_FOREACH( int attr, attrs ) { if ( deleteAttribute( attr ) ) { @@ -2963,7 +2971,7 @@ void QgsVectorLayer::uniqueValues( int index, QList &uniqueValues, int if ( mEditBuffer ) { QSet vals; - Q_FOREACH ( const QVariant& v, uniqueValues ) + Q_FOREACH( const QVariant& v, uniqueValues ) { vals << v.toString(); } @@ -3771,7 +3779,7 @@ void QgsVectorLayer::invalidateSymbolCountedFlag() void QgsVectorLayer::onRelationsLoaded() { - Q_FOREACH ( QgsAttributeEditorElement* elem, mAttributeEditorElements ) + Q_FOREACH( QgsAttributeEditorElement* elem, mAttributeEditorElements ) { if ( elem->type() == QgsAttributeEditorElement::AeTypeContainer ) { @@ -3780,7 +3788,7 @@ void QgsVectorLayer::onRelationsLoaded() continue; QList relations = cont->findElements( QgsAttributeEditorElement::AeTypeRelation ); - Q_FOREACH ( QgsAttributeEditorElement* relElem, relations ) + Q_FOREACH( QgsAttributeEditorElement* relElem, relations ) { QgsAttributeEditorRelation* rel = dynamic_cast< QgsAttributeEditorRelation* >( relElem ); if ( !rel ) @@ -3849,7 +3857,7 @@ QDomElement QgsAttributeEditorContainer::toDomElement( QDomDocument& doc ) const QDomElement elem = doc.createElement( "attributeEditorContainer" ); elem.setAttribute( "name", mName ); - Q_FOREACH ( QgsAttributeEditorElement* child, mChildren ) + Q_FOREACH( QgsAttributeEditorElement* child, mChildren ) { elem.appendChild( child->toDomElement( doc ) ); } @@ -3870,7 +3878,7 @@ QList QgsAttributeEditorContainer::findElements( Qgs { QList results; - Q_FOREACH ( QgsAttributeEditorElement* elem, mChildren ) + Q_FOREACH( QgsAttributeEditorElement* elem, mChildren ) { if ( elem->type() == type ) { diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 1f440e904eb..b4f898a762b 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -30,6 +30,7 @@ #include "qgsfeatureiterator.h" #include "qgseditorwidgetconfig.h" #include "qgsfield.h" +#include "qgspointv2.h" #include "qgssnapper.h" #include "qgsrelation.h" #include "qgsvectorsimplifymethod.h" @@ -966,18 +967,18 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer /** Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeometry */ bool hasGeometryType() const; - /**Returns the WKBType or WKBUnknown in case of error*/ + /** Returns the WKBType or WKBUnknown in case of error*/ QGis::WkbType wkbType() const; /** Return the provider type for this layer */ QString providerType() const; - /** reads vector layer specific state from project file Dom node. + /** Reads vector layer specific state from project file Dom node. * @note Called by QgsMapLayer::readXML(). */ virtual bool readXml( const QDomNode& layer_node ) override; - /** write vector layer specific state to project file Dom node. + /** Write vector layer specific state to project file Dom node. * @note Called by QgsMapLayer::writeXML(). */ virtual bool writeXml( QDomNode & layer_node, QDomDocument & doc ) override; @@ -1036,7 +1037,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ virtual bool applyNamedStyle( QString namedStyle, QString &errorMsg ); - /** convert a saved attribute editor element into a AttributeEditor structure as it's used internally. + /** Convert a saved attribute editor element into a AttributeEditor structure as it's used internally. * @param elem the DOM element * @param parent the QObject which will own this object */ @@ -1143,6 +1144,12 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ bool moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex ); + /** Moves the vertex at the given position number, + * ring and item (first number is index 0), and feature + * to the given coordinates + */ + bool moveVertex( const QgsPointV2& p, QgsFeatureId atFeatureId, int atVertex ); + /** Deletes a vertex from a feature */ bool deleteVertex( QgsFeatureId atFeatureId, int atVertex ); @@ -1152,7 +1159,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ bool deleteSelectedFeatures( int *deletedCount = 0 ); - /**Adds a ring to polygon/multipolygon features + /** Adds a ring to polygon/multipolygon features @return 0 in case of success, 1 problem with feature type, @@ -1163,7 +1170,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer 6 layer not editable */ int addRing( const QList& ring ); - /**Adds a new part polygon to a multipart feature + /** Adds a new part polygon to a multipart feature @return 0 in case of success, 1 if selected feature is not multipart, @@ -1175,14 +1182,14 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer 7 layer not editable */ int addPart( const QList& ring ); - /**Translates feature by dx, dy + /** Translates feature by dx, dy @param featureId id of the feature to translate @param dx translation of x-coordinate @param dy translation of y-coordinate @return 0 in case of success*/ int translateFeature( QgsFeatureId featureId, double dx, double dy ); - /**Splits parts cut by the given line + /** Splits parts cut by the given line * @param splitLine line that splits the layer features * @param topologicalEditing true if topological editing is enabled * @return @@ -1191,7 +1198,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ int splitParts( const QList& splitLine, bool topologicalEditing = false ); - /**Splits features cut by the given line + /** Splits features cut by the given line * @param splitLine line that splits the layer features * @param topologicalEditing true if topological editing is enabled * @return @@ -1200,7 +1207,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ int splitFeatures( const QList& splitLine, bool topologicalEditing = false ); - /**Changes the specified geometry such that it has no intersections with other + /** Changes the specified geometry such that it has no intersections with other * polygon (or multipolygon) geometries in this vector layer * @param geom geometry to modify * @param ignoreFeatures list of feature ids where intersections should be ignored @@ -1226,7 +1233,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ int addTopologicalPoints( const QgsPoint& p ); - /**Inserts vertices to the snapped segments. + /** Inserts vertices to the snapped segments. * This is useful for topological editing if snap to segment is enabled. * @param snapResults results collected from the snapping operation * @return 0 in case of success @@ -1252,7 +1259,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer /** Returns true if the provider has been modified since the last commit */ virtual bool isModified() const; - /**Snaps a point to the closest vertex if there is one within the snapping tolerance + /** Snaps a point to the closest vertex if there is one within the snapping tolerance * @param point The point which is set to the position of a vertex if there is one within the snapping tolerance. * If there is no point within this tolerance, point is left unchanged. * @param tolerance The snapping tolerance @@ -1260,7 +1267,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ bool snapPoint( QgsPoint& point, double tolerance ); - /**Snaps to segment or vertex within given tolerance + /** Snaps to segment or vertex within given tolerance * @param startPoint point to snap (in layer coordinates) * @param snappingTolerance distance tolerance for snapping * @param snappingResults snapping results. Key is the distance between startPoint and snapping target @@ -1272,7 +1279,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer QMultiMap < double, QgsSnappingResult > &snappingResults, QgsSnapper::SnappingType snap_to ); - /**Synchronises with changes in the datasource */ + /** Synchronises with changes in the datasource */ virtual void reload() override; /** Return new instance of QgsMapLayerRenderer that will be used for rendering of given context @@ -1293,16 +1300,16 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer /** Return the extent of the layer as a QRect */ QgsRectangle extent() override; - /** returns field list in the to-be-committed state */ + /** Returns field list in the to-be-committed state */ const QgsFields &pendingFields() const; - /** returns list of attributes */ + /** Returns list of attributes */ QgsAttributeList pendingAllAttributesList(); - /** returns list of attribute making up the primary key */ + /** Returns list of attribute making up the primary key */ QgsAttributeList pendingPkAttributesList(); - /** returns feature count after commit */ + /** Returns feature count after commit */ int pendingFeatureCount(); /** Make layer read-only (editing disabled) or not @@ -1313,7 +1320,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer /** Make layer editable */ bool startEditing(); - /** change feature's geometry */ + /** Change feature's geometry */ bool changeGeometry( QgsFeatureId fid, QgsGeometry* geom ); /** @@ -1336,7 +1343,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ); - /** add an attribute field (but does not commit it) + /** Add an attribute field (but does not commit it) returns true if the field was added */ bool addAttribute( const QgsField &field ); @@ -1414,7 +1421,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer const QSet& excludeAttributesWFS() const { return mExcludeAttributesWFS; } void setExcludeAttributesWFS( const QSet& att ) { mExcludeAttributesWFS = att; } - /** delete an attribute field (but does not commit it) */ + /** Delete an attribute field (but does not commit it) */ bool deleteAttribute( int attr ); /** @@ -1429,7 +1436,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer /** Insert a copy of the given features into the layer (but does not commit it) */ bool addFeatures( QgsFeatureList features, bool makeSelected = true ); - /** delete a feature from the layer (but does not commit it) */ + /** Delete a feature from the layer (but does not commit it) */ bool deleteFeature( QgsFeatureId fid ); /** @@ -1469,10 +1476,10 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ Q_DECL_DEPRECATED void setEditType( int idx, EditType edit ); - /** get the active layout for the attribute editor for this layer */ + /** Get the active layout for the attribute editor for this layer */ EditorLayout editorLayout(); - /** set the active layout for the attribute editor for this layer */ + /** Set the active layout for the attribute editor for this layer */ void setEditorLayout( EditorLayout editorLayout ); /** @@ -1529,10 +1536,10 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ Q_DECL_DEPRECATED void setCheckedState( int idx, QString checked, QString notChecked ); - /** get edit form */ + /** Get edit form */ QString editForm(); - /** set edit form */ + /** Set edit form */ void setEditForm( QString ui ); /** Type of feature form pop-up suppression after feature creation (overrides app setting) @@ -1543,16 +1550,16 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer * @note added in 2.1 */ void setFeatureFormSuppress( QgsVectorLayer::FeatureFormSuppress s ) { mFeatureFormSuppress = s; } - /** get annotation form */ + /** Get annotation form */ QString annotationForm() const { return mAnnotationForm; } - /** set annotation form for layer */ + /** Set annotation form for layer */ void setAnnotationForm( const QString& ui ); - /** get python function for edit form initialization */ + /** Get python function for edit form initialization */ QString editFormInit(); - /** set python function for edit form initialization */ + /** Set python function for edit form initialization */ void setEditFormInit( QString function ); /** @@ -1593,16 +1600,16 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ Q_DECL_DEPRECATED QSize widgetSize( int idx ); - /**is edit widget editable **/ + /** Is edit widget editable **/ bool fieldEditable( int idx ); - /**label widget on top **/ + /** Label widget on top **/ bool labelOnTop( int idx ); - /**set edit widget editable **/ + /** Set edit widget editable **/ void setFieldEditable( int idx, bool editable ); - /**label widget on top **/ + /** Label widget on top **/ void setLabelOnTop( int idx, bool onTop ); //! Buffer with uncommitted editing operations. Only valid after editing has been turned on. @@ -1640,16 +1647,16 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer /** Caches joined attributes if required (and not already done) */ void createJoinCaches(); - /**Returns unique values for column + /** Returns unique values for column @param index column index for attribute @param uniqueValues out: result list @param limit maximum number of values to return (-1 if unlimited) */ void uniqueValues( int index, QList &uniqueValues, int limit = -1 ); - /**Returns minimum value for an attribute column or invalid variant in case of error */ + /** Returns minimum value for an attribute column or invalid variant in case of error */ QVariant minimumValue( int index ); - /**Returns maximum value for an attribute column or invalid variant in case of error */ + /** Returns maximum value for an attribute column or invalid variant in case of error */ QVariant maximumValue( int index ); /** Fetches all values from a specified field name or expression. @@ -1917,14 +1924,14 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer private: // Private methods - /** vector layers are not copyable */ + /** Vector layers are not copyable */ QgsVectorLayer( const QgsVectorLayer & rhs ); - /** vector layers are not copyable */ + /** Vector layers are not copyable */ QgsVectorLayer & operator=( QgsVectorLayer const & rhs ); - /** bind layer to a specific data provider + /** Bind layer to a specific data provider @param provider should be "postgres", "ogr", or ?? @todo XXX should this return bool? Throw exceptions? */ @@ -1933,7 +1940,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer /** Goes through all features and finds a free id (e.g. to give it temporarily to a not-commited feature) */ QgsFeatureId findFreeId(); - /**Snaps to a geometry and adds the result to the multimap if it is within the snapping result + /** Snaps to a geometry and adds the result to the multimap if it is within the snapping result @param startPoint start point of the snap @param featureId id of feature @param geom geometry to snap @@ -1959,10 +1966,10 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer /** Pointer to data provider derived from the abastract base class QgsDataProvider */ QgsVectorDataProvider *mDataProvider; - /** index of the primary label field */ + /** Index of the primary label field */ QString mDisplayField; - /** the preview expression used to generate a human readable preview string for features */ + /** The preview expression used to generate a human readable preview string for features */ QString mDisplayExpression; /** Data provider key */ @@ -1980,21 +1987,21 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer */ QgsFeatureIds mSelectedFeatureIds; - /** field map to commit */ + /** Field map to commit */ QgsFields mUpdatedFields; - /**Map that stores the aliases for attributes. Key is the attribute name and value the alias for that attribute*/ + /** Map that stores the aliases for attributes. Key is the attribute name and value the alias for that attribute*/ QMap< QString, QString > mAttributeAliasMap; - /**Stores a list of attribute editor elements (Each holding a tree structure for a tab in the attribute editor)*/ + /** Stores a list of attribute editor elements (Each holding a tree structure for a tab in the attribute editor)*/ QList< QgsAttributeEditorElement* > mAttributeEditorElements; - /**Attributes which are not published in WMS*/ + /** Attributes which are not published in WMS*/ QSet mExcludeAttributesWMS; - /**Attributes which are not published in WFS*/ + /** Attributes which are not published in WFS*/ QSet mExcludeAttributesWFS; - /**Map that stores the tab for attributes in the edit form. Key is the tab order and value the tab name*/ + /** Map that stores the tab for attributes in the edit form. Key is the tab order and value the tab name*/ QList< TabData > mTabs; /** Geometry type as defined in enum WkbType (qgis.h) */ diff --git a/src/core/qgsvectorlayereditutils.cpp b/src/core/qgsvectorlayereditutils.cpp index 19fba6065f6..5655738ca7b 100644 --- a/src/core/qgsvectorlayereditutils.cpp +++ b/src/core/qgsvectorlayereditutils.cpp @@ -51,6 +51,12 @@ bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atF bool QgsVectorLayerEditUtils::moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex ) +{ + QgsPointV2 p( x, y ); + return moveVertex( p, atFeatureId, atVertex ); +} + +bool QgsVectorLayerEditUtils::moveVertex( const QgsPointV2& p, QgsFeatureId atFeatureId, int atVertex ) { if ( !L->hasGeometryType() ) return false; @@ -66,7 +72,7 @@ bool QgsVectorLayerEditUtils::moveVertex( double x, double y, QgsFeatureId atFea geometry = *f.constGeometry(); } - geometry.moveVertex( x, y, atVertex ); + geometry.moveVertex( p, atVertex ); L->editBuffer()->changeGeometry( atFeatureId, &geometry ); return true; diff --git a/src/core/qgsvectorlayereditutils.h b/src/core/qgsvectorlayereditutils.h index 1f2fc5cb8fd..20c64114af0 100644 --- a/src/core/qgsvectorlayereditutils.h +++ b/src/core/qgsvectorlayereditutils.h @@ -42,6 +42,12 @@ class CORE_EXPORT QgsVectorLayerEditUtils */ bool moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex ); + /** Moves the vertex at the given position number, + * ring and item (first number is index 0), and feature + * to the given coordinates + */ + bool moveVertex( const QgsPointV2& p, QgsFeatureId atFeatureId, int atVertex ); + /** Deletes a vertex from a feature */ bool deleteVertex( QgsFeatureId atFeatureId, int atVertex ); diff --git a/src/core/qgsvectorlayerrenderer.cpp b/src/core/qgsvectorlayerrenderer.cpp index 7d69c75f7e8..ebe9b1bc1d8 100644 --- a/src/core/qgsvectorlayerrenderer.cpp +++ b/src/core/qgsvectorlayerrenderer.cpp @@ -276,15 +276,15 @@ void QgsVectorLayerRenderer::drawRendererV2( QgsFeatureIterator& fit ) bool sel = mContext.showSelection() && mSelectedFeatureIds.contains( fet.id() ); bool drawMarker = ( mDrawVertexMarkers && mContext.drawEditingInformation() && ( !mVertexMarkerOnlyForSelection || sel ) ); - // render feature - bool rendered = mRendererV2->renderFeature( fet, mContext, -1, sel, drawMarker ); - if ( mCache ) { // Cache this for the use of (e.g.) modifying the feature's uncommitted geometry. mCache->cacheGeometry( fet.id(), *fet.constGeometry() ); } + // render feature + bool rendered = mRendererV2->renderFeature( fet, mContext, -1, sel, drawMarker ); + // labeling - register feature Q_UNUSED( rendered ); if ( rendered && mContext.labelingEngine() ) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 43cf88699b8..b01a4e0f6b8 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -175,6 +175,7 @@ SET(QGIS_GUI_SRCS qgsfilterlineedit.cpp qgsformannotationitem.cpp qgsgenericprojectionselector.cpp + qgsgeometryrubberband.cpp qgshighlight.cpp qgshistogramwidget.cpp qgsidentifymenu.cpp diff --git a/src/gui/qgsgeometryrubberband.cpp b/src/gui/qgsgeometryrubberband.cpp new file mode 100644 index 00000000000..accfd8738f7 --- /dev/null +++ b/src/gui/qgsgeometryrubberband.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** + qgsgeometryrubberband.cpp + ------------------------- + begin : December 2014 + copyright : (C) 2014 by Marco Hugentobler + email : marco at sourcepole dot ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsgeometryrubberband.h" +#include "qgsabstractgeometryv2.h" +#include "qgsmapcanvas.h" +#include "qgspointv2.h" +#include + +QgsGeometryRubberBand::QgsGeometryRubberBand( QgsMapCanvas* mapCanvas, QGis::GeometryType geomType ): QgsMapCanvasItem( mapCanvas ), + mGeometry( 0 ), mIconSize( 5 ), mIconType( ICON_CIRCLE ), mGeometryType( geomType ) +{ + mPen = QPen( QColor( 255, 0, 0 ) ); + mBrush = QBrush( QColor( 255, 0, 0 ) ); +} + +QgsGeometryRubberBand::~QgsGeometryRubberBand() +{ + delete mGeometry; +} + +void QgsGeometryRubberBand::paint( QPainter* painter ) +{ + if ( !mGeometry || !painter ) + { + return; + } + + painter->save(); + painter->translate( -pos() ); + + if ( mGeometryType == QGis::Polygon ) + { + painter->setBrush( mBrush ); + } + else + { + painter->setBrush( Qt::NoBrush ); + } + painter->setPen( mPen ); + + + QgsAbstractGeometryV2* paintGeom = mGeometry->clone(); + + paintGeom->transform( mMapCanvas->getCoordinateTransform()->transform() ); + paintGeom->draw( *painter ); + + //draw vertices + QgsVertexId vertexId; + QgsPointV2 vertex; + while ( paintGeom->nextVertex( vertexId, vertex ) ) + { + drawVertex( painter, vertex.x(), vertex.y() ); + } + + delete paintGeom; + painter->restore(); +} + +void QgsGeometryRubberBand::drawVertex( QPainter* p, double x, double y ) +{ + qreal s = ( mIconSize - 1 ) / 2; + + switch ( mIconType ) + { + case ICON_NONE: + break; + + case ICON_CROSS: + p->drawLine( QLineF( x - s, y, x + s, y ) ); + p->drawLine( QLineF( x, y - s, x, y + s ) ); + break; + + case ICON_X: + p->drawLine( QLineF( x - s, y - s, x + s, y + s ) ); + p->drawLine( QLineF( x - s, y + s, x + s, y - s ) ); + break; + + case ICON_BOX: + p->drawLine( QLineF( x - s, y - s, x + s, y - s ) ); + p->drawLine( QLineF( x + s, y - s, x + s, y + s ) ); + p->drawLine( QLineF( x + s, y + s, x - s, y + s ) ); + p->drawLine( QLineF( x - s, y + s, x - s, y - s ) ); + break; + + case ICON_FULL_BOX: + p->drawRect( x - s, y - s, mIconSize, mIconSize ); + break; + + case ICON_CIRCLE: + p->drawEllipse( x - s, y - s, mIconSize, mIconSize ); + break; + } +} + +void QgsGeometryRubberBand::setGeometry( QgsAbstractGeometryV2* geom ) +{ + delete mGeometry; + mGeometry = geom; + + if ( mGeometry ) + { + setRect( rubberBandRectangle() ); + } +} + +void QgsGeometryRubberBand::moveVertex( const QgsVertexId& id, const QgsPointV2& newPos ) +{ + if ( mGeometry ) + { + mGeometry->moveVertex( id, newPos ); + setRect( rubberBandRectangle() ); + } +} + +void QgsGeometryRubberBand::setFillColor( const QColor& c ) +{ + mBrush.setColor( c ); +} + +void QgsGeometryRubberBand::setOutlineColor( const QColor& c ) +{ + mPen.setColor( c ); +} + +void QgsGeometryRubberBand::setOutlineWidth( int width ) +{ + mPen.setWidth( width ); +} + +void QgsGeometryRubberBand::setLineStyle( Qt::PenStyle penStyle ) +{ + mPen.setStyle( penStyle ); +} + +void QgsGeometryRubberBand::setBrushStyle( Qt::BrushStyle brushStyle ) +{ + mBrush.setStyle( brushStyle ); +} + +QgsRectangle QgsGeometryRubberBand::rubberBandRectangle() const +{ + qreal scale = mMapCanvas->mapUnitsPerPixel(); + qreal s = ( mIconSize - 1 ) / 2.0 * scale; + qreal p = mPen.width() * scale; + return mGeometry->boundingBox().buffer( s + p ); +} diff --git a/src/gui/qgsgeometryrubberband.h b/src/gui/qgsgeometryrubberband.h new file mode 100644 index 00000000000..14545db88e5 --- /dev/null +++ b/src/gui/qgsgeometryrubberband.h @@ -0,0 +1,99 @@ +/*************************************************************************** + qgsgeometryrubberband.h + ----------------------- + begin : December 2014 + copyright : (C) 2014 by Marco Hugentobler + email : marco at sourcepole dot ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSGEOMETRYRUBBERBAND_H +#define QGSGEOMETRYRUBBERBAND_H + +#include "qgsmapcanvasitem.h" +#include +#include + + +class QgsAbstractGeometryV2; +class QgsPointV2; +struct QgsVertexId; + +/** A rubberband class for QgsAbstractGeometryV2 (considering curved geometries)*/ +class GUI_EXPORT QgsGeometryRubberBand: public QgsMapCanvasItem +{ + public: + enum IconType + { + /** + * No icon is used + */ + ICON_NONE, + /** + * A cross is used to highlight points (+) + */ + ICON_CROSS, + /** + * A cross is used to highlight points (x) + */ + ICON_X, + /** + * A box is used to highlight points (□) + */ + ICON_BOX, + /** + * A circle is used to highlight points (○) + */ + ICON_CIRCLE, + /** + * A full box is used to highlight points (■) + */ + ICON_FULL_BOX + }; + + QgsGeometryRubberBand( QgsMapCanvas* mapCanvas, QGis::GeometryType geomType = QGis::Line ); + ~QgsGeometryRubberBand(); + + /** Sets geometry (takes ownership). Geometry is expected to be in map coordinates */ + void setGeometry( QgsAbstractGeometryV2* geom ); + /** Returns a pointer to the geometry*/ + const QgsAbstractGeometryV2* geometry() { return mGeometry; } + /** Moves vertex to new position (in map coordinates)*/ + void moveVertex( const QgsVertexId& id, const QgsPointV2& newPos ); + /** Sets fill color for vertex markers*/ + void setFillColor( const QColor& c ); + /** Sets outline color for vertex markes*/ + void setOutlineColor( const QColor& c ); + /** Sets outline width*/ + void setOutlineWidth( int width ); + /** Sets pen style*/ + void setLineStyle( Qt::PenStyle penStyle ); + /** Sets brush style*/ + void setBrushStyle( Qt::BrushStyle brushStyle ); + /** Sets vertex marker icon type*/ + void setIconType( IconType iconType ) { mIconType = iconType; } + + protected: + virtual void paint( QPainter* painter ); + + private: + QgsAbstractGeometryV2* mGeometry; + QBrush mBrush; + QPen mPen; + int mIconSize; + IconType mIconType; + QGis::GeometryType mGeometryType; + + void drawVertex( QPainter* p, double x, double y ); + QgsRectangle rubberBandRectangle() const; +}; + +#endif // QGSGEOMETRYRUBBERBAND_H diff --git a/src/gui/qgsmaptool.cpp b/src/gui/qgsmaptool.cpp index 21ad73f3b51..0235812ff45 100644 --- a/src/gui/qgsmaptool.cpp +++ b/src/gui/qgsmaptool.cpp @@ -44,6 +44,12 @@ QgsPoint QgsMapTool::toMapCoordinates( const QPoint& point ) return mCanvas->getCoordinateTransform()->toMapCoordinates( point ); } +QgsPointV2 QgsMapTool::toMapCoordinates( QgsMapLayer* layer, const QgsPointV2& point ) +{ + QgsPoint result = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPoint( point.x(), point.y() ) ); + return QgsPointV2( result.x(), result.y() ); +} + QgsPoint QgsMapTool::toLayerCoordinates( QgsMapLayer* layer, const QPoint& point ) { diff --git a/src/gui/qgsmaptool.h b/src/gui/qgsmaptool.h index 073293a0585..deba8f507db 100644 --- a/src/gui/qgsmaptool.h +++ b/src/gui/qgsmaptool.h @@ -18,6 +18,7 @@ #include "qgsconfig.h" #include "qgsmessagebar.h" +#include "qgspointv2.h" #include #include @@ -178,6 +179,9 @@ class GUI_EXPORT QgsMapTool : public QObject //!transformation from layer's coordinates to map coordinates (which is different in case reprojection is used) QgsPoint toMapCoordinates( QgsMapLayer* layer, const QgsPoint& point ); + //!transformation from layer's coordinates to map coordinates (which is different in case reprojection is used) + QgsPointV2 toMapCoordinates( QgsMapLayer* layer, const QgsPointV2 &point ); + //! trnasformation of the rect from map coordinates to layer's coordinates QgsRectangle toLayerCoordinates( QgsMapLayer* layer, const QgsRectangle& rect ); diff --git a/src/gui/qgsvertexmarker.cpp b/src/gui/qgsvertexmarker.cpp index 3654665ba62..b9191818e9c 100644 --- a/src/gui/qgsvertexmarker.cpp +++ b/src/gui/qgsvertexmarker.cpp @@ -83,6 +83,10 @@ void QgsVertexMarker::paint( QPainter* p ) p->drawLine( QLineF( s, s, -s, s ) ); p->drawLine( QLineF( -s, s, -s, -s ) ); break; + + case ICON_CIRCLE: + p->drawEllipse( QPointF( 0, 0 ), s, s ); + break; } } diff --git a/src/gui/qgsvertexmarker.h b/src/gui/qgsvertexmarker.h index f73c57b7983..701e925fd32 100644 --- a/src/gui/qgsvertexmarker.h +++ b/src/gui/qgsvertexmarker.h @@ -34,7 +34,8 @@ class GUI_EXPORT QgsVertexMarker : public QgsMapCanvasItem ICON_NONE, ICON_CROSS, ICON_X, - ICON_BOX + ICON_BOX, + ICON_CIRCLE }; QgsVertexMarker( QgsMapCanvas* mapCanvas ); diff --git a/src/plugins/dxf2shp_converter/CMakeLists.txt b/src/plugins/dxf2shp_converter/CMakeLists.txt index c3c706232e4..7b420c121bf 100644 --- a/src/plugins/dxf2shp_converter/CMakeLists.txt +++ b/src/plugins/dxf2shp_converter/CMakeLists.txt @@ -33,6 +33,7 @@ ADD_LIBRARY (dxf2shpconverterplugin MODULE ${dxf2shpconverter_SRCS} ${dxf2shpcon INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} ../../core + ../../core/geometry ../../core/raster ../../gui ..