LCOV - code coverage report
Current view: top level - core/pal - layer.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 232 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 "pal.h"
      31                 :            : #include "layer.h"
      32                 :            : #include "palexception.h"
      33                 :            : #include "internalexception.h"
      34                 :            : #include "feature.h"
      35                 :            : #include "geomfunction.h"
      36                 :            : #include "util.h"
      37                 :            : #include "qgslabelingengine.h"
      38                 :            : #include "qgslogger.h"
      39                 :            : 
      40                 :            : #include <cmath>
      41                 :            : #include <vector>
      42                 :            : 
      43                 :            : using namespace pal;
      44                 :            : 
      45                 :          0 : Layer::Layer( QgsAbstractLabelProvider *provider, const QString &name, QgsPalLayerSettings::Placement arrangement, double defaultPriority, bool active, bool toLabel, Pal *pal, bool displayAll )
      46                 :          0 :   : mProvider( provider )
      47                 :          0 :   , mName( name )
      48                 :          0 :   , mPal( pal )
      49                 :          0 :   , mActive( active )
      50                 :          0 :   , mLabelLayer( toLabel )
      51                 :          0 :   , mDisplayAll( displayAll )
      52                 :          0 :   , mCentroidInside( false )
      53                 :          0 :   , mArrangement( arrangement )
      54                 :          0 :   , mMergeLines( false )
      55                 :          0 :   , mUpsidedownLabels( Upright )
      56                 :          0 : {
      57                 :          0 :   if ( defaultPriority < 0.0001 )
      58                 :          0 :     mDefaultPriority = 0.0001;
      59                 :          0 :   else if ( defaultPriority > 1.0 )
      60                 :          0 :     mDefaultPriority = 1.0;
      61                 :            :   else
      62                 :          0 :     mDefaultPriority = defaultPriority;
      63                 :          0 : }
      64                 :            : 
      65                 :          0 : Layer::~Layer()
      66                 :          0 : {
      67                 :          0 :   mMutex.lock();
      68                 :            : 
      69                 :          0 :   qDeleteAll( mFeatureParts );
      70                 :          0 :   qDeleteAll( mObstacleParts );
      71                 :            : 
      72                 :          0 :   mMutex.unlock();
      73                 :          0 : }
      74                 :            : 
      75                 :          0 : void Layer::setPriority( double priority )
      76                 :            : {
      77                 :          0 :   if ( priority >= 1.0 ) // low priority
      78                 :          0 :     mDefaultPriority = 1.0;
      79                 :          0 :   else if ( priority <= 0.0001 )
      80                 :          0 :     mDefaultPriority = 0.0001; // high priority
      81                 :            :   else
      82                 :          0 :     mDefaultPriority = priority;
      83                 :          0 : }
      84                 :            : 
      85                 :          0 : bool Layer::registerFeature( QgsLabelFeature *lf )
      86                 :            : {
      87                 :          0 :   if ( lf->size().width() < 0 || lf->size().height() < 0 )
      88                 :          0 :     return false;
      89                 :            : 
      90                 :          0 :   QMutexLocker locker( &mMutex );
      91                 :            : 
      92                 :          0 :   if ( mHashtable.contains( lf->id() ) )
      93                 :            :   {
      94                 :            :     //A feature with this id already exists. Don't throw an exception as sometimes,
      95                 :            :     //the same feature is added twice (dateline split with otf-reprojection)
      96                 :          0 :     return false;
      97                 :            :   }
      98                 :            : 
      99                 :            :   // assign label feature to this PAL layer
     100                 :          0 :   lf->setLayer( this );
     101                 :            : 
     102                 :            :   // Split MULTI GEOM and Collection in simple geometries
     103                 :            : 
     104                 :          0 :   bool addedFeature = false;
     105                 :            : 
     106                 :          0 :   double geom_size = -1, biggest_size = -1;
     107                 :          0 :   std::unique_ptr<FeaturePart> biggest_part;
     108                 :            : 
     109                 :            :   // break the (possibly multi-part) geometry into simple geometries
     110                 :          0 :   std::unique_ptr<QLinkedList<const GEOSGeometry *>> simpleGeometries( Util::unmulti( lf->geometry() ) );
     111                 :          0 :   if ( !simpleGeometries ) // unmulti() failed?
     112                 :            :   {
     113                 :          0 :     throw InternalException::UnknownGeometry();
     114                 :            :   }
     115                 :            : 
     116                 :          0 :   GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
     117                 :            : 
     118                 :          0 :   bool featureGeomIsObstacleGeom = lf->obstacleSettings().obstacleGeometry().isNull();
     119                 :            : 
     120                 :          0 :   while ( !simpleGeometries->isEmpty() )
     121                 :            :   {
     122                 :          0 :     const GEOSGeometry *geom = simpleGeometries->takeFirst();
     123                 :            : 
     124                 :            :     // ignore invalid geometries (e.g. polygons with self-intersecting rings)
     125                 :          0 :     if ( GEOSisValid_r( geosctxt, geom ) != 1 ) // 0=invalid, 1=valid, 2=exception
     126                 :            :     {
     127                 :          0 :       continue;
     128                 :            :     }
     129                 :            : 
     130                 :          0 :     int type = GEOSGeomTypeId_r( geosctxt, geom );
     131                 :            : 
     132                 :          0 :     if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON )
     133                 :            :     {
     134                 :          0 :       throw InternalException::UnknownGeometry();
     135                 :            :     }
     136                 :            : 
     137                 :          0 :     std::unique_ptr<FeaturePart> fpart = std::make_unique<FeaturePart>( lf, geom );
     138                 :            : 
     139                 :            :     // ignore invalid geometries
     140                 :          0 :     if ( ( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) ||
     141                 :          0 :          ( type == GEOS_POLYGON && fpart->nbPoints < 3 ) )
     142                 :            :     {
     143                 :          0 :       continue;
     144                 :            :     }
     145                 :            : 
     146                 :            :     // polygons: reorder coordinates
     147                 :          0 :     if ( type == GEOS_POLYGON && !GeomFunction::reorderPolygon( fpart->x, fpart->y ) )
     148                 :            :     {
     149                 :          0 :       continue;
     150                 :            :     }
     151                 :            : 
     152                 :            :     // is the feature well defined?  TODO Check epsilon
     153                 :          0 :     bool labelWellDefined = ( lf->size().width() > 0.0000001 && lf->size().height() > 0.0000001 );
     154                 :            : 
     155                 :          0 :     if ( lf->obstacleSettings().isObstacle() && featureGeomIsObstacleGeom )
     156                 :            :     {
     157                 :            :       //if we are not labeling the layer, only insert it into the obstacle list and avoid an
     158                 :            :       //unnecessary copy
     159                 :          0 :       if ( mLabelLayer && labelWellDefined )
     160                 :            :       {
     161                 :          0 :         addObstaclePart( new FeaturePart( *fpart ) );
     162                 :          0 :       }
     163                 :            :       else
     164                 :            :       {
     165                 :          0 :         addObstaclePart( fpart.release() );
     166                 :            :       }
     167                 :          0 :     }
     168                 :            : 
     169                 :            :     // feature has to be labeled?
     170                 :          0 :     if ( !mLabelLayer || !labelWellDefined )
     171                 :            :     {
     172                 :            :       //nothing more to do for this part
     173                 :          0 :       continue;
     174                 :            :     }
     175                 :            : 
     176                 :          0 :     if ( !lf->labelAllParts() && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
     177                 :            :     {
     178                 :          0 :       if ( type == GEOS_LINESTRING )
     179                 :          0 :         geom_size = fpart->length();
     180                 :          0 :       else if ( type == GEOS_POLYGON )
     181                 :          0 :         geom_size = fpart->area();
     182                 :            : 
     183                 :          0 :       if ( geom_size > biggest_size )
     184                 :            :       {
     185                 :          0 :         biggest_size = geom_size;
     186                 :          0 :         biggest_part.reset( fpart.release() );
     187                 :          0 :       }
     188                 :            :       // don't add the feature part now, do it later
     189                 :          0 :     }
     190                 :            :     else
     191                 :            :     {
     192                 :            :       // feature part is ready!
     193                 :          0 :       addFeaturePart( fpart.release(), lf->labelText() );
     194                 :          0 :       addedFeature = true;
     195                 :            :     }
     196                 :          0 :   }
     197                 :            : 
     198                 :          0 :   if ( lf->obstacleSettings().isObstacle() && !featureGeomIsObstacleGeom )
     199                 :            :   {
     200                 :            :     //do the same for the obstacle geometry
     201                 :          0 :     const QgsGeometry obstacleGeometry = lf->obstacleSettings().obstacleGeometry();
     202                 :          0 :     for ( auto it = obstacleGeometry.const_parts_begin(); it != obstacleGeometry.const_parts_end(); ++it )
     203                 :            :     {
     204                 :          0 :       geos::unique_ptr geom = QgsGeos::asGeos( *it );
     205                 :            : 
     206                 :          0 :       if ( !geom )
     207                 :            :       {
     208                 :          0 :         QgsDebugMsg( QStringLiteral( "Obstacle geometry passed to PAL labeling engine could not be converted to GEOS! %1" ).arg( ( *it )->asWkt() ) );
     209                 :          0 :         continue;
     210                 :            :       }
     211                 :            : 
     212                 :            :       // ignore invalid geometries (e.g. polygons with self-intersecting rings)
     213                 :          0 :       if ( GEOSisValid_r( geosctxt, geom.get() ) != 1 ) // 0=invalid, 1=valid, 2=exception
     214                 :            :       {
     215                 :            :         // this shouldn't happen -- we have already checked this while registering the feature
     216                 :          0 :         QgsDebugMsg( QStringLiteral( "Obstacle geometry passed to PAL labeling engine is not valid! %1" ).arg( ( *it )->asWkt() ) );
     217                 :          0 :         continue;
     218                 :            :       }
     219                 :            : 
     220                 :          0 :       int type = GEOSGeomTypeId_r( geosctxt, geom.get() );
     221                 :            : 
     222                 :          0 :       if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON )
     223                 :            :       {
     224                 :          0 :         throw InternalException::UnknownGeometry();
     225                 :            :       }
     226                 :            : 
     227                 :          0 :       std::unique_ptr<FeaturePart> fpart = std::make_unique<FeaturePart>( lf, geom.get() );
     228                 :            : 
     229                 :            :       // ignore invalid geometries
     230                 :          0 :       if ( ( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) ||
     231                 :          0 :            ( type == GEOS_POLYGON && fpart->nbPoints < 3 ) )
     232                 :            :       {
     233                 :          0 :         continue;
     234                 :            :       }
     235                 :            : 
     236                 :            :       // polygons: reorder coordinates
     237                 :          0 :       if ( type == GEOS_POLYGON && !GeomFunction::reorderPolygon( fpart->x, fpart->y ) )
     238                 :            :       {
     239                 :          0 :         continue;
     240                 :            :       }
     241                 :            : 
     242                 :          0 :       mGeosObstacleGeometries.emplace_back( std::move( geom ) );
     243                 :            : 
     244                 :            :       // feature part is ready!
     245                 :          0 :       addObstaclePart( fpart.release() );
     246                 :          0 :     }
     247                 :          0 :   }
     248                 :            : 
     249                 :          0 :   locker.unlock();
     250                 :            : 
     251                 :            :   // if using only biggest parts...
     252                 :          0 :   if ( ( !lf->labelAllParts() || lf->hasFixedPosition() ) && biggest_part )
     253                 :            :   {
     254                 :          0 :     addFeaturePart( biggest_part.release(), lf->labelText() );
     255                 :          0 :     addedFeature = true;
     256                 :          0 :   }
     257                 :            : 
     258                 :            :   // add feature to layer if we have added something
     259                 :          0 :   if ( addedFeature )
     260                 :            :   {
     261                 :          0 :     mHashtable.insert( lf->id(), lf );
     262                 :          0 :   }
     263                 :            : 
     264                 :          0 :   return addedFeature; // true if we've added something
     265                 :          0 : }
     266                 :            : 
     267                 :            : 
     268                 :          0 : void Layer::addFeaturePart( FeaturePart *fpart, const QString &labelText )
     269                 :            : {
     270                 :            :   // add to list of layer's feature parts
     271                 :          0 :   mFeatureParts << fpart;
     272                 :            : 
     273                 :            :   // add to hashtable with equally named feature parts
     274                 :          0 :   if ( mMergeLines && !labelText.isEmpty() )
     275                 :            :   {
     276                 :          0 :     mConnectedHashtable[ labelText ].append( fpart );
     277                 :          0 :   }
     278                 :          0 : }
     279                 :            : 
     280                 :          0 : void Layer::addObstaclePart( FeaturePart *fpart )
     281                 :            : {
     282                 :            :   // add to list of layer's feature parts
     283                 :          0 :   mObstacleParts.append( fpart );
     284                 :          0 : }
     285                 :            : 
     286                 :          0 : static FeaturePart *_findConnectedPart( FeaturePart *partCheck, const QVector<FeaturePart *> &otherParts )
     287                 :            : {
     288                 :            :   // iterate in the rest of the parts with the same label
     289                 :          0 :   auto it = otherParts.constBegin();
     290                 :          0 :   while ( it != otherParts.constEnd() )
     291                 :            :   {
     292                 :          0 :     if ( partCheck->isConnected( *it ) )
     293                 :            :     {
     294                 :            :       // stop checking for other connected parts
     295                 :          0 :       return *it;
     296                 :            :     }
     297                 :          0 :     ++it;
     298                 :            :   }
     299                 :            : 
     300                 :          0 :   return nullptr; // no connected part found...
     301                 :          0 : }
     302                 :            : 
     303                 :          0 : void Layer::joinConnectedFeatures()
     304                 :            : {
     305                 :            :   // go through all label texts
     306                 :          0 :   int connectedFeaturesId = 0;
     307                 :          0 :   for ( auto it = mConnectedHashtable.constBegin(); it != mConnectedHashtable.constEnd(); ++it )
     308                 :            :   {
     309                 :          0 :     QVector<FeaturePart *> parts = it.value();
     310                 :          0 :     connectedFeaturesId++;
     311                 :            : 
     312                 :            :     // need to start with biggest parts first, to avoid merging in side branches before we've
     313                 :            :     // merged the whole of the longest parts of the joined network
     314                 :          0 :     std::sort( parts.begin(), parts.end(), []( FeaturePart * a, FeaturePart * b )
     315                 :            :     {
     316                 :          0 :       return a->length() > b->length();
     317                 :            :     } );
     318                 :            : 
     319                 :            :     // go one-by-one part, try to merge
     320                 :          0 :     while ( parts.count() > 1 )
     321                 :            :     {
     322                 :            :       // part we'll be checking against other in this round
     323                 :          0 :       FeaturePart *partCheck = parts.takeFirst();
     324                 :            : 
     325                 :          0 :       FeaturePart *otherPart = _findConnectedPart( partCheck, parts );
     326                 :          0 :       if ( otherPart )
     327                 :            :       {
     328                 :            :         // merge points from partCheck to p->item
     329                 :          0 :         if ( otherPart->mergeWithFeaturePart( partCheck ) )
     330                 :            :         {
     331                 :          0 :           mConnectedFeaturesIds.insert( partCheck->featureId(), connectedFeaturesId );
     332                 :          0 :           mConnectedFeaturesIds.insert( otherPart->featureId(), connectedFeaturesId );
     333                 :            : 
     334                 :          0 :           mFeatureParts.removeOne( partCheck );
     335                 :          0 :           delete partCheck;
     336                 :          0 :         }
     337                 :          0 :       }
     338                 :            :     }
     339                 :          0 :   }
     340                 :          0 :   mConnectedHashtable.clear();
     341                 :          0 : }
     342                 :            : 
     343                 :          0 : int Layer::connectedFeatureId( QgsFeatureId featureId ) const
     344                 :            : {
     345                 :          0 :   return mConnectedFeaturesIds.value( featureId, -1 );
     346                 :            : }
     347                 :            : 
     348                 :          0 : void Layer::chopFeaturesAtRepeatDistance()
     349                 :            : {
     350                 :          0 :   GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
     351                 :          0 :   QLinkedList<FeaturePart *> newFeatureParts;
     352                 :          0 :   while ( !mFeatureParts.isEmpty() )
     353                 :            :   {
     354                 :          0 :     std::unique_ptr< FeaturePart > fpart( mFeatureParts.takeFirst() );
     355                 :          0 :     const GEOSGeometry *geom = fpart->geos();
     356                 :          0 :     double chopInterval = fpart->repeatDistance();
     357                 :            : 
     358                 :            :     // whether we CAN chop
     359                 :          0 :     bool canChop = false;
     360                 :          0 :     double featureLen = 0;
     361                 :          0 :     if ( chopInterval != 0. && GEOSGeomTypeId_r( geosctxt, geom ) == GEOS_LINESTRING )
     362                 :            :     {
     363                 :          0 :       featureLen = fpart->length();
     364                 :          0 :       if ( featureLen > chopInterval )
     365                 :          0 :         canChop = true;
     366                 :          0 :     }
     367                 :            : 
     368                 :            :     // whether we SHOULD chop
     369                 :          0 :     bool shouldChop = canChop;
     370                 :          0 :     int possibleSegments = 0;
     371                 :          0 :     if ( canChop )
     372                 :            :     {
     373                 :            :       // never chop into segments smaller than required for the actual label text
     374                 :          0 :       chopInterval *= std::ceil( fpart->getLabelWidth() / fpart->repeatDistance() );
     375                 :            : 
     376                 :            :       // now work out how many full segments we could chop this line into
     377                 :          0 :       possibleSegments = static_cast< int >( std::floor( featureLen / chopInterval ) );
     378                 :            : 
     379                 :            :       // ... and use this to work out the actual chop distance for this line. Otherwise, we risk the
     380                 :            :       // situation of:
     381                 :            :       // 1. Line length of 3cm
     382                 :            :       // 2. Repeat distance of 2cm
     383                 :            :       // 3. Label size is 1.5 cm
     384                 :            :       //
     385                 :            :       //      2cm    1cm
     386                 :            :       // /--Label--/----/
     387                 :            :       //
     388                 :            :       // i.e. the labels would be off center and gravitate toward line starts
     389                 :          0 :       chopInterval = featureLen / possibleSegments;
     390                 :            : 
     391                 :          0 :       shouldChop = possibleSegments > 1;
     392                 :          0 :     }
     393                 :            : 
     394                 :          0 :     if ( shouldChop )
     395                 :            :     {
     396                 :          0 :       const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosctxt, geom );
     397                 :            : 
     398                 :            :       // get number of points
     399                 :            :       unsigned int n;
     400                 :          0 :       GEOSCoordSeq_getSize_r( geosctxt, cs, &n );
     401                 :            : 
     402                 :            :       // Read points
     403                 :          0 :       std::vector<Point> points( n );
     404                 :          0 :       for ( unsigned int i = 0; i < n; ++i )
     405                 :            :       {
     406                 :            : #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
     407                 :          0 :         GEOSCoordSeq_getXY_r( geosctxt, cs, i, &points[i].x, &points[i].y );
     408                 :            : #else
     409                 :            :         GEOSCoordSeq_getX_r( geosctxt, cs, i, &points[i].x );
     410                 :            :         GEOSCoordSeq_getY_r( geosctxt, cs, i, &points[i].y );
     411                 :            : #endif
     412                 :          0 :       }
     413                 :            : 
     414                 :            :       // Cumulative length vector
     415                 :          0 :       std::vector<double> len( n, 0 );
     416                 :          0 :       for ( unsigned int i = 1; i < n; ++i )
     417                 :            :       {
     418                 :          0 :         double dx = points[i].x - points[i - 1].x;
     419                 :          0 :         double dy = points[i].y - points[i - 1].y;
     420                 :          0 :         len[i] = len[i - 1] + std::sqrt( dx * dx + dy * dy );
     421                 :          0 :       }
     422                 :            : 
     423                 :            :       // Walk along line
     424                 :          0 :       unsigned int cur = 0;
     425                 :          0 :       double lambda = 0;
     426                 :          0 :       std::vector<Point> part;
     427                 :            : 
     428                 :          0 :       QList<FeaturePart *> repeatParts;
     429                 :          0 :       repeatParts.reserve( possibleSegments );
     430                 :            : 
     431                 :          0 :       for ( int segment = 0; segment < possibleSegments; segment++ )
     432                 :            :       {
     433                 :          0 :         lambda += chopInterval;
     434                 :          0 :         for ( ; cur < n && lambda > len[cur]; ++cur )
     435                 :            :         {
     436                 :          0 :           part.push_back( points[cur] );
     437                 :          0 :         }
     438                 :          0 :         if ( cur >= n )
     439                 :            :         {
     440                 :            :           // Create final part
     441                 :          0 :           GEOSCoordSequence *cooSeq = GEOSCoordSeq_create_r( geosctxt, static_cast< unsigned int >( part.size() ), 2 );
     442                 :          0 :           for ( unsigned int i = 0; i < part.size(); ++i )
     443                 :            :           {
     444                 :            : #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
     445                 :          0 :             GEOSCoordSeq_setXY_r( geosctxt, cooSeq, i, part[i].x, part[i].y );
     446                 :            : #else
     447                 :            :             GEOSCoordSeq_setX_r( geosctxt, cooSeq, i, part[i].x );
     448                 :            :             GEOSCoordSeq_setY_r( geosctxt, cooSeq, i, part[i].y );
     449                 :            : #endif
     450                 :          0 :           }
     451                 :          0 :           GEOSGeometry *newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
     452                 :          0 :           FeaturePart *newfpart = new FeaturePart( fpart->feature(), newgeom );
     453                 :          0 :           newFeatureParts.append( newfpart );
     454                 :          0 :           repeatParts.push_back( newfpart );
     455                 :            : 
     456                 :          0 :           break;
     457                 :            :         }
     458                 :          0 :         double c = ( lambda - len[cur - 1] ) / ( len[cur] - len[cur - 1] );
     459                 :            :         Point p;
     460                 :          0 :         p.x = points[cur - 1].x + c * ( points[cur].x - points[cur - 1].x );
     461                 :          0 :         p.y = points[cur - 1].y + c * ( points[cur].y - points[cur - 1].y );
     462                 :          0 :         part.push_back( p );
     463                 :          0 :         GEOSCoordSequence *cooSeq = GEOSCoordSeq_create_r( geosctxt, static_cast< unsigned int >( part.size() ), 2 );
     464                 :          0 :         for ( std::size_t i = 0; i < part.size(); ++i )
     465                 :            :         {
     466                 :            : #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
     467                 :          0 :           GEOSCoordSeq_setXY_r( geosctxt, cooSeq, i, part[i].x, part[i].y );
     468                 :            : #else
     469                 :            :           GEOSCoordSeq_setX_r( geosctxt, cooSeq, static_cast< unsigned int >( i ), part[i].x );
     470                 :            :           GEOSCoordSeq_setY_r( geosctxt, cooSeq, static_cast< unsigned int >( i ), part[i].y );
     471                 :            : #endif
     472                 :          0 :         }
     473                 :            : 
     474                 :          0 :         GEOSGeometry *newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
     475                 :          0 :         FeaturePart *newfpart = new FeaturePart( fpart->feature(), newgeom );
     476                 :          0 :         newFeatureParts.append( newfpart );
     477                 :          0 :         part.clear();
     478                 :          0 :         part.push_back( p );
     479                 :          0 :         repeatParts.push_back( newfpart );
     480                 :          0 :       }
     481                 :            : 
     482                 :          0 :       for ( FeaturePart *partPtr : repeatParts )
     483                 :          0 :         partPtr->setTotalRepeats( repeatParts.count() );
     484                 :          0 :     }
     485                 :            :     else
     486                 :            :     {
     487                 :          0 :       newFeatureParts.append( fpart.release() );
     488                 :            :     }
     489                 :          0 :   }
     490                 :            : 
     491                 :          0 :   mFeatureParts = newFeatureParts;
     492                 :          0 : }
     493                 :            : 
     494                 :            : 
     495                 :            : template class QgsGenericSpatialIndex<pal::FeaturePart>;

Generated by: LCOV version 1.14