QGIS API Documentation 4.3.0-Master (90cb4ecf9ef)
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
426 {
427 mBounds = qgis::down_cast< QgsAnnotationItemEditOperationSetItemBounds * >( operation )->bounds();
429 }
430
433 break;
434 }
436}
437
439{
440 switch ( operation->type() )
441 {
443 {
444 QgsAnnotationItemEditOperationMoveNode *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationMoveNode * >( operation );
445 if ( moveOperation->nodeId().part == 0 )
446 {
447 switch ( mPlacementMode )
448 {
450 {
451 QgsRectangle modifiedBounds = mBounds;
452 switch ( moveOperation->nodeId().vertex )
453 {
454 case 0:
455 modifiedBounds.setXMinimum( moveOperation->after().x() );
456 modifiedBounds.setYMinimum( moveOperation->after().y() );
457 break;
458 case 1:
459 modifiedBounds.setXMaximum( moveOperation->after().x() );
460 modifiedBounds.setYMinimum( moveOperation->after().y() );
461 break;
462 case 2:
463 modifiedBounds.setXMaximum( moveOperation->after().x() );
464 modifiedBounds.setYMaximum( moveOperation->after().y() );
465 break;
466 case 3:
467 modifiedBounds.setXMinimum( moveOperation->after().x() );
468 modifiedBounds.setYMaximum( moveOperation->after().y() );
469 break;
470 default:
471 break;
472 }
474 }
476 {
477 const QgsRectangle currentBounds = context.currentItemBounds();
478 const QgsRectangle newBounds = QgsRectangle::fromCenterAndSize( moveOperation->after(), currentBounds.width(), currentBounds.height() );
480 }
482 {
483 const QgsRectangle currentBounds = context.currentItemBounds();
484 const QgsRectangle newBounds = QgsRectangle::fromCenterAndSize( currentBounds.center() + ( moveOperation->after() - moveOperation->before() ), currentBounds.width(), currentBounds.height() );
486 }
487 }
488 }
489 else
490 {
491 QgsAnnotationItemEditOperationMoveNode *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationMoveNode * >( operation );
492 return new QgsAnnotationItemEditOperationTransientResults( QgsGeometry( moveOperation->after().clone() ) );
493 }
494 break;
495 }
496
498 {
499 QgsAnnotationItemEditOperationTranslateItem *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationTranslateItem * >( operation );
500 switch ( mPlacementMode )
501 {
503 {
504 const QgsRectangle modifiedBounds(
505 mBounds.xMinimum() + moveOperation->translationX(),
506 mBounds.yMinimum() + moveOperation->translationY(),
507 mBounds.xMaximum() + moveOperation->translationX(),
508 mBounds.yMaximum() + moveOperation->translationY()
509 );
511 }
512
514 {
515 if ( callout() && !calloutAnchor().isEmpty() )
516 {
517 QgsGeometry anchor = calloutAnchor();
518
519 const double calloutOffsetWidthPixels = context.renderContext().convertToPainterUnits( offsetFromCallout().width(), offsetFromCalloutUnit() ) + moveOperation->translationXPixels();
520 const double calloutOffsetHeightPixels = context.renderContext().convertToPainterUnits( offsetFromCallout().height(), offsetFromCalloutUnit() ) + moveOperation->translationYPixels();
521
522 QPointF anchorPoint = anchor.asQPointF();
523 if ( context.renderContext().coordinateTransform().isValid() )
524 {
525 double x = anchorPoint.x();
526 double y = anchorPoint.y();
527 double z = 0.0;
529 anchorPoint = QPointF( x, y );
530 }
531
532 context.renderContext().mapToPixel().transformInPlace( anchorPoint.rx(), anchorPoint.ry() );
533
534 const double textOriginXPixels = anchorPoint.x() + calloutOffsetWidthPixels;
535 const double textOriginYPixels = anchorPoint.y() + calloutOffsetHeightPixels;
536
537 const double widthPixels = context.renderContext().convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
538 const double heightPixels = context.renderContext().convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
539
540 QgsLineString ls(
541 QVector<QgsPointXY> {
542 QgsPointXY( textOriginXPixels, textOriginYPixels ),
543 QgsPointXY( textOriginXPixels + widthPixels, textOriginYPixels ),
544 QgsPointXY( textOriginXPixels + widthPixels, textOriginYPixels + heightPixels ),
545 QgsPointXY( textOriginXPixels, textOriginYPixels + heightPixels ),
546 QgsPointXY( textOriginXPixels, textOriginYPixels )
547 }
548 );
549
550 QgsGeometry g( new QgsPolygon( ls.clone() ) );
551 g.transform( context.renderContext().mapToPixel().transform().inverted() );
554 }
555 else
556 {
557 const QgsRectangle currentBounds = context.currentItemBounds();
558 const QgsRectangle newBounds
559 = QgsRectangle::fromCenterAndSize( mBounds.center() + QgsVector( moveOperation->translationX(), moveOperation->translationY() ), currentBounds.width(), currentBounds.height() );
561 }
562 }
563
565 {
566 const QgsRectangle currentBounds = context.currentItemBounds();
567 const QgsRectangle newBounds
568 = QgsRectangle::fromCenterAndSize( currentBounds.center() + QgsVector( moveOperation->translationX(), moveOperation->translationY() ), currentBounds.width(), currentBounds.height() );
570 }
571 }
572 break;
573 }
574
576 {
577 const QgsRectangle newBounds = qgis::down_cast< QgsAnnotationItemEditOperationSetItemBounds * >( operation )->bounds();
579 }
580
584 break;
585 }
586 return nullptr;
587}
588
590{
592 switch ( mPlacementMode )
593 {
595 {
596 bounds = mBounds;
597 if ( callout() && !calloutAnchor().isEmpty() )
598 {
599 QgsGeometry anchor = calloutAnchor();
600 bounds.combineExtentWith( anchor.boundingBox() );
601 }
602 break;
603 }
604
606 if ( callout() && !calloutAnchor().isEmpty() )
607 {
609 }
610 else
611 {
612 bounds = QgsRectangle( mBounds.center(), mBounds.center() );
613 }
614 break;
615
617 bounds = mBounds;
618 break;
619 }
620
621 return bounds;
622}
623
625{
626 switch ( mPlacementMode )
627 {
630
632 {
633 const double widthPixels = context.convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
634 const double heightPixels = context.convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
635
636 QRectF boundsInPixels;
637 if ( callout() && !calloutAnchor().isEmpty() )
638 {
639 QgsGeometry anchor = calloutAnchor();
640
641 const double calloutOffsetWidthPixels = context.convertToPainterUnits( offsetFromCallout().width(), offsetFromCalloutUnit() );
642 const double calloutOffsetHeightPixels = context.convertToPainterUnits( offsetFromCallout().height(), offsetFromCalloutUnit() );
643
644 QPointF anchorPoint = anchor.asQPointF();
645 if ( context.coordinateTransform().isValid() )
646 {
647 double x = anchorPoint.x();
648 double y = anchorPoint.y();
649 double z = 0.0;
650 context.coordinateTransform().transformInPlace( x, y, z );
651 anchorPoint = QPointF( x, y );
652 }
653
654 context.mapToPixel().transformInPlace( anchorPoint.rx(), anchorPoint.ry() );
655
657 textRect( anchorPoint.x() + calloutOffsetWidthPixels, anchorPoint.y() + calloutOffsetHeightPixels, anchorPoint.x() + calloutOffsetWidthPixels + widthPixels, anchorPoint.y() + calloutOffsetHeightPixels + heightPixels );
658 QgsRectangle anchorRect( anchorPoint.x(), anchorPoint.y(), anchorPoint.x(), anchorPoint.y() );
659 anchorRect.combineExtentWith( textRect );
660
661 boundsInPixels = anchorRect.toRectF();
662 }
663 else
664 {
665 QPointF center = mBounds.center().toQPointF();
666 if ( context.coordinateTransform().isValid() )
667 {
668 double x = center.x();
669 double y = center.y();
670 double z = 0.0;
671 context.coordinateTransform().transformInPlace( x, y, z );
672 center = QPointF( x, y );
673 }
674
675 context.mapToPixel().transformInPlace( center.rx(), center.ry() );
676 boundsInPixels = QRectF( center.x() - widthPixels * 0.5, center.y() - heightPixels * 0.5, widthPixels, heightPixels );
677 }
678 const QgsPointXY topLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.top() );
679 const QgsPointXY topRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.top() );
680 const QgsPointXY bottomLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.bottom() );
681 const QgsPointXY bottomRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.bottom() );
682
683 const QgsRectangle boundsMapUnits = QgsRectangle( topLeft.x(), bottomLeft.y(), bottomRight.x(), topRight.y() );
685 return textRect;
686 }
687
689 {
690 const double widthPixels = context.convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
691 const double heightPixels = context.convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
692
693 QRectF boundsInPixels;
694
695 const double centerMapX = context.mapExtent().xMinimum() + mBounds.center().x() * context.mapExtent().width();
696 const double centerMapY = context.mapExtent().yMaximum() - mBounds.center().y() * context.mapExtent().height();
697 QPointF center( centerMapX, centerMapY );
698 if ( context.coordinateTransform().isValid() )
699 {
700 double x = centerMapX;
701 double y = centerMapY;
702 double z = 0.0;
703 context.coordinateTransform().transformInPlace( x, y, z );
704 center = QPointF( x, y );
705 }
706
707 context.mapToPixel().transformInPlace( center.rx(), center.ry() );
708 boundsInPixels = QRectF( center.x() - widthPixels * 0.5, center.y() - heightPixels * 0.5, widthPixels, heightPixels );
709
710 const QgsPointXY topLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.top() );
711 const QgsPointXY topRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.top() );
712 const QgsPointXY bottomLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.bottom() );
713 const QgsPointXY bottomRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.bottom() );
714
715 const QgsRectangle boundsMapUnits = QgsRectangle( topLeft.x(), bottomLeft.y(), bottomRight.x(), topRight.y() );
717 return textRect;
718 }
719 }
721}
722
724{
725 mBounds = bounds;
726}
727
729{
730 return mBackgroundSymbol.get();
731}
732
734{
735 mBackgroundSymbol.reset( symbol );
736}
737
739{
740 return mFrameSymbol.get();
741}
742
744{
745 mFrameSymbol.reset( symbol );
746}
747
749{
750 if ( const QgsAnnotationRectItem *otherRect = dynamic_cast< const QgsAnnotationRectItem * >( other ) )
751 {
752 setPlacementMode( otherRect->mPlacementMode );
753 setFixedSize( otherRect->mFixedSize );
754 setFixedSizeUnit( otherRect->mFixedSizeUnit );
755 setRotation( otherRect->mRotation );
756 setRotationMode( otherRect->mRotationMode );
757
758 setBackgroundEnabled( otherRect->mDrawBackground );
759 if ( otherRect->mBackgroundSymbol )
760 setBackgroundSymbol( otherRect->mBackgroundSymbol->clone() );
761
762 setFrameEnabled( otherRect->mDrawFrame );
763 if ( otherRect->mFrameSymbol )
764 setFrameSymbol( otherRect->mFrameSymbol->clone() );
765 }
766
768}
769
770bool QgsAnnotationRectItem::writeCommonProperties( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
771{
772 element.setAttribute( u"xMin"_s, qgsDoubleToString( mBounds.xMinimum() ) );
773 element.setAttribute( u"xMax"_s, qgsDoubleToString( mBounds.xMaximum() ) );
774 element.setAttribute( u"yMin"_s, qgsDoubleToString( mBounds.yMinimum() ) );
775 element.setAttribute( u"yMax"_s, qgsDoubleToString( mBounds.yMaximum() ) );
776 element.setAttribute( u"sizeMode"_s, qgsEnumValueToKey( mPlacementMode ) );
777 element.setAttribute( u"fixedWidth"_s, qgsDoubleToString( mFixedSize.width() ) );
778 element.setAttribute( u"fixedHeight"_s, qgsDoubleToString( mFixedSize.height() ) );
779 element.setAttribute( u"fixedSizeUnit"_s, QgsUnitTypes::encodeUnit( mFixedSizeUnit ) );
780 if ( !qgsDoubleNear( mRotation, 0 ) )
781 {
782 element.setAttribute( u"rotation"_s, qgsDoubleToString( mRotation ) );
783 }
784 if ( mRotationMode != Qgis::SymbolRotationMode::IgnoreMapRotation )
785 {
786 element.setAttribute( u"rotationMode"_s, qgsEnumValueToKey( mRotationMode ) );
787 }
788
789 element.setAttribute( u"backgroundEnabled"_s, mDrawBackground ? u"1"_s : u"0"_s );
790 if ( mBackgroundSymbol )
791 {
792 QDomElement backgroundElement = document.createElement( u"backgroundSymbol"_s );
793 backgroundElement.appendChild( QgsSymbolLayerUtils::saveSymbol( u"backgroundSymbol"_s, mBackgroundSymbol.get(), document, context ) );
794 element.appendChild( backgroundElement );
795 }
796
797 element.setAttribute( u"frameEnabled"_s, mDrawFrame ? u"1"_s : u"0"_s );
798 if ( mFrameSymbol )
799 {
800 QDomElement frameElement = document.createElement( u"frameSymbol"_s );
801 frameElement.appendChild( QgsSymbolLayerUtils::saveSymbol( u"frameSymbol"_s, mFrameSymbol.get(), document, context ) );
802 element.appendChild( frameElement );
803 }
804
805 return QgsAnnotationItem::writeCommonProperties( element, document, context );
806}
807
808bool QgsAnnotationRectItem::readCommonProperties( const QDomElement &element, const QgsReadWriteContext &context )
809{
810 mBounds.setXMinimum( element.attribute( u"xMin"_s ).toDouble() );
811 mBounds.setXMaximum( element.attribute( u"xMax"_s ).toDouble() );
812 mBounds.setYMinimum( element.attribute( u"yMin"_s ).toDouble() );
813 mBounds.setYMaximum( element.attribute( u"yMax"_s ).toDouble() );
814
815 mPlacementMode = qgsEnumKeyToValue( element.attribute( u"sizeMode"_s ), Qgis::AnnotationPlacementMode::SpatialBounds );
816
817 mFixedSize = QSizeF( element.attribute( u"fixedWidth"_s ).toDouble(), element.attribute( u"fixedHeight"_s ).toDouble() );
818 mFixedSizeUnit = QgsUnitTypes::decodeRenderUnit( element.attribute( u"fixedSizeUnit"_s ) );
819 mRotation = element.attribute( u"rotation"_s ).toDouble();
820 mRotationMode = qgsEnumKeyToValue( element.attribute( u"rotationMode"_s ), Qgis::SymbolRotationMode::IgnoreMapRotation );
821
822 mDrawBackground = element.attribute( u"backgroundEnabled"_s, u"0"_s ).toInt();
823 const QDomElement backgroundSymbolElem = element.firstChildElement( u"backgroundSymbol"_s ).firstChildElement();
824 if ( !backgroundSymbolElem.isNull() )
825 {
826 setBackgroundSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( backgroundSymbolElem, context ).release() );
827 }
828
829 mDrawFrame = element.attribute( u"frameEnabled"_s, u"0"_s ).toInt();
830 const QDomElement frameSymbolElem = element.firstChildElement( u"frameSymbol"_s ).firstChildElement();
831 if ( !frameSymbolElem.isNull() )
832 {
833 setFrameSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( frameSymbolElem, context ).release() );
834 }
835
836 return QgsAnnotationItem::readCommonProperties( element, context );
837}
838
840{
841 return mFixedSize;
842}
843
844void QgsAnnotationRectItem::setFixedSize( const QSizeF &size )
845{
846 mFixedSize = size;
847}
848
850{
851 return mFixedSizeUnit;
852}
853
855{
856 mFixedSizeUnit = unit;
857}
858
860{
861 return mPlacementMode;
862}
863
865{
866 mPlacementMode = mode;
867}
@ 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:5682
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:7812
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:7464
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7793
#define BUILTIN_UNREACHABLE
Definition qgis.h:8191
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7557
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