QGIS API Documentation  3.25.0-Master (dec16ba68b)
qgsmapclippingutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmapclippingutils.cpp
3  --------------------------------------
4  Date : June 2020
5  Copyright : (C) 2020 by 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 "qgsmapclippingutils.h"
17 #include "qgsgeometry.h"
18 #include "qgsrendercontext.h"
19 #include "qgsmapclippingregion.h"
20 #include "qgslogger.h"
21 #include <algorithm>
22 #include <QPointer>
23 
24 QList<QgsMapClippingRegion> QgsMapClippingUtils::collectClippingRegionsForLayer( const QgsRenderContext &context, const QgsMapLayer *layer )
25 {
26  QList< QgsMapClippingRegion > res;
27  const QList< QgsMapClippingRegion > regions = context.clippingRegions();
28  res.reserve( regions.size() );
29 
30  std::copy_if( regions.begin(), regions.end(), std::back_inserter( res ), [layer]( const QgsMapClippingRegion & region )
31  {
32  return region.appliesToLayer( layer );
33  } );
34 
35  return res;
36 }
37 
38 QgsGeometry QgsMapClippingUtils::calculateFeatureRequestGeometry( const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, bool &shouldFilter )
39 {
40  QgsGeometry result;
41  bool first = true;
42  shouldFilter = false;
43  for ( const QgsMapClippingRegion &region : regions )
44  {
45  if ( region.geometry().type() != QgsWkbTypes::PolygonGeometry )
46  continue;
47 
48  shouldFilter = true;
49  if ( first )
50  {
51  result = region.geometry();
52  first = false;
53  }
54  else
55  {
56  result = result.intersection( region.geometry() );
57  }
58  }
59 
60  if ( !shouldFilter )
61  return QgsGeometry();
62 
63  // filter out polygon parts from result only
65 
66  // lastly transform back to layer CRS
67  try
68  {
69  result.transform( context.coordinateTransform(), Qgis::TransformDirection::Reverse );
70  }
71  catch ( QgsCsException & )
72  {
73  QgsDebugMsg( QStringLiteral( "Could not transform clipping region to layer CRS" ) );
74  shouldFilter = false;
75  return QgsGeometry();
76  }
77 
78  return result;
79 }
80 
81 QgsGeometry QgsMapClippingUtils::calculateFeatureIntersectionGeometry( const QList<QgsMapClippingRegion> &regions, const QgsRenderContext &context, bool &shouldClip )
82 {
83  QgsGeometry result;
84  bool first = true;
85  shouldClip = false;
86  for ( const QgsMapClippingRegion &region : regions )
87  {
88  if ( region.geometry().type() != QgsWkbTypes::PolygonGeometry )
89  continue;
90 
92  continue;
93 
94  shouldClip = true;
95  if ( first )
96  {
97  result = region.geometry();
98  first = false;
99  }
100  else
101  {
102  result = result.intersection( region.geometry() );
103  }
104  }
105 
106  if ( !shouldClip )
107  return QgsGeometry();
108 
109  // filter out polygon parts from result only
111 
112  // lastly transform back to layer CRS
113  try
114  {
115  result.transform( context.coordinateTransform(), Qgis::TransformDirection::Reverse );
116  }
117  catch ( QgsCsException & )
118  {
119  QgsDebugMsg( QStringLiteral( "Could not transform clipping region to layer CRS" ) );
120  shouldClip = false;
121  return QgsGeometry();
122  }
123 
124  return result;
125 }
126 
127 QPainterPath QgsMapClippingUtils::calculatePainterClipRegion( const QList<QgsMapClippingRegion> &regions, const QgsRenderContext &context, QgsMapLayerType layerType, bool &shouldClip )
128 {
129  QgsGeometry result;
130  bool first = true;
131  shouldClip = false;
132  for ( const QgsMapClippingRegion &region : regions )
133  {
134  if ( region.geometry().type() != QgsWkbTypes::PolygonGeometry )
135  continue;
136 
137  switch ( layerType )
138  {
141  continue;
142  break;
143 
145  // for now, we ignore the region's featureClip behavior when rendering vector tiles
146  // TODO: ideally we should apply this during rendering, just like we do for normal
147  // vector layers
148  break;
149 
156  // for these layer types, we ignore the region's featureClip behavior.
157  break;
158 
159  }
160 
161  shouldClip = true;
162  if ( first )
163  {
164  result = region.geometry();
165  first = false;
166  }
167  else
168  {
169  result = result.intersection( region.geometry() );
170  }
171  }
172 
173  if ( !shouldClip )
174  return QPainterPath();
175 
176  // transform to painter coordinates
177  result.mapToPixel( context.mapToPixel() );
178 
179  return result.constGet()->asQPainterPath();
180 }
181 
182 QgsGeometry QgsMapClippingUtils::calculateLabelIntersectionGeometry( const QList<QgsMapClippingRegion> &regions, const QgsRenderContext &context, bool &shouldClip )
183 {
184  QgsGeometry result;
185  bool first = true;
186  shouldClip = false;
187  for ( const QgsMapClippingRegion &region : regions )
188  {
189  if ( region.geometry().type() != QgsWkbTypes::PolygonGeometry )
190  continue;
191 
192  // for labeling, we clip using either painter clip regions or intersects type regions.
193  // unlike feature rendering, we clip features to painter clip regions for labeling, because
194  // we want the label to sit within the clip region if possible
195  if ( region.featureClip() != QgsMapClippingRegion::FeatureClippingType::ClipPainterOnly &&
197  continue;
198 
199  shouldClip = true;
200  if ( first )
201  {
202  result = region.geometry();
203  first = false;
204  }
205  else
206  {
207  result = result.intersection( region.geometry() );
208  }
209  }
210 
211  if ( !shouldClip )
212  return QgsGeometry();
213 
214  // filter out polygon parts from result only
216 
217  // lastly transform back to layer CRS
218  try
219  {
220  result.transform( context.coordinateTransform(), Qgis::TransformDirection::Reverse );
221  }
222  catch ( QgsCsException & )
223  {
224  QgsDebugMsg( QStringLiteral( "Could not transform clipping region to layer CRS" ) );
225  shouldClip = false;
226  return QgsGeometry();
227  }
228 
229  return result;
230 }
virtual QPainterPath asQPainterPath() const =0
Returns the geometry represented as a QPainterPath.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
void mapToPixel(const QgsMapToPixel &mtp)
Transforms the geometry from map units to pixels in place.
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
bool convertGeometryCollectionToSubclass(QgsWkbTypes::GeometryType geomType)
Converts geometry collection to a the desired geometry type subclass (multi-point,...
A map clipping region (in map coordinates and CRS).
@ ClipPainterOnly
Applying clipping on the painter only (i.e. feature boundaries will be unchanged, but may be invisibl...
@ ClipToIntersection
Clip the geometry of these features to the region prior to rendering (i.e. feature boundaries will fo...
static QList< QgsMapClippingRegion > collectClippingRegionsForLayer(const QgsRenderContext &context, const QgsMapLayer *layer)
Collects the list of map clipping regions from a context which apply to a map layer.
static QPainterPath calculatePainterClipRegion(const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, QgsMapLayerType layerType, bool &shouldClip)
Returns a QPainterPath representing the intersection of clipping regions from context which should be...
static QgsGeometry calculateLabelIntersectionGeometry(const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, bool &shouldClip)
Returns the geometry representing the intersection of clipping regions from context which should be u...
static QgsGeometry calculateFeatureIntersectionGeometry(const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, bool &shouldClip)
Returns the geometry representing the intersection of clipping regions from context which should be u...
static QgsGeometry calculateFeatureRequestGeometry(const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, bool &shouldFilter)
Returns the geometry representing the intersection of clipping regions from context.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
Contains information about the context of a rendering operation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QList< QgsMapClippingRegion > clippingRegions() const
Returns the list of clipping regions to apply during the render.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38