Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsmaphittest.cpp 3 : : --------------------- 4 : : begin : September 2014 5 : : copyright : (C) 2014 by Martin Dobias 6 : : email : wonder dot sk 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 "qgsmaphittest.h" 17 : : 18 : : #include "qgsfeatureiterator.h" 19 : : #include "qgsproject.h" 20 : : #include "qgsrendercontext.h" 21 : : #include "qgsmaplayerstylemanager.h" 22 : : #include "qgsrenderer.h" 23 : : #include "qgspointdisplacementrenderer.h" 24 : : #include "qgsvectorlayer.h" 25 : : #include "qgssymbollayerutils.h" 26 : : #include "qgsgeometry.h" 27 : : #include "qgsgeometryengine.h" 28 : : #include "qgsexpressioncontextutils.h" 29 : : 30 : 0 : QgsMapHitTest::QgsMapHitTest( const QgsMapSettings &settings, const QgsGeometry &polygon, const LayerFilterExpression &layerFilterExpression ) 31 : 0 : : mSettings( settings ) 32 : 0 : , mLayerFilterExpression( layerFilterExpression ) 33 : 0 : , mOnlyExpressions( false ) 34 : : { 35 : 0 : if ( !polygon.isNull() && polygon.type() == QgsWkbTypes::PolygonGeometry ) 36 : : { 37 : 0 : mPolygon = polygon; 38 : 0 : } 39 : 0 : } 40 : : 41 : 0 : QgsMapHitTest::QgsMapHitTest( const QgsMapSettings &settings, const LayerFilterExpression &layerFilterExpression ) 42 : 0 : : mSettings( settings ) 43 : 0 : , mLayerFilterExpression( layerFilterExpression ) 44 : 0 : , mOnlyExpressions( true ) 45 : : { 46 : 0 : } 47 : : 48 : 0 : void QgsMapHitTest::run() 49 : : { 50 : : // TODO: do we need this temp image? 51 : 0 : QImage tmpImage( mSettings.outputSize(), mSettings.outputImageFormat() ); 52 : 0 : tmpImage.setDotsPerMeterX( mSettings.outputDpi() * 25.4 ); 53 : 0 : tmpImage.setDotsPerMeterY( mSettings.outputDpi() * 25.4 ); 54 : 0 : QPainter painter( &tmpImage ); 55 : : 56 : 0 : QgsRenderContext context = QgsRenderContext::fromMapSettings( mSettings ); 57 : 0 : context.setPainter( &painter ); // we are not going to draw anything, but we still need a working painter 58 : : 59 : 0 : const auto constLayers = mSettings.layers(); 60 : 0 : for ( QgsMapLayer *layer : constLayers ) 61 : : { 62 : 0 : QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ); 63 : 0 : if ( !vl || !vl->renderer() ) 64 : 0 : continue; 65 : : 66 : 0 : if ( !mOnlyExpressions ) 67 : : { 68 : 0 : if ( !vl->isInScaleRange( mSettings.scale() ) ) 69 : : { 70 : 0 : mHitTest[vl] = SymbolSet(); // no symbols -> will not be shown 71 : 0 : mHitTestRuleKey[vl] = SymbolSet(); 72 : 0 : continue; 73 : : } 74 : : 75 : 0 : context.setCoordinateTransform( mSettings.layerTransform( vl ) ); 76 : 0 : context.setExtent( mSettings.outputExtentToLayerExtent( vl, mSettings.visibleExtent() ) ); 77 : 0 : } 78 : : 79 : 0 : context.expressionContext() << QgsExpressionContextUtils::layerScope( vl ); 80 : 0 : SymbolSet &usedSymbols = mHitTest[vl]; 81 : 0 : SymbolSet &usedSymbolsRuleKey = mHitTestRuleKey[vl]; 82 : 0 : runHitTestLayer( vl, usedSymbols, usedSymbolsRuleKey, context ); 83 : : } 84 : : 85 : 0 : painter.end(); 86 : 0 : } 87 : : 88 : 0 : bool QgsMapHitTest::symbolVisible( QgsSymbol *symbol, QgsVectorLayer *layer ) const 89 : : { 90 : 0 : if ( !symbol || !layer || !mHitTest.contains( layer ) ) 91 : 0 : return false; 92 : : 93 : 0 : return mHitTest.value( layer ).contains( QgsSymbolLayerUtils::symbolProperties( symbol ) ); 94 : 0 : } 95 : : 96 : 0 : bool QgsMapHitTest::legendKeyVisible( const QString &ruleKey, QgsVectorLayer *layer ) const 97 : : { 98 : 0 : if ( !layer || !mHitTestRuleKey.contains( layer ) ) 99 : 0 : return false; 100 : : 101 : 0 : return mHitTestRuleKey.value( layer ).contains( ruleKey ); 102 : 0 : } 103 : : 104 : 0 : void QgsMapHitTest::runHitTestLayer( QgsVectorLayer *vl, SymbolSet &usedSymbols, SymbolSet &usedSymbolsRuleKey, QgsRenderContext &context ) 105 : : { 106 : 0 : QgsMapLayerStyleOverride styleOverride( vl ); 107 : 0 : if ( mSettings.layerStyleOverrides().contains( vl->id() ) ) 108 : 0 : styleOverride.setOverrideStyle( mSettings.layerStyleOverrides().value( vl->id() ) ); 109 : : 110 : 0 : std::unique_ptr< QgsFeatureRenderer > r( vl->renderer()->clone() ); 111 : 0 : bool moreSymbolsPerFeature = r->capabilities() & QgsFeatureRenderer::MoreSymbolsPerFeature; 112 : 0 : r->startRender( context, vl->fields() ); 113 : : 114 : 0 : QgsGeometry transformedPolygon = mPolygon; 115 : 0 : if ( !mOnlyExpressions && !mPolygon.isNull() ) 116 : : { 117 : 0 : if ( mSettings.destinationCrs() != vl->crs() ) 118 : : { 119 : 0 : QgsCoordinateTransform ct( mSettings.destinationCrs(), vl->crs(), mSettings.transformContext() ); 120 : 0 : transformedPolygon.transform( ct ); 121 : 0 : } 122 : 0 : } 123 : : 124 : 0 : QgsFeature f; 125 : 0 : QgsFeatureRequest request; 126 : 0 : std::unique_ptr< QgsGeometryEngine > polygonEngine; 127 : 0 : if ( !mOnlyExpressions ) 128 : : { 129 : 0 : if ( mPolygon.isNull() ) 130 : : { 131 : 0 : request.setFilterRect( context.extent() ); 132 : 0 : request.setFlags( QgsFeatureRequest::ExactIntersect ); 133 : 0 : } 134 : : else 135 : : { 136 : 0 : request.setFilterRect( transformedPolygon.boundingBox() ); 137 : 0 : polygonEngine.reset( QgsGeometry::createGeometryEngine( transformedPolygon.constGet() ) ); 138 : 0 : polygonEngine->prepareGeometry(); 139 : : } 140 : 0 : } 141 : 0 : QgsFeatureIterator fi = vl->getFeatures( request ); 142 : : 143 : 0 : SymbolSet lUsedSymbols; 144 : 0 : SymbolSet lUsedSymbolsRuleKey; 145 : 0 : bool allExpressionFalse = false; 146 : 0 : bool hasExpression = mLayerFilterExpression.contains( vl->id() ); 147 : 0 : std::unique_ptr<QgsExpression> expr; 148 : 0 : if ( hasExpression ) 149 : : { 150 : 0 : expr.reset( new QgsExpression( mLayerFilterExpression[vl->id()] ) ); 151 : 0 : expr->prepare( &context.expressionContext() ); 152 : 0 : } 153 : 0 : while ( fi.nextFeature( f ) ) 154 : : { 155 : 0 : context.expressionContext().setFeature( f ); 156 : : // filter out elements outside of the polygon 157 : 0 : if ( f.hasGeometry() && polygonEngine ) 158 : : { 159 : 0 : if ( !polygonEngine->intersects( f.geometry().constGet() ) ) 160 : : { 161 : 0 : continue; 162 : : } 163 : 0 : } 164 : : 165 : : // filter out elements where the expression is false 166 : 0 : if ( hasExpression ) 167 : : { 168 : 0 : if ( !expr->evaluate( &context.expressionContext() ).toBool() ) 169 : 0 : continue; 170 : : else 171 : 0 : allExpressionFalse = false; 172 : 0 : } 173 : : 174 : : //make sure we store string representation of symbol, not pointer 175 : : //otherwise layer style override changes will delete original symbols and leave hanging pointers 176 : 0 : const auto constLegendKeysForFeature = r->legendKeysForFeature( f, context ); 177 : 0 : for ( const QString &legendKey : constLegendKeysForFeature ) 178 : : { 179 : 0 : lUsedSymbolsRuleKey.insert( legendKey ); 180 : : } 181 : : 182 : 0 : if ( moreSymbolsPerFeature ) 183 : : { 184 : 0 : const auto constOriginalSymbolsForFeature = r->originalSymbolsForFeature( f, context ); 185 : 0 : for ( QgsSymbol *s : constOriginalSymbolsForFeature ) 186 : : { 187 : 0 : if ( s ) 188 : 0 : lUsedSymbols.insert( QgsSymbolLayerUtils::symbolProperties( s ) ); 189 : : } 190 : 0 : } 191 : : else 192 : : { 193 : 0 : QgsSymbol *s = r->originalSymbolForFeature( f, context ); 194 : 0 : if ( s ) 195 : 0 : lUsedSymbols.insert( QgsSymbolLayerUtils::symbolProperties( s ) ); 196 : : } 197 : 0 : } 198 : 0 : r->stopRender( context ); 199 : : 200 : 0 : if ( !allExpressionFalse ) 201 : : { 202 : : // QSet is implicitly shared => constant time 203 : 0 : usedSymbols = lUsedSymbols; 204 : 0 : usedSymbolsRuleKey = lUsedSymbolsRuleKey; 205 : 0 : } 206 : 0 : } 207 : :