QGIS API Documentation 4.3.0-Master (6402a64e93b)
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
220 if ( mDrawBackground && mBackgroundSymbol )
221 {
222 if ( rotated )
223 {
224 painter->save();
225 QgsPainting::rotatePainterAroundPoint( painter, rotationCenter, angle );
226 }
227 mBackgroundSymbol->startRender( context );
228 mBackgroundSymbol->renderPolygon( painterBounds, nullptr, nullptr, context );
229 mBackgroundSymbol->stopRender( context );
230 if ( rotated )
231 painter->restore();
232 }
233
235 {
236 QgsCallout::QgsCalloutContext calloutContext;
237 renderCallout( context, painterBounds, angle, calloutContext, feedback );
238 }
239
240 if ( rotated )
241 {
242 painter->save();
243 QgsPainting::rotatePainterAroundPoint( painter, rotationCenter, angle );
244 }
245
246 renderInBounds( context, painterBounds, feedback );
247
248 if ( mDrawFrame && mFrameSymbol )
249 {
250 mFrameSymbol->startRender( context );
251 mFrameSymbol->renderPolygon( painterBounds, nullptr, nullptr, context );
252 mFrameSymbol->stopRender( context );
253 }
254
255 if ( rotated )
256 painter->restore();
257}
258
259QList<QgsAnnotationItemNode> QgsAnnotationRectItem::nodesV2( const QgsAnnotationItemEditContext &context ) const
260{
261 QList<QgsAnnotationItemNode> res;
262 switch ( mPlacementMode )
263 {
265 {
266 res = {
267 QgsAnnotationItemNode( QgsVertexId( 0, 0, 0 ), QgsPointXY( mBounds.xMinimum(), mBounds.yMinimum() ), Qgis::AnnotationItemNodeType::VertexHandle, Qt::CursorShape::SizeBDiagCursor ),
268 QgsAnnotationItemNode( QgsVertexId( 0, 0, 1 ), QgsPointXY( mBounds.xMaximum(), mBounds.yMinimum() ), Qgis::AnnotationItemNodeType::VertexHandle, Qt::CursorShape::SizeFDiagCursor ),
269 QgsAnnotationItemNode( QgsVertexId( 0, 0, 2 ), QgsPointXY( mBounds.xMaximum(), mBounds.yMaximum() ), Qgis::AnnotationItemNodeType::VertexHandle, Qt::CursorShape::SizeBDiagCursor ),
270 QgsAnnotationItemNode( QgsVertexId( 0, 0, 3 ), QgsPointXY( mBounds.xMinimum(), mBounds.yMaximum() ), Qgis::AnnotationItemNodeType::VertexHandle, Qt::CursorShape::SizeFDiagCursor ),
271 };
272
273 QgsPointXY calloutNodePoint;
274 if ( !calloutAnchor().isEmpty() )
275 {
276 calloutNodePoint = calloutAnchor().asPoint();
277 }
278 else
279 {
280 calloutNodePoint = mBounds.center();
281 }
282 res.append( QgsAnnotationItemNode( QgsVertexId( 1, 0, 0 ), calloutNodePoint, Qgis::AnnotationItemNodeType::CalloutHandle ) );
283
284 return res;
285 }
286
288 {
289 res = { QgsAnnotationItemNode( QgsVertexId( 0, 0, 0 ), mBounds.center(), Qgis::AnnotationItemNodeType::VertexHandle ) };
290
291 QgsPointXY calloutNodePoint;
292 if ( !calloutAnchor().isEmpty() )
293 {
294 calloutNodePoint = calloutAnchor().asPoint();
295 }
296 else
297 {
298 calloutNodePoint = QgsPointXY( context.currentItemBounds().xMinimum(), context.currentItemBounds().yMinimum() );
299 }
300 res.append( QgsAnnotationItemNode( QgsVertexId( 1, 0, 0 ), calloutNodePoint, Qgis::AnnotationItemNodeType::CalloutHandle ) );
301
302 return res;
303 }
304
306 {
308 }
309 }
311}
312
314{
315 switch ( operation->type() )
316 {
318 {
319 QgsAnnotationItemEditOperationMoveNode *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationMoveNode * >( operation );
320 if ( moveOperation->nodeId().part == 0 )
321 {
322 switch ( mPlacementMode )
323 {
325 {
326 switch ( moveOperation->nodeId().vertex )
327 {
328 case 0:
329 mBounds = QgsRectangle( moveOperation->after().x(), moveOperation->after().y(), mBounds.xMaximum(), mBounds.yMaximum() );
330 break;
331 case 1:
332 mBounds = QgsRectangle( mBounds.xMinimum(), moveOperation->after().y(), moveOperation->after().x(), mBounds.yMaximum() );
333 break;
334 case 2:
335 mBounds = QgsRectangle( mBounds.xMinimum(), mBounds.yMinimum(), moveOperation->after().x(), moveOperation->after().y() );
336 break;
337 case 3:
338 mBounds = QgsRectangle( moveOperation->after().x(), mBounds.yMinimum(), mBounds.xMaximum(), moveOperation->after().y() );
339 break;
340 default:
341 break;
342 }
344 }
345
347 {
348 mBounds = QgsRectangle::fromCenterAndSize( moveOperation->after(), mBounds.width(), mBounds.height() );
350 }
351
353 {
354 const double deltaX = moveOperation->translationXPixels() / context.renderContext().outputSize().width();
355 const double deltaY = moveOperation->translationYPixels() / context.renderContext().outputSize().height();
356 mBounds = QgsRectangle::fromCenterAndSize( QgsPointXY( mBounds.center().x() + deltaX, mBounds.center().y() + deltaY ), mBounds.width(), mBounds.height() );
358 }
359 }
360 }
361 else if ( moveOperation->nodeId().part == 1 )
362 {
363 setCalloutAnchor( QgsGeometry::fromPoint( moveOperation->after() ) );
364 if ( !callout() )
365 {
366 setCallout( QgsApplication::calloutRegistry()->defaultCallout() );
367 }
369 }
370 break;
371 }
372
374 {
375 QgsAnnotationItemEditOperationTranslateItem *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationTranslateItem * >( operation );
376 switch ( mPlacementMode )
377 {
379 mBounds = QgsRectangle(
380 mBounds.xMinimum() + moveOperation->translationX(),
381 mBounds.yMinimum() + moveOperation->translationY(),
382 mBounds.xMaximum() + moveOperation->translationX(),
383 mBounds.yMaximum() + moveOperation->translationY()
384 );
385 break;
386
388 {
389 if ( callout() && !calloutAnchor().isEmpty() )
390 {
391 const double xOffset = context.renderContext().convertFromPainterUnits( moveOperation->translationXPixels(), offsetFromCalloutUnit() );
392 const double yOffset = context.renderContext().convertFromPainterUnits( moveOperation->translationYPixels(), offsetFromCalloutUnit() );
393 setOffsetFromCallout( QSizeF( offsetFromCallout().width() + xOffset, offsetFromCallout().height() + yOffset ) );
394 }
395 else
396 {
397 mBounds = QgsRectangle(
398 mBounds.xMinimum() + moveOperation->translationX(),
399 mBounds.yMinimum() + moveOperation->translationY(),
400 mBounds.xMaximum() + moveOperation->translationX(),
401 mBounds.yMaximum() + moveOperation->translationY()
402 );
403 }
404 break;
405 }
406
408 {
409 const double deltaX = moveOperation->translationXPixels() / context.renderContext().outputSize().width();
410 const double deltaY = moveOperation->translationYPixels() / context.renderContext().outputSize().height();
411 mBounds = QgsRectangle::fromCenterAndSize( QgsPointXY( mBounds.center().x() + deltaX, mBounds.center().y() + deltaY ), mBounds.width(), mBounds.height() );
412 break;
413 }
414 }
416 }
417
419 {
420 QgsAnnotationItemEditOperationRotateItem *rotateOperation = qgis::down_cast< QgsAnnotationItemEditOperationRotateItem * >( operation );
421 mRotation = std::fmod( mRotation + rotateOperation->angle(), 360.0 );
423 }
424
427 break;
428 }
430}
431
433{
434 switch ( operation->type() )
435 {
437 {
438 QgsAnnotationItemEditOperationMoveNode *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationMoveNode * >( operation );
439 if ( moveOperation->nodeId().part == 0 )
440 {
441 switch ( mPlacementMode )
442 {
444 {
445 QgsRectangle modifiedBounds = mBounds;
446 switch ( moveOperation->nodeId().vertex )
447 {
448 case 0:
449 modifiedBounds.setXMinimum( moveOperation->after().x() );
450 modifiedBounds.setYMinimum( moveOperation->after().y() );
451 break;
452 case 1:
453 modifiedBounds.setXMaximum( moveOperation->after().x() );
454 modifiedBounds.setYMinimum( moveOperation->after().y() );
455 break;
456 case 2:
457 modifiedBounds.setXMaximum( moveOperation->after().x() );
458 modifiedBounds.setYMaximum( moveOperation->after().y() );
459 break;
460 case 3:
461 modifiedBounds.setXMinimum( moveOperation->after().x() );
462 modifiedBounds.setYMaximum( moveOperation->after().y() );
463 break;
464 default:
465 break;
466 }
468 }
470 {
471 const QgsRectangle currentBounds = context.currentItemBounds();
472 const QgsRectangle newBounds = QgsRectangle::fromCenterAndSize( moveOperation->after(), currentBounds.width(), currentBounds.height() );
474 }
476 {
477 const QgsRectangle currentBounds = context.currentItemBounds();
478 const QgsRectangle newBounds = QgsRectangle::fromCenterAndSize( currentBounds.center() + ( moveOperation->after() - moveOperation->before() ), currentBounds.width(), currentBounds.height() );
480 }
481 }
482 }
483 else
484 {
485 QgsAnnotationItemEditOperationMoveNode *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationMoveNode * >( operation );
486 return new QgsAnnotationItemEditOperationTransientResults( QgsGeometry( moveOperation->after().clone() ) );
487 }
488 break;
489 }
490
492 {
493 QgsAnnotationItemEditOperationTranslateItem *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationTranslateItem * >( operation );
494 switch ( mPlacementMode )
495 {
497 {
498 const QgsRectangle modifiedBounds(
499 mBounds.xMinimum() + moveOperation->translationX(),
500 mBounds.yMinimum() + moveOperation->translationY(),
501 mBounds.xMaximum() + moveOperation->translationX(),
502 mBounds.yMaximum() + moveOperation->translationY()
503 );
505 }
506
508 {
509 if ( callout() && !calloutAnchor().isEmpty() )
510 {
511 QgsGeometry anchor = calloutAnchor();
512
513 const double calloutOffsetWidthPixels = context.renderContext().convertToPainterUnits( offsetFromCallout().width(), offsetFromCalloutUnit() ) + moveOperation->translationXPixels();
514 const double calloutOffsetHeightPixels = context.renderContext().convertToPainterUnits( offsetFromCallout().height(), offsetFromCalloutUnit() ) + moveOperation->translationYPixels();
515
516 QPointF anchorPoint = anchor.asQPointF();
517 if ( context.renderContext().coordinateTransform().isValid() )
518 {
519 double x = anchorPoint.x();
520 double y = anchorPoint.y();
521 double z = 0.0;
523 anchorPoint = QPointF( x, y );
524 }
525
526 context.renderContext().mapToPixel().transformInPlace( anchorPoint.rx(), anchorPoint.ry() );
527
528 const double textOriginXPixels = anchorPoint.x() + calloutOffsetWidthPixels;
529 const double textOriginYPixels = anchorPoint.y() + calloutOffsetHeightPixels;
530
531 const double widthPixels = context.renderContext().convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
532 const double heightPixels = context.renderContext().convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
533
534 QgsLineString ls(
535 QVector<QgsPointXY> {
536 QgsPointXY( textOriginXPixels, textOriginYPixels ),
537 QgsPointXY( textOriginXPixels + widthPixels, textOriginYPixels ),
538 QgsPointXY( textOriginXPixels + widthPixels, textOriginYPixels + heightPixels ),
539 QgsPointXY( textOriginXPixels, textOriginYPixels + heightPixels ),
540 QgsPointXY( textOriginXPixels, textOriginYPixels )
541 }
542 );
543
544 QgsGeometry g( new QgsPolygon( ls.clone() ) );
545 g.transform( context.renderContext().mapToPixel().transform().inverted() );
548 }
549 else
550 {
551 const QgsRectangle currentBounds = context.currentItemBounds();
552 const QgsRectangle newBounds
553 = QgsRectangle::fromCenterAndSize( mBounds.center() + QgsVector( moveOperation->translationX(), moveOperation->translationY() ), currentBounds.width(), currentBounds.height() );
555 }
556 }
557
559 {
560 const QgsRectangle currentBounds = context.currentItemBounds();
561 const QgsRectangle newBounds
562 = QgsRectangle::fromCenterAndSize( currentBounds.center() + QgsVector( moveOperation->translationX(), moveOperation->translationY() ), currentBounds.width(), currentBounds.height() );
564 }
565 }
566 break;
567 }
568
572 break;
573 }
574 return nullptr;
575}
576
578{
580 switch ( mPlacementMode )
581 {
583 {
584 bounds = mBounds;
585 if ( callout() && !calloutAnchor().isEmpty() )
586 {
587 QgsGeometry anchor = calloutAnchor();
588 bounds.combineExtentWith( anchor.boundingBox() );
589 }
590 break;
591 }
592
594 if ( callout() && !calloutAnchor().isEmpty() )
595 {
597 }
598 else
599 {
600 bounds = QgsRectangle( mBounds.center(), mBounds.center() );
601 }
602 break;
603
605 bounds = mBounds;
606 break;
607 }
608
609 return bounds;
610}
611
613{
614 switch ( mPlacementMode )
615 {
618
620 {
621 const double widthPixels = context.convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
622 const double heightPixels = context.convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
623
624 QRectF boundsInPixels;
625 if ( callout() && !calloutAnchor().isEmpty() )
626 {
627 QgsGeometry anchor = calloutAnchor();
628
629 const double calloutOffsetWidthPixels = context.convertToPainterUnits( offsetFromCallout().width(), offsetFromCalloutUnit() );
630 const double calloutOffsetHeightPixels = context.convertToPainterUnits( offsetFromCallout().height(), offsetFromCalloutUnit() );
631
632 QPointF anchorPoint = anchor.asQPointF();
633 if ( context.coordinateTransform().isValid() )
634 {
635 double x = anchorPoint.x();
636 double y = anchorPoint.y();
637 double z = 0.0;
638 context.coordinateTransform().transformInPlace( x, y, z );
639 anchorPoint = QPointF( x, y );
640 }
641
642 context.mapToPixel().transformInPlace( anchorPoint.rx(), anchorPoint.ry() );
643
645 textRect( anchorPoint.x() + calloutOffsetWidthPixels, anchorPoint.y() + calloutOffsetHeightPixels, anchorPoint.x() + calloutOffsetWidthPixels + widthPixels, anchorPoint.y() + calloutOffsetHeightPixels + heightPixels );
646 QgsRectangle anchorRect( anchorPoint.x(), anchorPoint.y(), anchorPoint.x(), anchorPoint.y() );
647 anchorRect.combineExtentWith( textRect );
648
649 boundsInPixels = anchorRect.toRectF();
650 }
651 else
652 {
653 QPointF center = mBounds.center().toQPointF();
654 if ( context.coordinateTransform().isValid() )
655 {
656 double x = center.x();
657 double y = center.y();
658 double z = 0.0;
659 context.coordinateTransform().transformInPlace( x, y, z );
660 center = QPointF( x, y );
661 }
662
663 context.mapToPixel().transformInPlace( center.rx(), center.ry() );
664 boundsInPixels = QRectF( center.x() - widthPixels * 0.5, center.y() - heightPixels * 0.5, widthPixels, heightPixels );
665 }
666 const QgsPointXY topLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.top() );
667 const QgsPointXY topRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.top() );
668 const QgsPointXY bottomLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.bottom() );
669 const QgsPointXY bottomRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.bottom() );
670
671 const QgsRectangle boundsMapUnits = QgsRectangle( topLeft.x(), bottomLeft.y(), bottomRight.x(), topRight.y() );
673 return textRect;
674 }
675
677 {
678 const double widthPixels = context.convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
679 const double heightPixels = context.convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
680
681 QRectF boundsInPixels;
682
683 const double centerMapX = context.mapExtent().xMinimum() + mBounds.center().x() * context.mapExtent().width();
684 const double centerMapY = context.mapExtent().yMaximum() - mBounds.center().y() * context.mapExtent().height();
685 QPointF center( centerMapX, centerMapY );
686 if ( context.coordinateTransform().isValid() )
687 {
688 double x = centerMapX;
689 double y = centerMapY;
690 double z = 0.0;
691 context.coordinateTransform().transformInPlace( x, y, z );
692 center = QPointF( x, y );
693 }
694
695 context.mapToPixel().transformInPlace( center.rx(), center.ry() );
696 boundsInPixels = QRectF( center.x() - widthPixels * 0.5, center.y() - heightPixels * 0.5, widthPixels, heightPixels );
697
698 const QgsPointXY topLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.top() );
699 const QgsPointXY topRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.top() );
700 const QgsPointXY bottomLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.bottom() );
701 const QgsPointXY bottomRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.bottom() );
702
703 const QgsRectangle boundsMapUnits = QgsRectangle( topLeft.x(), bottomLeft.y(), bottomRight.x(), topRight.y() );
705 return textRect;
706 }
707 }
709}
710
712{
713 mBounds = bounds;
714}
715
717{
718 return mBackgroundSymbol.get();
719}
720
722{
723 mBackgroundSymbol.reset( symbol );
724}
725
727{
728 return mFrameSymbol.get();
729}
730
732{
733 mFrameSymbol.reset( symbol );
734}
735
737{
738 if ( const QgsAnnotationRectItem *otherRect = dynamic_cast< const QgsAnnotationRectItem * >( other ) )
739 {
740 setPlacementMode( otherRect->mPlacementMode );
741 setFixedSize( otherRect->mFixedSize );
742 setFixedSizeUnit( otherRect->mFixedSizeUnit );
743 setRotation( otherRect->mRotation );
744 setRotationMode( otherRect->mRotationMode );
745
746 setBackgroundEnabled( otherRect->mDrawBackground );
747 if ( otherRect->mBackgroundSymbol )
748 setBackgroundSymbol( otherRect->mBackgroundSymbol->clone() );
749
750 setFrameEnabled( otherRect->mDrawFrame );
751 if ( otherRect->mFrameSymbol )
752 setFrameSymbol( otherRect->mFrameSymbol->clone() );
753 }
754
756}
757
758bool QgsAnnotationRectItem::writeCommonProperties( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
759{
760 element.setAttribute( u"xMin"_s, qgsDoubleToString( mBounds.xMinimum() ) );
761 element.setAttribute( u"xMax"_s, qgsDoubleToString( mBounds.xMaximum() ) );
762 element.setAttribute( u"yMin"_s, qgsDoubleToString( mBounds.yMinimum() ) );
763 element.setAttribute( u"yMax"_s, qgsDoubleToString( mBounds.yMaximum() ) );
764 element.setAttribute( u"sizeMode"_s, qgsEnumValueToKey( mPlacementMode ) );
765 element.setAttribute( u"fixedWidth"_s, qgsDoubleToString( mFixedSize.width() ) );
766 element.setAttribute( u"fixedHeight"_s, qgsDoubleToString( mFixedSize.height() ) );
767 element.setAttribute( u"fixedSizeUnit"_s, QgsUnitTypes::encodeUnit( mFixedSizeUnit ) );
768 element.setAttribute( u"rotation"_s, qgsDoubleToString( mRotation ) );
769 element.setAttribute( u"rotationMode"_s, qgsEnumValueToKey( mRotationMode ) );
770
771 element.setAttribute( u"backgroundEnabled"_s, mDrawBackground ? u"1"_s : u"0"_s );
772 if ( mBackgroundSymbol )
773 {
774 QDomElement backgroundElement = document.createElement( u"backgroundSymbol"_s );
775 backgroundElement.appendChild( QgsSymbolLayerUtils::saveSymbol( u"backgroundSymbol"_s, mBackgroundSymbol.get(), document, context ) );
776 element.appendChild( backgroundElement );
777 }
778
779 element.setAttribute( u"frameEnabled"_s, mDrawFrame ? u"1"_s : u"0"_s );
780 if ( mFrameSymbol )
781 {
782 QDomElement frameElement = document.createElement( u"frameSymbol"_s );
783 frameElement.appendChild( QgsSymbolLayerUtils::saveSymbol( u"frameSymbol"_s, mFrameSymbol.get(), document, context ) );
784 element.appendChild( frameElement );
785 }
786
787 return QgsAnnotationItem::writeCommonProperties( element, document, context );
788}
789
790bool QgsAnnotationRectItem::readCommonProperties( const QDomElement &element, const QgsReadWriteContext &context )
791{
792 mBounds.setXMinimum( element.attribute( u"xMin"_s ).toDouble() );
793 mBounds.setXMaximum( element.attribute( u"xMax"_s ).toDouble() );
794 mBounds.setYMinimum( element.attribute( u"yMin"_s ).toDouble() );
795 mBounds.setYMaximum( element.attribute( u"yMax"_s ).toDouble() );
796
797 mPlacementMode = qgsEnumKeyToValue( element.attribute( u"sizeMode"_s ), Qgis::AnnotationPlacementMode::SpatialBounds );
798
799 mFixedSize = QSizeF( element.attribute( u"fixedWidth"_s ).toDouble(), element.attribute( u"fixedHeight"_s ).toDouble() );
800 mFixedSizeUnit = QgsUnitTypes::decodeRenderUnit( element.attribute( u"fixedSizeUnit"_s ) );
801 mRotation = element.attribute( u"rotation"_s ).toDouble();
802 mRotationMode = qgsEnumKeyToValue( element.attribute( u"rotationMode"_s ), Qgis::SymbolRotationMode::IgnoreMapRotation );
803
804 mDrawBackground = element.attribute( u"backgroundEnabled"_s, u"0"_s ).toInt();
805 const QDomElement backgroundSymbolElem = element.firstChildElement( u"backgroundSymbol"_s ).firstChildElement();
806 if ( !backgroundSymbolElem.isNull() )
807 {
808 setBackgroundSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( backgroundSymbolElem, context ).release() );
809 }
810
811 mDrawFrame = element.attribute( u"frameEnabled"_s, u"0"_s ).toInt();
812 const QDomElement frameSymbolElem = element.firstChildElement( u"frameSymbol"_s ).firstChildElement();
813 if ( !frameSymbolElem.isNull() )
814 {
815 setFrameSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( frameSymbolElem, context ).release() );
816 }
817
818 return QgsAnnotationItem::readCommonProperties( element, context );
819}
820
822{
823 return mFixedSize;
824}
825
826void QgsAnnotationRectItem::setFixedSize( const QSizeF &size )
827{
828 mFixedSize = size;
829}
830
832{
833 return mFixedSizeUnit;
834}
835
837{
838 mFixedSizeUnit = unit;
839}
840
842{
843 return mPlacementMode;
844}
845
847{
848 mPlacementMode = mode;
849}
@ RespectMapRotation
Entity is rotated along with the map.
Definition qgis.h:849
@ IgnoreMapRotation
Entity ignores map rotation.
Definition qgis.h:850
@ VertexHandle
Node is a handle for manipulating vertices.
Definition qgis.h:2695
@ CalloutHandle
Node is a handle for manipulating callouts.
Definition qgis.h:2696
@ ScaleDependentBoundingBox
Item's bounding box will vary depending on map scale.
Definition qgis.h:2652
@ SupportsCallouts
Item supports callouts.
Definition qgis.h:2654
AnnotationItemEditOperationResult
Results from an edit operation on an annotation item.
Definition qgis.h:2706
@ Invalid
Operation has invalid parameters for the item, no change occurred.
Definition qgis.h:2708
@ Success
Item was modified successfully.
Definition qgis.h:2707
QFlags< AnnotationItemFlag > AnnotationItemFlags
Annotation item flags.
Definition qgis.h:2657
RenderUnit
Rendering size units.
Definition qgis.h:5655
AnnotationPlacementMode
Annotation item placement modes.
Definition qgis.h:2667
@ SpatialBounds
Item is rendered inside fixed spatial bounds, and size will depend on map scale.
Definition qgis.h:2668
@ FixedSize
Item is rendered at a fixed size, regardless of map scale. Item's location is georeferenced to a spat...
Definition qgis.h:2669
@ RelativeToMapFrame
Items size and placement is relative to the map's frame, and the item will always be rendered in the ...
Definition qgis.h:2670
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2864
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.
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:7743
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:7395
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7724
#define BUILTIN_UNREACHABLE
Definition qgis.h:8122
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7488
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