QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsrendereditemresults.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrendereditemresults.cpp
3 -------------------
4 begin : August 2021
5 copyright : (C) Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
18#include "qgsrendercontext.h"
19#include "qgslogger.h"
21#include "RTree.h"
22
23
25class QgsRenderedItemResultsSpatialIndex : public RTree<const QgsRenderedItemDetails *, float, 2, float>
26{
27 public:
28
29 explicit QgsRenderedItemResultsSpatialIndex( const QgsRectangle &maxBounds )
30 : mXMin( maxBounds.xMinimum() )
31 , mYMin( maxBounds.yMinimum() )
32 , mXRes( ( std::numeric_limits< float >::max() - 1 ) / ( maxBounds.xMaximum() - maxBounds.xMinimum() ) )
33 , mYRes( ( std::numeric_limits< float >::max() - 1 ) / ( maxBounds.yMaximum() - maxBounds.yMinimum() ) )
34 , mMaxBounds( maxBounds )
35 , mUseScale( !maxBounds.isNull() )
36 {}
37
38 void insert( const QgsRenderedItemDetails *details, const QgsRectangle &bounds )
39 {
40 std::array< float, 4 > scaledBounds = scaleBounds( bounds );
41 this->Insert(
42 {
43 scaledBounds[0], scaledBounds[ 1]
44 },
45 {
46 scaledBounds[2], scaledBounds[3]
47 },
48 details );
49 }
50
56 bool intersects( const QgsRectangle &bounds, const std::function< bool( const QgsRenderedItemDetails *details )> &callback ) const
57 {
58 std::array< float, 4 > scaledBounds = scaleBounds( bounds );
59 this->Search(
60 {
61 scaledBounds[0], scaledBounds[ 1]
62 },
63 {
64 scaledBounds[2], scaledBounds[3]
65 },
66 callback );
67 return true;
68 }
69
70 private:
71 double mXMin = 0;
72 double mYMin = 0;
73 double mXRes = 1;
74 double mYRes = 1;
75 QgsRectangle mMaxBounds;
76 bool mUseScale = false;
77
78 std::array<float, 4> scaleBounds( const QgsRectangle &bounds ) const
79 {
80 if ( mUseScale )
81 return
82 {
83 static_cast< float >( ( std::max( bounds.xMinimum(), mMaxBounds.xMinimum() ) - mXMin ) / mXRes ),
84 static_cast< float >( ( std::max( bounds.yMinimum(), mMaxBounds.yMinimum() ) - mYMin ) / mYRes ),
85 static_cast< float >( ( std::min( bounds.xMaximum(), mMaxBounds.xMaximum() ) - mXMin ) / mXRes ),
86 static_cast< float >( ( std::min( bounds.yMaximum(), mMaxBounds.yMaximum() ) - mYMin ) / mYRes )
87 };
88 else
89 return
90 {
91 static_cast< float >( bounds.xMinimum() ),
92 static_cast< float >( bounds.yMinimum() ),
93 static_cast< float >( bounds.xMaximum() ),
94 static_cast< float >( bounds.yMaximum() )
95 };
96 }
97};
99
101 : mExtent( extent.buffered( std::max( extent.width(), extent.height() ) * 1000 ) ) // RTree goes crazy if we insert geometries outside the bounds, so buffer them right out to be safe
102 , mAnnotationItemsIndex( std::make_unique< QgsRenderedItemResultsSpatialIndex >( mExtent ) )
103{
104
105}
106
108
109QList<QgsRenderedItemDetails *> QgsRenderedItemResults::renderedItems() const
110{
111 QList< QgsRenderedItemDetails * > res;
112 for ( const auto &it : mDetails )
113 {
114 std::transform( it.second.begin(), it.second.end(), std::back_inserter( res ), []( const auto & detail )
115 {
116 return detail.get();
117 } );
118 }
119 return res;
120}
121
122QList<const QgsRenderedAnnotationItemDetails *> QgsRenderedItemResults::renderedAnnotationItemsInBounds( const QgsRectangle &bounds ) const
123{
124 QList<const QgsRenderedAnnotationItemDetails *> res;
125
126 mAnnotationItemsIndex->intersects( bounds, [&res]( const QgsRenderedItemDetails * details )->bool
127 {
128 res << qgis::down_cast< const QgsRenderedAnnotationItemDetails * >( details );
129 return true;
130 } );
131 return res;
132}
133
134void QgsRenderedItemResults::appendResults( const QList<QgsRenderedItemDetails *> &results, const QgsRenderContext &context )
135{
136 QgsCoordinateTransform layerToMapTransform = context.coordinateTransform();
137 layerToMapTransform.setBallparkTransformsAreAppropriate( true );
138 for ( QgsRenderedItemDetails *details : results )
139 {
140 try
141 {
142 const QgsRectangle transformedBounds = layerToMapTransform.transformBoundingBox( details->boundingBox() );
143 details->setBoundingBox( transformedBounds );
144 }
145 catch ( QgsCsException & )
146 {
147 QgsDebugError( QStringLiteral( "Could not transform rendered item's bounds to map CRS" ) );
148 }
149
150 if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( details ) )
151 mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );
152
153
154 mDetails[ details->layerId() ].emplace_back( std::unique_ptr< QgsRenderedItemDetails >( details ) );
155 }
156}
157
158void QgsRenderedItemResults::transferResults( QgsRenderedItemResults *other, const QStringList &layerIds )
159{
160 for ( const QString &layerId : layerIds )
161 {
162 auto otherLayerIt = other->mDetails.find( layerId );
163 if ( otherLayerIt == other->mDetails.end() )
164 continue;
165
166 std::vector< std::unique_ptr< QgsRenderedItemDetails > > &source = otherLayerIt->second;
167
168 for ( std::unique_ptr< QgsRenderedItemDetails > &details : source )
169 {
170 if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( details.get() ) )
171 mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );
172
173 mDetails[layerId].emplace_back( std::move( details ) );
174 }
175
176 other->mDetails.erase( otherLayerIt );
177 }
178}
179
181{
182 for ( auto layerIt = other->mDetails.begin(); layerIt != other->mDetails.end(); ++layerIt )
183 {
184 std::vector< std::unique_ptr< QgsRenderedItemDetails > > &dest = mDetails[layerIt->first];
185 dest.reserve( layerIt->second.size() );
186 for ( auto it = layerIt->second.begin(); it != layerIt->second.end(); ++it )
187 {
188 if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( ( *it ).get() ) )
189 mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );
190
191 dest.emplace_back( std::move( *it ) );
192 }
193 }
194 other->mDetails.clear();
195}
196
197void QgsRenderedItemResults::eraseResultsFromLayers( const QStringList &layerIds )
198{
199 for ( const QString &layerId : layerIds )
200 {
201 auto it = mDetails.find( layerId );
202 if ( it != mDetails.end() )
203 mDetails.erase( it );
204 }
205}
206
207
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
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.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:67
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:201
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:211
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:196
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:206
Contains information about the context of a rendering operation.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Contains information about a rendered annotation item.
Base class for detailed information about a rendered item.
void setBoundingBox(const QgsRectangle &bounds)
Sets the bounding box of the item (in map units).
QString layerId() const
Returns the layer ID of the associated map layer.
QgsRectangle boundingBox() const
Returns the bounding box of the item (in map units).
Stores collated details of rendered items during a map rendering operation.
void transferResults(QgsRenderedItemResults *other, const QStringList &layerIds)
Transfers all results from an other QgsRenderedItemResults object where the items have layer IDs matc...
QList< const QgsRenderedAnnotationItemDetails * > renderedAnnotationItemsInBounds(const QgsRectangle &bounds) const
Returns a list with details of the rendered annotation items within the specified bounds.
QgsRenderedItemResults(const QgsRectangle &extent=QgsRectangle())
Constructor for QgsRenderedItemResults.
QList< QgsRenderedItemDetails * > renderedItems() const
Returns a list of all rendered items.
void eraseResultsFromLayers(const QStringList &layerIds)
Erases results from layers matching those in the specified list of layers IDs.
void appendResults(const QList< QgsRenderedItemDetails * > &results, const QgsRenderContext &context)
Appends rendered item details to the results object.
#define QgsDebugError(str)
Definition: qgslogger.h:38