mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-15 00:02:52 -04:00
More pal::rtree swaps
This commit is contained in:
parent
94306655b7
commit
727a4da01f
@ -38,7 +38,7 @@ namespace pal
|
||||
{
|
||||
public:
|
||||
//! Increase candidate's cost according to its collision with passed feature
|
||||
static void addObstacleCostPenalty( LabelPosition *lp, pal::FeaturePart *obstacle, Pal *pal );
|
||||
static void addObstacleCostPenalty( pal::LabelPosition *lp, pal::FeaturePart *obstacle, Pal *pal );
|
||||
|
||||
//! Calculates the costs for polygon label candidates
|
||||
static void setPolygonCandidatesCost( std::size_t nblp, std::vector<std::unique_ptr<pal::LabelPosition> > &lPos, QgsGenericSpatialIndex< FeaturePart > *obstacles, double bbx[4], double bby[4] );
|
||||
|
@ -265,7 +265,7 @@ bool LabelPosition::isInside( double *bbox )
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LabelPosition::isInConflict( LabelPosition *lp )
|
||||
bool LabelPosition::isInConflict( const LabelPosition *lp ) const
|
||||
{
|
||||
if ( this->probFeat == lp->probFeat ) // bugfix #1
|
||||
return false; // always overlaping itself !
|
||||
@ -276,7 +276,7 @@ bool LabelPosition::isInConflict( LabelPosition *lp )
|
||||
return isInConflictMultiPart( lp );
|
||||
}
|
||||
|
||||
bool LabelPosition::isInConflictSinglePart( LabelPosition *lp )
|
||||
bool LabelPosition::isInConflictSinglePart( const LabelPosition *lp ) const
|
||||
{
|
||||
if ( qgsDoubleNear( alpha, 0 ) && qgsDoubleNear( lp->alpha, 0 ) )
|
||||
{
|
||||
@ -303,14 +303,14 @@ bool LabelPosition::isInConflictSinglePart( LabelPosition *lp )
|
||||
}
|
||||
}
|
||||
|
||||
bool LabelPosition::isInConflictMultiPart( LabelPosition *lp )
|
||||
bool LabelPosition::isInConflictMultiPart( const LabelPosition *lp ) const
|
||||
{
|
||||
// check all parts against all parts of other one
|
||||
LabelPosition *tmp1 = this;
|
||||
const LabelPosition *tmp1 = this;
|
||||
while ( tmp1 )
|
||||
{
|
||||
// check tmp1 against parts of other label
|
||||
LabelPosition *tmp2 = lp;
|
||||
const LabelPosition *tmp2 = lp;
|
||||
while ( tmp2 )
|
||||
{
|
||||
if ( tmp1->isInConflictSinglePart( tmp2 ) )
|
||||
@ -383,7 +383,7 @@ void LabelPosition::validateCost()
|
||||
}
|
||||
}
|
||||
|
||||
FeaturePart *LabelPosition::getFeaturePart()
|
||||
FeaturePart *LabelPosition::getFeaturePart() const
|
||||
{
|
||||
return feature;
|
||||
}
|
||||
@ -428,81 +428,20 @@ void LabelPosition::setHasHardObstacleConflict( bool conflicts )
|
||||
nextPart->setHasHardObstacleConflict( conflicts );
|
||||
}
|
||||
|
||||
void LabelPosition::removeFromIndex( RTree<LabelPosition *, double, 2, double> &index )
|
||||
void LabelPosition::removeFromIndex( QgsGenericSpatialIndex<LabelPosition> &index )
|
||||
{
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
getBoundingBox( amin, amax );
|
||||
index.Remove( amin, amax, this );
|
||||
index.deleteData( this, QgsRectangle( amin[0], amin[1], amax[0], amax[1] ) );
|
||||
}
|
||||
|
||||
void LabelPosition::insertIntoIndex( RTree<LabelPosition *, double, 2, double> &index )
|
||||
void LabelPosition::insertIntoIndex( QgsGenericSpatialIndex<LabelPosition> &index )
|
||||
{
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
getBoundingBox( amin, amax );
|
||||
index.Insert( amin, amax, this );
|
||||
}
|
||||
|
||||
bool LabelPosition::pruneCallback( LabelPosition *candidatePosition, void *ctx )
|
||||
{
|
||||
FeaturePart *obstaclePart = ( reinterpret_cast< PruneCtx * >( ctx ) )->obstacle;
|
||||
|
||||
// test whether we should ignore this obstacle for the candidate. We do this if:
|
||||
// 1. it's not a hole, and the obstacle belongs to the same label feature as the candidate (e.g.,
|
||||
// features aren't obstacles for their own labels)
|
||||
// 2. it IS a hole, and the hole belongs to a different label feature to the candidate (e.g., holes
|
||||
// are ONLY obstacles for the labels of the feature they belong to)
|
||||
if ( ( !obstaclePart->getHoleOf() && candidatePosition->feature->hasSameLabelFeatureAs( obstaclePart ) )
|
||||
|| ( obstaclePart->getHoleOf() && !candidatePosition->feature->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
CostCalculator::addObstacleCostPenalty( candidatePosition, obstaclePart, ( reinterpret_cast< PruneCtx * >( ctx ) )->pal );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LabelPosition::countOverlapCallback( LabelPosition *lp, void *ctx )
|
||||
{
|
||||
LabelPosition *lp2 = reinterpret_cast< LabelPosition * >( ctx );
|
||||
|
||||
if ( lp2->isInConflict( lp ) )
|
||||
{
|
||||
lp2->nbOverlap++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LabelPosition::countFullOverlapCallback( LabelPosition *lp, void *ctx )
|
||||
{
|
||||
CountContext *context = reinterpret_cast< CountContext * >( ctx );
|
||||
LabelPosition *lp2 = context->lp;
|
||||
double *cost = context->cost;
|
||||
int *nbOv = context->nbOv;
|
||||
std::vector< double > &inactiveCost = *context->inactiveCost;
|
||||
if ( lp2->isInConflict( lp ) )
|
||||
{
|
||||
( *nbOv ) ++;
|
||||
*cost += inactiveCost[lp->probFeat] + lp->cost();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LabelPosition::removeOverlapCallback( LabelPosition *lp, void *ctx )
|
||||
{
|
||||
LabelPosition *lp2 = reinterpret_cast< LabelPosition * >( ctx );
|
||||
|
||||
if ( lp2->isInConflict( lp ) )
|
||||
{
|
||||
lp->nbOverlap--;
|
||||
lp2->nbOverlap--;
|
||||
}
|
||||
|
||||
return true;
|
||||
index.insertData( this, QgsRectangle( amin[0], amin[1], amax[0], amax[1] ) );
|
||||
}
|
||||
|
||||
double LabelPosition::getDistanceToPoint( double xp, double yp ) const
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "qgis_core.h"
|
||||
#include "pointset.h"
|
||||
#include "rtree.hpp"
|
||||
#include "qgsgenericspatialindex.h"
|
||||
#include <fstream>
|
||||
|
||||
namespace pal
|
||||
@ -139,7 +140,7 @@ namespace pal
|
||||
* \param ls other labelposition
|
||||
* \returns TRUE or FALSE
|
||||
*/
|
||||
bool isInConflict( LabelPosition *ls );
|
||||
bool isInConflict( const LabelPosition *ls ) const;
|
||||
|
||||
//! Returns bounding box - amin: xmin,ymin - amax: xmax,ymax
|
||||
void getBoundingBox( double amin[2], double amax[2] ) const;
|
||||
@ -176,11 +177,21 @@ namespace pal
|
||||
/**
|
||||
* Returns the feature corresponding to this labelposition
|
||||
*/
|
||||
FeaturePart *getFeaturePart();
|
||||
FeaturePart *getFeaturePart() const;
|
||||
|
||||
int getNumOverlaps() const { return nbOverlap; }
|
||||
void resetNumOverlaps() { nbOverlap = 0; } // called from problem.cpp, pal.cpp
|
||||
|
||||
/**
|
||||
* Increases the number of overlaps recorded against this position by 1.
|
||||
*/
|
||||
void incrementNumOverlaps() { nbOverlap++; }
|
||||
|
||||
/**
|
||||
* Decreases the number of overlaps recorded against this position by 1.
|
||||
*/
|
||||
void decrementNumOverlaps() { nbOverlap++; }
|
||||
|
||||
int getProblemFeatureId() const { return probFeat; }
|
||||
|
||||
/**
|
||||
@ -278,12 +289,12 @@ namespace pal
|
||||
/**
|
||||
* Removes the label position from the specified \a index.
|
||||
*/
|
||||
void removeFromIndex( RTree<LabelPosition *, double, 2, double> &index );
|
||||
void removeFromIndex( QgsGenericSpatialIndex<LabelPosition> &index );
|
||||
|
||||
/**
|
||||
* Inserts the label position into the specified \a index.
|
||||
*/
|
||||
void insertIntoIndex( RTree<LabelPosition *, double, 2, double> &index );
|
||||
void insertIntoIndex( QgsGenericSpatialIndex<LabelPosition> &index );
|
||||
|
||||
struct PruneCtx
|
||||
{
|
||||
@ -356,8 +367,8 @@ namespace pal
|
||||
|
||||
LabelPosition::Quadrant quadrant;
|
||||
|
||||
bool isInConflictSinglePart( LabelPosition *lp );
|
||||
bool isInConflictMultiPart( LabelPosition *lp );
|
||||
bool isInConflictSinglePart( const LabelPosition *lp ) const;
|
||||
bool isInConflictMultiPart( const LabelPosition *lp ) const;
|
||||
|
||||
private:
|
||||
double mCost;
|
||||
|
@ -256,15 +256,11 @@ bool Layer::registerFeature( QgsLabelFeature *lf )
|
||||
|
||||
void Layer::addFeaturePart( FeaturePart *fpart, const QString &labelText )
|
||||
{
|
||||
double bmin[2];
|
||||
double bmax[2];
|
||||
fpart->getBoundingBox( bmin, bmax );
|
||||
|
||||
// add to list of layer's feature parts
|
||||
mFeatureParts << fpart;
|
||||
|
||||
// add to r-tree for fast spatial access
|
||||
mFeatureIndex.insertData( fpart, QgsRectangle( bmin[0], bmin[1], bmax[0], bmax[1] ) );
|
||||
mFeatureIndex.insertData( fpart, fpart->boundingBox() );
|
||||
|
||||
// add to hashtable with equally named feature parts
|
||||
if ( mMergeLines && !labelText.isEmpty() )
|
||||
@ -275,15 +271,11 @@ void Layer::addFeaturePart( FeaturePart *fpart, const QString &labelText )
|
||||
|
||||
void Layer::addObstaclePart( FeaturePart *fpart )
|
||||
{
|
||||
double bmin[2];
|
||||
double bmax[2];
|
||||
fpart->getBoundingBox( bmin, bmax );
|
||||
|
||||
// add to list of layer's feature parts
|
||||
mObstacleParts.append( fpart );
|
||||
|
||||
// add to obstacle r-tree
|
||||
mObstacleIndex.insertData( fpart, QgsRectangle( bmin[0], bmin[1], bmax[0], bmax[1] ) );
|
||||
mObstacleIndex.insertData( fpart, fpart->boundingBox() );
|
||||
}
|
||||
|
||||
static FeaturePart *_findConnectedPart( FeaturePart *partCheck, const QVector<FeaturePart *> &otherParts )
|
||||
@ -329,22 +321,18 @@ void Layer::joinConnectedFeatures()
|
||||
if ( otherPart )
|
||||
{
|
||||
// remove partCheck from r-tree
|
||||
double checkpartBMin[2], checkpartBMax[2];
|
||||
partCheck->getBoundingBox( checkpartBMin, checkpartBMax );
|
||||
|
||||
double otherPartBMin[2], otherPartBMax[2];
|
||||
otherPart->getBoundingBox( otherPartBMin, otherPartBMax );
|
||||
const QgsRectangle checkPartBoundsBefore = partCheck->boundingBox();
|
||||
const QgsRectangle otherPartBoundsBefore = otherPart->boundingBox();
|
||||
|
||||
// merge points from partCheck to p->item
|
||||
if ( otherPart->mergeWithFeaturePart( partCheck ) )
|
||||
{
|
||||
// remove the parts we are joining from the index
|
||||
mFeatureIndex.deleteData( partCheck, QgsRectangle( checkpartBMin[0], checkpartBMin[1], checkpartBMax[0], checkpartBMax[1] ) );
|
||||
mFeatureIndex.deleteData( otherPart, QgsRectangle( otherPartBMin[0], otherPartBMin[1], otherPartBMax[0], otherPartBMax[1] ) ) ;
|
||||
mFeatureIndex.deleteData( partCheck, checkPartBoundsBefore );
|
||||
mFeatureIndex.deleteData( otherPart, otherPartBoundsBefore ) ;
|
||||
|
||||
// reinsert merged line to r-tree (probably not needed)
|
||||
otherPart->getBoundingBox( otherPartBMin, otherPartBMax );
|
||||
mFeatureIndex.insertData( otherPart, QgsRectangle( otherPartBMin[0], otherPartBMin[1], otherPartBMax[0], otherPartBMax[1] ) );
|
||||
mFeatureIndex.insertData( otherPart, otherPart->boundingBox() );
|
||||
|
||||
mConnectedFeaturesIds.insert( partCheck->featureId(), connectedFeaturesId );
|
||||
mConnectedFeaturesIds.insert( otherPart->featureId(), connectedFeaturesId );
|
||||
@ -411,9 +399,7 @@ void Layer::chopFeaturesAtRepeatDistance()
|
||||
|
||||
if ( shouldChop )
|
||||
{
|
||||
double bmin[2], bmax[2];
|
||||
fpart->getBoundingBox( bmin, bmax );
|
||||
mFeatureIndex.deleteData( fpart.get(), QgsRectangle( bmin[0], bmin[1], bmax[0], bmax[1] ) );
|
||||
mFeatureIndex.deleteData( fpart.get(), fpart->boundingBox() );
|
||||
|
||||
const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosctxt, geom );
|
||||
|
||||
@ -473,8 +459,7 @@ void Layer::chopFeaturesAtRepeatDistance()
|
||||
GEOSGeometry *newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
|
||||
FeaturePart *newfpart = new FeaturePart( fpart->feature(), newgeom );
|
||||
newFeatureParts.append( newfpart );
|
||||
newfpart->getBoundingBox( bmin, bmax );
|
||||
mFeatureIndex.insertData( newfpart, QgsRectangle( bmin[0], bmin[1], bmax[0], bmax[1] ) );
|
||||
mFeatureIndex.insertData( newfpart, newfpart->boundingBox() );
|
||||
repeatParts.push_back( newfpart );
|
||||
|
||||
break;
|
||||
@ -498,8 +483,7 @@ void Layer::chopFeaturesAtRepeatDistance()
|
||||
GEOSGeometry *newgeom = GEOSGeom_createLineString_r( geosctxt, cooSeq );
|
||||
FeaturePart *newfpart = new FeaturePart( fpart->feature(), newgeom );
|
||||
newFeatureParts.append( newfpart );
|
||||
newfpart->getBoundingBox( bmin, bmax );
|
||||
mFeatureIndex.insertData( newfpart, QgsRectangle( bmin[0], bmin[1], bmax[0], bmax[1] ) );
|
||||
mFeatureIndex.insertData( newfpart, newfpart->boundingBox() );
|
||||
part.clear();
|
||||
part.push_back( p );
|
||||
repeatParts.push_back( newfpart );
|
||||
|
@ -89,7 +89,7 @@ struct FeatCallBackCtx
|
||||
std::list< std::unique_ptr< Feats > > *features = nullptr;
|
||||
|
||||
QgsGenericSpatialIndex< FeaturePart > *obstacleIndex = nullptr;
|
||||
RTree<LabelPosition *, double, 2, double> *allCandidateIndex = nullptr;
|
||||
QgsGenericSpatialIndex<LabelPosition> *allCandidateIndex = nullptr;
|
||||
std::vector< std::unique_ptr< LabelPosition > > *positionsWithNoCandidates = nullptr;
|
||||
const GEOSPreparedGeometry *mapBoundary = nullptr;
|
||||
Pal *pal = nullptr;
|
||||
@ -105,7 +105,7 @@ struct ObstacleCallBackCtx
|
||||
|
||||
struct FilterContext
|
||||
{
|
||||
RTree<LabelPosition *, double, 2, double> *allCandidatesIndex = nullptr;
|
||||
QgsGenericSpatialIndex<LabelPosition> *allCandidatesIndex = nullptr;
|
||||
Pal *pal = nullptr;
|
||||
};
|
||||
|
||||
@ -189,7 +189,6 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
|
||||
context.layer = layer;
|
||||
layer->mFeatureIndex.intersects( QgsRectangle( amin[ 0], amin[1], amax[0], amax[1] ), [&context]( const FeaturePart * constFeaturePart )->bool
|
||||
{
|
||||
double amin[2], amax[2];
|
||||
FeaturePart *featurePart = const_cast< FeaturePart * >( constFeaturePart );
|
||||
|
||||
if ( context.pal->isCanceled() )
|
||||
@ -198,8 +197,7 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
|
||||
// Holes of the feature are obstacles
|
||||
for ( int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
|
||||
{
|
||||
featurePart->getSelfObstacle( i )->getBoundingBox( amin, amax );
|
||||
context.obstacleIndex->insertData( featurePart->getSelfObstacle( i ), QgsRectangle( amin[0], amin[1], amax[0], amax[1] ) );
|
||||
context.obstacleIndex->insertData( featurePart->getSelfObstacle( i ), featurePart->getSelfObstacle( i )->boundingBox() );
|
||||
|
||||
if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
|
||||
{
|
||||
@ -305,13 +303,27 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
|
||||
if ( filterCtx.pal->isCanceled() )
|
||||
return false; // do not continue searching
|
||||
|
||||
double amin[2], amax[2];
|
||||
part->getBoundingBox( amin, amax );
|
||||
|
||||
LabelPosition::PruneCtx pruneContext;
|
||||
pruneContext.obstacle = const_cast< FeaturePart * >( part );
|
||||
pruneContext.pal = filterCtx.pal;
|
||||
filterCtx.allCandidatesIndex->Search( amin, amax, LabelPosition::pruneCallback, static_cast< void * >( &pruneContext ) );
|
||||
filterCtx.allCandidatesIndex->intersects( part->boundingBox(), [&pruneContext]( const LabelPosition * candidatePosition ) -> bool{
|
||||
FeaturePart *obstaclePart = pruneContext.obstacle;
|
||||
|
||||
// test whether we should ignore this obstacle for the candidate. We do this if:
|
||||
// 1. it's not a hole, and the obstacle belongs to the same label feature as the candidate (e.g.,
|
||||
// features aren't obstacles for their own labels)
|
||||
// 2. it IS a hole, and the hole belongs to a different label feature to the candidate (e.g., holes
|
||||
// are ONLY obstacles for the labels of the feature they belong to)
|
||||
if ( ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
|
||||
|| ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
CostCalculator::addObstacleCostPenalty( const_cast< LabelPosition * >( candidatePosition ), obstaclePart, pruneContext.pal );
|
||||
|
||||
return true;
|
||||
} );
|
||||
|
||||
return true;
|
||||
} );
|
||||
@ -407,6 +419,8 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
|
||||
|
||||
int nbOverlaps = 0;
|
||||
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
while ( !features.empty() ) // foreach feature
|
||||
{
|
||||
if ( isCanceled() )
|
||||
@ -426,10 +440,18 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
|
||||
|
||||
//prob->feat[idlp] = j;
|
||||
|
||||
lp->getBoundingBox( amin, amax );
|
||||
|
||||
// lookup for overlapping candidate
|
||||
prob->mAllCandidatesIndex.Search( amin, amax, LabelPosition::countOverlapCallback, static_cast< void * >( lp.get() ) );
|
||||
lp->getBoundingBox( amin, amax );
|
||||
prob->mAllCandidatesIndex.intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp]( const LabelPosition * lp2 )->bool
|
||||
{
|
||||
if ( lp->isInConflict( lp2 ) )
|
||||
{
|
||||
lp->incrementNumOverlaps();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} );
|
||||
|
||||
nbOverlaps += lp->getNumOverlaps();
|
||||
|
||||
|
@ -148,14 +148,6 @@ namespace pal
|
||||
|
||||
int getGeosType() const { return type; }
|
||||
|
||||
void getBoundingBox( double min[2], double max[2] ) const
|
||||
{
|
||||
min[0] = xmin;
|
||||
min[1] = ymin;
|
||||
max[0] = xmax;
|
||||
max[1] = ymax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the point set bounding box.
|
||||
*/
|
||||
|
@ -108,7 +108,16 @@ void Problem::reduce()
|
||||
lp2->getBoundingBox( amin, amax );
|
||||
|
||||
mNbOverlap -= lp2->getNumOverlaps();
|
||||
mAllCandidatesIndex.Search( amin, amax, LabelPosition::removeOverlapCallback, reinterpret_cast< void * >( lp2 ) );
|
||||
mAllCandidatesIndex.intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp2]( const LabelPosition * lp ) -> bool
|
||||
{
|
||||
if ( lp2->isInConflict( lp ) )
|
||||
{
|
||||
const_cast< LabelPosition * >( lp )->decrementNumOverlaps();
|
||||
lp2->decrementNumOverlaps();
|
||||
}
|
||||
|
||||
return true;
|
||||
} );
|
||||
lp2->removeFromIndex( mAllCandidatesIndex );
|
||||
}
|
||||
|
||||
@ -127,59 +136,39 @@ void Problem::reduce()
|
||||
struct FalpContext
|
||||
{
|
||||
PriorityQueue *list = nullptr;
|
||||
LabelPosition *lp = nullptr;
|
||||
RTree <LabelPosition *, double, 2, double> *candidatesIndex = nullptr;
|
||||
const LabelPosition *lp = nullptr;
|
||||
QgsGenericSpatialIndex< LabelPosition > *candidatesIndex = nullptr;
|
||||
};
|
||||
|
||||
bool falpCallback2( LabelPosition *lp, void *ctx )
|
||||
{
|
||||
FalpContext *context = reinterpret_cast< FalpContext * >( ctx );
|
||||
LabelPosition *lp2 = context->lp;
|
||||
PriorityQueue *list = context->list;
|
||||
|
||||
if ( lp->getId() != lp2->getId() && list->isIn( lp->getId() ) && lp->isInConflict( lp2 ) )
|
||||
{
|
||||
list->decreaseKey( lp->getId() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ignoreLabel( LabelPosition *lp, PriorityQueue &list, RTree <LabelPosition *, double, 2, double> &candidatesIndex )
|
||||
void ignoreLabel( const LabelPosition *lp, PriorityQueue &list, QgsGenericSpatialIndex< LabelPosition > &candidatesIndex )
|
||||
{
|
||||
FalpContext context;
|
||||
context.candidatesIndex = nullptr;
|
||||
context.list = &list;
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
|
||||
if ( list.isIn( lp->getId() ) )
|
||||
{
|
||||
list.remove( lp->getId() );
|
||||
|
||||
lp->getBoundingBox( amin, amax );
|
||||
|
||||
context.lp = lp;
|
||||
candidatesIndex.Search( amin, amax, falpCallback2, &context );
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
lp->getBoundingBox( amin, amax );
|
||||
candidatesIndex.intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&context]( const LabelPosition * lp )->bool
|
||||
{
|
||||
const LabelPosition *lp2 = context.lp;
|
||||
PriorityQueue *list = context.list;
|
||||
|
||||
if ( lp->getId() != lp2->getId() && list->isIn( lp->getId() ) && lp->isInConflict( lp2 ) )
|
||||
{
|
||||
list->decreaseKey( lp->getId() );
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool falpCallback1( LabelPosition *lp, void *ctx )
|
||||
{
|
||||
FalpContext *context = reinterpret_cast< FalpContext * >( ctx );
|
||||
LabelPosition *lp2 = context->lp;
|
||||
PriorityQueue *list = context->list;
|
||||
|
||||
if ( lp2->isInConflict( lp ) )
|
||||
{
|
||||
ignoreLabel( lp, *list, *context->candidatesIndex );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Better initial solution
|
||||
* Step one FALP (Yamamoto, Camara, Lorena 2005)
|
||||
*/
|
||||
@ -243,8 +232,18 @@ void Problem::init_sol_falp()
|
||||
lp->getBoundingBox( amin, amax );
|
||||
|
||||
context.lp = lp;
|
||||
mAllCandidatesIndex.Search( amin, amax, falpCallback1, reinterpret_cast< void * >( &context ) );
|
||||
mActiveCandidatesIndex.Insert( amin, amax, lp );
|
||||
mAllCandidatesIndex.intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&context]( const LabelPosition * lp ) ->bool
|
||||
{
|
||||
const LabelPosition *lp2 = context.lp;
|
||||
PriorityQueue *list = context.list;
|
||||
|
||||
if ( lp2->isInConflict( lp ) )
|
||||
{
|
||||
ignoreLabel( lp, *list, *context.candidatesIndex );
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
mActiveCandidatesIndex.insertData( lp, QgsRectangle( amin[0], amin[1], amax[0], amax[1] ) );
|
||||
}
|
||||
|
||||
if ( mDisplayAll )
|
||||
@ -268,7 +267,14 @@ void Problem::init_sol_falp()
|
||||
lp->getBoundingBox( amin, amax );
|
||||
|
||||
|
||||
mActiveCandidatesIndex.Search( amin, amax, LabelPosition::countOverlapCallback, lp );
|
||||
mActiveCandidatesIndex.intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp]( const LabelPosition * lp2 )->bool
|
||||
{
|
||||
if ( lp->isInConflict( lp2 ) )
|
||||
{
|
||||
lp->incrementNumOverlaps();
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
|
||||
if ( lp->getNumOverlaps() < nbOverlap )
|
||||
{
|
||||
@ -300,52 +306,6 @@ struct ChainContext
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
bool chainCallback( LabelPosition *lp, void *context )
|
||||
{
|
||||
ChainContext *ctx = reinterpret_cast< ChainContext * >( context );
|
||||
|
||||
if ( lp->isInConflict( ctx->lp ) )
|
||||
{
|
||||
int feat, rfeat;
|
||||
bool sub = nullptr != ctx->featWrap;
|
||||
|
||||
feat = lp->getProblemFeatureId();
|
||||
if ( sub )
|
||||
{
|
||||
rfeat = feat;
|
||||
feat = ctx->featWrap[feat];
|
||||
}
|
||||
else
|
||||
rfeat = feat;
|
||||
|
||||
if ( feat >= 0 && ( *ctx->tmpsol )[feat] == lp->getId() )
|
||||
{
|
||||
if ( sub && feat < ctx->borderSize )
|
||||
{
|
||||
throw - 2;
|
||||
}
|
||||
}
|
||||
|
||||
// is there any cycles ?
|
||||
QLinkedList< ElemTrans * >::iterator cur;
|
||||
for ( cur = ctx->currentChain->begin(); cur != ctx->currentChain->end(); ++cur )
|
||||
{
|
||||
if ( ( *cur )->feat == feat )
|
||||
{
|
||||
throw - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !ctx->conflicts->contains( feat ) )
|
||||
{
|
||||
ctx->conflicts->append( feat );
|
||||
*ctx->delta_tmp += lp->cost() + ( * ctx->inactiveCost )[rfeat];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline Chain *Problem::chain( int seed )
|
||||
{
|
||||
|
||||
@ -373,8 +333,6 @@ inline Chain *Problem::chain( int seed )
|
||||
std::vector< int > tmpsol( mSol.activeLabelIds );
|
||||
|
||||
LabelPosition *lp = nullptr;
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
|
||||
ChainContext context;
|
||||
context.featWrap = nullptr;
|
||||
@ -386,6 +344,9 @@ inline Chain *Problem::chain( int seed )
|
||||
context.conflicts = &conflicts;
|
||||
context.delta_tmp = &delta_tmp;
|
||||
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
|
||||
delta = 0;
|
||||
while ( seed != -1 )
|
||||
{
|
||||
@ -416,11 +377,52 @@ inline Chain *Problem::chain( int seed )
|
||||
lp = mLabelPositions[ lid ].get();
|
||||
|
||||
// evaluate conflicts graph in solution after moving seed's label
|
||||
lp->getBoundingBox( amin, amax );
|
||||
|
||||
context.lp = lp;
|
||||
|
||||
mActiveCandidatesIndex.Search( amin, amax, chainCallback, reinterpret_cast< void * >( &context ) );
|
||||
lp->getBoundingBox( amin, amax );
|
||||
mActiveCandidatesIndex.intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&context]( const LabelPosition * lp ) -> bool
|
||||
{
|
||||
if ( lp->isInConflict( context.lp ) )
|
||||
{
|
||||
int feat, rfeat;
|
||||
bool sub = nullptr != context.featWrap;
|
||||
|
||||
feat = lp->getProblemFeatureId();
|
||||
if ( sub )
|
||||
{
|
||||
rfeat = feat;
|
||||
feat = context.featWrap[feat];
|
||||
}
|
||||
else
|
||||
rfeat = feat;
|
||||
|
||||
if ( feat >= 0 && ( *context.tmpsol )[feat] == lp->getId() )
|
||||
{
|
||||
if ( sub && feat < context.borderSize )
|
||||
{
|
||||
throw - 2;
|
||||
}
|
||||
}
|
||||
|
||||
// is there any cycles ?
|
||||
QLinkedList< ElemTrans * >::iterator cur;
|
||||
for ( cur = context.currentChain->begin(); cur != context.currentChain->end(); ++cur )
|
||||
{
|
||||
if ( ( *cur )->feat == feat )
|
||||
{
|
||||
throw - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !context.conflicts->contains( feat ) )
|
||||
{
|
||||
context.conflicts->append( feat );
|
||||
*context.delta_tmp += lp->cost() + ( * context.inactiveCost )[rfeat];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
|
||||
// no conflict -> end of chain
|
||||
if ( conflicts.isEmpty() )
|
||||
@ -626,28 +628,6 @@ struct NokContext
|
||||
int *wrap = nullptr;
|
||||
};
|
||||
|
||||
bool nokCallback( LabelPosition *lp, void *context )
|
||||
{
|
||||
NokContext *ctx = reinterpret_cast< NokContext *>( context );
|
||||
LabelPosition *lp2 = ctx->lp;
|
||||
bool *ok = ctx->ok;
|
||||
int *wrap = ctx->wrap;
|
||||
|
||||
if ( lp2->isInConflict( lp ) )
|
||||
{
|
||||
if ( wrap )
|
||||
{
|
||||
ok[wrap[lp->getProblemFeatureId()]] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ok[lp->getProblemFeatureId()] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Problem::chain_search()
|
||||
{
|
||||
if ( mFeatureCount == 0 )
|
||||
@ -659,8 +639,6 @@ void Problem::chain_search()
|
||||
int fid;
|
||||
int lid;
|
||||
int popit = 0;
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
|
||||
NokContext context;
|
||||
context.ok = ok;
|
||||
@ -678,6 +656,9 @@ void Problem::chain_search()
|
||||
|
||||
int iter = 0;
|
||||
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
|
||||
while ( true )
|
||||
{
|
||||
|
||||
@ -709,11 +690,28 @@ void Problem::chain_search()
|
||||
{
|
||||
LabelPosition *old = mLabelPositions[ mSol.activeLabelIds[fid] ].get();
|
||||
old->removeFromIndex( mActiveCandidatesIndex );
|
||||
|
||||
old->getBoundingBox( amin, amax );
|
||||
|
||||
context.lp = old;
|
||||
mAllCandidatesIndex.Search( amin, amax, nokCallback, &context );
|
||||
old->getBoundingBox( amin, amax );
|
||||
mAllCandidatesIndex.intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&context]( const LabelPosition * lp ) ->bool
|
||||
{
|
||||
LabelPosition *lp2 = context.lp;
|
||||
bool *ok = context.ok;
|
||||
int *wrap = context.wrap;
|
||||
|
||||
if ( lp2->isInConflict( lp ) )
|
||||
{
|
||||
if ( wrap )
|
||||
{
|
||||
ok[wrap[lp->getProblemFeatureId()]] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ok[lp->getProblemFeatureId()] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} );
|
||||
}
|
||||
|
||||
mSol.activeLabelIds[fid] = lid;
|
||||
@ -785,12 +783,13 @@ void Problem::solution_cost()
|
||||
context.inactiveCost = &mInactiveCost;
|
||||
context.nbOv = &nbOv;
|
||||
context.cost = &mSol.totalCost;
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
LabelPosition *lp = nullptr;
|
||||
|
||||
int nbHidden = 0;
|
||||
|
||||
double amin[2];
|
||||
double amax[2];
|
||||
|
||||
for ( std::size_t i = 0; i < mFeatureCount; i++ )
|
||||
{
|
||||
if ( mSol.activeLabelIds[i] == -1 )
|
||||
@ -803,10 +802,22 @@ void Problem::solution_cost()
|
||||
nbOv = 0;
|
||||
lp = mLabelPositions[ mSol.activeLabelIds[i] ].get();
|
||||
|
||||
lp->getBoundingBox( amin, amax );
|
||||
|
||||
context.lp = lp;
|
||||
mActiveCandidatesIndex.Search( amin, amax, LabelPosition::countFullOverlapCallback, &context );
|
||||
lp->getBoundingBox( amin, amax );
|
||||
mActiveCandidatesIndex.intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&context]( const LabelPosition * lp )->bool
|
||||
{
|
||||
LabelPosition *lp2 = context.lp;
|
||||
double *cost = context.cost;
|
||||
int *nbOv = context.nbOv;
|
||||
std::vector< double > &inactiveCost = *context.inactiveCost;
|
||||
if ( lp2->isInConflict( lp ) )
|
||||
{
|
||||
( *nbOv ) ++;
|
||||
*cost += inactiveCost[lp->getProblemFeatureId()] + lp->cost();
|
||||
}
|
||||
|
||||
return true;
|
||||
} );
|
||||
|
||||
mSol.totalCost += lp->cost();
|
||||
}
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include "qgis_core.h"
|
||||
#include <list>
|
||||
#include <QList>
|
||||
#include "rtree.hpp"
|
||||
#include "qgsgenericspatialindex.h"
|
||||
|
||||
namespace pal
|
||||
{
|
||||
@ -178,8 +178,8 @@ namespace pal
|
||||
|
||||
std::vector< std::unique_ptr< LabelPosition > > mLabelPositions;
|
||||
|
||||
RTree<LabelPosition *, double, 2, double> mAllCandidatesIndex;
|
||||
RTree<LabelPosition *, double, 2, double> mActiveCandidatesIndex;
|
||||
QgsGenericSpatialIndex<LabelPosition> mAllCandidatesIndex;
|
||||
QgsGenericSpatialIndex<LabelPosition> mActiveCandidatesIndex;
|
||||
|
||||
std::vector< std::unique_ptr< LabelPosition > > mPositionsWithNoCandidates;
|
||||
|
||||
|
@ -69,6 +69,7 @@ class GenericIndexVisitor : public SpatialIndex::IVisitor
|
||||
|
||||
template <class T>
|
||||
QgsGenericSpatialIndex< T >::QgsGenericSpatialIndex()
|
||||
: mMutex( QMutex::Recursive )
|
||||
{
|
||||
mStorageManager.reset( SpatialIndex::StorageManager::createNewMemoryStorageManager() );
|
||||
mRTree = createSpatialIndex( *mStorageManager );
|
||||
@ -142,6 +143,8 @@ bool QgsGenericSpatialIndex<T>::intersects( const QgsRectangle &rectangle, const
|
||||
namespace pal
|
||||
{
|
||||
class FeaturePart;
|
||||
class LabelPosition;
|
||||
}
|
||||
|
||||
template class QgsGenericSpatialIndex<pal::FeaturePart>;
|
||||
template class QgsGenericSpatialIndex<pal::LabelPosition>;
|
||||
|
Loading…
x
Reference in New Issue
Block a user