52 delete[] chain->
label;
58 : mAllCandidatesIndex( extent )
59 , mActiveCandidatesIndex( extent )
76 bool *ok =
new bool[mTotalCandidates];
79 for ( i = 0; i < mTotalCandidates; i++ )
90 for ( i = 0; i < static_cast< int >( mFeatureCount ); i++ )
93 for ( j = 0; j < mFeatNbLp[i]; j++ )
95 if ( !ok[mFeatStartId[i] + j] )
97 if ( mLabelPositions.at( mFeatStartId[i] + j )->getNumOverlaps() == 0 )
100 ok[mFeatStartId[i] + j] =
true;
103 counter += mFeatNbLp[i] - j - 1;
105 for ( k = j + 1; k < mFeatNbLp[i]; k++ )
108 lpid = mFeatStartId[i] + k;
110 lp2 = mLabelPositions[lpid ].get();
115 mAllCandidatesIndex.intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp2,
this](
const LabelPosition * lp ) ->
bool
117 if ( candidatesAreConflicting( lp2, lp ) )
128 mFeatNbLp[i] = j + 1;
136 this->mTotalCandidates -= counter;
151 if ( lp2->
getId() != lp->
getId() && list.
isIn( lp2->
getId() ) && candidatesAreConflicting( lp2, lp ) )
167 mSol.init( mFeatureCount );
176 for (
int i = 0; i < static_cast< int >( mFeatureCount ); i++ )
177 for (
int j = 0; j < mFeatNbLp[i]; j++ )
179 label = mFeatStartId[i] + j;
182 list.
insert( label, mLabelPositions.at( label )->getNumOverlaps() );
192 if (
pal->isCanceled() )
199 lp = mLabelPositions[ label ].get();
201 if ( lp->
getId() != label )
207 mSol.activeLabelIds[probFeatId] = label;
209 for (
int i = mFeatStartId[probFeatId]; i < mFeatStartId[probFeatId] + mFeatNbLp[probFeatId]; i++ )
211 ignoreLabel( mLabelPositions[ i ].get(), list, mAllCandidatesIndex );
217 std::vector< const LabelPosition * > conflictingPositions;
218 mAllCandidatesIndex.intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [lp, &conflictingPositions,
this](
const LabelPosition * lp2 ) ->
bool
220 if ( candidatesAreConflicting( lp, lp2 ) )
222 conflictingPositions.emplace_back( lp2 );
229 ignoreLabel( conflict, list, mAllCandidatesIndex );
232 mActiveCandidatesIndex.insert( lp,
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ) );
242 for ( std::size_t i = 0; i < mFeatureCount; i++ )
244 if ( mSol.activeLabelIds[i] == -1 )
246 nbOverlap = std::numeric_limits<int>::max();
247 start_p = mFeatStartId[i];
248 for ( p = 0; p < mFeatNbLp[i]; p++ )
250 lp = mLabelPositions[ start_p + p ].get();
256 mActiveCandidatesIndex.intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp,
this](
const LabelPosition * lp2 )->
bool
258 if ( candidatesAreConflicting( lp, lp2 ) )
271 mSol.activeLabelIds[i] = retainedLabel->
getId();
282 return pal->candidatesAreConflicting( lp1, lp2 );
285 inline Chain *Problem::chain(
int seed )
291 double delta_best = std::numeric_limits<double>::max();
296 Chain *retainedChain =
nullptr;
298 int max_degree =
pal->mEjChainDeg;
302 QLinkedList<ElemTrans *> currentChain;
303 QLinkedList<int> conflicts;
305 std::vector< int > tmpsol( mSol.activeLabelIds );
315 seedNbLp = mFeatNbLp[seed];
316 delta_min = std::numeric_limits<double>::max();
322 if ( tmpsol[seed] == -1 )
323 delta -= mInactiveCost[seed];
325 delta -= mLabelPositions.at( tmpsol[seed] )->cost();
327 for (
int i = -1; i < seedNbLp; i++ )
332 if ( !( tmpsol[seed] == -1 && i == -1 ) && i + mFeatStartId[seed] != tmpsol[seed] )
336 lid = mFeatStartId[seed] + i;
339 lp = mLabelPositions[ lid ].get();
344 mActiveCandidatesIndex.intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [lp, &delta_tmp, &conflicts, ¤tChain,
this](
const LabelPosition * lp2 ) ->
bool
346 if ( candidatesAreConflicting( lp2, lp ) )
351 QLinkedList< ElemTrans * >::iterator cur;
352 for ( cur = currentChain.begin(); cur != currentChain.end(); ++cur )
354 if ( ( *cur )->feat == feat )
360 if ( !conflicts.contains( feat ) )
362 conflicts.append( feat );
363 delta_tmp += lp2->cost() + mInactiveCost[feat];
370 if ( conflicts.isEmpty() )
372 if ( !retainedChain || delta + lp->
cost() < delta_best )
376 delete[] retainedChain->
label;
377 delete[] retainedChain->
feat;
381 retainedChain =
new Chain();
384 delta_best = delta + lp->
cost();
386 retainedChain->
degree = currentChain.size() + 1;
387 retainedChain->
feat =
new int[retainedChain->
degree];
388 retainedChain->
label =
new int[retainedChain->
degree];
389 QLinkedList<ElemTrans *>::iterator current = currentChain.begin();
392 while ( current != currentChain.end() )
395 retainedChain->
feat[j] = move->
feat;
400 retainedChain->
feat[j] = seed;
401 retainedChain->
label[j] = lid;
402 retainedChain->
delta = delta + lp->
cost();
407 else if ( conflicts.size() == 1 )
409 if ( delta_tmp < delta_min )
411 delta_min = delta_tmp;
413 next_seed = conflicts.takeFirst();
417 conflicts.takeFirst();
425 newChain->
degree = currentChain.size() + 1 + conflicts.size();
428 QLinkedList<ElemTrans *>::iterator current = currentChain.begin();
432 while ( current != currentChain.end() )
442 newChain->
feat[j] = seed;
443 newChain->
label[j] = lid;
444 newChain->
delta = delta + mLabelPositions.at( newChain->
label[j] )->cost();
448 while ( !conflicts.isEmpty() )
450 int ftid = conflicts.takeFirst();
451 newChain->
feat[j] = ftid;
452 newChain->
label[j] = -1;
453 newChain->
delta += mInactiveCost[ftid];
457 if ( newChain->
delta < delta_best )
462 delta_best = newChain->
delta;
463 retainedChain = newChain;
474 if ( !retainedChain || delta + mInactiveCost[seed] < delta_best )
478 delete[] retainedChain->
label;
479 delete[] retainedChain->
feat;
482 retainedChain =
new Chain();
484 delta_best = delta + mInactiveCost[seed];
486 retainedChain->
degree = currentChain.size() + 1;
487 retainedChain->
feat =
new int[retainedChain->
degree];
488 retainedChain->
label =
new int[retainedChain->
degree];
489 QLinkedList<ElemTrans *>::iterator current = currentChain.begin();
492 while ( current != currentChain.end() )
495 retainedChain->
feat[j] = move->
feat;
500 retainedChain->
feat[j] = seed;
501 retainedChain->
label[j] = -1;
502 retainedChain->
delta = delta + mInactiveCost[seed];
513 if ( next_seed == -1 )
517 else if ( currentChain.size() > max_degree )
528 currentChain.append( et );
532 mLabelPositions.at( et->
old_label )->removeFromIndex( mActiveCandidatesIndex );
537 mLabelPositions.at( et->
new_label )->insertIntoIndex( mActiveCandidatesIndex );
541 tmpsol[seed] = retainedLabel;
543 delta += mLabelPositions.at( retainedLabel )->cost();
548 while ( !currentChain.isEmpty() )
550 std::unique_ptr< ElemTrans > et( currentChain.takeFirst() );
554 mLabelPositions.at( et->
new_label )->removeFromIndex( mActiveCandidatesIndex );
559 mLabelPositions.at( et->
old_label )->insertIntoIndex( mActiveCandidatesIndex );
563 return retainedChain;
567 void Problem::chain_search()
569 if ( mFeatureCount == 0 )
574 bool *ok =
new bool[mFeatureCount];
579 Chain *retainedChain =
nullptr;
581 std::fill( ok, ok + mFeatureCount,
false );
599 for ( seed = ( iter + 1 ) % mFeatureCount;
600 ok[seed] && seed != iter;
601 seed = ( seed + 1 ) % mFeatureCount )
610 iter = ( iter + 1 ) % mFeatureCount;
611 retainedChain = chain( seed );
613 if ( retainedChain && retainedChain->
delta < -
EPSILON )
616 for ( i = 0; i < retainedChain->
degree; i++ )
618 fid = retainedChain->
feat[i];
619 lid = retainedChain->
label[i];
621 if ( mSol.activeLabelIds[fid] >= 0 )
623 LabelPosition *old = mLabelPositions[ mSol.activeLabelIds[fid] ].get();
626 mAllCandidatesIndex.intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&ok, old,
this](
const LabelPosition * lp ) ->
bool
628 if ( candidatesAreConflicting( old, lp ) )
637 mSol.activeLabelIds[fid] = lid;
639 if ( mSol.activeLabelIds[fid] >= 0 )
641 mLabelPositions.at( lid )->insertIntoIndex( mActiveCandidatesIndex );
646 mSol.totalCost += retainedChain->
delta;
664 QList<LabelPosition *> finalLabelPlacements;
667 for ( std::size_t i = 0; i < mFeatureCount; i++ )
669 const int labelId = mSol.activeLabelIds[i];
670 const bool foundNonOverlappingPlacement = labelId != -1;
671 const int startIndexForLabelPlacements = mFeatStartId[i];
672 const bool foundCandidatesForFeature = startIndexForLabelPlacements < static_cast< int >( mLabelPositions.size() );
674 if ( foundNonOverlappingPlacement )
676 finalLabelPlacements.push_back( mLabelPositions[ labelId ].get() );
678 else if ( foundCandidatesForFeature &&
680 || mLabelPositions.at( startIndexForLabelPlacements )->getFeaturePart()->layer()->displayAll()
681 || mLabelPositions.at( startIndexForLabelPlacements )->getFeaturePart()->alwaysShow() ) )
683 finalLabelPlacements.push_back( mLabelPositions[ startIndexForLabelPlacements ].get() );
685 else if ( unlabeled )
688 if ( foundCandidatesForFeature && ( i == mFeatureCount - 1 || startIndexForLabelPlacements != mFeatStartId[i + 1] ) )
689 unlabeled->push_back( mLabelPositions[ startIndexForLabelPlacements ].get() );
696 for (
const std::unique_ptr< LabelPosition > &position : mPositionsWithNoCandidates )
697 unlabeled->append( position.get() );
700 return finalLabelPlacements;
703 void Problem::solution_cost()
705 mSol.totalCost = 0.0;
712 for ( std::size_t i = 0; i < mFeatureCount; i++ )
714 if ( mSol.activeLabelIds[i] == -1 )
716 mSol.totalCost += mInactiveCost[i];
720 lp = mLabelPositions[ mSol.activeLabelIds[i] ].get();
723 mActiveCandidatesIndex.intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp,
this](
const LabelPosition * lp2 )->
bool
725 if ( candidatesAreConflicting( lp, lp2 ) )
733 mSol.totalCost += lp->
cost();
A rtree spatial index for use in the pal labeling engine.
bool intersects(const QgsRectangle &bounds, const std::function< bool(T *data)> &callback) const
Performs an intersection check against the index, for data intersecting the specified bounds.
A rectangle specified with double values.
Thrown when something is added in a Full set.
LabelPosition is a candidate feature label position.
void incrementNumOverlaps()
Increases the number of overlaps recorded against this position by 1.
void removeFromIndex(PalRtree< LabelPosition > &index)
Removes the label position from the specified index.
void decrementNumOverlaps()
Decreases the number of overlaps recorded against this position by 1.
int getId() const
Returns the id.
double cost() const
Returns the candidate label position's geographical cost.
int getNumOverlaps() const
int getProblemFeatureId() const
void getBoundingBox(double amin[2], double amax[2]) const
Returns bounding box - amin: xmin,ymin - amax: xmax,ymax.
void insertIntoIndex(PalRtree< LabelPosition > &index)
Inserts the label position into the specified index.
Custom priority queue for use in pal labeling engine.
void decreaseKey(int key)
void insert(int key, double p)
QList< LabelPosition * > getSolution(bool returnInactive, QList< LabelPosition * > *unlabeled=nullptr)
Solves the labeling problem, selecting the best candidate locations for all labels and returns a list...
Problem(const QgsRectangle &extent)
Constructor for Problem.
void delete_chain(Chain *chain)