Branch data Line data Source code
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 : 0 : QList<QgsMapClippingRegion> QgsMapClippingUtils::collectClippingRegionsForLayer( const QgsRenderContext &context, const QgsMapLayer *layer ) 25 : : { 26 : 0 : QList< QgsMapClippingRegion > res; 27 : 0 : const QList< QgsMapClippingRegion > regions = context.clippingRegions(); 28 : 0 : res.reserve( regions.size() ); 29 : : 30 : 0 : std::copy_if( regions.begin(), regions.end(), std::back_inserter( res ), [layer]( const QgsMapClippingRegion & region ) 31 : : { 32 : 0 : return region.appliesToLayer( layer ); 33 : : } ); 34 : : 35 : 0 : return res; 36 : 0 : } 37 : : 38 : 0 : QgsGeometry QgsMapClippingUtils::calculateFeatureRequestGeometry( const QList< QgsMapClippingRegion > ®ions, const QgsRenderContext &context, bool &shouldFilter ) 39 : : { 40 : 0 : QgsGeometry result; 41 : 0 : bool first = true; 42 : 0 : shouldFilter = false; 43 : 0 : for ( const QgsMapClippingRegion ®ion : regions ) 44 : : { 45 : 0 : if ( region.geometry().type() != QgsWkbTypes::PolygonGeometry ) 46 : 0 : continue; 47 : : 48 : 0 : shouldFilter = true; 49 : 0 : if ( first ) 50 : : { 51 : 0 : result = region.geometry(); 52 : 0 : first = false; 53 : 0 : } 54 : : else 55 : : { 56 : 0 : result = result.intersection( region.geometry() ); 57 : : } 58 : : } 59 : : 60 : 0 : if ( !shouldFilter ) 61 : 0 : return QgsGeometry(); 62 : : 63 : : // filter out polygon parts from result only 64 : 0 : result.convertGeometryCollectionToSubclass( QgsWkbTypes::PolygonGeometry ); 65 : : 66 : : // lastly transform back to layer CRS 67 : : try 68 : : { 69 : 0 : result.transform( context.coordinateTransform(), QgsCoordinateTransform::ReverseTransform ); 70 : 0 : } 71 : : catch ( QgsCsException & ) 72 : : { 73 : 0 : QgsDebugMsg( QStringLiteral( "Could not transform clipping region to layer CRS" ) ); 74 : 0 : shouldFilter = false; 75 : 0 : return QgsGeometry(); 76 : 0 : } 77 : : 78 : 0 : return result; 79 : 0 : } 80 : : 81 : 0 : QgsGeometry QgsMapClippingUtils::calculateFeatureIntersectionGeometry( const QList<QgsMapClippingRegion> ®ions, const QgsRenderContext &context, bool &shouldClip ) 82 : : { 83 : 0 : QgsGeometry result; 84 : 0 : bool first = true; 85 : 0 : shouldClip = false; 86 : 0 : for ( const QgsMapClippingRegion ®ion : regions ) 87 : : { 88 : 0 : if ( region.geometry().type() != QgsWkbTypes::PolygonGeometry ) 89 : 0 : continue; 90 : : 91 : 0 : if ( region.featureClip() != QgsMapClippingRegion::FeatureClippingType::ClipToIntersection ) 92 : 0 : continue; 93 : : 94 : 0 : shouldClip = true; 95 : 0 : if ( first ) 96 : : { 97 : 0 : result = region.geometry(); 98 : 0 : first = false; 99 : 0 : } 100 : : else 101 : : { 102 : 0 : result = result.intersection( region.geometry() ); 103 : : } 104 : : } 105 : : 106 : 0 : if ( !shouldClip ) 107 : 0 : return QgsGeometry(); 108 : : 109 : : // filter out polygon parts from result only 110 : 0 : result.convertGeometryCollectionToSubclass( QgsWkbTypes::PolygonGeometry ); 111 : : 112 : : // lastly transform back to layer CRS 113 : : try 114 : : { 115 : 0 : result.transform( context.coordinateTransform(), QgsCoordinateTransform::ReverseTransform ); 116 : 0 : } 117 : : catch ( QgsCsException & ) 118 : : { 119 : 0 : QgsDebugMsg( QStringLiteral( "Could not transform clipping region to layer CRS" ) ); 120 : 0 : shouldClip = false; 121 : 0 : return QgsGeometry(); 122 : 0 : } 123 : : 124 : 0 : return result; 125 : 0 : } 126 : : 127 : 0 : QPainterPath QgsMapClippingUtils::calculatePainterClipRegion( const QList<QgsMapClippingRegion> ®ions, const QgsRenderContext &context, QgsMapLayerType layerType, bool &shouldClip ) 128 : : { 129 : 0 : QgsGeometry result; 130 : 0 : bool first = true; 131 : 0 : shouldClip = false; 132 : 0 : for ( const QgsMapClippingRegion ®ion : regions ) 133 : : { 134 : 0 : if ( region.geometry().type() != QgsWkbTypes::PolygonGeometry ) 135 : 0 : continue; 136 : : 137 : 0 : switch ( layerType ) 138 : : { 139 : : case QgsMapLayerType::VectorLayer: 140 : 0 : if ( region.featureClip() != QgsMapClippingRegion::FeatureClippingType::ClipPainterOnly ) 141 : 0 : continue; 142 : 0 : break; 143 : : 144 : : case QgsMapLayerType::VectorTileLayer: 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 : 0 : break; 149 : : 150 : : case QgsMapLayerType::MeshLayer: 151 : : case QgsMapLayerType::RasterLayer: 152 : : case QgsMapLayerType::PluginLayer: 153 : : case QgsMapLayerType::PointCloudLayer: 154 : : case QgsMapLayerType::AnnotationLayer: 155 : : // for these layer types, we ignore the region's featureClip behavior. 156 : 0 : break; 157 : : 158 : : } 159 : : 160 : 0 : shouldClip = true; 161 : 0 : if ( first ) 162 : : { 163 : 0 : result = region.geometry(); 164 : 0 : first = false; 165 : 0 : } 166 : : else 167 : : { 168 : 0 : result = result.intersection( region.geometry() ); 169 : : } 170 : : } 171 : : 172 : 0 : if ( !shouldClip ) 173 : 0 : return QPainterPath(); 174 : : 175 : : // transform to painter coordinates 176 : 0 : result.mapToPixel( context.mapToPixel() ); 177 : : 178 : 0 : return result.constGet()->asQPainterPath(); 179 : 0 : } 180 : : 181 : 0 : QgsGeometry QgsMapClippingUtils::calculateLabelIntersectionGeometry( const QList<QgsMapClippingRegion> ®ions, const QgsRenderContext &context, bool &shouldClip ) 182 : : { 183 : 0 : QgsGeometry result; 184 : 0 : bool first = true; 185 : 0 : shouldClip = false; 186 : 0 : for ( const QgsMapClippingRegion ®ion : regions ) 187 : : { 188 : 0 : if ( region.geometry().type() != QgsWkbTypes::PolygonGeometry ) 189 : 0 : continue; 190 : : 191 : : // for labeling, we clip using either painter clip regions or intersects type regions. 192 : : // unlike feature rendering, we clip features to painter clip regions for labeling, because 193 : : // we want the label to sit within the clip region if possible 194 : 0 : if ( region.featureClip() != QgsMapClippingRegion::FeatureClippingType::ClipPainterOnly && 195 : 0 : region.featureClip() != QgsMapClippingRegion::FeatureClippingType::ClipToIntersection ) 196 : 0 : continue; 197 : : 198 : 0 : shouldClip = true; 199 : 0 : if ( first ) 200 : : { 201 : 0 : result = region.geometry(); 202 : 0 : first = false; 203 : 0 : } 204 : : else 205 : : { 206 : 0 : result = result.intersection( region.geometry() ); 207 : : } 208 : : } 209 : : 210 : 0 : if ( !shouldClip ) 211 : 0 : return QgsGeometry(); 212 : : 213 : : // filter out polygon parts from result only 214 : 0 : result.convertGeometryCollectionToSubclass( QgsWkbTypes::PolygonGeometry ); 215 : : 216 : : // lastly transform back to layer CRS 217 : : try 218 : : { 219 : 0 : result.transform( context.coordinateTransform(), QgsCoordinateTransform::ReverseTransform ); 220 : 0 : } 221 : : catch ( QgsCsException & ) 222 : : { 223 : 0 : QgsDebugMsg( QStringLiteral( "Could not transform clipping region to layer CRS" ) ); 224 : 0 : shouldClip = false; 225 : 0 : return QgsGeometry(); 226 : 0 : } 227 : : 228 : 0 : return result; 229 : 0 : }