LCOV - code coverage report
Current view: top level - core/pal - pal.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 282 0.0 %
Date: 2021-03-26 12:19:53 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 ( 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                 :            :         }
     201                 :            : 
     202                 :          0 :         std::sort( candidates.begin(), candidates.end(), CostCalculator::candidateSortGrow );
     203                 :            : 
     204                 :            :         // valid features are added to fFeats
     205                 :          0 :         std::unique_ptr< Feats > ft = std::make_unique< Feats >();
     206                 :          0 :         ft->feature = featurePart;
     207                 :          0 :         ft->shape = nullptr;
     208                 :          0 :         ft->candidates = std::move( candidates );
     209                 :          0 :         ft->priority = featurePart->calculatePriority();
     210                 :          0 :         features.emplace_back( std::move( ft ) );
     211                 :          0 :       }
     212                 :            :       else
     213                 :            :       {
     214                 :            :         // no candidates, so generate a default "point on surface" one
     215                 :          0 :         std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart );
     216                 :          0 :         if ( !unplacedPosition )
     217                 :          0 :           continue;
     218                 :            : 
     219                 :          0 :         if ( layer->displayAll() )
     220                 :            :         {
     221                 :            :           // if we are displaying all labels, we throw the default candidate in too
     222                 :          0 :           unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
     223                 :          0 :           candidates.emplace_back( std::move( unplacedPosition ) );
     224                 :            : 
     225                 :            :           // valid features are added to fFeats
     226                 :          0 :           std::unique_ptr< Feats > ft = std::make_unique< Feats >();
     227                 :          0 :           ft->feature = featurePart;
     228                 :          0 :           ft->shape = nullptr;
     229                 :          0 :           ft->candidates = std::move( candidates );
     230                 :          0 :           ft->priority = featurePart->calculatePriority();
     231                 :          0 :           features.emplace_back( std::move( ft ) );
     232                 :          0 :         }
     233                 :            :         else
     234                 :            :         {
     235                 :            :           // not displaying all labels for this layer, so it goes into the unlabeled feature list
     236                 :          0 :           prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
     237                 :            :         }
     238                 :          0 :       }
     239                 :          0 :     }
     240                 :          0 :     if ( isCanceled() )
     241                 :          0 :       return nullptr;
     242                 :            : 
     243                 :            :     // collate all layer obstacles
     244                 :          0 :     for ( FeaturePart *obstaclePart : std::as_const( layer->mObstacleParts ) )
     245                 :            :     {
     246                 :          0 :       if ( isCanceled() )
     247                 :          0 :         break; // do not continue searching
     248                 :            : 
     249                 :            :       // insert into obstacles
     250                 :          0 :       obstacles.insert( obstaclePart, obstaclePart->boundingBox() );
     251                 :          0 :       allObstacleParts.emplace_back( obstaclePart );
     252                 :          0 :       obstacleCount++;
     253                 :          0 :     }
     254                 :            : 
     255                 :          0 :     if ( isCanceled() )
     256                 :          0 :       return nullptr;
     257                 :            : 
     258                 :          0 :     locker.unlock();
     259                 :          0 : 
     260                 :          0 :     if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
     261                 :            :     {
     262                 :          0 :       layersWithFeaturesInBBox << layer->name();
     263                 :          0 :     }
     264                 :          0 :     previousFeatureCount = features.size();
     265                 :          0 :     previousObstacleCount = obstacleCount;
     266                 :          0 :   }
     267                 :          0 :   palLocker.unlock();
     268                 :          0 : 
     269                 :          0 :   if ( isCanceled() )
     270                 :          0 :     return nullptr;
     271                 :          0 : 
     272                 :          0 :   prob->mLayerCount = layersWithFeaturesInBBox.size();
     273                 :          0 :   prob->labelledLayersName = layersWithFeaturesInBBox;
     274                 :          0 : 
     275                 :          0 :   prob->mFeatureCount = features.size();
     276                 :          0 :   prob->mTotalCandidates = 0;
     277                 :          0 :   prob->mFeatNbLp.resize( prob->mFeatureCount );
     278                 :          0 :   prob->mFeatStartId.resize( prob->mFeatureCount );
     279                 :          0 :   prob->mInactiveCost.resize( prob->mFeatureCount );
     280                 :            : 
     281                 :          0 :   if ( !features.empty() )
     282                 :            :   {
     283                 :            :     // Filtering label positions against obstacles
     284                 :          0 :     for ( FeaturePart *obstaclePart : allObstacleParts )
     285                 :            :     {
     286                 :          0 :       if ( isCanceled() )
     287                 :          0 :         break; // do not continue searching
     288                 :            : 
     289                 :          0 :       allCandidatesFirstRound.intersects( obstaclePart->boundingBox(), [obstaclePart, this]( const LabelPosition * candidatePosition ) -> bool
     290                 :            :       {
     291                 :            :         // test whether we should ignore this obstacle for the candidate. We do this if:
     292                 :            :         // 1. it's not a hole, and the obstacle belongs to the same label feature as the candidate (e.g.,
     293                 :            :         // features aren't obstacles for their own labels)
     294                 :            :         // 2. it IS a hole, and the hole belongs to a different label feature to the candidate (e.g., holes
     295                 :            :         // are ONLY obstacles for the labels of the feature they belong to)
     296                 :          0 :         if ( ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
     297                 :          0 :              || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
     298                 :            :         {
     299                 :          0 :           return true;
     300                 :            :         }
     301                 :            : 
     302                 :          0 :         CostCalculator::addObstacleCostPenalty( const_cast< LabelPosition * >( candidatePosition ), obstaclePart, this );
     303                 :            : 
     304                 :          0 :         return true;
     305                 :          0 :       } );
     306                 :            :     }
     307                 :            : 
     308                 :          0 :     if ( isCanceled() )
     309                 :            :     {
     310                 :          0 :       return nullptr;
     311                 :            :     }
     312                 :            : 
     313                 :          0 :     int idlp = 0;
     314                 :          0 :     for ( std::size_t i = 0; i < prob->mFeatureCount; i++ ) /* for each feature into prob */
     315                 :            :     {
     316                 :          0 :       std::unique_ptr< Feats > feat = std::move( features.front() );
     317                 :          0 :       features.pop_front();
     318                 :            : 
     319                 :          0 :       prob->mFeatStartId[i] = idlp;
     320                 :          0 :       prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
     321                 :            : 
     322                 :          0 :       std::size_t maxCandidates = 0;
     323                 :          0 :       switch ( feat->feature->getGeosType() )
     324                 :            :       {
     325                 :            :         case GEOS_POINT:
     326                 :            :           // this is usually 0, i.e. no maximum
     327                 :          0 :           maxCandidates = feat->feature->maximumPointCandidates();
     328                 :          0 :           break;
     329                 :            : 
     330                 :            :         case GEOS_LINESTRING:
     331                 :          0 :           maxCandidates = feat->feature->maximumLineCandidates();
     332                 :          0 :           break;
     333                 :            : 
     334                 :            :         case GEOS_POLYGON:
     335                 :          0 :           maxCandidates = std::max( static_cast< std::size_t >( 16 ), feat->feature->maximumPolygonCandidates() );
     336                 :          0 :           break;
     337                 :            :       }
     338                 :            : 
     339                 :          0 :       if ( isCanceled() )
     340                 :          0 :         return nullptr;
     341                 :            : 
     342                 :          0 :       auto pruneHardConflicts = [&]
     343                 :            :       {
     344                 :          0 :         switch ( mPlacementVersion )
     345                 :            :         {
     346                 :            :           case QgsLabelingEngineSettings::PlacementEngineVersion1:
     347                 :          0 :             break;
     348                 :            : 
     349                 :            :           case QgsLabelingEngineSettings::PlacementEngineVersion2:
     350                 :            :           {
     351                 :            :             // v2 placement rips out candidates where the candidate cost is too high when compared to
     352                 :            :             // their inactive cost
     353                 :            : 
     354                 :            :             // note, we start this at the SECOND candidate (you'll see why after this loop)
     355                 :          0 :             feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
     356                 :            :             {
     357                 :          0 :               if ( candidate->hasHardObstacleConflict() )
     358                 :            :               {
     359                 :          0 :                 return true;
     360                 :            :               }
     361                 :          0 :               return false;
     362                 :          0 :             } ), feat->candidates.end() );
     363                 :            : 
     364                 :          0 :             if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() && !feat->feature->layer()->displayAll() )
     365                 :            :             {
     366                 :            :               // we've going to end up removing ALL candidates for this label. Oh well, that's allowed. We just need to
     367                 :            :               // make sure we move this last candidate to the unplaced labels list
     368                 :          0 :               prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
     369                 :          0 :               feat->candidates.clear();
     370                 :          0 :             }
     371                 :            :           }
     372                 :          0 :         }
     373                 :          0 :       };
     374                 :            : 
     375                 :            :       // if we're not showing all labels (including conflicts) for this layer, then we prune the candidates
     376                 :            :       // upfront to avoid extra work...
     377                 :          0 :       if ( !feat->feature->layer()->displayAll() )
     378                 :            :       {
     379                 :          0 :         pruneHardConflicts();
     380                 :          0 :       }
     381                 :            : 
     382                 :          0 :       if ( feat->candidates.empty() )
     383                 :          0 :         continue;
     384                 :            : 
     385                 :            :       // calculate final costs
     386                 :          0 :       CostCalculator::finalizeCandidatesCosts( feat.get(), bbx, bby );
     387                 :            : 
     388                 :            :       // sort candidates list, best label to worst
     389                 :          0 :       std::sort( feat->candidates.begin(), feat->candidates.end(), CostCalculator::candidateSortGrow );
     390                 :            : 
     391                 :            :       // but if we ARE showing all labels (including conflicts), let's go ahead and prune them now.
     392                 :            :       // Since we've calculated all their costs and sorted them, if we've hit the situation that ALL
     393                 :            :       // candidates have conflicts, then at least when we pick the first candidate to display it will be
     394                 :            :       // the lowest cost (i.e. best possible) overlapping candidate...
     395                 :          0 :       if ( feat->feature->layer()->displayAll() )
     396                 :            :       {
     397                 :          0 :         pruneHardConflicts();
     398                 :          0 :       }
     399                 :            : 
     400                 :            : 
     401                 :            :       // only keep the 'maxCandidates' best candidates
     402                 :          0 :       if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
     403                 :            :       {
     404                 :          0 :         feat->candidates.resize( maxCandidates );
     405                 :          0 :       }
     406                 :            : 
     407                 :          0 :       if ( isCanceled() )
     408                 :          0 :         return nullptr;
     409                 :            : 
     410                 :            :       // update problem's # candidate
     411                 :          0 :       prob->mFeatNbLp[i] = static_cast< int >( feat->candidates.size() );
     412                 :          0 :       prob->mTotalCandidates += static_cast< int >( feat->candidates.size() );
     413                 :            : 
     414                 :            :       // add all candidates into a rtree (to speed up conflicts searching)
     415                 :          0 :       for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
     416                 :            :       {
     417                 :          0 :         candidate->insertIntoIndex( prob->allCandidatesIndex() );
     418                 :          0 :         candidate->setProblemIds( static_cast< int >( i ), idlp++ );
     419                 :            :       }
     420                 :          0 :       features.emplace_back( std::move( feat ) );
     421                 :          0 :     }
     422                 :            : 
     423                 :          0 :     int nbOverlaps = 0;
     424                 :            : 
     425                 :            :     double amin[2];
     426                 :            :     double amax[2];
     427                 :          0 :     while ( !features.empty() ) // for each feature
     428                 :            :     {
     429                 :          0 :       if ( isCanceled() )
     430                 :          0 :         return nullptr;
     431                 :            : 
     432                 :          0 :       std::unique_ptr< Feats > feat = std::move( features.front() );
     433                 :          0 :       features.pop_front();
     434                 :            : 
     435                 :          0 :       for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
     436                 :            :       {
     437                 :          0 :         std::unique_ptr< LabelPosition > lp = std::move( candidate );
     438                 :            : 
     439                 :          0 :         lp->resetNumOverlaps();
     440                 :            : 
     441                 :            :         // make sure that candidate's cost is less than 1
     442                 :          0 :         lp->validateCost();
     443                 :            : 
     444                 :            :         //prob->feat[idlp] = j;
     445                 :            : 
     446                 :            :         // lookup for overlapping candidate
     447                 :          0 :         lp->getBoundingBox( amin, amax );
     448                 :          0 :         prob->allCandidatesIndex().intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp]( const LabelPosition * lp2 )->bool
     449                 :            :         {
     450                 :          0 :           if ( lp->isInConflict( lp2 ) )
     451                 :            :           {
     452                 :          0 :             lp->incrementNumOverlaps();
     453                 :          0 :           }
     454                 :            : 
     455                 :          0 :           return true;
     456                 :            : 
     457                 :            :         } );
     458                 :            : 
     459                 :          0 :         nbOverlaps += lp->getNumOverlaps();
     460                 :            : 
     461                 :          0 :         prob->addCandidatePosition( std::move( lp ) );
     462                 :            : 
     463                 :          0 :         if ( isCanceled() )
     464                 :          0 :           return nullptr;
     465                 :          0 :       }
     466                 :          0 :     }
     467                 :          0 :     nbOverlaps /= 2;
     468                 :          0 :     prob->mAllNblp = prob->mTotalCandidates;
     469                 :          0 :     prob->mNbOverlap = nbOverlaps;
     470                 :          0 :   }
     471                 :            : 
     472                 :          0 :   return prob;
     473                 :          0 : }
     474                 :            : 
     475                 :          0 : void Pal::registerCancellationCallback( Pal::FnIsCanceled fnCanceled, void *context )
     476                 :            : {
     477                 :          0 :   fnIsCanceled = fnCanceled;
     478                 :          0 :   fnIsCanceledContext = context;
     479                 :          0 : }
     480                 :            : 
     481                 :          0 : std::unique_ptr<Problem> Pal::extractProblem( const QgsRectangle &extent, const QgsGeometry &mapBoundary )
     482                 :            : {
     483                 :          0 :   return extract( extent, mapBoundary );
     484                 :            : }
     485                 :            : 
     486                 :          0 : QList<LabelPosition *> Pal::solveProblem( Problem *prob, bool displayAll, QList<LabelPosition *> *unlabeled )
     487                 :            : {
     488                 :          0 :   if ( !prob )
     489                 :          0 :     return QList<LabelPosition *>();
     490                 :            : 
     491                 :          0 :   prob->reduce();
     492                 :            : 
     493                 :            :   try
     494                 :            :   {
     495                 :          0 :     prob->chain_search();
     496                 :          0 :   }
     497                 :            :   catch ( InternalException::Empty & )
     498                 :            :   {
     499                 :          0 :     return QList<LabelPosition *>();
     500                 :          0 :   }
     501                 :            : 
     502                 :          0 :   return prob->getSolution( displayAll, unlabeled );
     503                 :          0 : }
     504                 :            : 
     505                 :          0 : void Pal::setMinIt( int min_it )
     506                 :            : {
     507                 :          0 :   if ( min_it >= 0 )
     508                 :          0 :     mTabuMinIt = min_it;
     509                 :          0 : }
     510                 :            : 
     511                 :          0 : void Pal::setMaxIt( int max_it )
     512                 :            : {
     513                 :          0 :   if ( max_it > 0 )
     514                 :          0 :     mTabuMaxIt = max_it;
     515                 :          0 : }
     516                 :            : 
     517                 :          0 : void Pal::setPopmusicR( int r )
     518                 :            : {
     519                 :          0 :   if ( r > 0 )
     520                 :          0 :     mPopmusicR = r;
     521                 :          0 : }
     522                 :            : 
     523                 :          0 : void Pal::setEjChainDeg( int degree )
     524                 :            : {
     525                 :          0 :   this->mEjChainDeg = degree;
     526                 :          0 : }
     527                 :            : 
     528                 :          0 : void Pal::setTenure( int tenure )
     529                 :            : {
     530                 :          0 :   this->mTenure = tenure;
     531                 :          0 : }
     532                 :            : 
     533                 :          0 : void Pal::setCandListSize( double fact )
     534                 :            : {
     535                 :          0 :   this->mCandListSize = fact;
     536                 :          0 : }
     537                 :            : 
     538                 :          0 : void Pal::setShowPartialLabels( bool show )
     539                 :            : {
     540                 :          0 :   this->mShowPartialLabels = show;
     541                 :          0 : }
     542                 :            : 
     543                 :          0 : QgsLabelingEngineSettings::PlacementEngineVersion Pal::placementVersion() const
     544                 :            : {
     545                 :          0 :   return mPlacementVersion;
     546                 :            : }
     547                 :            : 
     548                 :          0 : void Pal::setPlacementVersion( QgsLabelingEngineSettings::PlacementEngineVersion placementVersion )
     549                 :            : {
     550                 :          0 :   mPlacementVersion = placementVersion;
     551                 :          0 : }
     552                 :            : 
     553                 :          0 : int Pal::getMinIt()
     554                 :            : {
     555                 :          0 :   return mTabuMaxIt;
     556                 :            : }
     557                 :            : 
     558                 :          0 : int Pal::getMaxIt()
     559                 :            : {
     560                 :          0 :   return mTabuMinIt;
     561                 :            : }
     562                 :            : 
     563                 :          0 : bool Pal::showPartialLabels() const
     564                 :            : {
     565                 :          0 :   return mShowPartialLabels;
     566                 :            : }

Generated by: LCOV version 1.14