51 , mDirectionToLine( directionToLine )
60 while ( this->alpha > 2 * M_PI )
61 this->alpha -= 2 * M_PI;
63 while ( this->alpha < 0 )
64 this->alpha += 2 * M_PI;
66 const double beta = this->alpha + M_PI_2;
68 double dx1, dx2, dy1, dy2;
70 dx1 = std::cos( this->alpha ) *
w;
71 dy1 = std::sin( this->alpha ) *
w;
73 dx2 = std::cos( beta ) *
h;
74 dy2 = std::sin( beta ) *
h;
82 x[2] = x1 + dx1 + dx2;
83 y[2] = y1 + dy1 + dy2;
89 if ( !
feature->layer()->isCurved() &&
90 this->alpha > M_PI_2 && this->alpha <= 3 * M_PI_2 )
92 if (
feature->onlyShowUprightLabels() )
115 if ( this->alpha < M_PI )
126 for (
int i = 0; i <
nbPoints; ++i )
134 createOuterBoundsGeom();
150 if ( other.mNextPart )
151 mNextPart = std::make_unique< LabelPosition >( *other.mNextPart );
155 mDirectionToLine = other.mDirectionToLine;
156 mQuadrant = other.mQuadrant;
157 mHasObstacleConflict = other.mHasObstacleConflict;
158 mUpsideDownCharCount = other.mUpsideDownCharCount;
160 createOuterBoundsGeom();
165 if ( mPreparedOuterBoundsGeos )
168 mPreparedOuterBoundsGeos =
nullptr;
175 if ( !mOuterBoundsGeos && !
mGeos )
180 if ( GEOSPreparedIntersects_r(
QgsGeosContext::get(), geometry, mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
184 else if ( mNextPart )
186 return mNextPart->intersects( geometry );
189 catch ( QgsGeosException &e )
191 qWarning(
"GEOS exception: %s", e.what() );
202 if ( !mOuterBoundsGeos && !
mGeos )
207 if ( GEOSPreparedContains_r(
QgsGeosContext::get(), geometry, mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) != 1 )
211 else if ( mNextPart )
213 return mNextPart->within( geometry );
216 catch ( QgsGeosException &e )
218 qWarning(
"GEOS exception: %s", e.what() );
233 const double minDuplicateSeparation = std::max(
getFeaturePart()->
feature()->thinningSettings().noRepeatDistance(),
242 if ( !noDuplicateDistanceApplies && allowOverlapAtNoCost )
248 if ( bothLabelsAreSinglePart )
253 if ( mOuterBoundsGeos )
263 if ( noDuplicateDistanceApplies )
265 const double minSeparationSquared = minDuplicateSeparation * minDuplicateSeparation;
266 for ( std::size_t i = 0; i < static_cast< std::size_t >(
nbPoints ); ++i )
268 const double lx1 =
x[i];
269 const double ly1 =
y[i];
270 for ( std::size_t j = 0; j < static_cast< std::size_t>( lp->
nbPoints ); ++j )
272 const double lx2 = lp->
x[j];
273 const double ly2 = lp->
y[j];
274 if ( ( lx2 - lx1 ) * ( lx2 - lx1 ) + ( ly2 - ly1 ) * ( ly2 - ly1 ) < minSeparationSquared )
286 if ( noDuplicateDistanceApplies )
290#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
292 mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos, minDuplicateSeparation ) )
300 catch ( QgsGeosException &e )
302 QgsDebugError( QStringLiteral(
"GEOS exception: %1" ).arg( e.what() ) );
308 if ( !mOuterBoundsGeos && !
mGeos )
314 mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 );
317 catch ( QgsGeosException &e )
319 qWarning(
"GEOS exception: %s", e.what() );
329 return isInConflictMultiPart( lp );
332bool LabelPosition::isInConflictMultiPart(
const LabelPosition *lp )
const
334 if ( !mMultipartGeos )
335 createMultiPartGeosGeom();
337 if ( !lp->mMultipartGeos )
338 lp->createMultiPartGeosGeom();
347 if ( noRepeatDistanceApplies )
349 const double minSeparation = std::max(
getFeaturePart()->
feature()->thinningSettings().noRepeatDistance(),
354#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
363 catch ( QgsGeosException &e )
365 QgsDebugError( QStringLiteral(
"GEOS exception: %1" ).arg( e.what() ) );
372 const bool result = ( GEOSPreparedIntersects_r( geosctxt,
preparedMultiPartGeom(), lp->mMultipartGeos ) == 1 );
375 catch ( QgsGeosException &e )
377 qWarning(
"GEOS exception: %s", e.what() );
386void LabelPosition::createOuterBoundsGeom()
389 if ( outerBounds.isNull() )
394 const double beta = this->
alpha + M_PI_2;
396 const double dx1 = std::cos( this->
alpha ) * outerBounds.width();
397 const double dy1 = std::sin( this->
alpha ) * outerBounds.width();
399 const double dx2 = std::cos( beta ) * outerBounds.height();
400 const double dy2 = std::sin( beta ) * outerBounds.height();
402 mOuterBoundsX.resize( 5 );
403 mOuterBoundsY.resize( 5 );
405 const double x1 =
x[0] + outerBounds.left();
406 const double y1 =
y[0] + outerBounds.top();
408 mOuterBoundsX[0] = x1;
409 mOuterBoundsY[0] = y1;
411 mOuterBoundsX[1] = x1 + dx1;
412 mOuterBoundsY[1] = y1 + dy1;
414 mOuterBoundsX[2] = x1 + dx1 + dx2;
415 mOuterBoundsY[2] = y1 + dy1 + dy2;
417 mOuterBoundsX[3] = x1 + dx2;
418 mOuterBoundsY[3] = y1 + dy2;
420 mOuterBoundsX[4] = mOuterBoundsX[0];
421 mOuterBoundsY[4] = mOuterBoundsY[0];
423 GEOSCoordSequence *coord =
nullptr;
424#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
426 coord = GEOSCoordSeq_copyFromArrays_r( geosctxt, mOuterBoundsX.data(), mOuterBoundsY.data(),
nullptr,
nullptr, 5 );
428 coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
429 for (
int i = 0; i <
nbPoints; ++i )
431 GEOSCoordSeq_setXY_r( geosctxt, coord, i, mOuterBoundsX[i], mOuterBoundsY[i] );
435 mOuterBoundsGeos.reset( GEOSGeom_createPolygon_r( geosctxt, GEOSGeom_createLinearRing_r( geosctxt, coord ),
nullptr, 0 ) );
439 auto xminmax = std::minmax_element( mOuterBoundsX.begin(), mOuterBoundsX.end() );
440 mOuterBoundsXMin = *xminmax.first;
441 mOuterBoundsXMax = *xminmax.second;
442 auto yminmax = std::minmax_element( mOuterBoundsY.begin(), mOuterBoundsY.end() );
443 mOuterBoundsYMin = *yminmax.first;
444 mOuterBoundsYMax = *yminmax.second;
447int LabelPosition::partCount()
const
450 return mNextPart->partCount() + 1;
462 return ( i >= 0 && i < 4 ?
x[i] : -1 );
467 return ( i >= 0 && i < 4 ?
y[i] : -1 );
479 mCost -= int ( mCost );
493 mNextPart->getBoundingBox( amin, amax );
497 amin[0] = std::numeric_limits<double>::max();
498 amax[0] = std::numeric_limits<double>::lowest();
499 amin[1] = std::numeric_limits<double>::max();
500 amax[1] = std::numeric_limits<double>::lowest();
502 for (
int c = 0;
c < 4;
c++ )
504 if ( mOuterBoundsGeos )
506 if ( mOuterBoundsX[
c] < amin[0] )
507 amin[0] = mOuterBoundsX[
c];
508 if ( mOuterBoundsX[
c] > amax[0] )
509 amax[0] = mOuterBoundsX[
c];
510 if ( mOuterBoundsY[
c] < amin[1] )
511 amin[1] = mOuterBoundsY[
c];
512 if ( mOuterBoundsY[
c] > amax[1] )
513 amax[1] = mOuterBoundsY[
c];
517 if (
x[
c] < amin[0] )
519 if (
x[
c] > amax[0] )
521 if (
y[
c] < amin[1] )
523 if (
y[
c] > amax[1] )
534 return QgsRectangle( amin[0], amin[1], amax[0], amax[1] );
539 if ( !mBoundsForConflictIndex.isNull() )
540 return mBoundsForConflictIndex;
543 mBoundsForConflictIndex = bounds;
545 const double labelMarginDistance =
feature->feature()->thinningSettings().labelMarginDistance();
546 if ( labelMarginDistance > 0 )
548 mBoundsForConflictIndex.grow( labelMarginDistance );
551 const double noRepeatDistance =
feature->feature()->thinningSettings().noRepeatDistance();
552 if ( noRepeatDistance > 0 )
558 const QList< QgsAbstractLabelingEngineRule * > rules =
pal->rules();
561 const QgsRectangle modifiedBounds = rule->modifyCandidateConflictSearchBoundingBox( bounds );
564 return mBoundsForConflictIndex;
569 if ( other->mOuterBoundsGeos )
571 const double x1 = ( mOuterBoundsXMin > other->mOuterBoundsXMin ? mOuterBoundsXMin : other->mOuterBoundsXMin );
572 const double x2 = ( mOuterBoundsXMax < other->mOuterBoundsXMax ? mOuterBoundsXMax : other->mOuterBoundsXMax );
575 const double y1 = ( mOuterBoundsYMin > other->mOuterBoundsYMin ? mOuterBoundsYMin : other->mOuterBoundsYMin );
576 const double y2 = ( mOuterBoundsYMax < other->mOuterBoundsYMax ? mOuterBoundsYMax : other->mOuterBoundsYMax );
581 const double x1 = ( mOuterBoundsXMin > other->
xmin ? mOuterBoundsXMin : other->
xmin );
582 const double x2 = ( mOuterBoundsXMax < other->
xmax ? mOuterBoundsXMax : other->
xmax );
585 const double y1 = ( mOuterBoundsYMin > other->
ymin ? mOuterBoundsYMin : other->
ymin );
586 const double y2 = ( mOuterBoundsYMax < other->
ymax ? mOuterBoundsYMax : other->
ymax );
593 mHasObstacleConflict = conflicts;
595 mNextPart->setConflictsWithObstacle( conflicts );
600 mHasHardConflict = conflicts;
602 mNextPart->setHasHardObstacleConflict( conflicts );
617 if ( !mMultipartGeos )
618 createMultiPartGeosGeom();
620 return mMultipartGeos;
623void LabelPosition::createMultiPartGeosGeom()
const
627 std::vector< const GEOSGeometry * > geometries;
632 if ( !GEOSisEmpty_r( geosctxt, partGeos ) )
633 geometries.emplace_back( partGeos );
637 const std::size_t partCount = geometries.size();
639 for ( std::size_t i = 0; i < partCount; ++i )
641 geomarr[i ] = GEOSGeom_clone_r( geosctxt, geometries[i] );
644 mMultipartGeos = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomarr, partCount );
650 if ( !mMultipartGeos )
651 createMultiPartGeosGeom();
653 if ( !mMultipartPreparedGeos )
657 return mMultipartPreparedGeos;
662 return mPreparedOuterBoundsGeos;
672 bool contains =
false;
676 if ( useOuterBounds && mOuterBoundsGeos )
678 contains = mOuterBoundsX[0] <= xp && mOuterBoundsX[1] >= xp && mOuterBoundsY[0] <= yp && mOuterBoundsY[2] >= yp;
682 contains =
x[0] <= xp &&
x[1] >= xp &&
y[0] <= yp &&
y[2] >= yp;
687 if ( useOuterBounds && mPreparedOuterBoundsGeos )
692 contains = ( GEOSPreparedContainsProperly_r( geosctxt, mPreparedOuterBoundsGeos, point.get() ) == 1 );
694 catch ( QgsGeosException & )
705 double distance = -1;
710 if ( useOuterBounds && mOuterBoundsGeos )
712 const double dx = std::max( std::max( mOuterBoundsX[0] - xp, 0.0 ), xp - mOuterBoundsX[1] );
713 const double dy = std::max( std::max( mOuterBoundsY[0] - yp, 0.0 ), yp - mOuterBoundsY[2] );
714 distance = std::sqrt( dx * dx + dy * dy );
718 const double dx = std::max( std::max(
x[0] - xp, 0.0 ), xp -
x[1] );
719 const double dy = std::max( std::max(
y[0] - yp, 0.0 ), yp -
y[2] );
720 distance = std::sqrt( dx * dx + dy * dy );
725 if ( useOuterBounds && mPreparedOuterBoundsGeos )
729 geos::unique_ptr geosPt( GEOSGeom_createPointFromXY_r( geosctxt, xp, yp ) );
734 unsigned int nPoints = 0;
735 GEOSCoordSeq_getSize_r( geosctxt, nearestCoord.get(), &nPoints );
742 ( void )GEOSCoordSeq_getXY_r( geosctxt, nearestCoord.get(), 0, &nx, &ny );
746 catch ( QgsGeosException &e )
748 qWarning(
"GEOS exception: %s", e.what() );
760 if ( mNextPart && distance > 0 )
761 return std::min( distance, mNextPart->getDistanceToPoint( xp, yp, useOuterBounds ) );
769 if ( !mOuterBoundsGeos && !
mGeos )
778 if ( GEOSPreparedIntersects_r( geosctxt, line->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
782 else if ( mNextPart )
784 return mNextPart->crossesLine( line );
787 catch ( QgsGeosException &e )
789 qWarning(
"GEOS exception: %s", e.what() );
800 if ( !mOuterBoundsGeos && !
mGeos )
803 if ( !polygon->
mGeos )
809 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1
810 && GEOSPreparedContains_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) != 1 )
814 else if ( mNextPart )
816 return mNextPart->crossesBoundary( polygon );
819 catch ( QgsGeosException &e )
821 qWarning(
"GEOS exception: %s", e.what() );
832 const double totalCost = polygonIntersectionCostForParts( polygon );
833 const int n = partCount();
834 return std::ceil( totalCost / n );
840 if ( !mOuterBoundsGeos && !
mGeos )
843 if ( !polygon->
mGeos )
849 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
854 catch ( QgsGeosException &e )
856 qWarning(
"GEOS exception: %s", e.what() );
862 return mNextPart->intersectsWithPolygon( polygon );
870double LabelPosition::polygonIntersectionCostForParts(
PointSet *polygon )
const
873 if ( !mOuterBoundsGeos && !
mGeos )
876 if ( !polygon->
mGeos )
883 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
891 for (
int i = 0; i < 4; ++i )
896 for (
int a = 0; a < 2; ++a )
900 px = (
x[i] +
x[( i + 1 ) % 4] ) / 2.0;
901 py = (
y[i] +
y[( i + 1 ) % 4] ) / 2.0;
905 px = (
x[0] +
x[2] ) / 2.0;
906 py = (
y[0] +
y[2] ) / 2.0;
913 catch ( QgsGeosException &e )
915 qWarning(
"GEOS exception: %s", e.what() );
924 cost += mNextPart->polygonIntersectionCostForParts( polygon );
932 double angleDiff = 0.0;
933 double angleLast = 0.0;
939 double diff = std::fabs( tmp->
getAlpha() - angleLast );
940 if ( diff > 2 * M_PI )
942 diff = std::min( diff, 2 * M_PI - diff );
A rtree spatial index for use in the pal labeling engine.
void insert(T *data, const QgsRectangle &bounds)
Inserts new data into the spatial index, with the specified bounds.
void remove(T *data, const QgsRectangle &bounds)
Removes existing data from the spatial index, with the specified bounds.
LabelQuadrantPosition
Label quadrant positions.
@ AllowOverlapAtNoCost
Labels may freely overlap other labels, at no cost.
Abstract base class for labeling engine rules.
static double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
static GEOSContextHandle_t get()
Returns a thread local instance of a GEOS context, safe for use in the current thread.
double noRepeatDistance() const
Returns the minimum distance (in label units) between labels for this feature and other labels with t...
const QgsLabelFeatureThinningSettings & thinningSettings() const
Returns the thinning settings for this label.
Qgis::LabelOverlapHandling overlapHandling() const
Returns the technique to use for handling overlapping labels for the feature.
QString labelText() const
Text of the label.
QRectF outerBounds() const
Returns the extreme outer bounds of the label feature, including any surrounding content like borders...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
A rectangle specified with double values.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
QgsLabelFeature * feature()
Returns the parent feature.
LabelPosition is a candidate feature label position.
bool outerBoundingBoxIntersects(const LabelPosition *other) const
Returns true if the outer bounding box of this pointset intersects the outer bounding box of another ...
QgsRectangle outerBoundingBox() const
Returns bounding box.
bool intersectsWithPolygon(PointSet *polygon) const
Returns true if any intersection between polygon and position exists.
bool isInConflict(const LabelPosition *ls) const
Check whether or not this overlap with another labelPosition.
double getAlpha() const
Returns the angle to rotate text (in radians).
double angleDifferential()
Returns the angle differential of all LabelPosition parts.
double alpha
Rotation in radians.
LabelPosition(int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, LabelDirectionToLine directionToLine=LabelDirectionToLine::SameDirection, Qgis::LabelQuadrantPosition quadrant=Qgis::LabelQuadrantPosition::Over)
create a new LabelPosition
const GEOSGeometry * multiPartGeom() const
Returns a GEOS representation of all label parts as a multipolygon.
~LabelPosition() override
bool crossesLine(PointSet *line) const
Returns true if this label crosses the specified line.
int getId() const
Returns the id.
const GEOSPreparedGeometry * preparedOuterBoundsGeom() const
Returns the prepared outer boundary geometry.
void validateCost()
Make sure the cost is less than 1.
QgsRectangle boundingBoxForCandidateConflicts(Pal *pal) const
Returns the bounding box to use for candidate conflicts.
void removeFromIndex(PalRtree< LabelPosition > &index, Pal *pal)
Removes the label position from the specified index.
double cost() const
Returns the candidate label position's geographical cost.
void setConflictsWithObstacle(bool conflicts)
Sets whether the position is marked as conflicting with an obstacle feature.
bool intersects(const GEOSPreparedGeometry *geometry)
Returns true if the label position intersects a geometry.
void setHasHardObstacleConflict(bool conflicts)
Sets whether the position is marked as having a hard conflict with an obstacle feature.
bool crossesBoundary(PointSet *polygon) const
Returns true if this label crosses the boundary of the specified polygon.
LabelDirectionToLine
Label directions in relation to line or polygon ring directions.
double getDistanceToPoint(double xp, double yp, bool useOuterBounds) const
Gets distance from this label to a point.
Qgis::LabelQuadrantPosition quadrant() const
Returns the quadrant associated with this label position.
FeaturePart * getFeaturePart() const
Returns the feature corresponding to this labelposition.
const GEOSPreparedGeometry * preparedMultiPartGeom() const
Returns a prepared GEOS representation of all label parts as a multipolygon.
double getX(int i=0) const
Returns the down-left x coordinate.
void getBoundingBox(double amin[2], double amax[2]) const
Returns bounding box - amin: xmin,ymin - amax: xmax,ymax.
double getY(int i=0) const
Returns the down-left y coordinate.
bool within(const GEOSPreparedGeometry *geometry)
Returns true if the label position is within a geometry.
int polygonIntersectionCost(PointSet *polygon) const
Returns cost of position intersection with polygon (testing area of intersection and center).
LabelPosition * nextPart() const
Returns the next part of this label position (i.e.
void insertIntoIndex(PalRtree< LabelPosition > &index, Pal *pal)
Inserts the label position into the specified index.
The underlying raw pal geometry class.
friend class LabelPosition
void createGeosGeom() const
bool boundingBoxIntersects(const PointSet *other) const
Returns true if the bounding box of this pointset intersects the bounding box of another pointset.
const GEOSPreparedGeometry * preparedGeom() const
const GEOSGeometry * geos() const
Returns the point set's GEOS geometry.
double minDistanceToPoint(double px, double py, double *rx=nullptr, double *ry=nullptr) const
Returns the squared minimum distance between the point set geometry and the point (px,...
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
std::unique_ptr< GEOSCoordSequence, GeosDeleter > coord_sequence_unique_ptr
Scoped GEOS coordinate sequence pointer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
#define QgsDebugError(str)
struct GEOSGeom_t GEOSGeometry