Skip to content
Permalink
Browse files

[pal] Scale coordinates stored in pal rtrees to full float ranges

Avoids crashes caused by super-heavy labeling jobs, where a huge
number of candidates causes the rtree to branch out to a level
where the leaf size becomes comparable to float epsilon values
  • Loading branch information
nyalldawson committed Dec 24, 2019
1 parent 5f848be commit 13d409658fe040ec37c6c12008d972478087c87f
Showing with 63 additions and 17 deletions.
  1. +8 −3 src/core/pal/pal.cpp
  2. +42 −12 src/core/pal/palrtree.h
  3. +6 −1 src/core/pal/problem.cpp
  4. +7 −1 src/core/pal/problem.h
@@ -91,11 +91,16 @@ Layer *Pal::addLayer( QgsAbstractLabelProvider *provider, const QString &layerNa

std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeometry &mapBoundary )
{
// expand out the incoming buffer by 1000x -- that's the visible map extent, yet we may be getting features which exceed this extent
// (while 1000x may seem excessive here, this value is only used for scaling coordinates in the spatial indexes
// and the consequence of inserting coordinates outside this extent is worse than the consequence of setting this value too large.)
const QgsRectangle maxCoordinateExtentForSpatialIndices = extent.buffered( std::max( extent.width(), extent.height() ) * 1000 );

// to store obstacles
PalRtree< FeaturePart > obstacles;
PalRtree< LabelPosition > allCandidatesFirstRound;
PalRtree< FeaturePart > obstacles( maxCoordinateExtentForSpatialIndices );
PalRtree< LabelPosition > allCandidatesFirstRound( maxCoordinateExtentForSpatialIndices );
std::vector< FeaturePart * > allObstacleParts;
std::unique_ptr< Problem > prob = qgis::make_unique< Problem >();
std::unique_ptr< Problem > prob = qgis::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );

double bbx[4];
double bby[4];
@@ -35,6 +35,20 @@ class PalRtree : public RTree<T *, float, 2, float>
{
public:

/**
* Constructor for PalRtree. The \a maxBounds argument specifies the maximum bounding box
* for all coordinates which will be stored in the index.
*/
PalRtree( const QgsRectangle &maxBounds )
: mXMin( maxBounds.xMinimum() )
, mYMin( maxBounds.yMinimum() )
, mXRes( ( std::numeric_limits< float >::max() - 1 ) / ( maxBounds.xMaximum() - maxBounds.xMinimum() ) )
, mYRes( ( std::numeric_limits< float >::max() - 1 ) / ( maxBounds.yMaximum() - maxBounds.yMinimum() ) )
, mMaxBounds( maxBounds )
{

}

/**
* Inserts new \a data into the spatial index, with the specified \a bounds.
*
@@ -43,14 +57,13 @@ class PalRtree : public RTree<T *, float, 2, float>
*/
void insert( T *data, const QgsRectangle &bounds )
{
std::array< float, 4 > scaledBounds = scaleBounds( bounds );
this->Insert(
{
static_cast< float >( bounds.xMinimum() ),
static_cast< float >( bounds.yMinimum() )
scaledBounds[0], scaledBounds[ 1]
},
{
static_cast< float>( bounds.xMaximum() ),
static_cast< float >( bounds.yMaximum() )
scaledBounds[2], scaledBounds[3]
},
data );
}
@@ -63,14 +76,13 @@ class PalRtree : public RTree<T *, float, 2, float>
*/
void remove( T *data, const QgsRectangle &bounds )
{
std::array< float, 4 > scaledBounds = scaleBounds( bounds );
this->Remove(
{
static_cast< float >( bounds.xMinimum() ),
static_cast< float >( bounds.yMinimum() )
scaledBounds[0], scaledBounds[ 1]
},
{
static_cast< float>( bounds.xMaximum() ),
static_cast< float >( bounds.yMaximum() )
scaledBounds[2], scaledBounds[3]
},
data );
}
@@ -82,18 +94,36 @@ class PalRtree : public RTree<T *, float, 2, float>
*/
bool intersects( const QgsRectangle &bounds, const std::function< bool( T *data )> &callback ) const
{
std::array< float, 4 > scaledBounds = scaleBounds( bounds );
this->Search(
{
static_cast< float >( bounds.xMinimum() ),
static_cast< float >( bounds.yMinimum() )
scaledBounds[0], scaledBounds[ 1]
},
{
static_cast< float>( bounds.xMaximum() ),
static_cast< float >( bounds.yMaximum() )
scaledBounds[2], scaledBounds[3]
},
callback );
return true;
}

private:

// Coordinates are scaled inside the index so that they cover the maximum range for float values
double mXMin = 0;
double mYMin = 0;
double mXRes = 1;
double mYRes = 1;
const QgsRectangle mMaxBounds;
std::array<float, 4> scaleBounds( const QgsRectangle &bounds ) const
{
return
{
static_cast< float >( ( std::max( bounds.xMinimum(), mMaxBounds.xMinimum() ) - mXMin ) / mXRes ),
static_cast< float >( ( std::max( bounds.yMinimum(), mMaxBounds.yMinimum() ) - mYMin ) / mYRes ),
static_cast< float >( ( std::min( bounds.xMaximum(), mMaxBounds.xMaximum() ) - mXMin ) / mXRes ),
static_cast< float >( ( std::min( bounds.yMaximum(), mMaxBounds.yMaximum() ) - mYMin ) / mYRes )
};
}
};

#endif
@@ -54,7 +54,12 @@ inline void delete_chain( Chain *chain )
}
}

Problem::Problem() = default;
Problem::Problem( const QgsRectangle &extent )
: mAllCandidatesIndex( extent )
, mActiveCandidatesIndex( extent )
{

}

Problem::~Problem() = default;

@@ -71,7 +71,13 @@ namespace pal
friend class Pal;

public:
Problem();

/**
* Constructor for Problem.
*
* The \a extent argument specifies the bounds of the incoming coordinates.
*/
Problem( const QgsRectangle &extent );

//Problem(char *lorena_file, bool displayAll);

0 comments on commit 13d4096

Please sign in to comment.
You can’t perform that action at this time.