QGIS API Documentation 4.3.0-Master (b46232e4f9a)
Loading...
Searching...
No Matches
qgsannotationrectitem.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsannotationrectitem.cpp
3 ----------------
4 begin : July 2024
5 copyright : (C) 2024 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
22#include "qgsapplication.h"
23#include "qgscalloutsregistry.h"
24#include "qgsfillsymbol.h"
25#include "qgsfillsymbollayer.h"
26#include "qgsgeometry.h"
27#include "qgslinestring.h"
28#include "qgslinesymbollayer.h"
29#include "qgspainting.h"
30#include "qgspolygon.h"
31#include "qgsrendercontext.h"
32#include "qgssymbollayerutils.h"
33#include "qgsunittypes.h"
34
35#include <QString>
36
37using namespace Qt::StringLiterals;
38
41 , mBounds( bounds )
42{
43 mBackgroundSymbol = std::make_unique< QgsFillSymbol >(
44 QgsSymbolLayerList { new QgsSimpleFillSymbolLayer( QColor( 255, 255, 255 ), Qt::BrushStyle::SolidPattern, QColor( 0, 0, 0 ), Qt::PenStyle::NoPen ) }
45 );
46 QgsSimpleLineSymbolLayer *borderSymbol = new QgsSimpleLineSymbolLayer( QColor( 0, 0, 0 ) );
47 borderSymbol->setPenJoinStyle( Qt::MiterJoin );
48 mFrameSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList { borderSymbol } );
49}
50
52
66
67double QgsAnnotationRectItem::appliedRotation( double mapRotation ) const
68{
69 double angle = mRotation;
71 angle += mapRotation;
72 return angle;
73}
74
76{
77 const double angle = appliedRotation( renderContext.mapToPixel().mapRotation() );
78 if ( qgsDoubleNear( angle, 0 ) )
79 return QgsGeometry::fromRect( layerBounds );
80
81 // Rotate in painter coordinates, then convert back to layer coordinates so
82 // the preview matches the rendered item.
83 QgsRectangle mapBounds = layerBounds;
84 if ( renderContext.coordinateTransform().isValid() )
85 {
86 try
87 {
88 mapBounds = renderContext.coordinateTransform().transformBoundingBox( layerBounds );
89 }
90 catch ( QgsCsException & )
91 {
92 return QgsGeometry::fromRect( layerBounds );
93 }
94 }
95
96 QPointF center = mapBounds.center().toQPointF();
97 renderContext.mapToPixel().transformInPlace( center.rx(), center.ry() );
98 const double mupp = renderContext.mapToPixel().mapUnitsPerPixel();
99 const double widthPixels = mapBounds.width() / mupp;
100 const double heightPixels = mapBounds.height() / mupp;
101 const QRectF painterBounds( center.x() - widthPixels * 0.5, center.y() - heightPixels * 0.5, widthPixels, heightPixels );
102
103 QTransform transform;
104 transform.translate( center.x(), center.y() );
105 transform.rotate( angle );
106 transform.translate( -center.x(), -center.y() );
107
108 QVector< QgsPointXY > points;
109 points.reserve( 5 );
110 for ( const QPointF &corner : { painterBounds.topLeft(), painterBounds.topRight(), painterBounds.bottomRight(), painterBounds.bottomLeft() } )
111 points.append( QgsPointXY( transform.map( corner ) ) );
112 points.append( points.constFirst() );
113
114 QgsGeometry geometry( new QgsPolygon( new QgsLineString( points ) ) );
115 geometry.transform( renderContext.mapToPixel().transform().inverted() );
116 if ( renderContext.coordinateTransform().isValid() )
118 return geometry;
119}
120
121QgsRectangle QgsAnnotationRectItem::boundsFromPixelRect( const QgsMapToPixel *mapToPixel, const QPointF &centerPixel, double widthPixels, double heightPixels )
122{
123 const QgsPointXY centerMap = mapToPixel->toMapCoordinates( centerPixel.x(), centerPixel.y() );
124 const double mupp = mapToPixel->mapUnitsPerPixel();
125 const double halfWidthMap = 0.5 * widthPixels * mupp;
126 const double halfHeightMap = 0.5 * heightPixels * mupp;
127 return QgsRectangle( centerMap.x() - halfWidthMap, centerMap.y() - halfHeightMap, centerMap.x() + halfWidthMap, centerMap.y() + halfHeightMap );
128}
129
131{
132 QgsRectangle bounds = mBounds;
134 {
135 try
136 {
137 bounds = context.coordinateTransform().transformBoundingBox( mBounds );
138 }
139 catch ( QgsCsException & )
140 {
141 return;
142 }
143 }
144
145 QRectF painterBounds;
146
147 switch ( mPlacementMode )
148 {
150 {
151 // Use the unrotated pixel size around the transformed center, not the
152 // axis-aligned bbox, so the size stays correct when the map is rotated.
153 QPointF center = bounds.center().toQPointF();
154 context.mapToPixel().transformInPlace( center.rx(), center.ry() );
155 const double mupp = context.mapToPixel().mapUnitsPerPixel();
156 const double widthPixels = bounds.width() / mupp;
157 const double heightPixels = bounds.height() / mupp;
158 painterBounds = QRectF( center.x() - widthPixels * 0.5, center.y() - heightPixels * 0.5, widthPixels, heightPixels );
159 break;
160 }
161
163 {
164 const double widthPixels = context.convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
165 const double heightPixels = context.convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
166
167 if ( callout() && !calloutAnchor().isEmpty() )
168 {
169 QgsGeometry anchor = calloutAnchor();
170
171 const double calloutOffsetWidthPixels = context.convertToPainterUnits( offsetFromCallout().width(), offsetFromCalloutUnit() );
172 const double calloutOffsetHeightPixels = context.convertToPainterUnits( offsetFromCallout().height(), offsetFromCalloutUnit() );
173
174 QPointF anchorPoint = anchor.asQPointF();
175 if ( context.coordinateTransform().isValid() )
176 {
177 double x = anchorPoint.x();
178 double y = anchorPoint.y();
179 double z = 0.0;
180 context.coordinateTransform().transformInPlace( x, y, z );
181 anchorPoint = QPointF( x, y );
182 }
183
184 context.mapToPixel().transformInPlace( anchorPoint.rx(), anchorPoint.ry() );
185
186 painterBounds = QRectF( anchorPoint.x() + calloutOffsetWidthPixels, anchorPoint.y() + calloutOffsetHeightPixels, widthPixels, heightPixels );
187 }
188 else
189 {
190 QPointF center = bounds.center().toQPointF();
191
192 context.mapToPixel().transformInPlace( center.rx(), center.ry() );
193 painterBounds = QRectF( center.x() - widthPixels * 0.5, center.y() - heightPixels * 0.5, widthPixels, heightPixels );
194 }
195 break;
196 }
197
199 {
200 const double widthPixels = context.convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
201 const double heightPixels = context.convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
202
203 QPointF center = bounds.center().toQPointF();
204 center.rx() *= context.outputSize().width();
205 center.ry() *= context.outputSize().height();
206
207 painterBounds = QRectF( center.x() - widthPixels * 0.5, center.y() - heightPixels * 0.5, widthPixels, heightPixels );
208 break;
209 }
210 }
211
212 if ( painterBounds.width() < 1 || painterBounds.height() < 1 )
213 return;
214
215 QPainter *painter = context.painter();
216 double angle = appliedRotation( context.mapToPixel().mapRotation() );
217 const bool rotated = !qgsDoubleNear( angle, 0 );
218 const QPointF rotationCenter = painterBounds.center();
219
221 if ( mDrawBackground && mBackgroundSymbol )
222 {
223 if ( rotated )
224 {
225 painterState.save();
226 QgsPainting::rotatePainterAroundPoint( painter, rotationCenter, angle );
227 }
228 mBackgroundSymbol->startRender( context );
229 mBackgroundSymbol->renderPolygon( painterBounds, nullptr, nullptr, context );
230 mBackgroundSymbol->stopRender( context );
231 if ( rotated )
232 painterState.restore();
233 }
234
236 {
237 QgsCallout::QgsCalloutContext calloutContext;
238 renderCallout( context, painterBounds, angle, calloutContext, feedback );
239 }
240
241 if ( rotated )
242 {
243 painterState.save();
244 QgsPainting::rotatePainterAroundPoint( painter, rotationCenter, angle );
245 }
246
247 renderInBounds( context, painterBounds, feedback );
248
249 if ( mDrawFrame && mFrameSymbol )
250 {
251 mFrameSymbol->startRender( context );
252 mFrameSymbol->renderPolygon( painterBounds, nullptr, nullptr, context );
253 mFrameSymbol->stopRender( context );
254 }
255}
256
257QList<QgsAnnotationItemNode> QgsAnnotationRectItem::nodesV2( const QgsAnnotationItemEditContext &context ) const
258{
259 QList<QgsAnnotationItemNode> res;
260 switch ( mPlacementMode )
261 {
263 {
264 res = {
265 QgsAnnotationItemNode( QgsVertexId( 0, 0, 0 ), QgsPointXY( mBounds.xMinimum(), mBounds.yMinimum() ), Qgis::AnnotationItemNodeType::VertexHandle, Qt::CursorShape::SizeBDiagCursor ),
266 QgsAnnotationItemNode( QgsVertexId( 0, 0, 1 ), QgsPointXY( mBounds.xMaximum(), mBounds.yMinimum() ), Qgis::AnnotationItemNodeType::VertexHandle, Qt::CursorShape::SizeFDiagCursor ),
267 QgsAnnotationItemNode( QgsVertexId( 0, 0, 2 ), QgsPointXY( mBounds.xMaximum(), mBounds.yMaximum() ), Qgis::AnnotationItemNodeType::VertexHandle, Qt::CursorShape::SizeBDiagCursor ),
268 QgsAnnotationItemNode( QgsVertexId( 0, 0, 3 ), QgsPointXY( mBounds.xMinimum(), mBounds.yMaximum() ), Qgis::AnnotationItemNodeType::VertexHandle, Qt::CursorShape::SizeFDiagCursor ),
269 };
270
271 QgsPointXY calloutNodePoint;
272 if ( !calloutAnchor().isEmpty() )
273 {
274 calloutNodePoint = calloutAnchor().asPoint();
275 }
276 else
277 {
278 calloutNodePoint = mBounds.center();
279 }
280 res.append( QgsAnnotationItemNode( QgsVertexId( 1, 0, 0 ), calloutNodePoint, Qgis::AnnotationItemNodeType::CalloutHandle ) );
281
282 return res;
283 }
284
286 {
287 res = { QgsAnnotationItemNode( QgsVertexId( 0, 0, 0 ), mBounds.center(), Qgis::AnnotationItemNodeType::VertexHandle ) };
288
289 QgsPointXY calloutNodePoint;
290 if ( !calloutAnchor().isEmpty() )
291 {
292 calloutNodePoint = calloutAnchor().asPoint();
293 }
294 else
295 {
296 calloutNodePoint = QgsPointXY( context.currentItemBounds().xMinimum(), context.currentItemBounds().yMinimum() );
297 }
298 res.append( QgsAnnotationItemNode( QgsVertexId( 1, 0, 0 ), calloutNodePoint, Qgis::AnnotationItemNodeType::CalloutHandle ) );
299
300 return res;
301 }
302
304 {
306 }
307 }
309}
310
312{
313 switch ( operation->type() )
314 {
316 {
317 QgsAnnotationItemEditOperationMoveNode *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationMoveNode * >( operation );
318 if ( moveOperation->nodeId().part == 0 )
319 {
320 switch ( mPlacementMode )
321 {
323 {
324 switch ( moveOperation->nodeId().vertex )
325 {
326 case 0:
327 mBounds = QgsRectangle( moveOperation->after().x(), moveOperation->after().y(), mBounds.xMaximum(), mBounds.yMaximum() );
328 break;
329 case 1:
330 mBounds = QgsRectangle( mBounds.xMinimum(), moveOperation->after().y(), moveOperation->after().x(), mBounds.yMaximum() );
331 break;
332 case 2:
333 mBounds = QgsRectangle( mBounds.xMinimum(), mBounds.yMinimum(), moveOperation->after().x(), moveOperation->after().y() );
334 break;
335 case 3:
336 mBounds = QgsRectangle( moveOperation->after().x(), mBounds.yMinimum(), mBounds.xMaximum(), moveOperation->after().y() );
337 break;
338 default:
339 break;
340 }
342 }
343
345 {
346 mBounds = QgsRectangle::fromCenterAndSize( moveOperation->after(), mBounds.width(), mBounds.height() );
348 }
349
351 {
352 const double deltaX = moveOperation->translationXPixels() / context.renderContext().outputSize().width();
353 const double deltaY = moveOperation->translationYPixels() / context.renderContext().outputSize().height();
354 mBounds = QgsRectangle::fromCenterAndSize( QgsPointXY( mBounds.center().x() + deltaX, mBounds.center().y() + deltaY ), mBounds.width(), mBounds.height() );
356 }
357 }
358 }
359 else if ( moveOperation->nodeId().part == 1 )
360 {
361 setCalloutAnchor( QgsGeometry::fromPoint( moveOperation->after() ) );
362 if ( !callout() )
363 {
364 setCallout( QgsApplication::calloutRegistry()->defaultCallout().release() );
365 }
367 }
368 break;
369 }
370
372 {
373 QgsAnnotationItemEditOperationTranslateItem *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationTranslateItem * >( operation );
374 switch ( mPlacementMode )
375 {
377 mBounds = QgsRectangle(
378 mBounds.xMinimum() + moveOperation->translationX(),
379 mBounds.yMinimum() + moveOperation->translationY(),
380 mBounds.xMaximum() + moveOperation->translationX(),
381 mBounds.yMaximum() + moveOperation->translationY()
382 );
383 break;
384
386 {
387 if ( callout() && !calloutAnchor().isEmpty() )
388 {
389 const double xOffset = context.renderContext().convertFromPainterUnits( moveOperation->translationXPixels(), offsetFromCalloutUnit() );
390 const double yOffset = context.renderContext().convertFromPainterUnits( moveOperation->translationYPixels(), offsetFromCalloutUnit() );
391 setOffsetFromCallout( QSizeF( offsetFromCallout().width() + xOffset, offsetFromCallout().height() + yOffset ) );
392 }
393 else
394 {
395 mBounds = QgsRectangle(
396 mBounds.xMinimum() + moveOperation->translationX(),
397 mBounds.yMinimum() + moveOperation->translationY(),
398 mBounds.xMaximum() + moveOperation->translationX(),
399 mBounds.yMaximum() + moveOperation->translationY()
400 );
401 }
402 break;
403 }
404
406 {
407 const double deltaX = moveOperation->translationXPixels() / context.renderContext().outputSize().width();
408 const double deltaY = moveOperation->translationYPixels() / context.renderContext().outputSize().height();
409 mBounds = QgsRectangle::fromCenterAndSize( QgsPointXY( mBounds.center().x() + deltaX, mBounds.center().y() + deltaY ), mBounds.width(), mBounds.height() );
410 break;
411 }
412 }
414 }
415
417 {
418 QgsAnnotationItemEditOperationRotateItem *rotateOperation = qgis::down_cast< QgsAnnotationItemEditOperationRotateItem * >( operation );
419 mRotation = std::fmod( mRotation + rotateOperation->angle(), 360.0 );
421 }
422
424 {
425 mBounds = qgis::down_cast< QgsAnnotationItemEditOperationSetItemBounds * >( operation )->bounds();
427 }
428
431 break;
432 }
434}
435
437{
438 switch ( operation->type() )
439 {
441 {
442 QgsAnnotationItemEditOperationMoveNode *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationMoveNode * >( operation );
443 if ( moveOperation->nodeId().part == 0 )
444 {
445 switch ( mPlacementMode )
446 {
448 {
449 QgsRectangle modifiedBounds = mBounds;
450 switch ( moveOperation->nodeId().vertex )
451 {
452 case 0:
453 modifiedBounds.setXMinimum( moveOperation->after().x() );
454 modifiedBounds.setYMinimum( moveOperation->after().y() );
455 break;
456 case 1:
457 modifiedBounds.setXMaximum( moveOperation->after().x() );
458 modifiedBounds.setYMinimum( moveOperation->after().y() );
459 break;
460 case 2:
461 modifiedBounds.setXMaximum( moveOperation->after().x() );
462 modifiedBounds.setYMaximum( moveOperation->after().y() );
463 break;
464 case 3:
465 modifiedBounds.setXMinimum( moveOperation->after().x() );
466 modifiedBounds.setYMaximum( moveOperation->after().y() );
467 break;
468 default:
469 break;
470 }
472 }
474 {
475 const QgsRectangle currentBounds = context.currentItemBounds();
476 const QgsRectangle newBounds = QgsRectangle::fromCenterAndSize( moveOperation->after(), currentBounds.width(), currentBounds.height() );
478 }
480 {
481 const QgsRectangle currentBounds = context.currentItemBounds();
482 const QgsRectangle newBounds = QgsRectangle::fromCenterAndSize( currentBounds.center() + ( moveOperation->after() - moveOperation->before() ), currentBounds.width(), currentBounds.height() );
484 }
485 }
486 }
487 else
488 {
489 QgsAnnotationItemEditOperationMoveNode *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationMoveNode * >( operation );
490 return new QgsAnnotationItemEditOperationTransientResults( QgsGeometry( moveOperation->after().clone() ) );
491 }
492 break;
493 }
494
496 {
497 QgsAnnotationItemEditOperationTranslateItem *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationTranslateItem * >( operation );
498 switch ( mPlacementMode )
499 {
501 {
502 const QgsRectangle modifiedBounds(
503 mBounds.xMinimum() + moveOperation->translationX(),
504 mBounds.yMinimum() + moveOperation->translationY(),
505 mBounds.xMaximum() + moveOperation->translationX(),
506 mBounds.yMaximum() + moveOperation->translationY()
507 );
509 }
510
512 {
513 if ( callout() && !calloutAnchor().isEmpty() )
514 {
515 QgsGeometry anchor = calloutAnchor();
516
517 const double calloutOffsetWidthPixels = context.renderContext().convertToPainterUnits( offsetFromCallout().width(), offsetFromCalloutUnit() ) + moveOperation->translationXPixels();
518 const double calloutOffsetHeightPixels = context.renderContext().convertToPainterUnits( offsetFromCallout().height(), offsetFromCalloutUnit() ) + moveOperation->translationYPixels();
519
520 QPointF anchorPoint = anchor.asQPointF();
521 if ( context.renderContext().coordinateTransform().isValid() )
522 {
523 double x = anchorPoint.x();
524 double y = anchorPoint.y();
525 double z = 0.0;
527 anchorPoint = QPointF( x, y );
528 }
529
530 context.renderContext().mapToPixel().transformInPlace( anchorPoint.rx(), anchorPoint.ry() );
531
532 const double textOriginXPixels = anchorPoint.x() + calloutOffsetWidthPixels;
533 const double textOriginYPixels = anchorPoint.y() + calloutOffsetHeightPixels;
534
535 const double widthPixels = context.renderContext().convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
536 const double heightPixels = context.renderContext().convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
537
538 QgsLineString ls(
539 QVector<QgsPointXY> {
540 QgsPointXY( textOriginXPixels, textOriginYPixels ),
541 QgsPointXY( textOriginXPixels + widthPixels, textOriginYPixels ),
542 QgsPointXY( textOriginXPixels + widthPixels, textOriginYPixels + heightPixels ),
543 QgsPointXY( textOriginXPixels, textOriginYPixels + heightPixels ),
544 QgsPointXY( textOriginXPixels, textOriginYPixels )
545 }
546 );
547
548 QgsGeometry g( new QgsPolygon( ls.clone() ) );
549 g.transform( context.renderContext().mapToPixel().transform().inverted() );
552 }
553 else
554 {
555 const QgsRectangle currentBounds = context.currentItemBounds();
556 const QgsRectangle newBounds
557 = QgsRectangle::fromCenterAndSize( mBounds.center() + QgsVector( moveOperation->translationX(), moveOperation->translationY() ), currentBounds.width(), currentBounds.height() );
559 }
560 }
561
563 {
564 const QgsRectangle currentBounds = context.currentItemBounds();
565 const QgsRectangle newBounds
566 = QgsRectangle::fromCenterAndSize( currentBounds.center() + QgsVector( moveOperation->translationX(), moveOperation->translationY() ), currentBounds.width(), currentBounds.height() );
568 }
569 }
570 break;
571 }
572
574 {
575 const QgsRectangle newBounds = qgis::down_cast< QgsAnnotationItemEditOperationSetItemBounds * >( operation )->bounds();
577 }
578
582 break;
583 }
584 return nullptr;
585}
586
588{
590 switch ( mPlacementMode )
591 {
593 {
594 bounds = mBounds;
595 if ( callout() && !calloutAnchor().isEmpty() )
596 {
597 QgsGeometry anchor = calloutAnchor();
598 bounds.combineExtentWith( anchor.boundingBox() );
599 }
600 break;
601 }
602
604 if ( callout() && !calloutAnchor().isEmpty() )
605 {
607 }
608 else
609 {
610 bounds = QgsRectangle( mBounds.center(), mBounds.center() );
611 }
612 break;
613
615 bounds = mBounds;
616 break;
617 }
618
619 return bounds;
620}
621
623{
624 switch ( mPlacementMode )
625 {
628
630 {
631 const double widthPixels = context.convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
632 const double heightPixels = context.convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
633
634 QRectF boundsInPixels;
635 if ( callout() && !calloutAnchor().isEmpty() )
636 {
637 QgsGeometry anchor = calloutAnchor();
638
639 const double calloutOffsetWidthPixels = context.convertToPainterUnits( offsetFromCallout().width(), offsetFromCalloutUnit() );
640 const double calloutOffsetHeightPixels = context.convertToPainterUnits( offsetFromCallout().height(), offsetFromCalloutUnit() );
641
642 QPointF anchorPoint = anchor.asQPointF();
643 if ( context.coordinateTransform().isValid() )
644 {
645 double x = anchorPoint.x();
646 double y = anchorPoint.y();
647 double z = 0.0;
648 context.coordinateTransform().transformInPlace( x, y, z );
649 anchorPoint = QPointF( x, y );
650 }
651
652 context.mapToPixel().transformInPlace( anchorPoint.rx(), anchorPoint.ry() );
653
655 textRect( anchorPoint.x() + calloutOffsetWidthPixels, anchorPoint.y() + calloutOffsetHeightPixels, anchorPoint.x() + calloutOffsetWidthPixels + widthPixels, anchorPoint.y() + calloutOffsetHeightPixels + heightPixels );
656 QgsRectangle anchorRect( anchorPoint.x(), anchorPoint.y(), anchorPoint.x(), anchorPoint.y() );
657 anchorRect.combineExtentWith( textRect );
658
659 boundsInPixels = anchorRect.toRectF();
660 }
661 else
662 {
663 QPointF center = mBounds.center().toQPointF();
664 if ( context.coordinateTransform().isValid() )
665 {
666 double x = center.x();
667 double y = center.y();
668 double z = 0.0;
669 context.coordinateTransform().transformInPlace( x, y, z );
670 center = QPointF( x, y );
671 }
672
673 context.mapToPixel().transformInPlace( center.rx(), center.ry() );
674 boundsInPixels = QRectF( center.x() - widthPixels * 0.5, center.y() - heightPixels * 0.5, widthPixels, heightPixels );
675 }
676 const QgsPointXY topLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.top() );
677 const QgsPointXY topRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.top() );
678 const QgsPointXY bottomLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.bottom() );
679 const QgsPointXY bottomRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.bottom() );
680
681 const QgsRectangle boundsMapUnits = QgsRectangle( topLeft.x(), bottomLeft.y(), bottomRight.x(), topRight.y() );
683 return textRect;
684 }
685
687 {
688 const double widthPixels = context.convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
689 const double heightPixels = context.convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
690
691 QRectF boundsInPixels;
692
693 const double centerMapX = context.mapExtent().xMinimum() + mBounds.center().x() * context.mapExtent().width();
694 const double centerMapY = context.mapExtent().yMaximum() - mBounds.center().y() * context.mapExtent().height();
695 QPointF center( centerMapX, centerMapY );
696 if ( context.coordinateTransform().isValid() )
697 {
698 double x = centerMapX;
699 double y = centerMapY;
700 double z = 0.0;
701 context.coordinateTransform().transformInPlace( x, y, z );
702 center = QPointF( x, y );
703 }
704
705 context.mapToPixel().transformInPlace( center.rx(), center.ry() );
706 boundsInPixels = QRectF( center.x() - widthPixels * 0.5, center.y() - heightPixels * 0.5, widthPixels, heightPixels );
707
708 const QgsPointXY topLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.top() );
709 const QgsPointXY topRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.top() );
710 const QgsPointXY bottomLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.bottom() );
711 const QgsPointXY bottomRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.bottom() );
712
713 const QgsRectangle boundsMapUnits = QgsRectangle( topLeft.x(), bottomLeft.y(), bottomRight.x(), topRight.y() );
715 return textRect;
716 }
717 }
719}
720
722{
723 mBounds = bounds;
724}
725
727{
728 return mBackgroundSymbol.get();
729}
730
732{
733 mBackgroundSymbol.reset( symbol );
734}
735
737{
738 return mFrameSymbol.get();
739}
740
742{
743 mFrameSymbol.reset( symbol );
744}
745
747{
748 if ( const QgsAnnotationRectItem *otherRect = dynamic_cast< const QgsAnnotationRectItem * >( other ) )
749 {
750 setPlacementMode( otherRect->mPlacementMode );
751 setFixedSize( otherRect->mFixedSize );
752 setFixedSizeUnit( otherRect->mFixedSizeUnit );
753 setRotation( otherRect->mRotation );
754 setRotationMode( otherRect->mRotationMode );
755
756 setBackgroundEnabled( otherRect->mDrawBackground );
757 if ( otherRect->mBackgroundSymbol )
758 setBackgroundSymbol( otherRect->mBackgroundSymbol->clone() );
759
760 setFrameEnabled( otherRect->mDrawFrame );
761 if ( otherRect->mFrameSymbol )
762 setFrameSymbol( otherRect->mFrameSymbol->clone() );
763 }
764
766}
767
768bool QgsAnnotationRectItem::writeCommonProperties( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
769{
770 element.setAttribute( u"xMin"_s, qgsDoubleToString( mBounds.xMinimum() ) );
771 element.setAttribute( u"xMax"_s, qgsDoubleToString( mBounds.xMaximum() ) );
772 element.setAttribute( u"yMin"_s, qgsDoubleToString( mBounds.yMinimum() ) );
773 element.setAttribute( u"yMax"_s, qgsDoubleToString( mBounds.yMaximum() ) );
774 element.setAttribute( u"sizeMode"_s, qgsEnumValueToKey( mPlacementMode ) );
775 element.setAttribute( u"fixedWidth"_s, qgsDoubleToString( mFixedSize.width() ) );
776 element.setAttribute( u"fixedHeight"_s, qgsDoubleToString( mFixedSize.height() ) );
777 element.setAttribute( u"fixedSizeUnit"_s, QgsUnitTypes::encodeUnit( mFixedSizeUnit ) );
778 if ( !qgsDoubleNear( mRotation, 0 ) )
779 {
780 element.setAttribute( u"rotation"_s, qgsDoubleToString( mRotation ) );
781 }
782 if ( mRotationMode != Qgis::SymbolRotationMode::IgnoreMapRotation )
783 {
784 element.setAttribute( u"rotationMode"_s, qgsEnumValueToKey( mRotationMode ) );
785 }
786
787 element.setAttribute( u"backgroundEnabled"_s, mDrawBackground ? u"1"_s : u"0"_s );
788 if ( mBackgroundSymbol )
789 {
790 QDomElement backgroundElement = document.createElement( u"backgroundSymbol"_s );
791 backgroundElement.appendChild( QgsSymbolLayerUtils::saveSymbol( u"backgroundSymbol"_s, mBackgroundSymbol.get(), document, context ) );
792 element.appendChild( backgroundElement );
793 }
794
795 element.setAttribute( u"frameEnabled"_s, mDrawFrame ? u"1"_s : u"0"_s );
796 if ( mFrameSymbol )
797 {
798 QDomElement frameElement = document.createElement( u"frameSymbol"_s );
799 frameElement.appendChild( QgsSymbolLayerUtils::saveSymbol( u"frameSymbol"_s, mFrameSymbol.get(), document, context ) );
800 element.appendChild( frameElement );
801 }
802
803 return QgsAnnotationItem::writeCommonProperties( element, document, context );
804}
805
806bool QgsAnnotationRectItem::readCommonProperties( const QDomElement &element, const QgsReadWriteContext &context )
807{
808 mBounds.setXMinimum( element.attribute( u"xMin"_s ).toDouble() );
809 mBounds.setXMaximum( element.attribute( u"xMax"_s ).toDouble() );
810 mBounds.setYMinimum( element.attribute( u"yMin"_s ).toDouble() );
811 mBounds.setYMaximum( element.attribute( u"yMax"_s ).toDouble() );
812
813 mPlacementMode = qgsEnumKeyToValue( element.attribute( u"sizeMode"_s ), Qgis::AnnotationPlacementMode::SpatialBounds );
814
815 mFixedSize = QSizeF( element.attribute( u"fixedWidth"_s ).toDouble(), element.attribute( u"fixedHeight"_s ).toDouble() );
816 mFixedSizeUnit = QgsUnitTypes::decodeRenderUnit( element.attribute( u"fixedSizeUnit"_s ) );
817 mRotation = element.attribute( u"rotation"_s ).toDouble();
818 mRotationMode = qgsEnumKeyToValue( element.attribute( u"rotationMode"_s ), Qgis::SymbolRotationMode::IgnoreMapRotation );
819
820 mDrawBackground = element.attribute( u"backgroundEnabled"_s, u"0"_s ).toInt();
821 const QDomElement backgroundSymbolElem = element.firstChildElement( u"backgroundSymbol"_s ).firstChildElement();
822 if ( !backgroundSymbolElem.isNull() )
823 {
824 setBackgroundSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( backgroundSymbolElem, context ).release() );
825 }
826
827 mDrawFrame = element.attribute( u"frameEnabled"_s, u"0"_s ).toInt();
828 const QDomElement frameSymbolElem = element.firstChildElement( u"frameSymbol"_s ).firstChildElement();
829 if ( !frameSymbolElem.isNull() )
830 {
831 setFrameSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( frameSymbolElem, context ).release() );
832 }
833
834 return QgsAnnotationItem::readCommonProperties( element, context );
835}
836
838{
839 return mFixedSize;
840}
841
842void QgsAnnotationRectItem::setFixedSize( const QSizeF &size )
843{
844 mFixedSize = size;
845}
846
848{
849 return mFixedSizeUnit;
850}
851
853{
854 mFixedSizeUnit = unit;
855}
856
858{
859 return mPlacementMode;
860}
861
863{
864 mPlacementMode = mode;
865}
@ RespectMapRotation
Entity is rotated along with the map.
Definition qgis.h:869
@ IgnoreMapRotation
Entity ignores map rotation.
Definition qgis.h:870
@ VertexHandle
Node is a handle for manipulating vertices.
Definition qgis.h:2715
@ CalloutHandle
Node is a handle for manipulating callouts.
Definition qgis.h:2716
@ ScaleDependentBoundingBox
Item's bounding box will vary depending on map scale.
Definition qgis.h:2672
@ SupportsCallouts
Item supports callouts.
Definition qgis.h:2674
AnnotationItemEditOperationResult
Results from an edit operation on an annotation item.
Definition qgis.h:2726
@ Invalid
Operation has invalid parameters for the item, no change occurred.
Definition qgis.h:2728
@ Success
Item was modified successfully.
Definition qgis.h:2727
QFlags< AnnotationItemFlag > AnnotationItemFlags
Annotation item flags.
Definition qgis.h:2677
RenderUnit
Rendering size units.
Definition qgis.h:5758
AnnotationPlacementMode
Annotation item placement modes.
Definition qgis.h:2687
@ SpatialBounds
Item is rendered inside fixed spatial bounds, and size will depend on map scale.
Definition qgis.h:2688
@ FixedSize
Item is rendered at a fixed size, regardless of map scale. Item's location is georeferenced to a spat...
Definition qgis.h:2689
@ RelativeToMapFrame
Items size and placement is relative to the map's frame, and the item will always be rendered in the ...
Definition qgis.h:2690
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2884
Abstract base class for annotation item edit operations.
virtual Type type() const =0
Returns the operation type.
Encapsulates the context for an annotation item edit operation.
QgsRectangle currentItemBounds() const
Returns the current rendered bounds of the item, in the annotation layer's CRS.
QgsRenderContext renderContext() const
Returns the render context associated with the edit operation.
Annotation item edit operation consisting of moving a node.
double translationYPixels() const
Returns the y-axis translation, in pixels.
QgsPoint before() const
Returns the node position before the move occurred (in layer coordinates).
QgsPoint after() const
Returns the node position after the move occurred (in layer coordinates).
double translationXPixels() const
Returns the x-axis translation, in pixels.
QgsVertexId nodeId() const
Returns the associated node ID.
Annotation item edit operation consisting of rotating an item.
double angle() const
Returns the rotation angle value (in degrees clockwise).
Encapsulates the transient results of an in-progress annotation edit operation.
Annotation item edit operation consisting of translating (moving) an item.
double translationY() const
Returns the y-axis translation, in layer units.
double translationX() const
Returns the x-axis translation, in layer units.
double translationYPixels() const
Returns the y-axis translation, in pixels.
double translationXPixels() const
Returns the x-axis translation, in pixels.
Contains information about a node used for editing an annotation item.
virtual bool writeCommonProperties(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Writes common properties from the base class into an XML element.
void setCallout(QgsCallout *callout)
Sets the item's callout renderer, responsible for drawing item callouts.
void setOffsetFromCallout(const QSizeF &offset)
Sets the offset of the annotation item from the calloutAnchor().
QgsGeometry calloutAnchor() const
Returns the callout's anchor geometry.
Qgis::RenderUnit offsetFromCalloutUnit() const
Returns the units for the offsetFromCallout().
virtual void copyCommonProperties(const QgsAnnotationItem *other)
Copies common properties from the base class from an other item.
void setCalloutAnchor(const QgsGeometry &anchor)
Sets the callout's anchor geometry.
QSizeF offsetFromCallout() const
Returns the (optional) offset of the annotation item from the calloutAnchor().
QgsCallout * callout() const
Returns the item's callout renderer, responsible for drawing item callouts.
virtual bool readCommonProperties(const QDomElement &element, const QgsReadWriteContext &context)
Reads common properties from the base class from the given DOM element.
void renderCallout(QgsRenderContext &context, const QRectF &rect, double angle, QgsCallout::QgsCalloutContext &calloutContext, QgsFeedback *feedback)
Renders the item's callout.
virtual void renderInBounds(QgsRenderContext &context, const QRectF &painterRect, QgsFeedback *feedback)=0
Renders the item to the specified render context.
QgsGeometry rotatedBoundsGeometry(const QgsRectangle &layerBounds, const QgsRenderContext &renderContext) const
Returns the polygon geometry (in layer coordinates) for the item occupying the given layerBounds,...
Qgis::AnnotationItemFlags flags() const override
Returns item flags.
static QgsRectangle boundsFromPixelRect(const QgsMapToPixel *mapToPixel, const QPointF &centerPixel, double widthPixels, double heightPixels)
Returns the axis-aligned bounds, in map coordinates, of a rectangle centered on centerPixel with size...
void copyCommonProperties(const QgsAnnotationItem *other) override
Copies common properties from the base class from an other item.
void setRotationMode(Qgis::SymbolRotationMode mode)
Sets the mode used to render the item with respect to the rotation of the map.
double appliedRotation(double mapRotation) const
Returns the effective on-screen rotation of the item, in degrees clockwise.
QgsRectangle bounds() const
Returns the bounds of the item.
void render(QgsRenderContext &context, QgsFeedback *feedback) override
Renders the item to the specified render context.
QgsAnnotationItemEditOperationTransientResults * transientEditResultsV2(QgsAbstractAnnotationItemEditOperation *operation, const QgsAnnotationItemEditContext &context) override
Retrieves the results of a transient (in progress) edit operation on the item.
Qgis::AnnotationItemEditOperationResult applyEditV2(QgsAbstractAnnotationItemEditOperation *operation, const QgsAnnotationItemEditContext &context) override
Applies an edit operation to the item.
~QgsAnnotationRectItem() override
bool readCommonProperties(const QDomElement &element, const QgsReadWriteContext &context) override
Reads common properties from the base class from the given DOM element.
Qgis::RenderUnit fixedSizeUnit() const
Returns the units to use for fixed item sizes, when the placementMode() is Qgis::AnnotationPlacementM...
QSizeF fixedSize() const
Returns the fixed size to use for the item, when the placementMode() is Qgis::AnnotationPlacementMode...
void setFixedSizeUnit(Qgis::RenderUnit unit)
Sets the unit to use for fixed item sizes, when the placementMode() is Qgis::AnnotationPlacementMode:...
QgsAnnotationRectItem(const QgsRectangle &bounds)
Constructor for QgsAnnotationRectItem, rendering the annotation within the specified bounds geometry.
void setBackgroundSymbol(QgsFillSymbol *symbol)
Sets the symbol used to render the item's background.
void setFrameSymbol(QgsFillSymbol *symbol)
Sets the symbol used to render the item's frame.
void setBounds(const QgsRectangle &bounds)
Sets the bounds of the item.
void setPlacementMode(Qgis::AnnotationPlacementMode mode)
Sets the placement mode for the item.
bool writeCommonProperties(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Writes common properties from the base class into an XML element.
const QgsFillSymbol * frameSymbol() const
Returns the symbol used to render the item's frame.
void setBackgroundEnabled(bool enabled)
Sets whether the item's background should be rendered.
QList< QgsAnnotationItemNode > nodesV2(const QgsAnnotationItemEditContext &context) const override
Returns the nodes for the item, used for editing the item.
void setRotation(double rotation)
Sets the rotation of the item, in degrees clockwise.
void setFrameEnabled(bool enabled)
Sets whether the item's frame should be rendered.
QgsRectangle boundingBox() const override
Returns the bounding box of the item's geographic location, in the parent layer's coordinate referenc...
void setFixedSize(const QSizeF &size)
Sets the fixed size to use for the item, when the placementMode() is Qgis::AnnotationPlacementMode::F...
Qgis::AnnotationPlacementMode placementMode() const
Returns the placement mode for the item.
const QgsFillSymbol * backgroundSymbol() const
Returns the symbol used to render the item's background.
static QgsCalloutRegistry * calloutRegistry()
Returns the application's callout registry, used for managing callout types.
Contains additional contextual information about the context in which a callout is being rendered.
Definition qgscallout.h:248
void transformInPlace(double &x, double &y, double &z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Custom exception class for Coordinate Reference System related exceptions.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
QPointF asQPointF() const
Returns contents of the geometry as a QPointF if wkbType is WKBPoint, otherwise returns a null QPoint...
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static QgsGeometry fromPoint(const QgsPoint &point)
Creates a new geometry from a QgsPoint object.
Line string geometry type, with support for z-dimension and m-values.
Perform transforms between map coordinates and device coordinates.
double mapUnitsPerPixel() const
Returns the current map units per pixel.
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
void transformInPlace(double &x, double &y) const
Transforms map coordinates to device coordinates.
static void rotatePainterAroundPoint(QPainter *painter, const QPointF &point, double angle)
Rotates a painter by angle degrees clockwise around point.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:168
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:138
double x
Definition qgspoint.h:56
double y
Definition qgspoint.h:57
Polygon geometry type.
Definition qgspolygon.h:37
A container for the context for various read/write operations on objects.
A rectangle specified with double values.
double xMinimum
double yMinimum
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
QRectF toRectF() const
Returns a QRectF with same coordinates as the rectangle.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
double yMaximum
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
static QgsRectangle fromCenterAndSize(const QgsPointXY &center, double width, double height)
Creates a new rectangle, given the specified center point and width and height.
QgsPointXY center
Contains information about the context of a rendering operation.
double convertFromPainterUnits(double size, Qgis::RenderUnit unit) const
Converts a size from painter units (pixels) to the specified render unit.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
QSize outputSize() const
Returns the size of the resulting rendered image, in pixels.
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Scoped object for saving and restoring a QPainter object's state.
@ NoSave
Do not save the current painter state.
void save()
Performs a manual save on the painter state, pushing another saved state to the painter.
void restore()
Performs a manual restore on the painter state, popping the latest state from the painter.
Renders polygons using a single fill and stroke color.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
Represent a 2-dimensional vector.
Definition qgsvector.h:34
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:8018
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:7632
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7999
#define BUILTIN_UNREACHABLE
Definition qgis.h:8397
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7725
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:35
int vertex
Vertex number.
int part
Part number.
Definition qgsvertexid.h:96