QGIS API Documentation  3.25.0-Master (6b426f5f8a)
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 
16 #include "qgsrendereditemresults.h"
17 #include "qgsrendereditemdetails.h"
18 #include "qgsrendercontext.h"
19 #include "qgslogger.h"
21 #include "RTree.h"
22 
23 
25 class 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 
109 QList<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 
122 QList<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 
134 void 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  QgsDebugMsg( 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 
158 void 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 
197 void 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 SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
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 QgsDebugMsg(str)
Definition: qgslogger.h:38