QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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"
20#include "qgslogger.h"
21#include <algorithm>
22#include <QPointer>
23
24QList<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
38QgsGeometry 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() != Qgis::GeometryType::Polygon )
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
64 result.convertGeometryCollectionToSubclass( Qgis::GeometryType::Polygon );
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
81QgsGeometry 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() != Qgis::GeometryType::Polygon )
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
110 result.convertGeometryCollectionToSubclass( Qgis::GeometryType::Polygon );
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
127QPainterPath QgsMapClippingUtils::calculatePainterClipRegion( const QList<QgsMapClippingRegion> &regions, const QgsRenderContext &context, Qgis::LayerType 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() != Qgis::GeometryType::Polygon )
135 continue;
136
137 switch ( layerType )
138 {
139 case Qgis::LayerType::Vector:
141 continue;
142 break;
143
144 case Qgis::LayerType::VectorTile:
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
150 case Qgis::LayerType::Mesh:
151 case Qgis::LayerType::Raster:
152 case Qgis::LayerType::Plugin:
153 case Qgis::LayerType::PointCloud:
154 case Qgis::LayerType::Annotation:
155 case Qgis::LayerType::Group:
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
182QgsGeometry 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() != Qgis::GeometryType::Polygon )
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
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
215 result.convertGeometryCollectionToSubclass( Qgis::GeometryType::Polygon );
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}
LayerType
Types of layers that can be added to a map.
Definition: qgis.h:115
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:164
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 QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
bool convertGeometryCollectionToSubclass(Qgis::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 QPainterPath calculatePainterClipRegion(const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, Qgis::LayerType layerType, bool &shouldClip)
Returns a QPainterPath representing the intersection of clipping regions from context which should be...
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 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.
QList< QgsMapClippingRegion > clippingRegions() const
Returns the list of clipping regions to apply during the render.
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.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38