52 , mQuadrant( quadrant )
53 , mDirectionToLine( directionToLine )
55 , mHasObstacleConflict( false )
56 , mUpsideDownCharCount( 0 )
64 while ( this->alpha > 2 * M_PI )
65 this->alpha -= 2 * M_PI;
67 while ( this->alpha < 0 )
68 this->alpha += 2 * M_PI;
70 const double beta = this->alpha + M_PI_2;
72 double dx1, dx2, dy1, dy2;
74 dx1 = std::cos( this->alpha ) *
w;
75 dy1 = std::sin( this->alpha ) *
w;
77 dx2 = std::cos( beta ) *
h;
78 dy2 = std::sin( beta ) *
h;
86 x[2] = x1 + dx1 + dx2;
87 y[2] = y1 + dy1 + dy2;
94 this->alpha > M_PI_2 && this->alpha <= 3 * M_PI_2 )
119 if ( this->alpha < M_PI )
130 for (
int i = 0; i <
nbPoints; ++i )
138 createOuterBoundsGeom();
154 if ( other.mNextPart )
155 mNextPart = std::make_unique< LabelPosition >( *other.mNextPart );
159 mDirectionToLine = other.mDirectionToLine;
160 mQuadrant = other.mQuadrant;
161 mHasObstacleConflict = other.mHasObstacleConflict;
162 mUpsideDownCharCount = other.mUpsideDownCharCount;
164 createOuterBoundsGeom();
169 if ( mPreparedOuterBoundsGeos )
172 mPreparedOuterBoundsGeos =
nullptr;
179 if ( !mOuterBoundsGeos && !
mGeos )
184 if ( GEOSPreparedIntersects_r(
QgsGeosContext::get(), geometry, mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
188 else if ( mNextPart )
190 return mNextPart->intersects( geometry );
193 catch ( GEOSException &e )
195 qWarning(
"GEOS exception: %s", e.what() );
206 if ( !mOuterBoundsGeos && !
mGeos )
211 if ( GEOSPreparedContains_r(
QgsGeosContext::get(), geometry, mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) != 1 )
215 else if ( mNextPart )
217 return mNextPart->within( geometry );
220 catch ( GEOSException &e )
222 qWarning(
"GEOS exception: %s", e.what() );
237 const double minDuplicateSeparation = std::max(
getFeaturePart()->
feature()->thinningSettings().noRepeatDistance(),
246 if ( !noDuplicateDistanceApplies && allowOverlapAtNoCost )
252 if ( bothLabelsAreSinglePart )
257 if ( mOuterBoundsGeos )
267 if ( noDuplicateDistanceApplies )
269 const double minSeparationSquared = minDuplicateSeparation * minDuplicateSeparation;
270 for ( std::size_t i = 0; i < static_cast< std::size_t >(
nbPoints ); ++i )
272 const double lx1 =
x[i];
273 const double ly1 =
y[i];
274 for ( std::size_t j = 0; j < static_cast< std::size_t>( lp->
nbPoints ); ++j )
276 const double lx2 = lp->
x[j];
277 const double ly2 = lp->
y[j];
278 if ( ( lx2 - lx1 ) * ( lx2 - lx1 ) + ( ly2 - ly1 ) * ( ly2 - ly1 ) < minSeparationSquared )
290 if ( noDuplicateDistanceApplies )
294#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
296 mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos, minDuplicateSeparation ) )
304 catch ( GEOSException &e )
306 QgsDebugError( QStringLiteral(
"GEOS exception: %1" ).arg( e.what() ) );
312 if ( !mOuterBoundsGeos && !
mGeos )
318 mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 );
321 catch ( GEOSException &e )
323 qWarning(
"GEOS exception: %s", e.what() );
333 return isInConflictMultiPart( lp );
336bool LabelPosition::isInConflictMultiPart(
const LabelPosition *lp )
const
338 if ( !mMultipartGeos )
339 createMultiPartGeosGeom();
341 if ( !lp->mMultipartGeos )
342 lp->createMultiPartGeosGeom();
351 if ( noRepeatDistanceApplies )
353 const double minSeparation = std::max(
getFeaturePart()->
feature()->thinningSettings().noRepeatDistance(),
358#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
367 catch ( GEOSException &e )
369 QgsDebugError( QStringLiteral(
"GEOS exception: %1" ).arg( e.what() ) );
376 const bool result = ( GEOSPreparedIntersects_r( geosctxt,
preparedMultiPartGeom(), lp->mMultipartGeos ) == 1 );
379 catch ( GEOSException &e )
381 qWarning(
"GEOS exception: %s", e.what() );
390void LabelPosition::createOuterBoundsGeom()
393 if ( outerBounds.isNull() )
398 const double beta = this->
alpha + M_PI_2;
400 const double dx1 = std::cos( this->
alpha ) * outerBounds.width();
401 const double dy1 = std::sin( this->
alpha ) * outerBounds.width();
403 const double dx2 = std::cos( beta ) * outerBounds.height();
404 const double dy2 = std::sin( beta ) * outerBounds.height();
406 mOuterBoundsX.resize( 5 );
407 mOuterBoundsY.resize( 5 );
409 const double x1 =
x[0] + outerBounds.left();
410 const double y1 =
y[0] + outerBounds.top();
412 mOuterBoundsX[0] = x1;
413 mOuterBoundsY[0] = y1;
415 mOuterBoundsX[1] = x1 + dx1;
416 mOuterBoundsY[1] = y1 + dy1;
418 mOuterBoundsX[2] = x1 + dx1 + dx2;
419 mOuterBoundsY[2] = y1 + dy1 + dy2;
421 mOuterBoundsX[3] = x1 + dx2;
422 mOuterBoundsY[3] = y1 + dy2;
424 mOuterBoundsX[4] = mOuterBoundsX[0];
425 mOuterBoundsY[4] = mOuterBoundsY[0];
427 GEOSCoordSequence *coord =
nullptr;
428#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
430 coord = GEOSCoordSeq_copyFromArrays_r( geosctxt, mOuterBoundsX.data(), mOuterBoundsY.data(),
nullptr,
nullptr, 5 );
432 coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
433 for (
int i = 0; i <
nbPoints; ++i )
435 GEOSCoordSeq_setXY_r( geosctxt, coord, i, mOuterBoundsX[i], mOuterBoundsY[i] );
439 mOuterBoundsGeos.reset( GEOSGeom_createPolygon_r( geosctxt, GEOSGeom_createLinearRing_r( geosctxt, coord ),
nullptr, 0 ) );
443 auto xminmax = std::minmax_element( mOuterBoundsX.begin(), mOuterBoundsX.end() );
444 mOuterBoundsXMin = *xminmax.first;
445 mOuterBoundsXMax = *xminmax.second;
446 auto yminmax = std::minmax_element( mOuterBoundsY.begin(), mOuterBoundsY.end() );
447 mOuterBoundsYMin = *yminmax.first;
448 mOuterBoundsYMax = *yminmax.second;
451int LabelPosition::partCount()
const
454 return mNextPart->partCount() + 1;
466 return ( i >= 0 && i < 4 ?
x[i] : -1 );
471 return ( i >= 0 && i < 4 ?
y[i] : -1 );
483 mCost -= int ( mCost );
497 mNextPart->getBoundingBox( amin, amax );
501 amin[0] = std::numeric_limits<double>::max();
502 amax[0] = std::numeric_limits<double>::lowest();
503 amin[1] = std::numeric_limits<double>::max();
504 amax[1] = std::numeric_limits<double>::lowest();
506 for (
int c = 0;
c < 4;
c++ )
508 if ( mOuterBoundsGeos )
510 if ( mOuterBoundsX[
c] < amin[0] )
511 amin[0] = mOuterBoundsX[
c];
512 if ( mOuterBoundsX[
c] > amax[0] )
513 amax[0] = mOuterBoundsX[
c];
514 if ( mOuterBoundsY[
c] < amin[1] )
515 amin[1] = mOuterBoundsY[
c];
516 if ( mOuterBoundsY[
c] > amax[1] )
517 amax[1] = mOuterBoundsY[
c];
521 if (
x[
c] < amin[0] )
523 if (
x[
c] > amax[0] )
525 if (
y[
c] < amin[1] )
527 if (
y[
c] > amax[1] )
538 return QgsRectangle( amin[0], amin[1], amax[0], amax[1] );
543 if ( !mBoundsForConflictIndex.
isNull() )
544 return mBoundsForConflictIndex;
547 mBoundsForConflictIndex = bounds;
550 if ( labelMarginDistance > 0 )
552 mBoundsForConflictIndex.
grow( labelMarginDistance );
556 if ( noRepeatDistance > 0 )
562 const QList< QgsAbstractLabelingEngineRule * > rules =
pal->rules();
565 const QgsRectangle modifiedBounds = rule->modifyCandidateConflictSearchBoundingBox( bounds );
568 return mBoundsForConflictIndex;
573 if ( other->mOuterBoundsGeos )
575 const double x1 = ( mOuterBoundsXMin > other->mOuterBoundsXMin ? mOuterBoundsXMin : other->mOuterBoundsXMin );
576 const double x2 = ( mOuterBoundsXMax < other->mOuterBoundsXMax ? mOuterBoundsXMax : other->mOuterBoundsXMax );
579 const double y1 = ( mOuterBoundsYMin > other->mOuterBoundsYMin ? mOuterBoundsYMin : other->mOuterBoundsYMin );
580 const double y2 = ( mOuterBoundsYMax < other->mOuterBoundsYMax ? mOuterBoundsYMax : other->mOuterBoundsYMax );
585 const double x1 = ( mOuterBoundsXMin > other->
xmin ? mOuterBoundsXMin : other->
xmin );
586 const double x2 = ( mOuterBoundsXMax < other->
xmax ? mOuterBoundsXMax : other->
xmax );
589 const double y1 = ( mOuterBoundsYMin > other->
ymin ? mOuterBoundsYMin : other->
ymin );
590 const double y2 = ( mOuterBoundsYMax < other->
ymax ? mOuterBoundsYMax : other->
ymax );
597 mHasObstacleConflict = conflicts;
599 mNextPart->setConflictsWithObstacle( conflicts );
604 mHasHardConflict = conflicts;
606 mNextPart->setHasHardObstacleConflict( conflicts );
621 if ( !mMultipartGeos )
622 createMultiPartGeosGeom();
624 return mMultipartGeos;
627void LabelPosition::createMultiPartGeosGeom()
const
631 std::vector< const GEOSGeometry * > geometries;
636 if ( !GEOSisEmpty_r( geosctxt, partGeos ) )
637 geometries.emplace_back( partGeos );
641 const std::size_t partCount = geometries.size();
643 for ( std::size_t i = 0; i < partCount; ++i )
645 geomarr[i ] = GEOSGeom_clone_r( geosctxt, geometries[i] );
648 mMultipartGeos = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomarr, partCount );
654 if ( !mMultipartGeos )
655 createMultiPartGeosGeom();
657 if ( !mMultipartPreparedGeos )
661 return mMultipartPreparedGeos;
666 return mPreparedOuterBoundsGeos;
676 bool contains =
false;
680 if ( useOuterBounds && mOuterBoundsGeos )
682 contains = mOuterBoundsX[0] <= xp && mOuterBoundsX[1] >= xp && mOuterBoundsY[0] <= yp && mOuterBoundsY[2] >= yp;
686 contains =
x[0] <= xp &&
x[1] >= xp &&
y[0] <= yp &&
y[2] >= yp;
691 if ( useOuterBounds && mPreparedOuterBoundsGeos )
695 geos::unique_ptr point( GEOSGeom_createPointFromXY_r( geosctxt, xp, yp ) );
696 contains = ( GEOSPreparedContainsProperly_r( geosctxt, mPreparedOuterBoundsGeos, point.get() ) == 1 );
698 catch ( GEOSException & )
709 double distance = -1;
714 if ( useOuterBounds && mOuterBoundsGeos )
716 const double dx = std::max( std::max( mOuterBoundsX[0] - xp, 0.0 ), xp - mOuterBoundsX[1] );
717 const double dy = std::max( std::max( mOuterBoundsY[0] - yp, 0.0 ), yp - mOuterBoundsY[2] );
718 distance = std::sqrt( dx * dx + dy * dy );
722 const double dx = std::max( std::max(
x[0] - xp, 0.0 ), xp -
x[1] );
723 const double dy = std::max( std::max(
y[0] - yp, 0.0 ), yp -
y[2] );
724 distance = std::sqrt( dx * dx + dy * dy );
729 if ( useOuterBounds && mPreparedOuterBoundsGeos )
733 geos::unique_ptr geosPt( GEOSGeom_createPointFromXY_r( geosctxt, xp, yp ) );
735 const geos::coord_sequence_unique_ptr nearestCoord( GEOSPreparedNearestPoints_r( geosctxt, mPreparedOuterBoundsGeos, geosPt.get() ) );
738 unsigned int nPoints = 0;
739 GEOSCoordSeq_getSize_r( geosctxt, nearestCoord.get(), &nPoints );
746 ( void )GEOSCoordSeq_getXY_r( geosctxt, nearestCoord.get(), 0, &nx, &ny );
750 catch ( GEOSException &e )
752 qWarning(
"GEOS exception: %s", e.what() );
764 if ( mNextPart && distance > 0 )
765 return std::min( distance, mNextPart->getDistanceToPoint( xp, yp, useOuterBounds ) );
773 if ( !mOuterBoundsGeos && !
mGeos )
782 if ( GEOSPreparedIntersects_r( geosctxt, line->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
786 else if ( mNextPart )
788 return mNextPart->crossesLine( line );
791 catch ( GEOSException &e )
793 qWarning(
"GEOS exception: %s", e.what() );
804 if ( !mOuterBoundsGeos && !
mGeos )
807 if ( !polygon->
mGeos )
813 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1
814 && GEOSPreparedContains_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) != 1 )
818 else if ( mNextPart )
820 return mNextPart->crossesBoundary( polygon );
823 catch ( GEOSException &e )
825 qWarning(
"GEOS exception: %s", e.what() );
836 const double totalCost = polygonIntersectionCostForParts( polygon );
837 const int n = partCount();
838 return std::ceil( totalCost / n );
844 if ( !mOuterBoundsGeos && !
mGeos )
847 if ( !polygon->
mGeos )
853 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
858 catch ( GEOSException &e )
860 qWarning(
"GEOS exception: %s", e.what() );
866 return mNextPart->intersectsWithPolygon( polygon );
874double LabelPosition::polygonIntersectionCostForParts(
PointSet *polygon )
const
877 if ( !mOuterBoundsGeos && !
mGeos )
880 if ( !polygon->
mGeos )
887 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
895 for (
int i = 0; i < 4; ++i )
900 for (
int a = 0; a < 2; ++a )
904 px = (
x[i] +
x[( i + 1 ) % 4] ) / 2.0;
905 py = (
y[i] +
y[( i + 1 ) % 4] ) / 2.0;
909 px = (
x[0] +
x[2] ) / 2.0;
910 py = (
y[0] +
y[2] ) / 2.0;
917 catch ( GEOSException &e )
919 qWarning(
"GEOS exception: %s", e.what() );
928 cost += mNextPart->polygonIntersectionCostForParts( polygon );
936 double angleDiff = 0.0;
937 double angleLast = 0.0;
943 double diff = std::fabs( tmp->
getAlpha() - angleLast );
944 if ( diff > 2 * M_PI )
946 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 labelMarginDistance() const
Returns the minimum distance (in label units) between labels for this feature and other labels.
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 grow(double delta)
Grows the rectangle in place by the specified amount.
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.
Represents a part of a label feature.
bool onlyShowUprightLabels() const
Returns true if feature's label must be displayed upright.
QgsLabelFeature * feature()
Returns the parent feature.
Layer * layer()
Returns the layer that feature belongs to.
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.
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.
bool isCurved() const
Returns true if the layer has curved labels.
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.
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