LCOV - code coverage report
Current view: top level - core/vector - qgsvectorlayerdiagramprovider.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 200 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsvectorlayerdiagramprovider.cpp
       3                 :            :   --------------------------------------
       4                 :            :   Date                 : September 2015
       5                 :            :   Copyright            : (C) 2015 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 "qgsvectorlayerdiagramprovider.h"
      17                 :            : 
      18                 :            : #include "qgsgeometry.h"
      19                 :            : #include "qgslabelsearchtree.h"
      20                 :            : #include "qgsvectorlayer.h"
      21                 :            : #include "qgsvectorlayerfeatureiterator.h"
      22                 :            : #include "diagram/qgsdiagram.h"
      23                 :            : #include "qgsgeos.h"
      24                 :            : #include "qgslabelingresults.h"
      25                 :            : 
      26                 :            : #include "feature.h"
      27                 :            : #include "labelposition.h"
      28                 :            : 
      29                 :          0 : QgsVectorLayerDiagramProvider::QgsVectorLayerDiagramProvider( QgsVectorLayer *layer, bool ownFeatureLoop )
      30                 :          0 :   : QgsAbstractLabelProvider( layer )
      31                 :          0 :   , mSettings( *layer->diagramLayerSettings() )
      32                 :          0 :   , mDiagRenderer( layer->diagramRenderer()->clone() )
      33                 :          0 :   , mFields( layer->fields() )
      34                 :          0 :   , mLayerCrs( layer->crs() )
      35                 :          0 :   , mSource( ownFeatureLoop ? new QgsVectorLayerFeatureSource( layer ) : nullptr )
      36                 :          0 :   , mOwnsSource( ownFeatureLoop )
      37                 :          0 : {
      38                 :          0 :   init();
      39                 :          0 : }
      40                 :            : 
      41                 :            : 
      42                 :          0 : void QgsVectorLayerDiagramProvider::init()
      43                 :            : {
      44                 :          0 :   mName = mLayerId;
      45                 :          0 :   mPriority = 1 - mSettings.priority() / 10.0; // convert 0..10 --> 1..0
      46                 :          0 :   mPlacement = QgsPalLayerSettings::Placement( mSettings.placement() );
      47                 :          0 : }
      48                 :            : 
      49                 :            : 
      50                 :          0 : QgsVectorLayerDiagramProvider::~QgsVectorLayerDiagramProvider()
      51                 :          0 : {
      52                 :          0 :   if ( mOwnsSource )
      53                 :          0 :     delete mSource;
      54                 :            : 
      55                 :          0 :   qDeleteAll( mFeatures );
      56                 :            : 
      57                 :            :   // renderer is owned by mSettings
      58                 :          0 : }
      59                 :            : 
      60                 :            : 
      61                 :          0 : QList<QgsLabelFeature *> QgsVectorLayerDiagramProvider::labelFeatures( QgsRenderContext &context )
      62                 :            : {
      63                 :          0 :   if ( !mSource )
      64                 :            :   {
      65                 :            :     // we have created the provider with "own feature loop" == false
      66                 :            :     // so it is assumed that prepare() has been already called followed by registerFeature() calls
      67                 :          0 :     return mFeatures;
      68                 :            :   }
      69                 :            : 
      70                 :          0 :   QSet<QString> attributeNames;
      71                 :          0 :   if ( !prepare( context, attributeNames ) )
      72                 :          0 :     return QList<QgsLabelFeature *>();
      73                 :            : 
      74                 :          0 :   QgsRectangle layerExtent = context.extent();
      75                 :          0 :   if ( mSettings.coordinateTransform().isValid() )
      76                 :          0 :     layerExtent = mSettings.coordinateTransform().transformBoundingBox( context.extent(), QgsCoordinateTransform::ReverseTransform );
      77                 :            : 
      78                 :          0 :   QgsFeatureRequest request;
      79                 :          0 :   request.setFilterRect( layerExtent );
      80                 :          0 :   request.setSubsetOfAttributes( attributeNames, mFields );
      81                 :          0 :   const QgsFeatureFilterProvider *featureFilterProvider = context.featureFilterProvider();
      82                 :          0 :   if ( featureFilterProvider )
      83                 :            :   {
      84                 :          0 :     featureFilterProvider->filterFeatures( qobject_cast<QgsVectorLayer *>( mLayer ), request );
      85                 :          0 :   }
      86                 :          0 :   QgsFeatureIterator fit = mSource->getFeatures( request );
      87                 :            : 
      88                 :          0 :   QgsFeature fet;
      89                 :          0 :   while ( fit.nextFeature( fet ) )
      90                 :            :   {
      91                 :          0 :     context.expressionContext().setFeature( fet );
      92                 :          0 :     registerFeature( fet, context );
      93                 :            :   }
      94                 :            : 
      95                 :          0 :   return mFeatures;
      96                 :          0 : }
      97                 :            : 
      98                 :            : 
      99                 :          0 : void QgsVectorLayerDiagramProvider::drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const
     100                 :            : {
     101                 :            : #if 1 // XXX strk
     102                 :            :   // features are pre-rotated but not scaled/translated,
     103                 :            :   // so we only disable rotation here. Ideally, they'd be
     104                 :            :   // also pre-scaled/translated, as suggested here:
     105                 :            :   // https://github.com/qgis/QGIS/issues/20071
     106                 :          0 :   QgsMapToPixel xform = context.mapToPixel();
     107                 :          0 :   xform.setMapRotation( 0, 0, 0 );
     108                 :            : #else
     109                 :            :   const QgsMapToPixel &xform = context.mapToPixel();
     110                 :            : #endif
     111                 :            : 
     112                 :          0 :   QgsDiagramLabelFeature *dlf = dynamic_cast<QgsDiagramLabelFeature *>( label->getFeaturePart()->feature() );
     113                 :            : 
     114                 :          0 :   QgsFeature feature;
     115                 :          0 :   feature.setFields( mFields );
     116                 :          0 :   feature.setValid( true );
     117                 :          0 :   feature.setId( label->getFeaturePart()->featureId() );
     118                 :          0 :   feature.setAttributes( dlf->attributes() );
     119                 :            : 
     120                 :          0 :   context.expressionContext().setFeature( feature );
     121                 :            : 
     122                 :            :   //calculate top-left point for diagram
     123                 :            :   //first, calculate the centroid of the label (accounts for PAL creating
     124                 :            :   //rotated labels when we do not want to draw the diagrams rotated)
     125                 :          0 :   double centerX = 0;
     126                 :          0 :   double centerY = 0;
     127                 :          0 :   for ( int i = 0; i < 4; ++i )
     128                 :            :   {
     129                 :          0 :     centerX += label->getX( i );
     130                 :          0 :     centerY += label->getY( i );
     131                 :          0 :   }
     132                 :          0 :   QgsPointXY outPt( centerX / 4.0, centerY / 4.0 );
     133                 :            :   //then, calculate the top left point for the diagram with this center position
     134                 :          0 :   QgsPointXY centerPt = xform.transform( outPt.x() - label->getWidth() / 2,
     135                 :          0 :                                          outPt.y() - label->getHeight() / 2 );
     136                 :            : 
     137                 :          0 :   mSettings.renderer()->renderDiagram( feature, context, centerPt.toQPointF(), mSettings.dataDefinedProperties() );
     138                 :            : 
     139                 :            :   //insert into label search tree to manipulate position interactively
     140                 :          0 :   mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, QString(), QFont(), true, false );
     141                 :            : 
     142                 :          0 : }
     143                 :            : 
     144                 :            : 
     145                 :          0 : bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext &context, QSet<QString> &attributeNames )
     146                 :            : {
     147                 :          0 :   QgsDiagramLayerSettings &s2 = mSettings;
     148                 :          0 :   const QgsMapSettings &mapSettings = mEngine->mapSettings();
     149                 :            : 
     150                 :          0 :   if ( context.coordinateTransform().isValid() )
     151                 :            :     // this is context for layer rendering
     152                 :          0 :     s2.setCoordinateTransform( context.coordinateTransform() );
     153                 :            :   else
     154                 :            :   {
     155                 :            :     // otherwise fall back to creating our own CT
     156                 :          0 :     s2.setCoordinateTransform( QgsCoordinateTransform( mLayerCrs, mapSettings.destinationCrs(), context.transformContext() ) );
     157                 :            :   }
     158                 :            : 
     159                 :          0 :   s2.setRenderer( mDiagRenderer );
     160                 :            : 
     161                 :          0 :   bool result = s2.prepare( context.expressionContext() );
     162                 :            : 
     163                 :            :   //add attributes needed by the diagram renderer
     164                 :          0 :   attributeNames.unite( s2.referencedFields( context.expressionContext() ) );
     165                 :            : 
     166                 :          0 :   return result;
     167                 :          0 : }
     168                 :            : 
     169                 :            : 
     170                 :          0 : void QgsVectorLayerDiagramProvider::registerFeature( QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry )
     171                 :            : {
     172                 :          0 :   QgsLabelFeature *label = registerDiagram( feature, context, obstacleGeometry );
     173                 :          0 :   if ( label )
     174                 :          0 :     mFeatures << label;
     175                 :          0 : }
     176                 :            : 
     177                 :          0 : void QgsVectorLayerDiagramProvider::setClipFeatureGeometry( const QgsGeometry &geometry )
     178                 :            : {
     179                 :          0 :   mLabelClipFeatureGeom = geometry;
     180                 :          0 : }
     181                 :            : 
     182                 :          0 : QgsLabelFeature *QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature &feat, QgsRenderContext &context, const QgsGeometry &obstacleGeometry )
     183                 :            : {
     184                 :          0 :   const QgsMapSettings &mapSettings = mEngine->mapSettings();
     185                 :            : 
     186                 :          0 :   const QgsDiagramRenderer *dr = mSettings.renderer();
     187                 :          0 :   if ( dr )
     188                 :            :   {
     189                 :          0 :     QList<QgsDiagramSettings> settingList = dr->diagramSettings();
     190                 :          0 :     if ( !settingList.isEmpty() && settingList.at( 0 ).scaleBasedVisibility )
     191                 :            :     {
     192                 :          0 :       double maxScale = settingList.at( 0 ).maximumScale;
     193                 :          0 :       if ( maxScale > 0 && context.rendererScale() < maxScale )
     194                 :            :       {
     195                 :          0 :         return nullptr;
     196                 :            :       }
     197                 :            : 
     198                 :          0 :       double minScale = settingList.at( 0 ).minimumScale;
     199                 :          0 :       if ( minScale > 0 && context.rendererScale() > minScale )
     200                 :            :       {
     201                 :          0 :         return nullptr;
     202                 :            :       }
     203                 :          0 :     }
     204                 :          0 :   }
     205                 :            : 
     206                 :            :   // data defined show diagram? check this before doing any other processing
     207                 :          0 :   if ( !mSettings.dataDefinedProperties().valueAsBool( QgsDiagramLayerSettings::Show, context.expressionContext(), true ) )
     208                 :          0 :     return nullptr;
     209                 :            : 
     210                 :            :   // data defined obstacle?
     211                 :          0 :   bool isObstacle = mSettings.dataDefinedProperties().valueAsBool( QgsDiagramLayerSettings::IsObstacle, context.expressionContext(), mSettings.isObstacle() );
     212                 :            : 
     213                 :            :   //convert geom to geos
     214                 :          0 :   QgsGeometry geom = feat.geometry();
     215                 :          0 :   QgsGeometry extentGeom = QgsGeometry::fromRect( mapSettings.visibleExtent() );
     216                 :          0 :   if ( !qgsDoubleNear( mapSettings.rotation(), 0.0 ) )
     217                 :            :   {
     218                 :            :     //PAL features are prerotated, so extent also needs to be unrotated
     219                 :          0 :     extentGeom.rotate( -mapSettings.rotation(), mapSettings.visibleExtent().center() );
     220                 :          0 :   }
     221                 :            : 
     222                 :          0 :   if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, mSettings.coordinateTransform(), extentGeom ) )
     223                 :            :   {
     224                 :          0 :     geom = QgsPalLabeling::prepareGeometry( geom, context, mSettings.coordinateTransform(), extentGeom );
     225                 :          0 :   }
     226                 :          0 :   if ( geom.isEmpty() )
     227                 :          0 :     return nullptr;
     228                 :            : 
     229                 :          0 :   const QgsGeometry clipGeometry = mLabelClipFeatureGeom.isNull() ? context.featureClipGeometry() : mLabelClipFeatureGeom;
     230                 :          0 :   if ( !clipGeometry.isEmpty() )
     231                 :            :   {
     232                 :          0 :     const QgsWkbTypes::GeometryType expectedType = geom.type();
     233                 :          0 :     geom = geom.intersection( clipGeometry );
     234                 :          0 :     geom.convertGeometryCollectionToSubclass( expectedType );
     235                 :          0 :   }
     236                 :          0 :   if ( geom.isEmpty() )
     237                 :          0 :     return nullptr;
     238                 :            : 
     239                 :          0 :   QgsGeometry preparedObstacleGeom;
     240                 :          0 :   if ( isObstacle && !obstacleGeometry.isNull() && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, mSettings.coordinateTransform(), extentGeom ) )
     241                 :            :   {
     242                 :          0 :     preparedObstacleGeom = QgsPalLabeling::prepareGeometry( obstacleGeometry, context, mSettings.coordinateTransform(), extentGeom );
     243                 :          0 :   }
     244                 :          0 :   else if ( mSettings.isObstacle() && !obstacleGeometry.isNull() )
     245                 :            :   {
     246                 :          0 :     preparedObstacleGeom = obstacleGeometry;
     247                 :          0 :   }
     248                 :            : 
     249                 :          0 :   double diagramWidth = 0;
     250                 :          0 :   double diagramHeight = 0;
     251                 :          0 :   if ( dr )
     252                 :            :   {
     253                 :          0 :     QSizeF diagSize = dr->sizeMapUnits( feat, context );
     254                 :          0 :     if ( diagSize.isValid() )
     255                 :            :     {
     256                 :          0 :       diagramWidth = diagSize.width();
     257                 :          0 :       diagramHeight = diagSize.height();
     258                 :          0 :     }
     259                 :          0 :   }
     260                 :            : 
     261                 :            :   //  feature to the layer
     262                 :          0 :   bool alwaysShow = mSettings.showAllDiagrams();
     263                 :          0 :   context.expressionContext().setOriginalValueVariable( alwaysShow );
     264                 :          0 :   alwaysShow = mSettings.dataDefinedProperties().valueAsBool( QgsDiagramLayerSettings::AlwaysShow, context.expressionContext(), alwaysShow );
     265                 :            : 
     266                 :            :   // new style data defined position
     267                 :          0 :   bool ddPos = false;
     268                 :          0 :   double ddPosX = 0.0;
     269                 :          0 :   double ddPosY = 0.0;
     270                 :          0 :   if ( mSettings.dataDefinedProperties().hasProperty( QgsDiagramLayerSettings::PositionX )
     271                 :          0 :        && mSettings.dataDefinedProperties().property( QgsDiagramLayerSettings::PositionX ).isActive()
     272                 :          0 :        && mSettings.dataDefinedProperties().hasProperty( QgsDiagramLayerSettings::PositionY )
     273                 :          0 :        && mSettings.dataDefinedProperties().property( QgsDiagramLayerSettings::PositionY ).isActive() )
     274                 :            :   {
     275                 :          0 :     ddPosX = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::PositionX, context.expressionContext(), std::numeric_limits<double>::quiet_NaN() );
     276                 :          0 :     ddPosY = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::PositionY, context.expressionContext(), std::numeric_limits<double>::quiet_NaN() );
     277                 :            : 
     278                 :          0 :     ddPos = !std::isnan( ddPosX ) && !std::isnan( ddPosY );
     279                 :            : 
     280                 :          0 :     if ( ddPos )
     281                 :            :     {
     282                 :          0 :       QgsCoordinateTransform ct = mSettings.coordinateTransform();
     283                 :          0 :       if ( ct.isValid() && !ct.isShortCircuited() )
     284                 :            :       {
     285                 :          0 :         double z = 0;
     286                 :          0 :         ct.transformInPlace( ddPosX, ddPosY, z );
     287                 :          0 :       }
     288                 :            :       //data defined diagram position is always centered
     289                 :          0 :       ddPosX -= diagramWidth / 2.0;
     290                 :          0 :       ddPosY -= diagramHeight / 2.0;
     291                 :          0 :     }
     292                 :          0 :   }
     293                 :            : 
     294                 :          0 :   QgsDiagramLabelFeature *lf = new QgsDiagramLabelFeature( feat.id(), QgsGeos::asGeos( geom ), QSizeF( diagramWidth, diagramHeight ) );
     295                 :          0 :   lf->setHasFixedPosition( ddPos );
     296                 :          0 :   lf->setFixedPosition( QgsPointXY( ddPosX, ddPosY ) );
     297                 :          0 :   lf->setHasFixedAngle( true );
     298                 :          0 :   lf->setFixedAngle( 0 );
     299                 :          0 :   lf->setAlwaysShow( alwaysShow );
     300                 :          0 :   QgsLabelObstacleSettings os;
     301                 :          0 :   os.setIsObstacle( isObstacle );
     302                 :          0 :   os.setObstacleGeometry( preparedObstacleGeom );
     303                 :          0 :   lf->setObstacleSettings( os );
     304                 :            : 
     305                 :          0 :   if ( dr )
     306                 :            :   {
     307                 :            :     //append the diagram attributes to lbl
     308                 :          0 :     lf->setAttributes( feat.attributes() );
     309                 :          0 :   }
     310                 :            : 
     311                 :            :   // data defined priority?
     312                 :          0 :   if ( mSettings.dataDefinedProperties().hasProperty( QgsDiagramLayerSettings::Priority )
     313                 :          0 :        && mSettings.dataDefinedProperties().property( QgsDiagramLayerSettings::Priority ).isActive() )
     314                 :            :   {
     315                 :          0 :     context.expressionContext().setOriginalValueVariable( mSettings.priority() );
     316                 :          0 :     double priorityD = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::Priority, context.expressionContext(), mSettings.priority() );
     317                 :          0 :     priorityD = std::clamp( priorityD, 0.0, 10.0 );
     318                 :          0 :     priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
     319                 :          0 :     lf->setPriority( priorityD );
     320                 :          0 :   }
     321                 :            : 
     322                 :            :   // z-Index
     323                 :          0 :   double zIndex = mSettings.zIndex();
     324                 :          0 :   if ( mSettings.dataDefinedProperties().hasProperty( QgsDiagramLayerSettings::ZIndex )
     325                 :          0 :        && mSettings.dataDefinedProperties().property( QgsDiagramLayerSettings::ZIndex ).isActive() )
     326                 :            :   {
     327                 :          0 :     context.expressionContext().setOriginalValueVariable( zIndex );
     328                 :          0 :     zIndex = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::ZIndex, context.expressionContext(), zIndex );
     329                 :          0 :   }
     330                 :          0 :   lf->setZIndex( zIndex );
     331                 :            : 
     332                 :            :   // label distance
     333                 :          0 :   QgsPointXY ptZero = mapSettings.mapToPixel().toMapCoordinates( 0, 0 );
     334                 :          0 :   QgsPointXY ptOne = mapSettings.mapToPixel().toMapCoordinates( 1, 0 );
     335                 :          0 :   double dist = mSettings.distance();
     336                 :            : 
     337                 :          0 :   if ( mSettings.dataDefinedProperties().hasProperty( QgsDiagramLayerSettings::Distance )
     338                 :          0 :        && mSettings.dataDefinedProperties().property( QgsDiagramLayerSettings::Distance ).isActive() )
     339                 :            :   {
     340                 :          0 :     context.expressionContext().setOriginalValueVariable( dist );
     341                 :          0 :     dist = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::Distance, context.expressionContext(), dist );
     342                 :          0 :   }
     343                 :            : 
     344                 :          0 :   dist *= ptOne.distance( ptZero );
     345                 :            : 
     346                 :          0 :   lf->setDistLabel( dist );
     347                 :          0 :   return lf;
     348                 :          0 : }
     349                 :            : 

Generated by: LCOV version 1.14