55 , mDirectionToLine( directionToLine )
57 , mHasObstacleConflict( false )
58 , mUpsideDownCharCount( 0 )
66 while ( this->alpha > 2 * M_PI )
67 this->alpha -= 2 * M_PI;
69 while ( this->alpha < 0 )
70 this->alpha += 2 * M_PI;
72 const double beta = this->alpha + M_PI_2;
74 double dx1, dx2, dy1, dy2;
76 dx1 = std::cos( this->alpha ) *
w;
77 dy1 = std::sin( this->alpha ) *
w;
79 dx2 = std::cos( beta ) *
h;
80 dy2 = std::sin( beta ) *
h;
88 x[2] = x1 + dx1 + dx2;
89 y[2] = y1 + dy1 + dy2;
95 if ( !
feature->layer()->isCurved() &&
96 this->alpha > M_PI_2 && this->alpha <= 3 * M_PI_2 )
98 if (
feature->onlyShowUprightLabels() )
121 if ( this->alpha < M_PI )
132 for (
int i = 0; i <
nbPoints; ++i )
140 createOuterBoundsGeom();
156 if ( other.mNextPart )
157 mNextPart = std::make_unique< LabelPosition >( *other.mNextPart );
161 mDirectionToLine = other.mDirectionToLine;
162 mQuadrant = other.mQuadrant;
163 mHasObstacleConflict = other.mHasObstacleConflict;
164 mUpsideDownCharCount = other.mUpsideDownCharCount;
166 createOuterBoundsGeom();
171 if ( mPreparedOuterBoundsGeos )
174 mPreparedOuterBoundsGeos =
nullptr;
181 if ( !mOuterBoundsGeos && !
mGeos )
186 if ( GEOSPreparedIntersects_r(
QgsGeosContext::get(), geometry, mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
190 else if ( mNextPart )
192 return mNextPart->intersects( geometry );
195 catch ( QgsGeosException &e )
197 qWarning(
"GEOS exception: %s", e.what() );
208 if ( !mOuterBoundsGeos && !
mGeos )
213 if ( GEOSPreparedContains_r(
QgsGeosContext::get(), geometry, mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) != 1 )
217 else if ( mNextPart )
219 return mNextPart->within( geometry );
222 catch ( QgsGeosException &e )
224 qWarning(
"GEOS exception: %s", e.what() );
239 const double minDuplicateSeparation = std::max(
getFeaturePart()->
feature()->thinningSettings().noRepeatDistance(),
248 if ( !noDuplicateDistanceApplies && allowOverlapAtNoCost )
254 if ( bothLabelsAreSinglePart )
259 if ( mOuterBoundsGeos )
269 if ( noDuplicateDistanceApplies )
271 const double minSeparationSquared = minDuplicateSeparation * minDuplicateSeparation;
272 for ( std::size_t i = 0; i < static_cast< std::size_t >(
nbPoints ); ++i )
274 const double lx1 =
x[i];
275 const double ly1 =
y[i];
276 for ( std::size_t j = 0; j < static_cast< std::size_t>( lp->
nbPoints ); ++j )
278 const double lx2 = lp->
x[j];
279 const double ly2 = lp->
y[j];
280 if ( ( lx2 - lx1 ) * ( lx2 - lx1 ) + ( ly2 - ly1 ) * ( ly2 - ly1 ) < minSeparationSquared )
292 if ( noDuplicateDistanceApplies )
296#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
298 mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos, minDuplicateSeparation ) )
306 catch ( QgsGeosException &e )
308 QgsDebugError( QStringLiteral(
"GEOS exception: %1" ).arg( e.what() ) );
314 if ( !mOuterBoundsGeos && !
mGeos )
320 mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 );
323 catch ( QgsGeosException &e )
325 qWarning(
"GEOS exception: %s", e.what() );
335 return isInConflictMultiPart( lp );
338bool LabelPosition::isInConflictMultiPart(
const LabelPosition *lp )
const
340 if ( !mMultipartGeos )
341 createMultiPartGeosGeom();
343 if ( !lp->mMultipartGeos )
344 lp->createMultiPartGeosGeom();
353 if ( noRepeatDistanceApplies )
355 const double minSeparation = std::max(
getFeaturePart()->
feature()->thinningSettings().noRepeatDistance(),
360#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
369 catch ( QgsGeosException &e )
371 QgsDebugError( QStringLiteral(
"GEOS exception: %1" ).arg( e.what() ) );
378 const bool result = ( GEOSPreparedIntersects_r( geosctxt,
preparedMultiPartGeom(), lp->mMultipartGeos ) == 1 );
381 catch ( QgsGeosException &e )
383 qWarning(
"GEOS exception: %s", e.what() );
392void LabelPosition::createOuterBoundsGeom()
395 if ( outerBounds.isNull() )
400 const double beta = this->
alpha + M_PI_2;
402 const double dx1 = std::cos( this->
alpha ) * outerBounds.width();
403 const double dy1 = std::sin( this->
alpha ) * outerBounds.width();
405 const double dx2 = std::cos( beta ) * outerBounds.height();
406 const double dy2 = std::sin( beta ) * outerBounds.height();
408 mOuterBoundsX.resize( 5 );
409 mOuterBoundsY.resize( 5 );
411 const double x1 =
x[0] + outerBounds.left();
412 const double y1 =
y[0] + outerBounds.top();
414 mOuterBoundsX[0] = x1;
415 mOuterBoundsY[0] = y1;
417 mOuterBoundsX[1] = x1 + dx1;
418 mOuterBoundsY[1] = y1 + dy1;
420 mOuterBoundsX[2] = x1 + dx1 + dx2;
421 mOuterBoundsY[2] = y1 + dy1 + dy2;
423 mOuterBoundsX[3] = x1 + dx2;
424 mOuterBoundsY[3] = y1 + dy2;
426 mOuterBoundsX[4] = mOuterBoundsX[0];
427 mOuterBoundsY[4] = mOuterBoundsY[0];
429 GEOSCoordSequence *coord =
nullptr;
430#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
432 coord = GEOSCoordSeq_copyFromArrays_r( geosctxt, mOuterBoundsX.data(), mOuterBoundsY.data(),
nullptr,
nullptr, 5 );
434 coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
435 for (
int i = 0; i <
nbPoints; ++i )
437 GEOSCoordSeq_setXY_r( geosctxt, coord, i, mOuterBoundsX[i], mOuterBoundsY[i] );
441 mOuterBoundsGeos.reset( GEOSGeom_createPolygon_r( geosctxt, GEOSGeom_createLinearRing_r( geosctxt, coord ),
nullptr, 0 ) );
445 auto xminmax = std::minmax_element( mOuterBoundsX.begin(), mOuterBoundsX.end() );
446 mOuterBoundsXMin = *xminmax.first;
447 mOuterBoundsXMax = *xminmax.second;
448 auto yminmax = std::minmax_element( mOuterBoundsY.begin(), mOuterBoundsY.end() );
449 mOuterBoundsYMin = *yminmax.first;
450 mOuterBoundsYMax = *yminmax.second;
453int LabelPosition::partCount()
const
456 return mNextPart->partCount() + 1;
468 return ( i >= 0 && i < 4 ?
x[i] : -1 );
473 return ( i >= 0 && i < 4 ?
y[i] : -1 );
485 mCost -= int ( mCost );
499 mNextPart->getBoundingBox( amin, amax );
503 amin[0] = std::numeric_limits<double>::max();
504 amax[0] = std::numeric_limits<double>::lowest();
505 amin[1] = std::numeric_limits<double>::max();
506 amax[1] = std::numeric_limits<double>::lowest();
508 for (
int c = 0;
c < 4;
c++ )
510 if ( mOuterBoundsGeos )
512 if ( mOuterBoundsX[
c] < amin[0] )
513 amin[0] = mOuterBoundsX[
c];
514 if ( mOuterBoundsX[
c] > amax[0] )
515 amax[0] = mOuterBoundsX[
c];
516 if ( mOuterBoundsY[
c] < amin[1] )
517 amin[1] = mOuterBoundsY[
c];
518 if ( mOuterBoundsY[
c] > amax[1] )
519 amax[1] = mOuterBoundsY[
c];
523 if (
x[
c] < amin[0] )
525 if (
x[
c] > amax[0] )
527 if (
y[
c] < amin[1] )
529 if (
y[
c] > amax[1] )
540 return QgsRectangle( amin[0], amin[1], amax[0], amax[1] );
545 if ( !mBoundsForConflictIndex.isNull() )
546 return mBoundsForConflictIndex;
549 mBoundsForConflictIndex = bounds;
551 const double labelMarginDistance =
feature->feature()->thinningSettings().labelMarginDistance();
552 if ( labelMarginDistance > 0 )
554 mBoundsForConflictIndex.grow( labelMarginDistance );
557 const double noRepeatDistance =
feature->feature()->thinningSettings().noRepeatDistance();
558 if ( noRepeatDistance > 0 )
564 const QList< QgsAbstractLabelingEngineRule * > rules =
pal->rules();
567 const QgsRectangle modifiedBounds = rule->modifyCandidateConflictSearchBoundingBox( bounds );
570 return mBoundsForConflictIndex;
575 if ( other->mOuterBoundsGeos )
577 const double x1 = ( mOuterBoundsXMin > other->mOuterBoundsXMin ? mOuterBoundsXMin : other->mOuterBoundsXMin );
578 const double x2 = ( mOuterBoundsXMax < other->mOuterBoundsXMax ? mOuterBoundsXMax : other->mOuterBoundsXMax );
581 const double y1 = ( mOuterBoundsYMin > other->mOuterBoundsYMin ? mOuterBoundsYMin : other->mOuterBoundsYMin );
582 const double y2 = ( mOuterBoundsYMax < other->mOuterBoundsYMax ? mOuterBoundsYMax : other->mOuterBoundsYMax );
587 const double x1 = ( mOuterBoundsXMin > other->
xmin ? mOuterBoundsXMin : other->
xmin );
588 const double x2 = ( mOuterBoundsXMax < other->
xmax ? mOuterBoundsXMax : other->
xmax );
591 const double y1 = ( mOuterBoundsYMin > other->
ymin ? mOuterBoundsYMin : other->
ymin );
592 const double y2 = ( mOuterBoundsYMax < other->
ymax ? mOuterBoundsYMax : other->
ymax );
599 mHasObstacleConflict = conflicts;
601 mNextPart->setConflictsWithObstacle( conflicts );
606 mHasHardConflict = conflicts;
608 mNextPart->setHasHardObstacleConflict( conflicts );
623 if ( !mMultipartGeos )
624 createMultiPartGeosGeom();
626 return mMultipartGeos;
629void LabelPosition::createMultiPartGeosGeom()
const
633 std::vector< const GEOSGeometry * > geometries;
638 if ( !GEOSisEmpty_r( geosctxt, partGeos ) )
639 geometries.emplace_back( partGeos );
643 const std::size_t partCount = geometries.size();
645 for ( std::size_t i = 0; i < partCount; ++i )
647 geomarr[i ] = GEOSGeom_clone_r( geosctxt, geometries[i] );
650 mMultipartGeos = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomarr, partCount );
656 if ( !mMultipartGeos )
657 createMultiPartGeosGeom();
659 if ( !mMultipartPreparedGeos )
663 return mMultipartPreparedGeos;
668 return mPreparedOuterBoundsGeos;
678 bool contains =
false;
682 if ( useOuterBounds && mOuterBoundsGeos )
684 contains = mOuterBoundsX[0] <= xp && mOuterBoundsX[1] >= xp && mOuterBoundsY[0] <= yp && mOuterBoundsY[2] >= yp;
688 contains =
x[0] <= xp &&
x[1] >= xp &&
y[0] <= yp &&
y[2] >= yp;
693 if ( useOuterBounds && mPreparedOuterBoundsGeos )
698 contains = ( GEOSPreparedContainsProperly_r( geosctxt, mPreparedOuterBoundsGeos, point.get() ) == 1 );
700 catch ( QgsGeosException & )
711 double distance = -1;
716 if ( useOuterBounds && mOuterBoundsGeos )
718 const double dx = std::max( std::max( mOuterBoundsX[0] - xp, 0.0 ), xp - mOuterBoundsX[1] );
719 const double dy = std::max( std::max( mOuterBoundsY[0] - yp, 0.0 ), yp - mOuterBoundsY[2] );
720 distance = std::sqrt( dx * dx + dy * dy );
724 const double dx = std::max( std::max(
x[0] - xp, 0.0 ), xp -
x[1] );
725 const double dy = std::max( std::max(
y[0] - yp, 0.0 ), yp -
y[2] );
726 distance = std::sqrt( dx * dx + dy * dy );
731 if ( useOuterBounds && mPreparedOuterBoundsGeos )
735 geos::unique_ptr geosPt( GEOSGeom_createPointFromXY_r( geosctxt, xp, yp ) );
740 unsigned int nPoints = 0;
741 GEOSCoordSeq_getSize_r( geosctxt, nearestCoord.get(), &nPoints );
748 ( void )GEOSCoordSeq_getXY_r( geosctxt, nearestCoord.get(), 0, &nx, &ny );
752 catch ( QgsGeosException &e )
754 qWarning(
"GEOS exception: %s", e.what() );
766 if ( mNextPart && distance > 0 )
767 return std::min( distance, mNextPart->getDistanceToPoint( xp, yp, useOuterBounds ) );
775 if ( !mOuterBoundsGeos && !
mGeos )
784 if ( GEOSPreparedIntersects_r( geosctxt, line->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
788 else if ( mNextPart )
790 return mNextPart->crossesLine( line );
793 catch ( QgsGeosException &e )
795 qWarning(
"GEOS exception: %s", e.what() );
806 if ( !mOuterBoundsGeos && !
mGeos )
809 if ( !polygon->
mGeos )
815 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1
816 && GEOSPreparedContains_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) != 1 )
820 else if ( mNextPart )
822 return mNextPart->crossesBoundary( polygon );
825 catch ( QgsGeosException &e )
827 qWarning(
"GEOS exception: %s", e.what() );
838 const double totalCost = polygonIntersectionCostForParts( polygon );
839 const int n = partCount();
840 return std::ceil( totalCost / n );
846 if ( !mOuterBoundsGeos && !
mGeos )
849 if ( !polygon->
mGeos )
855 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
860 catch ( QgsGeosException &e )
862 qWarning(
"GEOS exception: %s", e.what() );
868 return mNextPart->intersectsWithPolygon( polygon );
876double LabelPosition::polygonIntersectionCostForParts(
PointSet *polygon )
const
879 if ( !mOuterBoundsGeos && !
mGeos )
882 if ( !polygon->
mGeos )
889 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
897 for (
int i = 0; i < 4; ++i )
902 for (
int a = 0; a < 2; ++a )
906 px = (
x[i] +
x[( i + 1 ) % 4] ) / 2.0;
907 py = (
y[i] +
y[( i + 1 ) % 4] ) / 2.0;
911 px = (
x[0] +
x[2] ) / 2.0;
912 py = (
y[0] +
y[2] ) / 2.0;
919 catch ( QgsGeosException &e )
921 qWarning(
"GEOS exception: %s", e.what() );
930 cost += mNextPart->polygonIntersectionCostForParts( polygon );
938 double angleDiff = 0.0;
939 double angleLast = 0.0;
945 double diff = std::fabs( tmp->
getAlpha() - angleLast );
946 if ( diff > 2 * M_PI )
948 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