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

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  *    libpal - Automated Placement of Labels Library
       3                 :            :  *
       4                 :            :  *    Copyright (C) 2008 Maxence Laurent, MIS-TIC, HEIG-VD
       5                 :            :  *                             University of Applied Sciences, Western Switzerland
       6                 :            :  *                             http://www.hes-so.ch
       7                 :            :  *
       8                 :            :  *    Contact:
       9                 :            :  *        maxence.laurent <at> heig-vd <dot> ch
      10                 :            :  *     or
      11                 :            :  *        eric.taillard <at> heig-vd <dot> ch
      12                 :            :  *
      13                 :            :  * This file is part of libpal.
      14                 :            :  *
      15                 :            :  * libpal is free software: you can redistribute it and/or modify
      16                 :            :  * it under the terms of the GNU General Public License as published by
      17                 :            :  * the Free Software Foundation, either version 3 of the License, or
      18                 :            :  * (at your option) any later version.
      19                 :            :  *
      20                 :            :  * libpal is distributed in the hope that it will be useful,
      21                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      22                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      23                 :            :  * GNU General Public License for more details.
      24                 :            :  *
      25                 :            :  * You should have received a copy of the GNU General Public License
      26                 :            :  * along with libpal.  If not, see <http://www.gnu.org/licenses/>.
      27                 :            :  *
      28                 :            :  */
      29                 :            : 
      30                 :            : #include "qgsgeometry.h"
      31                 :            : #include "pal.h"
      32                 :            : #include "layer.h"
      33                 :            : #include "palexception.h"
      34                 :            : #include "palstat.h"
      35                 :            : #include "costcalculator.h"
      36                 :            : #include "feature.h"
      37                 :            : #include "geomfunction.h"
      38                 :            : #include "labelposition.h"
      39                 :            : #include "problem.h"
      40                 :            : #include "pointset.h"
      41                 :            : #include "internalexception.h"
      42                 :            : #include "util.h"
      43                 :            : #include "palrtree.h"
      44                 :            : #include "qgssettings.h"
      45                 :            : #include <cfloat>
      46                 :            : #include <list>
      47                 :            : 
      48                 :            : using namespace pal;
      49                 :            : 
      50                 :          0 : Pal::Pal()
      51                 :            : {
      52                 :          0 :   QgsSettings settings;
      53                 :          0 :   mGlobalCandidatesLimitPoint = settings.value( QStringLiteral( "rendering/label_candidates_limit_points" ), 0, QgsSettings::Core ).toInt();
      54                 :          0 :   mGlobalCandidatesLimitLine = settings.value( QStringLiteral( "rendering/label_candidates_limit_lines" ), 0, QgsSettings::Core ).toInt();
      55                 :          0 :   mGlobalCandidatesLimitPolygon = settings.value( QStringLiteral( "rendering/label_candidates_limit_polygons" ), 0, QgsSettings::Core ).toInt();
      56                 :          0 : }
      57                 :            : 
      58                 :          0 : Pal::~Pal() = default;
      59                 :            : 
      60                 :          0 : void Pal::removeLayer( Layer *layer )
      61                 :            : {
      62                 :          0 :   if ( !layer )
      63                 :          0 :     return;
      64                 :            : 
      65                 :          0 :   mMutex.lock();
      66                 :            : 
      67                 :          0 :   for ( auto it = mLayers.begin(); it != mLayers.end(); ++it )
      68                 :            :   {
      69                 :          0 :     if ( it->second.get() == layer )
      70                 :            :     {
      71                 :          0 :       mLayers.erase( it );
      72                 :          0 :       break;
      73                 :            :     }
      74                 :          0 :   }
      75                 :          0 :   mMutex.unlock();
      76                 :          0 : }
      77                 :            : 
      78                 :          0 : Layer *Pal::addLayer( QgsAbstractLabelProvider *provider, const QString &layerName, QgsPalLayerSettings::Placement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll )
      79                 :            : {
      80                 :          0 :   mMutex.lock();
      81                 :            : 
      82                 :            :   Q_ASSERT( mLayers.find( provider ) == mLayers.end() );
      83                 :            : 
      84                 :          0 :   std::unique_ptr< Layer > layer = std::make_unique< Layer >( provider, layerName, arrangement, defaultPriority, active, toLabel, this, displayAll );
      85                 :          0 :   Layer *res = layer.get();
      86                 :          0 :   mLayers.insert( std::pair<QgsAbstractLabelProvider *, std::unique_ptr< Layer >>( provider, std::move( layer ) ) );
      87                 :          0 :   mMutex.unlock();
      88                 :            : 
      89                 :          0 :   return res;
      90                 :          0 : }
      91                 :            : 
      92                 :          0 : std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeometry &mapBoundary )
      93                 :            : {
      94                 :            :   // expand out the incoming buffer by 1000x -- that's the visible map extent, yet we may be getting features which exceed this extent
      95                 :            :   // (while 1000x may seem excessive here, this value is only used for scaling coordinates in the spatial indexes
      96                 :            :   // and the consequence of inserting coordinates outside this extent is worse than the consequence of setting this value too large.)
      97                 :          0 :   const QgsRectangle maxCoordinateExtentForSpatialIndices = extent.buffered( std::max( extent.width(), extent.height() ) * 1000 );
      98                 :            : 
      99                 :            :   // to store obstacles
     100                 :          0 :   PalRtree< FeaturePart > obstacles( maxCoordinateExtentForSpatialIndices );
     101                 :          0 :   PalRtree< LabelPosition > allCandidatesFirstRound( maxCoordinateExtentForSpatialIndices );
     102                 :          0 :   std::vector< FeaturePart * > allObstacleParts;
     103                 :          0 :   std::unique_ptr< Problem > prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
     104                 :            : 
     105                 :            :   double bbx[4];
     106                 :            :   double bby[4];
     107                 :            : 
     108                 :          0 :   bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.xMinimum();
     109                 :          0 :   bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.yMinimum();
     110                 :          0 :   bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.xMaximum();
     111                 :          0 :   bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.yMaximum();
     112                 :            : 
     113                 :          0 :   prob->pal = this;
     114                 :            : 
     115                 :          0 :   std::list< std::unique_ptr< Feats > > features;
     116                 :            : 
     117                 :            :   // prepare map boundary
     118                 :          0 :   geos::unique_ptr mapBoundaryGeos( QgsGeos::asGeos( mapBoundary ) );
     119                 :          0 :   geos::prepared_unique_ptr mapBoundaryPrepared( GEOSPrepare_r( QgsGeos::getGEOSHandler(), mapBoundaryGeos.get() ) );
     120                 :            : 
     121                 :          0 :   int obstacleCount = 0;
     122                 :            : 
     123                 :            :   // first step : extract features from layers
     124                 :            : 
     125                 :          0 :   std::size_t previousFeatureCount = 0;
     126                 :          0 :   int previousObstacleCount = 0;
     127                 :            : 
     128                 :          0 :   QStringList layersWithFeaturesInBBox;
     129                 :            : 
     130                 :          0 :   QMutexLocker palLocker( &mMutex );
     131                 :          0 :   for ( const auto &it : mLayers )
     132                 :            :   {
     133                 :          0 :     Layer *layer = it.second.get();
     134                 :          0 :     if ( !layer )
     135                 :            :     {
     136                 :            :       // invalid layer name
     137                 :          0 :       continue;
     138                 :            :     }
     139                 :            : 
     140                 :            :     // only select those who are active
     141                 :          0 :     if ( !layer->active() )
     142                 :          0 :       continue;
     143                 :            : 
     144                 :            :     // check for connected features with the same label text and join them
     145                 :          0 :     if ( layer->mergeConnectedLines() )
     146                 :          0 :       layer->joinConnectedFeatures();
     147                 :            : 
     148                 :          0 :     if ( isCanceled() )
     149                 :          0 :       return nullptr;
     150                 :            : 
     151                 :          0 :     layer->chopFeaturesAtRepeatDistance();
     152                 :            : 
     153                 :          0 :     if ( isCanceled() )
     154                 :          0 :       return nullptr;
     155                 :            : 
     156                 :          0 :     QMutexLocker locker( &layer->mMutex );
     157                 :            : 
     158                 :            :     // generate candidates for all features
     159                 :          0 :     for ( const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->mFeatureParts ) )
     160                 :            :     {
     161                 :          0 :       if ( isCanceled() )
     162                 :          0 :         break;
     163                 :            : 
     164                 :            :       // Holes of the feature are obstacles
     165                 :          0 :       for ( int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
     166                 :            :       {
     167                 :          0 :         FeaturePart *selfObstacle =  featurePart->getSelfObstacle( i );
     168                 :          0 :         obstacles.insert( selfObstacle, selfObstacle->boundingBox() );
     169                 :          0 :         allObstacleParts.emplace_back( selfObstacle );
     170                 :            : 
     171                 :          0 :         if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
     172                 :            :         {
     173                 :            :           //ERROR: SHOULD HAVE A PARENT!!!!!
     174                 :          0 :         }
     175                 :          0 :       }
     176                 :            : 
     177                 :            :       // generate candidates for the feature part
     178                 :          0 :       std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates( this );
     179                 :            : 
     180                 :          0 :       if ( isCanceled() )
     181                 :          0 :         break;
     182                 :            : 
     183                 :            :       // purge candidates that are outside the bbox
     184                 :          0 :       candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared, this]( std::unique_ptr< LabelPosition > &candidate )
     185                 :            :       {
     186                 :          0 :         if ( showPartialLabels() )
     187                 :          0 :           return !candidate->intersects( mapBoundaryPrepared.get() );
     188                 :            :         else
     189                 :          0 :           return !candidate->within( mapBoundaryPrepared.get() );
     190                 :          0 :       } ), candidates.end() );
     191                 :            : 
     192                 :          0 :       if ( isCanceled() )
     193                 :          0 :         break;
     194                 :            : 
     195                 :          0 :       if ( !candidates.empty() )
     196                 :            :       {
     197                 :          0 :         for ( std::unique_ptr< LabelPosition > &candidate : candidates )
     198                 :            :         {
     199                 :          0 :           candidate->insertIntoIndex( allCandidatesFirstRound );
     200                 :          0 :           candidate->setGlobalId( mNextCandidateId++ );
     201                 :            :         }
     202                 :            : 
     203                 :          0 :         std::sort( candidates.begin(), candidates.end(), CostCalculator::candidateSortGrow );
     204                 :            : 
     205                 :            :         // valid features are added to fFeats
     206                 :          0 :         std::unique_ptr< Feats > ft = std::make_unique< Feats >();
     207                 :          0 :         ft->feature = featurePart.get();
     208                 :          0 :         ft->shape = nullptr;
     209                 :          0 :         ft->candidates = std::move( candidates );
     210                 :          0 :         ft->priority = featurePart->calculatePriority();
     211                 :          0 :         features.emplace_back( std::move( ft ) );
     212                 :          0 :       }
     213                 :            :       else
     214                 :            :       {
     215                 :            :         // no candidates, so generate a default "point on surface" one
     216                 :          0 :         std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
     217                 :          0 :         if ( !unplacedPosition )
     218                 :          0 :           continue;
     219                 :            : 
     220                 :          0 :         if ( layer->displayAll() )
     221                 :            :         {
     222                 :            :           // if we are displaying all labels, we throw the default candidate in too
     223                 :          0 :           unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
     224                 :          0 :           unplacedPosition->setGlobalId( mNextCandidateId++ );
     225                 :          0 :           candidates.emplace_back( std::move( unplacedPosition ) );
     226                 :            : 
     227                 :            :           // valid features are added to fFeats
     228                 :          0 :           std::unique_ptr< Feats > ft = std::make_unique< Feats >();
     229                 :          0 :           ft->feature = featurePart.get();
     230                 :          0 :           ft->shape = nullptr;
     231                 :          0 :           ft->candidates = std::move( candidates );
     232                 :          0 :           ft->priority = featurePart->calculatePriority();
     233                 :          0 :           features.emplace_back( std::move( ft ) );
     234                 :          0 :         }
     235                 :            :         else
     236                 :            :         {
     237                 :            :           // not displaying all labels for this layer, so it goes into the unlabeled feature list
     238                 :          0 :           prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
     239                 :            :         }
     240                 :          0 :       }
     241                 :          0 :     }
     242                 :          0 :     if ( isCanceled() )
     243                 :          0 :       return nullptr;
     244                 :            : 
     245                 :            :     // collate all layer obstacles
     246                 :          0 :     for ( FeaturePart *obstaclePart : std::as_const( layer->mObstacleParts ) )
     247                 :            :     {
     248                 :          0 :       if ( isCanceled() )
     249                 :          0 :         break; // do not continue searching
     250                 :            : 
     251                 :            :       // insert into obstacles
     252                 :          0 :       obstacles.insert( obstaclePart, obstaclePart->boundingBox() );
     253                 :          0 :       allObstacleParts.emplace_back( obstaclePart );
     254                 :          0 :       obstacleCount++;
     255                 :            :     }
     256                 :            : 
     257                 :          0 :     if ( isCanceled() )
     258                 :          0 :       return nullptr;
     259                 :            : 
     260                 :          0 :     locker.unlock();
     261                 :          0 : 
     262                 :          0 :     if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
     263                 :          0 :     {
     264                 :          0 :       layersWithFeaturesInBBox << layer->name();
     265                 :          0 :     }
     266                 :          0 :     previousFeatureCount = features.size();
     267                 :          0 :     previousObstacleCount = obstacleCount;
     268                 :          0 :   }
     269                 :          0 :   palLocker.unlock();
     270                 :            : 
     271                 :          0 :   if ( isCanceled() )
     272                 :          0 :     return nullptr;
     273                 :          0 : 
     274                 :          0 :   prob->mLayerCount = layersWithFeaturesInBBox.size();
     275                 :          0 :   prob->labelledLayersName = layersWithFeaturesInBBox;
     276                 :          0 : 
     277                 :          0 :   prob->mFeatureCount = features.size();
     278                 :          0 :   prob->mTotalCandidates = 0;
     279                 :          0 :   prob->mFeatNbLp.resize( prob->mFeatureCount );
     280                 :          0 :   prob->mFeatStartId.resize( prob->mFeatureCount );
     281                 :          0 :   prob->mInactiveCost.resize( prob->mFeatureCount );
     282                 :          0 : 
     283                 :          0 :   if ( !features.empty() )
     284                 :            :   {
     285                 :          0 :     // Filtering label positions against obstacles
     286                 :          0 :     for ( FeaturePart *obstaclePart : allObstacleParts )
     287                 :          0 :     {
     288                 :          0 :       if ( isCanceled() )
     289                 :          0 :         break; // do not continue searching
     290                 :            : 
     291                 :          0 :       allCandidatesFirstRound.intersects( obstaclePart->boundingBox(), [obstaclePart, this]( const LabelPosition * candidatePosition ) -> bool
     292                 :            :       {
     293                 :            :         // test whether we should ignore this obstacle for the candidate. We do this if:
     294                 :            :         // 1. it's not a hole, and the obstacle belongs to the same label feature as the candidate (e.g.,
     295                 :            :         // features aren't obstacles for their own labels)
     296                 :            :         // 2. it IS a hole, and the hole belongs to a different label feature to the candidate (e.g., holes
     297                 :            :         // are ONLY obstacles for the labels of the feature they belong to)
     298                 :          0 :         if ( ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
     299                 :          0 :              || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
     300                 :            :         {
     301                 :          0 :           return true;
     302                 :            :         }
     303                 :            : 
     304                 :          0 :         CostCalculator::addObstacleCostPenalty( const_cast< LabelPosition * >( candidatePosition ), obstaclePart, this );
     305                 :            : 
     306                 :          0 :         return true;
     307                 :          0 :       } );
     308                 :            :     }
     309                 :            : 
     310                 :          0 :     if ( isCanceled() )
     311                 :            :     {
     312                 :          0 :       return nullptr;
     313                 :            :     }
     314                 :            : 
     315                 :          0 :     int idlp = 0;
     316                 :          0 :     for ( std::size_t i = 0; i < prob->mFeatureCount; i++ ) /* for each feature into prob */
     317                 :            :     {
     318                 :          0 :       std::unique_ptr< Feats > feat = std::move( features.front() );
     319                 :          0 :       features.pop_front();
     320                 :            : 
     321                 :          0 :       prob->mFeatStartId[i] = idlp;
     322                 :          0 :       prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
     323                 :            : 
     324                 :          0 :       std::size_t maxCandidates = 0;
     325                 :          0 :       switch ( feat->feature->getGeosType() )
     326                 :            :       {
     327                 :            :         case GEOS_POINT:
     328                 :            :           // this is usually 0, i.e. no maximum
     329                 :          0 :           maxCandidates = feat->feature->maximumPointCandidates();
     330                 :          0 :           break;
     331                 :            : 
     332                 :            :         case GEOS_LINESTRING:
     333                 :          0 :           maxCandidates = feat->feature->maximumLineCandidates();
     334                 :          0 :           break;
     335                 :            : 
     336                 :            :         case GEOS_POLYGON:
     337                 :          0 :           maxCandidates = std::max( static_cast< std::size_t >( 16 ), feat->feature->maximumPolygonCandidates() );
     338                 :          0 :           break;
     339                 :            :       }
     340                 :            : 
     341                 :          0 :       if ( isCanceled() )
     342                 :          0 :         return nullptr;
     343                 :            : 
     344                 :          0 :       auto pruneHardConflicts = [&]
     345                 :            :       {
     346                 :          0 :         switch ( mPlacementVersion )
     347                 :            :         {
     348                 :            :           case QgsLabelingEngineSettings::PlacementEngineVersion1:
     349                 :          0 :             break;
     350                 :            : 
     351                 :            :           case QgsLabelingEngineSettings::PlacementEngineVersion2:
     352                 :            :           {
     353                 :            :             // v2 placement rips out candidates where the candidate cost is too high when compared to
     354                 :            :             // their inactive cost
     355                 :            : 
     356                 :            :             // note, we start this at the SECOND candidate (you'll see why after this loop)
     357                 :          0 :             feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
     358                 :            :             {
     359                 :          0 :               if ( candidate->hasHardObstacleConflict() )
     360                 :            :               {
     361                 :          0 :                 return true;
     362                 :            :               }
     363                 :          0 :               return false;
     364                 :          0 :             } ), feat->candidates.end() );
     365                 :            : 
     366                 :          0 :             if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() && !feat->feature->layer()->displayAll() )
     367                 :            :             {
     368                 :            :               // we've going to end up removing ALL candidates for this label. Oh well, that's allowed. We just need to
     369                 :            :               // make sure we move this last candidate to the unplaced labels list
     370                 :          0 :               prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
     371                 :          0 :               feat->candidates.clear();
     372                 :          0 :             }
     373                 :            :           }
     374                 :          0 :         }
     375                 :          0 :       };
     376                 :            : 
     377                 :            :       // if we're not showing all labels (including conflicts) for this layer, then we prune the candidates
     378                 :            :       // upfront to avoid extra work...
     379                 :          0 :       if ( !feat->feature->layer()->displayAll() )
     380                 :            :       {
     381                 :          0 :         pruneHardConflicts();
     382                 :          0 :       }
     383                 :            : 
     384                 :          0 :       if ( feat->candidates.empty() )
     385                 :          0 :         continue;
     386                 :            : 
     387                 :            :       // calculate final costs
     388                 :          0 :       CostCalculator::finalizeCandidatesCosts( feat.get(), bbx, bby );
     389                 :            : 
     390                 :            :       // sort candidates list, best label to worst
     391                 :          0 :       std::sort( feat->candidates.begin(), feat->candidates.end(), CostCalculator::candidateSortGrow );
     392                 :            : 
     393                 :            :       // but if we ARE showing all labels (including conflicts), let's go ahead and prune them now.
     394                 :            :       // Since we've calculated all their costs and sorted them, if we've hit the situation that ALL
     395                 :            :       // candidates have conflicts, then at least when we pick the first candidate to display it will be
     396                 :            :       // the lowest cost (i.e. best possible) overlapping candidate...
     397                 :          0 :       if ( feat->feature->layer()->displayAll() )
     398                 :            :       {
     399                 :          0 :         pruneHardConflicts();
     400                 :          0 :       }
     401                 :            : 
     402                 :            : 
     403                 :            :       // only keep the 'maxCandidates' best candidates
     404                 :          0 :       if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
     405                 :            :       {
     406                 :          0 :         feat->candidates.resize( maxCandidates );
     407                 :          0 :       }
     408                 :            : 
     409                 :          0 :       if ( isCanceled() )
     410                 :          0 :         return nullptr;
     411                 :            : 
     412                 :            :       // update problem's # candidate
     413                 :          0 :       prob->mFeatNbLp[i] = static_cast< int >( feat->candidates.size() );
     414                 :          0 :       prob->mTotalCandidates += static_cast< int >( feat->candidates.size() );
     415                 :            : 
     416                 :            :       // add all candidates into a rtree (to speed up conflicts searching)
     417                 :          0 :       for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
     418                 :            :       {
     419                 :          0 :         candidate->insertIntoIndex( prob->allCandidatesIndex() );
     420                 :          0 :         candidate->setProblemIds( static_cast< int >( i ), idlp++ );
     421                 :            :       }
     422                 :          0 :       features.emplace_back( std::move( feat ) );
     423                 :          0 :     }
     424                 :            : 
     425                 :          0 :     int nbOverlaps = 0;
     426                 :            : 
     427                 :            :     double amin[2];
     428                 :            :     double amax[2];
     429                 :          0 :     while ( !features.empty() ) // for each feature
     430                 :            :     {
     431                 :          0 :       if ( isCanceled() )
     432                 :          0 :         return nullptr;
     433                 :            : 
     434                 :          0 :       std::unique_ptr< Feats > feat = std::move( features.front() );
     435                 :          0 :       features.pop_front();
     436                 :            : 
     437                 :          0 :       for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
     438                 :            :       {
     439                 :          0 :         std::unique_ptr< LabelPosition > lp = std::move( candidate );
     440                 :            : 
     441                 :          0 :         lp->resetNumOverlaps();
     442                 :            : 
     443                 :            :         // make sure that candidate's cost is less than 1
     444                 :          0 :         lp->validateCost();
     445                 :            : 
     446                 :            :         //prob->feat[idlp] = j;
     447                 :            : 
     448                 :            :         // lookup for overlapping candidate
     449                 :          0 :         lp->getBoundingBox( amin, amax );
     450                 :          0 :         prob->allCandidatesIndex().intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp, this]( const LabelPosition * lp2 )->bool
     451                 :            :         {
     452                 :          0 :           if ( candidatesAreConflicting( lp.get(), lp2 ) )
     453                 :            :           {
     454                 :          0 :             lp->incrementNumOverlaps();
     455                 :          0 :           }
     456                 :            : 
     457                 :          0 :           return true;
     458                 :            : 
     459                 :            :         } );
     460                 :            : 
     461                 :          0 :         nbOverlaps += lp->getNumOverlaps();
     462                 :            : 
     463                 :          0 :         prob->addCandidatePosition( std::move( lp ) );
     464                 :            : 
     465                 :          0 :         if ( isCanceled() )
     466                 :          0 :           return nullptr;
     467                 :          0 :       }
     468                 :          0 :     }
     469                 :          0 :     nbOverlaps /= 2;
     470                 :          0 :     prob->mAllNblp = prob->mTotalCandidates;
     471                 :          0 :     prob->mNbOverlap = nbOverlaps;
     472                 :          0 :   }
     473                 :            : 
     474                 :          0 :   return prob;
     475                 :          0 : }
     476                 :            : 
     477                 :          0 : void Pal::registerCancellationCallback( Pal::FnIsCanceled fnCanceled, void *context )
     478                 :            : {
     479                 :          0 :   fnIsCanceled = fnCanceled;
     480                 :          0 :   fnIsCanceledContext = context;
     481                 :          0 : }
     482                 :            : 
     483                 :          0 : std::unique_ptr<Problem> Pal::extractProblem( const QgsRectangle &extent, const QgsGeometry &mapBoundary )
     484                 :            : {
     485                 :          0 :   return extract( extent, mapBoundary );
     486                 :            : }
     487                 :            : 
     488                 :          0 : QList<LabelPosition *> Pal::solveProblem( Problem *prob, bool displayAll, QList<LabelPosition *> *unlabeled )
     489                 :            : {
     490                 :          0 :   if ( !prob )
     491                 :          0 :     return QList<LabelPosition *>();
     492                 :            : 
     493                 :          0 :   prob->reduce();
     494                 :            : 
     495                 :            :   try
     496                 :            :   {
     497                 :          0 :     prob->chain_search();
     498                 :          0 :   }
     499                 :            :   catch ( InternalException::Empty & )
     500                 :            :   {
     501                 :          0 :     return QList<LabelPosition *>();
     502                 :          0 :   }
     503                 :            : 
     504                 :          0 :   return prob->getSolution( displayAll, unlabeled );
     505                 :          0 : }
     506                 :            : 
     507                 :          0 : void Pal::setMinIt( int min_it )
     508                 :            : {
     509                 :          0 :   if ( min_it >= 0 )
     510                 :          0 :     mTabuMinIt = min_it;
     511                 :          0 : }
     512                 :            : 
     513                 :          0 : void Pal::setMaxIt( int max_it )
     514                 :            : {
     515                 :          0 :   if ( max_it > 0 )
     516                 :          0 :     mTabuMaxIt = max_it;
     517                 :          0 : }
     518                 :            : 
     519                 :          0 : void Pal::setPopmusicR( int r )
     520                 :            : {
     521                 :          0 :   if ( r > 0 )
     522                 :          0 :     mPopmusicR = r;
     523                 :          0 : }
     524                 :            : 
     525                 :          0 : void Pal::setEjChainDeg( int degree )
     526                 :            : {
     527                 :          0 :   this->mEjChainDeg = degree;
     528                 :          0 : }
     529                 :            : 
     530                 :          0 : void Pal::setTenure( int tenure )
     531                 :            : {
     532                 :          0 :   this->mTenure = tenure;
     533                 :          0 : }
     534                 :            : 
     535                 :          0 : void Pal::setCandListSize( double fact )
     536                 :            : {
     537                 :          0 :   this->mCandListSize = fact;
     538                 :          0 : }
     539                 :            : 
     540                 :          0 : void Pal::setShowPartialLabels( bool show )
     541                 :            : {
     542                 :          0 :   this->mShowPartialLabels = show;
     543                 :          0 : }
     544                 :            : 
     545                 :          0 : QgsLabelingEngineSettings::PlacementEngineVersion Pal::placementVersion() const
     546                 :            : {
     547                 :          0 :   return mPlacementVersion;
     548                 :            : }
     549                 :            : 
     550                 :          0 : void Pal::setPlacementVersion( QgsLabelingEngineSettings::PlacementEngineVersion placementVersion )
     551                 :            : {
     552                 :          0 :   mPlacementVersion = placementVersion;
     553                 :          0 : }
     554                 :            : 
     555                 :          0 : bool Pal::candidatesAreConflicting( const LabelPosition *lp1, const LabelPosition *lp2 ) const
     556                 :            : {
     557                 :            :   // we cache the value -- this can be costly to calculate, and we check this multiple times
     558                 :            :   // per candidate during the labeling problem solving
     559                 :            : 
     560                 :            :   // conflicts are commutative - so we always store them in the cache using the smaller id as the first element of the key pair
     561                 :          0 :   auto key = qMakePair( std::min( lp1->globalId(), lp2->globalId() ), std::max( lp1->globalId(), lp2->globalId() ) );
     562                 :          0 :   auto it = mCandidateConflicts.constFind( key );
     563                 :          0 :   if ( it != mCandidateConflicts.constEnd() )
     564                 :          0 :     return *it;
     565                 :            : 
     566                 :          0 :   const bool res = lp1->isInConflict( lp2 );
     567                 :          0 :   mCandidateConflicts.insert( key, res );
     568                 :          0 :   return res;
     569                 :          0 : }
     570                 :            : 
     571                 :          0 : int Pal::getMinIt()
     572                 :            : {
     573                 :          0 :   return mTabuMaxIt;
     574                 :            : }
     575                 :            : 
     576                 :          0 : int Pal::getMaxIt()
     577                 :            : {
     578                 :          0 :   return mTabuMinIt;
     579                 :            : }
     580                 :            : 
     581                 :          0 : bool Pal::showPartialLabels() const
     582                 :            : {
     583                 :          0 :   return mShowPartialLabels;
     584                 :            : }

Generated by: LCOV version 1.14