QGIS API Documentation 3.99.0-Master (09f76ad7019)
Loading...
Searching...
No Matches
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
17
18#include "RTree.h"
19#include "qgslogger.h"
20#include "qgsrendercontext.h"
23
24#include <QString>
25
26using namespace Qt::StringLiterals;
27
29class QgsRenderedItemResultsSpatialIndex : public RTree<const QgsRenderedItemDetails *, float, 2, float>
30{
31 public:
32
33 explicit QgsRenderedItemResultsSpatialIndex( const QgsRectangle &maxBounds )
34 : mXMin( maxBounds.xMinimum() )
35 , mYMin( maxBounds.yMinimum() )
36 , mXRes( ( std::numeric_limits< float >::max() - 1 ) / ( maxBounds.xMaximum() - maxBounds.xMinimum() ) )
37 , mYRes( ( std::numeric_limits< float >::max() - 1 ) / ( maxBounds.yMaximum() - maxBounds.yMinimum() ) )
38 , mMaxBounds( maxBounds )
39 , mUseScale( !maxBounds.isNull() )
40 {}
41
42 void insert( const QgsRenderedItemDetails *details, const QgsRectangle &bounds )
43 {
44 std::array< float, 4 > scaledBounds = scaleBounds( bounds );
45 const float aMin[2]
46 {
47 scaledBounds[0], scaledBounds[ 1]
48 };
49 const float aMax[2]
50 {
51 scaledBounds[2], scaledBounds[ 3]
52 };
53 this->Insert(
54 aMin,
55 aMax,
56 details );
57 }
58
64 bool intersects( const QgsRectangle &bounds, const std::function< bool( const QgsRenderedItemDetails *details )> &callback ) const
65 {
66 std::array< float, 4 > scaledBounds = scaleBounds( bounds );
67 const float aMin[2]
68 {
69 scaledBounds[0], scaledBounds[ 1]
70 };
71 const float aMax[2]
72 {
73 scaledBounds[2], scaledBounds[ 3]
74 };
75 this->Search(
76 aMin,
77 aMax,
78 callback );
79 return true;
80 }
81
82 private:
83 double mXMin = 0;
84 double mYMin = 0;
85 double mXRes = 1;
86 double mYRes = 1;
87 QgsRectangle mMaxBounds;
88 bool mUseScale = false;
89
90 std::array<float, 4> scaleBounds( const QgsRectangle &bounds ) const
91 {
92 if ( mUseScale )
93 return
94 {
95 static_cast< float >( ( std::max( bounds.xMinimum(), mMaxBounds.xMinimum() ) - mXMin ) / mXRes ),
96 static_cast< float >( ( std::max( bounds.yMinimum(), mMaxBounds.yMinimum() ) - mYMin ) / mYRes ),
97 static_cast< float >( ( std::min( bounds.xMaximum(), mMaxBounds.xMaximum() ) - mXMin ) / mXRes ),
98 static_cast< float >( ( std::min( bounds.yMaximum(), mMaxBounds.yMaximum() ) - mYMin ) / mYRes )
99 };
100 else
101 return
102 {
103 static_cast< float >( bounds.xMinimum() ),
104 static_cast< float >( bounds.yMinimum() ),
105 static_cast< float >( bounds.xMaximum() ),
106 static_cast< float >( bounds.yMaximum() )
107 };
108 }
109};
111
113 : 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
114 , mAnnotationItemsIndex( std::make_unique< QgsRenderedItemResultsSpatialIndex >( mExtent ) )
115{
116
117}
118
120
121QList<QgsRenderedItemDetails *> QgsRenderedItemResults::renderedItems() const
122{
123 QList< QgsRenderedItemDetails * > res;
124 for ( const auto &it : mDetails )
125 {
126 std::transform( it.second.begin(), it.second.end(), std::back_inserter( res ), []( const auto & detail )
127 {
128 return detail.get();
129 } );
130 }
131 return res;
132}
133
134QList<const QgsRenderedAnnotationItemDetails *> QgsRenderedItemResults::renderedAnnotationItemsInBounds( const QgsRectangle &bounds ) const
135{
136 QList<const QgsRenderedAnnotationItemDetails *> res;
137
138 mAnnotationItemsIndex->intersects( bounds, [&res]( const QgsRenderedItemDetails * details )->bool
139 {
140 res << qgis::down_cast< const QgsRenderedAnnotationItemDetails * >( details );
141 return true;
142 } );
143 return res;
144}
145
146void QgsRenderedItemResults::appendResults( const QList<QgsRenderedItemDetails *> &results, const QgsRenderContext &context )
147{
148 QgsCoordinateTransform layerToMapTransform = context.coordinateTransform();
149 layerToMapTransform.setBallparkTransformsAreAppropriate( true );
150 for ( QgsRenderedItemDetails *details : results )
151 {
152 try
153 {
154 const QgsRectangle transformedBounds = layerToMapTransform.transformBoundingBox( details->boundingBox() );
155 details->setBoundingBox( transformedBounds );
156 }
157 catch ( QgsCsException & )
158 {
159 QgsDebugError( u"Could not transform rendered item's bounds to map CRS"_s );
160 }
161
162 if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( details ) )
163 mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );
164
165
166 mDetails[ details->layerId() ].emplace_back( std::unique_ptr< QgsRenderedItemDetails >( details ) );
167 }
168}
169
170void QgsRenderedItemResults::transferResults( QgsRenderedItemResults *other, const QStringList &layerIds )
171{
172 for ( const QString &layerId : layerIds )
173 {
174 auto otherLayerIt = other->mDetails.find( layerId );
175 if ( otherLayerIt == other->mDetails.end() )
176 continue;
177
178 std::vector< std::unique_ptr< QgsRenderedItemDetails > > &source = otherLayerIt->second;
179
180 for ( std::unique_ptr< QgsRenderedItemDetails > &details : source )
181 {
182 if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( details.get() ) )
183 mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );
184
185 mDetails[layerId].emplace_back( std::move( details ) );
186 }
187
188 other->mDetails.erase( otherLayerIt );
189 }
190}
191
193{
194 for ( auto layerIt = other->mDetails.begin(); layerIt != other->mDetails.end(); ++layerIt )
195 {
196 std::vector< std::unique_ptr< QgsRenderedItemDetails > > &dest = mDetails[layerIt->first];
197 dest.reserve( layerIt->second.size() );
198 for ( auto it = layerIt->second.begin(); it != layerIt->second.end(); ++it )
199 {
200 if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( ( *it ).get() ) )
201 mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );
202
203 dest.emplace_back( std::move( *it ) );
204 }
205 }
206 other->mDetails.clear();
207}
208
209void QgsRenderedItemResults::eraseResultsFromLayers( const QStringList &layerIds )
210{
211 for ( const QString &layerId : layerIds )
212 {
213 auto it = mDetails.find( layerId );
214 if ( it != mDetails.end() )
215 mDetails.erase( it );
216 }
217}
218
219
Handles coordinate transforms between two 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.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
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 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:59