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() )
130 const QgsPointXY mapPoint =
event->mapPoint();
132 switch ( mCurrentAction )
134 case Action::NoAction:
140 if ( !renderedItemResults )
162 if ( closestItem->
itemId() != mHoveredItemId || closestItem->
layerId() != mHoveredItemLayerId )
164 setHoveredItem( closestItem, itemBounds );
169 if ( closestItem->
itemId() == mSelectedItemId && closestItem->
layerId() == mSelectedItemLayerId )
171 double currentNodeDistance = std::numeric_limits< double >::max();
172 mHoveredItemNodesSpatialIndex->intersects( searchRect, [&hoveredNode, ¤tNodeDistance, &mapPoint,
this](
int index )->
bool
175 const double nodeDistance = thisNode.
point().sqrDist( mapPoint );
176 if ( nodeDistance < currentNodeDistance )
178 hoveredNode = thisNode;
179 currentNodeDistance = nodeDistance;
188 if ( mHoveredNodeRubberBand )
189 mHoveredNodeRubberBand->hide();
190 setCursor( mHoveredItemId == mSelectedItemId && mHoveredItemLayerId == mSelectedItemLayerId ? Qt::OpenHandCursor : Qt::ArrowCursor );
194 if ( !mHoveredNodeRubberBand )
195 createHoveredNodeBand();
199 mHoveredNodeRubberBand->show();
206 case Action::MoveItem:
208 if (
QgsAnnotationItem *item = annotationItemFromId( mSelectedItemLayerId, mSelectedItemId ) )
214 std::unique_ptr< QgsAnnotationItemEditOperationTransientResults > operationResults( item->transientEditResults( &operation ) );
215 if ( operationResults )
218 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
219 mTemporaryRubberBand->
setWidth( scaleFactor );
224 mTemporaryRubberBand.
reset();
230 case Action::MoveNode:
232 if (
QgsAnnotationItem *item = annotationItemFromId( mSelectedItemLayerId, mSelectedItemId ) )
237 std::unique_ptr< QgsAnnotationItemEditOperationTransientResults > operationResults( item->transientEditResults( &operation ) );
238 if ( operationResults )
241 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
242 mTemporaryRubberBand->
setWidth( scaleFactor );
247 mTemporaryRubberBand.
reset();
258 switch ( mCurrentAction )
260 case Action::NoAction:
262 if ( event->button() != Qt::LeftButton )
265 if ( mHoveredItemId.isEmpty() || !mHoverRubberBand )
269 if ( mHoveredItemId == mSelectedItemId && mHoveredItemLayerId == mSelectedItemLayerId )
274 const QgsPointXY mapPoint =
event->mapPoint();
279 double currentNodeDistance = std::numeric_limits< double >::max();
280 mHoveredItemNodesSpatialIndex->intersects( searchRect, [&hoveredNode, ¤tNodeDistance, &mapPoint,
this](
int index )->
bool
283 const double nodeDistance = thisNode.
point().sqrDist( mapPoint );
284 if ( nodeDistance < currentNodeDistance )
286 hoveredNode = thisNode;
287 currentNodeDistance = nodeDistance;
292 mMoveStartPointCanvasCrs = mapPoint;
294 if ( mHoverRubberBand )
295 mHoverRubberBand->hide();
296 if ( mSelectedRubberBand )
297 mSelectedRubberBand->hide();
301 mCurrentAction = Action::MoveItem;
305 mCurrentAction = Action::MoveNode;
306 mTargetNode = hoveredNode;
313 mSelectedItemId = mHoveredItemId;
314 mSelectedItemLayerId = mHoveredItemLayerId;
316 if ( !mSelectedRubberBand )
317 createSelectedItemBand();
320 mSelectedRubberBand->show();
324 emit
itemSelected( annotationLayerFromId( mSelectedItemLayerId ), mSelectedItemId );
329 case Action::MoveItem:
331 if ( event->button() == Qt::RightButton )
333 mCurrentAction = Action::NoAction;
334 mTemporaryRubberBand.
reset();
335 if ( mSelectedRubberBand )
338 mSelectedRubberBand->show();
340 mHoveredItemNodeRubberBands.clear();
343 else if ( event->button() == Qt::LeftButton )
351 switch (
layer->applyEdit( &operation ) )
355 mRefreshSelectedItemAfterRedraw =
true;
363 mTemporaryRubberBand.
reset();
364 mCurrentAction = Action::NoAction;
370 case Action::MoveNode:
372 if ( event->button() == Qt::RightButton )
374 mCurrentAction = Action::NoAction;
375 mTemporaryRubberBand.
reset();
376 mHoveredItemNodeRubberBands.clear();
377 mTemporaryRubberBand.
reset();
380 else if ( event->button() == Qt::LeftButton )
386 switch (
layer->applyEdit( &operation ) )
390 mRefreshSelectedItemAfterRedraw =
true;
399 mTemporaryRubberBand.
reset();
400 mHoveredItemNodeRubberBands.clear();
401 mHoveredItemNodes.clear();
402 mTemporaryRubberBand.
reset();
403 mCurrentAction = Action::NoAction;
413 switch ( mCurrentAction )
415 case Action::NoAction:
416 case Action::MoveItem:
418 if ( event->button() != Qt::LeftButton )
421 mCurrentAction = Action::NoAction;
422 if ( mHoveredItemId == mSelectedItemId && mHoveredItemLayerId == mSelectedItemLayerId )
429 switch (
layer->applyEdit( &operation ) )
433 mRefreshSelectedItemAfterRedraw =
true;
445 mSelectedItemId = mHoveredItemId;
446 mSelectedItemLayerId = mHoveredItemLayerId;
448 if ( !mSelectedRubberBand )
449 createSelectedItemBand();
452 mSelectedRubberBand->show();
456 emit
itemSelected( annotationLayerFromId( mSelectedItemLayerId ), mSelectedItemId );
461 case Action::MoveNode:
468 switch ( mCurrentAction )
470 case Action::NoAction:
472 if ( event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete )
475 if ( !
layer || mSelectedItemId.isEmpty() )
478 layer->removeItem( mSelectedItemId );
483 else if ( event->key() == Qt::Key_Left
484 || event->key() == Qt::Key_Right
485 || event->key() == Qt::Key_Up
486 || event->key() == Qt::Key_Down )
495 switch (
layer->applyEdit( &operation ) )
499 mRefreshSelectedItemAfterRedraw =
true;
510 case Action::MoveNode:
512 if ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace )
517 switch (
layer->applyEdit( &operation ) )
521 mRefreshSelectedItemAfterRedraw =
true;
531 mTemporaryRubberBand.
reset();
532 mHoveredItemNodeRubberBands.clear();
533 mHoveredItemNodes.clear();
534 mTemporaryRubberBand.
reset();
535 mCurrentAction = Action::NoAction;
543 case Action::MoveItem:
546 if ( event->key() == Qt::Key_Escape )
548 mCurrentAction = Action::NoAction;
549 mTemporaryRubberBand.
reset();
550 if ( mSelectedRubberBand )
553 mSelectedRubberBand->show();
555 mHoveredItemNodeRubberBands.clear();
564void QgsMapToolModifyAnnotation::onCanvasRefreshed()
566 if ( mRefreshSelectedItemAfterRedraw )
569 if ( !renderedItemResults )
574 const QList<QgsRenderedItemDetails *> items = renderedItemResults->
renderedItems();
577 if ( const QgsRenderedAnnotationItemDetails *annotationItem = dynamic_cast< const QgsRenderedAnnotationItemDetails *>( item ) )
579 if ( annotationItem->itemId() == mSelectedItemId && annotationItem->layerId() == mSelectedItemLayerId )
584 if ( it != items.end() )
589 if ( !mSelectedRubberBand )
590 createSelectedItemBand();
593 mSelectedRubberBand->show();
596 mRefreshSelectedItemAfterRedraw =
false;
601 mHoveredItemNodeRubberBands.clear();
602 if ( mHoveredNodeRubberBand )
603 mHoveredNodeRubberBand->hide();
604 mHoveredItemId = item->
itemId();
605 mHoveredItemLayerId = item->
layerId();
606 if ( !mHoverRubberBand )
609 mHoverRubberBand->show();
620 if ( !annotationItem )
625 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
627 const QList< QgsAnnotationItemNode > itemNodes = annotationItem->
nodes();
631 vertexNodeBand->
setWidth( scaleFactor );
633 vertexNodeBand->
setColor( QColor( 200, 0, 120, 255 ) );
636 mHoveredItemNodesSpatialIndex = std::make_unique< QgsAnnotationItemNodesSpatialIndex >();
638 mHoveredItemNodes.clear();
639 mHoveredItemNodes.reserve( itemNodes.size() );
645 nodeMapPoint = layerToMapTransform.
transform( node.point() );
652 switch ( node.type() )
655 vertexNodeBand->
addPoint( nodeMapPoint );
659 mHoveredItemNodesSpatialIndex->insert( index,
QgsRectangle( nodeMapPoint.
x(), nodeMapPoint.
y(),
660 nodeMapPoint.
x(), nodeMapPoint.
y() ) );
663 transformedNode.
setPoint( nodeMapPoint );
664 mHoveredItemNodes.append( transformedNode );
669 mHoveredItemNodeRubberBands.emplace_back( vertexNodeBand );
672QSizeF QgsMapToolModifyAnnotation::deltaForKeyEvent(
QgsAnnotationLayer *layer,
const QgsPointXY &originalCanvasPoint, QKeyEvent *event )
674 const double canvasDpi =
canvas()->window()->windowHandle()->screen()->physicalDotsPerInch();
677 double incrementPixels = 0.0;
678 if ( event->modifiers() & Qt::ShiftModifier )
681 incrementPixels = 20.0 / 25.4 * canvasDpi;
683 else if ( event->modifiers() & Qt::AltModifier )
691 incrementPixels = 5.0 / 25.4 * canvasDpi;
694 double deltaXPixels = 0;
695 double deltaYPixels = 0;
696 switch ( event->key() )
699 deltaXPixels = -incrementPixels;
702 deltaXPixels = incrementPixels;
705 deltaYPixels = -incrementPixels;
708 deltaYPixels = incrementPixels;
717 const QgsPointXY afterMoveCanvasPoint( originalCanvasPoint.
x() + deltaXPixels, originalCanvasPoint.
y() + deltaYPixels );
721 return QSizeF( afterMoveLayerPoint.
x() - beforeMoveLayerPoint.
x(), afterMoveLayerPoint.
y() - beforeMoveLayerPoint.
y() );
727 double closestItemDistance = std::numeric_limits< double >::max();
728 int closestItemZ = 0;
733 if ( !annotationItem )
737 const double itemDistance = itemBounds.
contains( mapPoint ) ? 0 : itemBounds.
distance( mapPoint );
738 if ( !closestItem || itemDistance < closestItemDistance || ( itemDistance == closestItemDistance && annotationItem->
zIndex() > closestItemZ ) )
741 closestItemDistance = itemDistance;
742 closestItemZ = annotationItem->
zIndex();
749QgsAnnotationLayer *QgsMapToolModifyAnnotation::annotationLayerFromId(
const QString &layerId )
757QgsAnnotationItem *QgsMapToolModifyAnnotation::annotationItemFromId(
const QString &layerId,
const QString &itemId )
760 return layer ?
layer->item( itemId ) :
nullptr;
763void QgsMapToolModifyAnnotation::clearHoveredItem()
765 if ( mHoverRubberBand )
766 mHoverRubberBand->hide();
767 if ( mHoveredNodeRubberBand )
768 mHoveredNodeRubberBand->hide();
770 mHoveredItemId.clear();
771 mHoveredItemLayerId.clear();
772 mHoveredItemNodeRubberBands.clear();
773 mHoveredItemNodesSpatialIndex.reset();
778void QgsMapToolModifyAnnotation::clearSelectedItem()
780 if ( mSelectedRubberBand )
781 mSelectedRubberBand->hide();
783 const bool hadSelection = !mSelectedItemId.isEmpty();
784 mSelectedItemId.clear();
785 mSelectedItemLayerId.clear();
790void QgsMapToolModifyAnnotation::createHoverBand()
792 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
795 mHoverRubberBand->
setWidth( scaleFactor );
797 mHoverRubberBand->
setColor( QColor( 100, 100, 100, 155 ) );
800void QgsMapToolModifyAnnotation::createHoveredNodeBand()
802 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
806 mHoveredNodeRubberBand->
setWidth( scaleFactor );
807 mHoveredNodeRubberBand->
setIconSize( scaleFactor * 5 );
808 mHoveredNodeRubberBand->
setColor( QColor( 200, 0, 120, 255 ) );
811void QgsMapToolModifyAnnotation::createSelectedItemBand()
813 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
816 mSelectedRubberBand->
setWidth( scaleFactor );
818 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.
@ 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...
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.
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 > nodes() const
Returns the nodes for the item, used for editing the item.
int zIndex() const
Returns the item's z index, which controls the order in which annotation items are rendered in the la...
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
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.
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 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.
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 setWidth(int width)
Sets the width of the line.
QgsGeometry asGeometry() const
Returns the rubberband as a Geometry.
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_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 setIconSize(int iconSize)
Sets the size of the point icons.
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.