36class QgsAnnotationItemNodesSpatialIndex :
public RTree<int, float, 2, float>
42 std::array< float, 4 > scaledBounds = scaleBounds( bounds );
45 scaledBounds[0], scaledBounds[ 1]
48 scaledBounds[2], scaledBounds[3]
61 std::array< float, 4 > scaledBounds = scaleBounds( bounds );
64 scaledBounds[0], scaledBounds[ 1]
67 scaledBounds[2], scaledBounds[3]
77 bool intersects(
const QgsRectangle &bounds,
const std::function<
bool(
int index )> &callback )
const
79 std::array< float, 4 > scaledBounds = scaleBounds( bounds );
82 scaledBounds[0], scaledBounds[ 1]
85 scaledBounds[2], scaledBounds[3]
92 std::array<float, 4> scaleBounds(
const QgsRectangle &bounds )
const
96 static_cast< float >( bounds.
xMinimum() ),
97 static_cast< float >( bounds.
yMinimum() ),
98 static_cast< float >( bounds.
xMaximum() ),
99 static_cast< float >( bounds.
yMaximum() )
127 mLastHoverPoint =
event->originalPixelPoint();
131 const QgsPointXY mapPoint =
event->mapPoint();
138 switch ( mCurrentAction )
140 case Action::NoAction:
142 setHoveredItemFromPoint( mapPoint );
146 case Action::MoveItem:
148 if (
QgsAnnotationItem *item = annotationItemFromId( mSelectedItemLayerId, mSelectedItemId ) )
153 event->
pixelPoint().x() - mMoveStartPointPixels.
x(),
154 event->
pixelPoint().y() - mMoveStartPointPixels.
y() );
155 std::unique_ptr< QgsAnnotationItemEditOperationTransientResults > operationResults( item->transientEditResultsV2( &operation, context ) );
156 if ( operationResults )
159 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
160 mTemporaryRubberBand->
setWidth( scaleFactor );
165 mTemporaryRubberBand.
reset();
171 case Action::MoveNode:
173 if (
QgsAnnotationItem *item = annotationItemFromId( mSelectedItemLayerId, mSelectedItemId ) )
177 event->
pixelPoint().x() - mMoveStartPointPixels.
x(),
178 event->
pixelPoint().y() - mMoveStartPointPixels.
y() );
179 std::unique_ptr< QgsAnnotationItemEditOperationTransientResults > operationResults( item->transientEditResultsV2( &operation, context ) );
180 if ( operationResults )
183 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
184 mTemporaryRubberBand->
setWidth( scaleFactor );
189 mTemporaryRubberBand.
reset();
205 switch ( mCurrentAction )
207 case Action::NoAction:
209 if ( event->button() != Qt::LeftButton )
212 if ( mHoveredItemId.isEmpty() || !mHoverRubberBand )
216 else if ( mHoveredItemId == mSelectedItemId && mHoveredItemLayerId == mSelectedItemLayerId )
221 const QgsPointXY mapPoint =
event->mapPoint();
226 double currentNodeDistance = std::numeric_limits< double >::max();
227 mHoveredItemNodesSpatialIndex->intersects( searchRect, [&hoveredNode, ¤tNodeDistance, &mapPoint,
this](
int index )->
bool
230 const double nodeDistance = thisNode.
point().sqrDist( mapPoint );
231 if ( nodeDistance < currentNodeDistance )
233 hoveredNode = thisNode;
234 currentNodeDistance = nodeDistance;
239 mMoveStartPointCanvasCrs = mapPoint;
240 mMoveStartPointPixels =
event->pixelPoint();
242 if ( mHoverRubberBand )
243 mHoverRubberBand->hide();
244 if ( mSelectedRubberBand )
245 mSelectedRubberBand->hide();
249 mCurrentAction = Action::MoveItem;
253 mCurrentAction = Action::MoveNode;
254 mTargetNode = hoveredNode;
261 mSelectedItemId = mHoveredItemId;
262 mSelectedItemLayerId = mHoveredItemLayerId;
263 mSelectedItemBounds = mHoveredItemBounds;
265 if ( !mSelectedRubberBand )
266 createSelectedItemBand();
269 mSelectedRubberBand->show();
273 emit
itemSelected( annotationLayerFromId( mSelectedItemLayerId ), mSelectedItemId );
278 case Action::MoveItem:
280 if ( event->button() == Qt::RightButton )
282 mCurrentAction = Action::NoAction;
283 mTemporaryRubberBand.
reset();
284 if ( mSelectedRubberBand )
287 mSelectedRubberBand->show();
289 mHoveredItemNodeRubberBands.clear();
292 else if ( event->button() == Qt::LeftButton )
300 event->
pixelPoint().x() - mMoveStartPointPixels.
x(),
301 event->
pixelPoint().y() - mMoveStartPointPixels.
y() );
302 switch (
layer->applyEditV2( &operation, context ) )
306 mRefreshSelectedItemAfterRedraw =
true;
314 mTemporaryRubberBand.
reset();
315 mCurrentAction = Action::NoAction;
321 case Action::MoveNode:
323 if ( event->button() == Qt::RightButton )
325 mCurrentAction = Action::NoAction;
326 mTemporaryRubberBand.
reset();
327 mHoveredItemNodeRubberBands.clear();
328 mTemporaryRubberBand.
reset();
331 else if ( event->button() == Qt::LeftButton )
337 event->
pixelPoint().x() - mMoveStartPointPixels.
x(),
338 event->
pixelPoint().y() - mMoveStartPointPixels.
y() );
339 switch (
layer->applyEditV2( &operation, context ) )
343 mRefreshSelectedItemAfterRedraw =
true;
352 mTemporaryRubberBand.
reset();
353 mHoveredItemNodeRubberBands.clear();
354 mHoveredItemNodes.clear();
355 mTemporaryRubberBand.
reset();
356 mCurrentAction = Action::NoAction;
366 switch ( mCurrentAction )
368 case Action::NoAction:
369 case Action::MoveItem:
371 if ( event->button() != Qt::LeftButton )
374 mCurrentAction = Action::NoAction;
375 if ( mHoveredItemId == mSelectedItemId && mHoveredItemLayerId == mSelectedItemLayerId )
386 switch (
layer->applyEditV2( &operation, context ) )
390 mRefreshSelectedItemAfterRedraw =
true;
402 mSelectedItemId = mHoveredItemId;
403 mSelectedItemLayerId = mHoveredItemLayerId;
404 mSelectedItemBounds = mHoveredItemBounds;
406 if ( !mSelectedRubberBand )
407 createSelectedItemBand();
410 mSelectedRubberBand->show();
414 emit
itemSelected( annotationLayerFromId( mSelectedItemLayerId ), mSelectedItemId );
419 case Action::MoveNode:
431 switch ( mCurrentAction )
433 case Action::NoAction:
435 if ( event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete )
438 if ( !
layer || mSelectedItemId.isEmpty() )
441 layer->removeItem( mSelectedItemId );
446 else if ( event->key() == Qt::Key_Left
447 || event->key() == Qt::Key_Right
448 || event->key() == Qt::Key_Up
449 || event->key() == Qt::Key_Down )
457 switch (
layer->applyEditV2( &operation, context ) )
461 mRefreshSelectedItemAfterRedraw =
true;
472 case Action::MoveNode:
474 if ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace )
479 switch (
layer->applyEditV2( &operation, context ) )
483 mRefreshSelectedItemAfterRedraw =
true;
493 mTemporaryRubberBand.
reset();
494 mHoveredItemNodeRubberBands.clear();
495 mHoveredItemNodes.clear();
496 mTemporaryRubberBand.
reset();
497 mCurrentAction = Action::NoAction;
505 case Action::MoveItem:
508 if ( event->key() == Qt::Key_Escape )
510 mCurrentAction = Action::NoAction;
511 mTemporaryRubberBand.
reset();
512 if ( mSelectedRubberBand )
515 mSelectedRubberBand->show();
517 mHoveredItemNodeRubberBands.clear();
526void QgsMapToolModifyAnnotation::onCanvasRefreshed()
528 bool needsSelectedItemRefresh = mRefreshSelectedItemAfterRedraw;
529 if (
QgsAnnotationItem *item = annotationItemFromId( mSelectedItemLayerId, mSelectedItemId ) )
533 needsSelectedItemRefresh =
true;
537 if ( needsSelectedItemRefresh )
540 if ( !renderedItemResults )
545 const QList<QgsRenderedItemDetails *> items = renderedItemResults->
renderedItems();
548 if ( const QgsRenderedAnnotationItemDetails *annotationItem = dynamic_cast< const QgsRenderedAnnotationItemDetails *>( item ) )
550 if ( annotationItem->itemId() == mSelectedItemId && annotationItem->layerId() == mSelectedItemLayerId )
555 if ( it != items.end() )
560 if ( !mSelectedRubberBand )
561 createSelectedItemBand();
564 mSelectedRubberBand->show();
565 mSelectedItemBounds = mHoveredItemBounds;
571 const QgsPointXY mapPoint = canvas()->mapSettings().mapToPixel().toMapCoordinates( mLastHoverPoint );
572 setHoveredItemFromPoint( mapPoint );
574 mRefreshSelectedItemAfterRedraw =
false;
579 mHoveredItemNodeRubberBands.clear();
580 if ( mHoveredNodeRubberBand )
581 mHoveredNodeRubberBand->hide();
582 mHoveredItemId = item->
itemId();
583 mHoveredItemLayerId = item->
layerId();
584 mHoveredItemBounds = itemMapBounds;
585 if ( !mHoverRubberBand )
588 mHoverRubberBand->show();
599 if ( !annotationItem )
604 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
610 const QList< QgsAnnotationItemNode > itemNodes = annotationItem->
nodesV2( context );
614 vertexNodeBand->
setWidth( scaleFactor );
616 vertexNodeBand->
setColor( QColor( 200, 0, 120, 255 ) );
619 calloutNodeBand->
setWidth( scaleFactor );
621 calloutNodeBand->
setColor( QColor( 120, 200, 0, 255 ) );
626 mHoveredItemNodesSpatialIndex = std::make_unique< QgsAnnotationItemNodesSpatialIndex >();
628 mHoveredItemNodes.clear();
629 mHoveredItemNodes.reserve( itemNodes.size() );
635 nodeMapPoint = layerToMapTransform.
transform( node.point() );
642 switch ( node.type() )
645 vertexNodeBand->
addPoint( nodeMapPoint );
649 calloutNodeBand->
addPoint( nodeMapPoint );
653 mHoveredItemNodesSpatialIndex->insert( index,
QgsRectangle( nodeMapPoint.
x(), nodeMapPoint.
y(),
654 nodeMapPoint.
x(), nodeMapPoint.
y() ) );
657 transformedNode.
setPoint( nodeMapPoint );
658 mHoveredItemNodes.append( transformedNode );
663 mHoveredItemNodeRubberBands.emplace_back( vertexNodeBand );
664 mHoveredItemNodeRubberBands.emplace_back( calloutNodeBand );
667QSizeF QgsMapToolModifyAnnotation::deltaForKeyEvent(
QgsAnnotationLayer *layer,
const QgsPointXY &originalCanvasPoint, QKeyEvent *event )
669 const double canvasDpi =
canvas()->window()->windowHandle()->screen()->physicalDotsPerInch();
672 double incrementPixels = 0.0;
673 if ( event->modifiers() & Qt::ShiftModifier )
676 incrementPixels = 20.0 / 25.4 * canvasDpi;
678 else if ( event->modifiers() & Qt::AltModifier )
686 incrementPixels = 5.0 / 25.4 * canvasDpi;
689 double deltaXPixels = 0;
690 double deltaYPixels = 0;
691 switch ( event->key() )
694 deltaXPixels = -incrementPixels;
697 deltaXPixels = incrementPixels;
700 deltaYPixels = -incrementPixels;
703 deltaYPixels = incrementPixels;
712 const QgsPointXY afterMoveCanvasPoint( originalCanvasPoint.
x() + deltaXPixels, originalCanvasPoint.
y() + deltaYPixels );
716 return QSizeF( afterMoveLayerPoint.
x() - beforeMoveLayerPoint.
x(), afterMoveLayerPoint.
y() - beforeMoveLayerPoint.
y() );
722 double closestItemDistance = std::numeric_limits< double >::max();
723 double closestItemArea = std::numeric_limits< double >::max();
728 if ( !annotationItem )
732 const double itemDistance = itemBounds.
contains( mapPoint ) ? 0 : itemBounds.
distance( mapPoint );
733 if ( !closestItem || itemDistance < closestItemDistance || ( itemDistance == closestItemDistance && itemBounds.
area() < closestItemArea ) )
736 closestItemDistance = itemDistance;
737 closestItemArea = itemBounds.
area();
744QgsAnnotationLayer *QgsMapToolModifyAnnotation::annotationLayerFromId(
const QString &layerId )
752QgsAnnotationItem *QgsMapToolModifyAnnotation::annotationItemFromId(
const QString &layerId,
const QString &itemId )
755 return layer ?
layer->item( itemId ) :
nullptr;
758void QgsMapToolModifyAnnotation::setHoveredItemFromPoint(
const QgsPointXY &mapPoint )
764 if ( !renderedItemResults )
786 if ( closestItem->
itemId() != mHoveredItemId || closestItem->
layerId() != mHoveredItemLayerId )
788 setHoveredItem( closestItem, itemBounds );
793 if ( closestItem->
itemId() == mSelectedItemId && closestItem->
layerId() == mSelectedItemLayerId )
795 double currentNodeDistance = std::numeric_limits< double >::max();
796 mHoveredItemNodesSpatialIndex->intersects( searchRect, [&hoveredNode, ¤tNodeDistance, &mapPoint,
this](
int index )->
bool
798 if ( index >= mHoveredItemNodes.size() )
802 const double nodeDistance = thisNode.
point().
sqrDist( mapPoint );
803 if ( nodeDistance < currentNodeDistance )
805 hoveredNode = thisNode;
806 currentNodeDistance = nodeDistance;
815 if ( mHoveredNodeRubberBand )
816 mHoveredNodeRubberBand->hide();
817 setCursor( mHoveredItemId == mSelectedItemId && mHoveredItemLayerId == mSelectedItemLayerId ? Qt::OpenHandCursor : Qt::ArrowCursor );
821 if ( !mHoveredNodeRubberBand )
822 createHoveredNodeBand();
826 mHoveredNodeRubberBand->show();
832void QgsMapToolModifyAnnotation::clearHoveredItem()
834 if ( mHoverRubberBand )
835 mHoverRubberBand->hide();
836 if ( mHoveredNodeRubberBand )
837 mHoveredNodeRubberBand->hide();
839 mHoveredItemId.clear();
840 mHoveredItemLayerId.clear();
841 mHoveredItemNodeRubberBands.clear();
842 mHoveredItemNodesSpatialIndex.reset();
847void QgsMapToolModifyAnnotation::clearSelectedItem()
849 if ( mSelectedRubberBand )
850 mSelectedRubberBand->hide();
852 const bool hadSelection = !mSelectedItemId.isEmpty();
853 mSelectedItemId.clear();
854 mSelectedItemLayerId.clear();
859void QgsMapToolModifyAnnotation::createHoverBand()
861 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
864 mHoverRubberBand->
setWidth( scaleFactor );
866 mHoverRubberBand->
setColor( QColor( 100, 100, 100, 155 ) );
869void QgsMapToolModifyAnnotation::createHoveredNodeBand()
871 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
875 mHoveredNodeRubberBand->
setWidth( scaleFactor );
876 mHoveredNodeRubberBand->
setIconSize( scaleFactor * 5 );
877 mHoveredNodeRubberBand->
setColor( QColor( 200, 0, 120, 255 ) );
880void QgsMapToolModifyAnnotation::createSelectedItemBand()
882 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
885 mSelectedRubberBand->
setWidth( scaleFactor );
887 mSelectedRubberBand->
setColor( QColor( 50, 50, 50, 200 ) );
void reset(T *p=nullptr)
Will reset the managed pointer to p.
@ VertexHandle
Node is a handle for manipulating vertices.
@ CalloutHandle
Node is a handle for manipulating callouts.
@ ScaleDependentBoundingBox
Item's bounding box will vary depending on map scale.
@ Invalid
Operation has invalid parameters for the item, no change occurred.
@ Success
Item was modified successfully.
@ ItemCleared
The operation results in the item being cleared, and the item should be removed from the layer as a r...
Encapsulates the context for an annotation item edit operation.
void setCurrentItemBounds(const QgsRectangle &bounds)
Sets the current rendered bounds of the item, in the annotation layer's CRS.
void setRenderContext(const QgsRenderContext &context)
Sets the render context associated with the edit operation.
Annotation item edit operation consisting of adding a node.
Annotation item edit operation consisting of deleting a node.
Annotation item edit operation consisting of moving a node.
Annotation item edit operation consisting of translating (moving) an item.
Contains information about a node used for editing an annotation item.
void setPoint(QgsPointXY point)
Sets the node's position, in geographic coordinates.
QgsPointXY point() const
Returns the node's position, in geographic coordinates.
Qt::CursorShape cursor() const
Returns the mouse cursor shape to use when hovering the node.
QgsVertexId id() const
Returns the ID number of the node, used for uniquely identifying the node in the item.
Abstract base class for annotation items which are drawn with QgsAnnotationLayers.
virtual QList< QgsAnnotationItemNode > nodesV2(const QgsAnnotationItemEditContext &context) const
Returns the nodes for the item, used for editing the item.
Represents a map layer containing a set of georeferenced annotations, e.g.
Custom exception class for Coordinate Reference System related exceptions.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
Map canvas is a class for displaying all GIS data types on a canvas.
const QgsRenderedItemResults * renderedItemResults(bool allowOutdatedResults=true) const
Gets access to the rendered item results (may be nullptr), which includes the results of rendering an...
void mapCanvasRefreshed()
Emitted when canvas finished a refresh request.
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
QgsCoordinateReferenceSystem crs
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsPointXY mapPoint() const
mapPoint returns the point in coordinates
QPoint pixelPoint() const
The snapped mouse cursor in pixel coordinates.
QgsPointLocator::Match mapPointMatch() const
Returns the matching data from the most recently snapped point.
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
A class to represent a 2D point.
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
bool isEmpty() const
Returns true if the geometry is empty.
Point geometry type, with support for z-dimension and m-values.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsAnnotationLayer * mainAnnotationLayer()
Returns the main annotation layer associated with the project.
void setDirty(bool b=true)
Flag the project as dirty (modified).
A rectangle specified with double values.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
double area() const
Returns the area of the rectangle.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
void grow(double delta)
Grows the rectangle in place by the specified amount.
double distance(const QgsPointXY &point) const
Returns the distance from point to the nearest point on the boundary of the rectangle.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
Contains information about a rendered annotation item.
QString itemId() const
Returns the item ID of the associated annotation item.
Base class for detailed information about a rendered item.
QString layerId() const
Returns the layer ID of the associated map layer.
QgsRectangle boundingBox() const
Returns the bounding box of the item (in map units).
Stores collated details of rendered items during a map rendering operation.
QList< const QgsRenderedAnnotationItemDetails * > renderedAnnotationItemsInBounds(const QgsRectangle &bounds) const
Returns a list with details of the rendered annotation items within the specified bounds.
QList< QgsRenderedItemDetails * > renderedItems() const
Returns a list of all rendered items.
A class for drawing transient features (e.g.
void setIconSize(double iconSize)
Sets the size of the point icons.
QgsGeometry asGeometry() const
Returns the rubberband as a Geometry.
void setWidth(double width)
Sets the width of the line.
void reset(Qgis::GeometryType geometryType=Qgis::GeometryType::Line)
Clears all the geometries in this rubberband.
void setSecondaryStrokeColor(const QColor &color)
Sets a secondary stroke color for the rubberband which will be drawn under the main stroke color.
@ ICON_X
A cross is used to highlight points (x)
@ ICON_FULL_BOX
A full box is used to highlight points (■)
@ ICON_BOX
A box is used to highlight points (□)
void setColor(const QColor &color)
Sets the color for the rubberband.
void setToGeometry(const QgsGeometry &geom, QgsVectorLayer *layer)
Sets this rubber band to geom.
void setIcon(IconType icon)
Sets the icon type to highlight point geometries.
void copyPointsFrom(const QgsRubberBand *other)
Copies the points from another rubber band.
void setTranslationOffset(double dx, double dy)
Adds translation to original coordinates (all in map coordinates)
void addPoint(const QgsPointXY &p, bool doUpdate=true, int geometryIndex=0, int ringIndex=0)
Adds a vertex to the rubberband and update canvas.
Class that shows snapping marker on map canvas for the current snapping match.
A class to represent a vector.
double y() const
Returns the vector's y-component.
double x() const
Returns the vector's x-component.