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() )
129 const QgsPointXY mapPoint =
event->mapPoint();
131 switch ( mCurrentAction )
133 case Action::NoAction:
139 if ( !renderedItemResults )
161 if ( closestItem->
itemId() != mHoveredItemId || closestItem->
layerId() != mHoveredItemLayerId )
163 setHoveredItem( closestItem, itemBounds );
168 if ( closestItem->
itemId() == mSelectedItemId && closestItem->
layerId() == mSelectedItemLayerId )
170 double currentNodeDistance = std::numeric_limits< double >::max();
171 mHoveredItemNodesSpatialIndex->intersects( searchRect, [&hoveredNode, ¤tNodeDistance, &mapPoint,
this](
int index )->
bool
174 const double nodeDistance = thisNode.
point().sqrDist( mapPoint );
175 if ( nodeDistance < currentNodeDistance )
177 hoveredNode = thisNode;
178 currentNodeDistance = nodeDistance;
187 if ( mHoveredNodeRubberBand )
188 mHoveredNodeRubberBand->hide();
189 setCursor( mHoveredItemId == mSelectedItemId && mHoveredItemLayerId == mSelectedItemLayerId ? Qt::OpenHandCursor : Qt::ArrowCursor );
193 if ( !mHoveredNodeRubberBand )
194 createHoveredNodeBand();
198 mHoveredNodeRubberBand->show();
205 case Action::MoveItem:
207 if (
QgsAnnotationItem *item = annotationItemFromId( mSelectedItemLayerId, mSelectedItemId ) )
213 std::unique_ptr< QgsAnnotationItemEditOperationTransientResults > operationResults( item->transientEditResults( &operation ) );
214 if ( operationResults )
217 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
218 mTemporaryRubberBand->
setWidth( scaleFactor );
223 mTemporaryRubberBand.
reset();
229 case Action::MoveNode:
231 if (
QgsAnnotationItem *item = annotationItemFromId( mSelectedItemLayerId, mSelectedItemId ) )
236 std::unique_ptr< QgsAnnotationItemEditOperationTransientResults > operationResults( item->transientEditResults( &operation ) );
237 if ( operationResults )
240 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
241 mTemporaryRubberBand->
setWidth( scaleFactor );
246 mTemporaryRubberBand.
reset();
257 switch ( mCurrentAction )
259 case Action::NoAction:
261 if ( event->button() != Qt::LeftButton )
264 if ( mHoveredItemId.isEmpty() || !mHoverRubberBand )
268 if ( mHoveredItemId == mSelectedItemId && mHoveredItemLayerId == mSelectedItemLayerId )
273 const QgsPointXY mapPoint =
event->mapPoint();
278 double currentNodeDistance = std::numeric_limits< double >::max();
279 mHoveredItemNodesSpatialIndex->intersects( searchRect, [&hoveredNode, ¤tNodeDistance, &mapPoint,
this](
int index )->
bool
282 const double nodeDistance = thisNode.
point().sqrDist( mapPoint );
283 if ( nodeDistance < currentNodeDistance )
285 hoveredNode = thisNode;
286 currentNodeDistance = nodeDistance;
291 mMoveStartPointCanvasCrs = mapPoint;
293 if ( mHoverRubberBand )
294 mHoverRubberBand->hide();
295 if ( mSelectedRubberBand )
296 mSelectedRubberBand->hide();
300 mCurrentAction = Action::MoveItem;
304 mCurrentAction = Action::MoveNode;
305 mTargetNode = hoveredNode;
312 mSelectedItemId = mHoveredItemId;
313 mSelectedItemLayerId = mHoveredItemLayerId;
315 if ( !mSelectedRubberBand )
316 createSelectedItemBand();
319 mSelectedRubberBand->show();
323 emit
itemSelected( annotationLayerFromId( mSelectedItemLayerId ), mSelectedItemId );
328 case Action::MoveItem:
330 if ( event->button() == Qt::RightButton )
332 mCurrentAction = Action::NoAction;
333 mTemporaryRubberBand.
reset();
334 if ( mSelectedRubberBand )
337 mSelectedRubberBand->show();
339 mHoveredItemNodeRubberBands.clear();
342 else if ( event->button() == Qt::LeftButton )
350 switch (
layer->applyEdit( &operation ) )
354 mRefreshSelectedItemAfterRedraw =
true;
362 mTemporaryRubberBand.
reset();
363 mCurrentAction = Action::NoAction;
369 case Action::MoveNode:
371 if ( event->button() == Qt::RightButton )
373 mCurrentAction = Action::NoAction;
374 mTemporaryRubberBand.
reset();
375 mHoveredItemNodeRubberBands.clear();
376 mTemporaryRubberBand.
reset();
379 else if ( event->button() == Qt::LeftButton )
385 switch (
layer->applyEdit( &operation ) )
389 mRefreshSelectedItemAfterRedraw =
true;
398 mTemporaryRubberBand.
reset();
399 mHoveredItemNodeRubberBands.clear();
400 mHoveredItemNodes.clear();
401 mTemporaryRubberBand.
reset();
402 mCurrentAction = Action::NoAction;
412 switch ( mCurrentAction )
414 case Action::NoAction:
415 case Action::MoveItem:
417 if ( event->button() != Qt::LeftButton )
420 mCurrentAction = Action::NoAction;
421 if ( mHoveredItemId == mSelectedItemId && mHoveredItemLayerId == mSelectedItemLayerId )
428 switch (
layer->applyEdit( &operation ) )
432 mRefreshSelectedItemAfterRedraw =
true;
444 mSelectedItemId = mHoveredItemId;
445 mSelectedItemLayerId = mHoveredItemLayerId;
447 if ( !mSelectedRubberBand )
448 createSelectedItemBand();
451 mSelectedRubberBand->show();
455 emit
itemSelected( annotationLayerFromId( mSelectedItemLayerId ), mSelectedItemId );
460 case Action::MoveNode:
467 switch ( mCurrentAction )
469 case Action::NoAction:
471 if ( event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete )
474 if ( !
layer || mSelectedItemId.isEmpty() )
477 layer->removeItem( mSelectedItemId );
482 else if ( event->key() == Qt::Key_Left
483 || event->key() == Qt::Key_Right
484 || event->key() == Qt::Key_Up
485 || event->key() == Qt::Key_Down )
494 switch (
layer->applyEdit( &operation ) )
498 mRefreshSelectedItemAfterRedraw =
true;
509 case Action::MoveNode:
511 if ( event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace )
516 switch (
layer->applyEdit( &operation ) )
520 mRefreshSelectedItemAfterRedraw =
true;
530 mTemporaryRubberBand.
reset();
531 mHoveredItemNodeRubberBands.clear();
532 mHoveredItemNodes.clear();
533 mTemporaryRubberBand.
reset();
534 mCurrentAction = Action::NoAction;
542 case Action::MoveItem:
545 if ( event->key() == Qt::Key_Escape )
547 mCurrentAction = Action::NoAction;
548 mTemporaryRubberBand.
reset();
549 if ( mSelectedRubberBand )
552 mSelectedRubberBand->show();
554 mHoveredItemNodeRubberBands.clear();
563void QgsMapToolModifyAnnotation::onCanvasRefreshed()
565 if ( mRefreshSelectedItemAfterRedraw )
568 if ( !renderedItemResults )
573 const QList<QgsRenderedItemDetails *> items = renderedItemResults->
renderedItems();
576 if ( const QgsRenderedAnnotationItemDetails *annotationItem = dynamic_cast< const QgsRenderedAnnotationItemDetails *>( item ) )
578 if ( annotationItem->itemId() == mSelectedItemId && annotationItem->layerId() == mSelectedItemLayerId )
583 if ( it != items.end() )
588 if ( !mSelectedRubberBand )
589 createSelectedItemBand();
592 mSelectedRubberBand->show();
595 mRefreshSelectedItemAfterRedraw =
false;
600 mHoveredItemNodeRubberBands.clear();
601 if ( mHoveredNodeRubberBand )
602 mHoveredNodeRubberBand->hide();
603 mHoveredItemId = item->
itemId();
604 mHoveredItemLayerId = item->
layerId();
605 if ( !mHoverRubberBand )
608 mHoverRubberBand->show();
619 if ( !annotationItem )
624 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
626 const QList< QgsAnnotationItemNode > itemNodes = annotationItem->
nodes();
630 vertexNodeBand->
setWidth( scaleFactor );
632 vertexNodeBand->
setColor( QColor( 200, 0, 120, 255 ) );
635 mHoveredItemNodesSpatialIndex = std::make_unique< QgsAnnotationItemNodesSpatialIndex >();
637 mHoveredItemNodes.clear();
638 mHoveredItemNodes.reserve( itemNodes.size() );
644 nodeMapPoint = layerToMapTransform.
transform( node.point() );
651 switch ( node.type() )
654 vertexNodeBand->
addPoint( nodeMapPoint );
658 mHoveredItemNodesSpatialIndex->insert( index,
QgsRectangle( nodeMapPoint.
x(), nodeMapPoint.
y(),
659 nodeMapPoint.
x(), nodeMapPoint.
y() ) );
662 transformedNode.
setPoint( nodeMapPoint );
663 mHoveredItemNodes.append( transformedNode );
668 mHoveredItemNodeRubberBands.emplace_back( vertexNodeBand );
671QSizeF QgsMapToolModifyAnnotation::deltaForKeyEvent(
QgsAnnotationLayer *layer,
const QgsPointXY &originalCanvasPoint, QKeyEvent *event )
673 const double canvasDpi =
canvas()->window()->windowHandle()->screen()->physicalDotsPerInch();
676 double incrementPixels = 0.0;
677 if ( event->modifiers() & Qt::ShiftModifier )
680 incrementPixels = 20.0 / 25.4 * canvasDpi;
682 else if ( event->modifiers() & Qt::AltModifier )
690 incrementPixels = 5.0 / 25.4 * canvasDpi;
693 double deltaXPixels = 0;
694 double deltaYPixels = 0;
695 switch ( event->key() )
698 deltaXPixels = -incrementPixels;
701 deltaXPixels = incrementPixels;
704 deltaYPixels = -incrementPixels;
707 deltaYPixels = incrementPixels;
716 const QgsPointXY afterMoveCanvasPoint( originalCanvasPoint.
x() + deltaXPixels, originalCanvasPoint.
y() + deltaYPixels );
720 return QSizeF( afterMoveLayerPoint.
x() - beforeMoveLayerPoint.
x(), afterMoveLayerPoint.
y() - beforeMoveLayerPoint.
y() );
726 double closestItemDistance = std::numeric_limits< double >::max();
727 int closestItemZ = 0;
732 if ( !annotationItem )
736 const double itemDistance = itemBounds.
contains( mapPoint ) ? 0 : itemBounds.
distance( mapPoint );
737 if ( !closestItem || itemDistance < closestItemDistance || ( itemDistance == closestItemDistance && annotationItem->
zIndex() > closestItemZ ) )
740 closestItemDistance = itemDistance;
741 closestItemZ = annotationItem->
zIndex();
748QgsAnnotationLayer *QgsMapToolModifyAnnotation::annotationLayerFromId(
const QString &layerId )
756QgsAnnotationItem *QgsMapToolModifyAnnotation::annotationItemFromId(
const QString &layerId,
const QString &itemId )
759 return layer ?
layer->item( itemId ) :
nullptr;
762void QgsMapToolModifyAnnotation::clearHoveredItem()
764 if ( mHoverRubberBand )
765 mHoverRubberBand->hide();
766 if ( mHoveredNodeRubberBand )
767 mHoveredNodeRubberBand->hide();
769 mHoveredItemId.clear();
770 mHoveredItemLayerId.clear();
771 mHoveredItemNodeRubberBands.clear();
772 mHoveredItemNodesSpatialIndex.reset();
777void QgsMapToolModifyAnnotation::clearSelectedItem()
779 if ( mSelectedRubberBand )
780 mSelectedRubberBand->hide();
782 const bool hadSelection = !mSelectedItemId.isEmpty();
783 mSelectedItemId.clear();
784 mSelectedItemLayerId.clear();
789void QgsMapToolModifyAnnotation::createHoverBand()
791 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
794 mHoverRubberBand->
setWidth( scaleFactor );
796 mHoverRubberBand->
setColor( QColor( 100, 100, 100, 155 ) );
799void QgsMapToolModifyAnnotation::createHoveredNodeBand()
801 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
805 mHoveredNodeRubberBand->
setWidth( scaleFactor );
806 mHoveredNodeRubberBand->
setIconSize( scaleFactor * 5 );
807 mHoveredNodeRubberBand->
setColor( QColor( 200, 0, 120, 255 ) );
810void QgsMapToolModifyAnnotation::createSelectedItemBand()
812 const double scaleFactor =
canvas()->fontMetrics().xHeight() * .2;
815 mSelectedRubberBand->
setWidth( scaleFactor );
817 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 SIP_HOLDGIL
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.
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
void grow(double delta)
Grows the rectangle in place by the specified amount.
bool contains(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle contains other rectangle.
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 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 reset(QgsWkbTypes::GeometryType geometryType=QgsWkbTypes::LineGeometry)
Clears all the geometries in this rubberband.
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 SIP_HOLDGIL
Returns the vector's y-component.
double x() const SIP_HOLDGIL
Returns the vector's x-component.