54 , mQuadrant( quadrant )
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;
96 this->alpha > M_PI_2 && this->alpha <= 3 * M_PI_2 )
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 ( GEOSException &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 ( GEOSException &e )
224 qWarning(
"GEOS exception: %s", e.what() );
249 if ( mOuterBoundsGeos )
261 if ( !mOuterBoundsGeos && !
mGeos )
268 mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 );
271 catch ( GEOSException &e )
273 qWarning(
"GEOS exception: %s", e.what() );
282 return isInConflictMultiPart( lp );
285bool LabelPosition::isInConflictMultiPart(
const LabelPosition *lp )
const
287 if ( !mMultipartGeos )
288 createMultiPartGeosGeom();
290 if ( !lp->mMultipartGeos )
291 lp->createMultiPartGeosGeom();
296 const bool result = ( GEOSPreparedIntersects_r( geosctxt,
preparedMultiPartGeom(), lp->mMultipartGeos ) == 1 );
299 catch ( GEOSException &e )
301 qWarning(
"GEOS exception: %s", e.what() );
309void LabelPosition::createOuterBoundsGeom()
312 if ( outerBounds.isNull() )
317 const double beta = this->
alpha + M_PI_2;
319 const double dx1 = std::cos( this->
alpha ) * outerBounds.width();
320 const double dy1 = std::sin( this->
alpha ) * outerBounds.width();
322 const double dx2 = std::cos( beta ) * outerBounds.height();
323 const double dy2 = std::sin( beta ) * outerBounds.height();
325 mOuterBoundsX.resize( 5 );
326 mOuterBoundsY.resize( 5 );
328 const double x1 =
x[0] + outerBounds.left();
329 const double y1 =
y[0] + outerBounds.top();
331 mOuterBoundsX[0] = x1;
332 mOuterBoundsY[0] = y1;
334 mOuterBoundsX[1] = x1 + dx1;
335 mOuterBoundsY[1] = y1 + dy1;
337 mOuterBoundsX[2] = x1 + dx1 + dx2;
338 mOuterBoundsY[2] = y1 + dy1 + dy2;
340 mOuterBoundsX[3] = x1 + dx2;
341 mOuterBoundsY[3] = y1 + dy2;
343 mOuterBoundsX[4] = mOuterBoundsX[0];
344 mOuterBoundsY[4] = mOuterBoundsY[0];
346 GEOSCoordSequence *coord =
nullptr;
347#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
349 coord = GEOSCoordSeq_copyFromArrays_r( geosctxt, mOuterBoundsX.data(), mOuterBoundsY.data(),
nullptr,
nullptr, 5 );
351 coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
352 for (
int i = 0; i <
nbPoints; ++i )
354 GEOSCoordSeq_setXY_r( geosctxt, coord, i, mOuterBoundsX[i], mOuterBoundsY[i] );
358 mOuterBoundsGeos.reset( GEOSGeom_createPolygon_r( geosctxt, GEOSGeom_createLinearRing_r( geosctxt, coord ),
nullptr, 0 ) );
362 auto xminmax = std::minmax_element( mOuterBoundsX.begin(), mOuterBoundsX.end() );
363 mOuterBoundsXMin = *xminmax.first;
364 mOuterBoundsXMax = *xminmax.second;
365 auto yminmax = std::minmax_element( mOuterBoundsY.begin(), mOuterBoundsY.end() );
366 mOuterBoundsYMin = *yminmax.first;
367 mOuterBoundsYMax = *yminmax.second;
370int LabelPosition::partCount()
const
373 return mNextPart->partCount() + 1;
385 return ( i >= 0 && i < 4 ?
x[i] : -1 );
390 return ( i >= 0 && i < 4 ?
y[i] : -1 );
402 mCost -= int ( mCost );
416 mNextPart->getBoundingBox( amin, amax );
420 amin[0] = std::numeric_limits<double>::max();
421 amax[0] = std::numeric_limits<double>::lowest();
422 amin[1] = std::numeric_limits<double>::max();
423 amax[1] = std::numeric_limits<double>::lowest();
425 for (
int c = 0;
c < 4;
c++ )
427 if ( mOuterBoundsGeos )
429 if ( mOuterBoundsX[
c] < amin[0] )
430 amin[0] = mOuterBoundsX[
c];
431 if ( mOuterBoundsX[
c] > amax[0] )
432 amax[0] = mOuterBoundsX[
c];
433 if ( mOuterBoundsY[
c] < amin[1] )
434 amin[1] = mOuterBoundsY[
c];
435 if ( mOuterBoundsY[
c] > amax[1] )
436 amax[1] = mOuterBoundsY[
c];
440 if (
x[
c] < amin[0] )
442 if (
x[
c] > amax[0] )
444 if (
y[
c] < amin[1] )
446 if (
y[
c] > amax[1] )
457 return QgsRectangle( amin[0], amin[1], amax[0], amax[1] );
464 const QList< QgsAbstractLabelingEngineRule * > rules =
pal->rules();
467 const QgsRectangle modifiedBounds = rule->modifyCandidateConflictSearchBoundingBox( bounds );
470 return bufferedBounds;
475 if ( other->mOuterBoundsGeos )
477 const double x1 = ( mOuterBoundsXMin > other->mOuterBoundsXMin ? mOuterBoundsXMin : other->mOuterBoundsXMin );
478 const double x2 = ( mOuterBoundsXMax < other->mOuterBoundsXMax ? mOuterBoundsXMax : other->mOuterBoundsXMax );
481 const double y1 = ( mOuterBoundsYMin > other->mOuterBoundsYMin ? mOuterBoundsYMin : other->mOuterBoundsYMin );
482 const double y2 = ( mOuterBoundsYMax < other->mOuterBoundsYMax ? mOuterBoundsYMax : other->mOuterBoundsYMax );
487 const double x1 = ( mOuterBoundsXMin > other->
xmin ? mOuterBoundsXMin : other->
xmin );
488 const double x2 = ( mOuterBoundsXMax < other->
xmax ? mOuterBoundsXMax : other->
xmax );
491 const double y1 = ( mOuterBoundsYMin > other->
ymin ? mOuterBoundsYMin : other->
ymin );
492 const double y2 = ( mOuterBoundsYMax < other->
ymax ? mOuterBoundsYMax : other->
ymax );
499 mHasObstacleConflict = conflicts;
501 mNextPart->setConflictsWithObstacle( conflicts );
506 mHasHardConflict = conflicts;
508 mNextPart->setHasHardObstacleConflict( conflicts );
523 if ( !mMultipartGeos )
524 createMultiPartGeosGeom();
526 return mMultipartGeos;
529void LabelPosition::createMultiPartGeosGeom()
const
533 std::vector< const GEOSGeometry * > geometries;
538 if ( !GEOSisEmpty_r( geosctxt, partGeos ) )
539 geometries.emplace_back( partGeos );
543 const std::size_t partCount = geometries.size();
545 for ( std::size_t i = 0; i < partCount; ++i )
547 geomarr[i ] = GEOSGeom_clone_r( geosctxt, geometries[i] );
550 mMultipartGeos = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomarr, partCount );
556 if ( !mMultipartGeos )
557 createMultiPartGeosGeom();
559 if ( !mMultipartPreparedGeos )
563 return mMultipartPreparedGeos;
568 return mPreparedOuterBoundsGeos;
578 bool contains =
false;
582 if ( useOuterBounds && mOuterBoundsGeos )
584 contains = mOuterBoundsX[0] <= xp && mOuterBoundsX[1] >= xp && mOuterBoundsY[0] <= yp && mOuterBoundsY[2] >= yp;
588 contains =
x[0] <= xp &&
x[1] >= xp &&
y[0] <= yp &&
y[2] >= yp;
593 if ( useOuterBounds && mPreparedOuterBoundsGeos )
597 geos::unique_ptr point( GEOSGeom_createPointFromXY_r( geosctxt, xp, yp ) );
598 contains = ( GEOSPreparedContainsProperly_r( geosctxt, mPreparedOuterBoundsGeos, point.get() ) == 1 );
600 catch ( GEOSException & )
611 double distance = -1;
616 if ( useOuterBounds && mOuterBoundsGeos )
618 const double dx = std::max( std::max( mOuterBoundsX[0] - xp, 0.0 ), xp - mOuterBoundsX[1] );
619 const double dy = std::max( std::max( mOuterBoundsY[0] - yp, 0.0 ), yp - mOuterBoundsY[2] );
620 distance = std::sqrt( dx * dx + dy * dy );
624 const double dx = std::max( std::max(
x[0] - xp, 0.0 ), xp -
x[1] );
625 const double dy = std::max( std::max(
y[0] - yp, 0.0 ), yp -
y[2] );
626 distance = std::sqrt( dx * dx + dy * dy );
631 if ( useOuterBounds && mPreparedOuterBoundsGeos )
635 geos::unique_ptr geosPt( GEOSGeom_createPointFromXY_r( geosctxt, xp, yp ) );
637 const geos::coord_sequence_unique_ptr nearestCoord( GEOSPreparedNearestPoints_r( geosctxt, mPreparedOuterBoundsGeos, geosPt.get() ) );
640 unsigned int nPoints = 0;
641 GEOSCoordSeq_getSize_r( geosctxt, nearestCoord.get(), &nPoints );
648 ( void )GEOSCoordSeq_getXY_r( geosctxt, nearestCoord.get(), 0, &nx, &ny );
652 catch ( GEOSException &e )
654 qWarning(
"GEOS exception: %s", e.what() );
666 if ( mNextPart && distance > 0 )
667 return std::min( distance, mNextPart->getDistanceToPoint( xp, yp, useOuterBounds ) );
675 if ( !mOuterBoundsGeos && !
mGeos )
684 if ( GEOSPreparedIntersects_r( geosctxt, line->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
688 else if ( mNextPart )
690 return mNextPart->crossesLine( line );
693 catch ( GEOSException &e )
695 qWarning(
"GEOS exception: %s", e.what() );
706 if ( !mOuterBoundsGeos && !
mGeos )
709 if ( !polygon->
mGeos )
715 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1
716 && GEOSPreparedContains_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) != 1 )
720 else if ( mNextPart )
722 return mNextPart->crossesBoundary( polygon );
725 catch ( GEOSException &e )
727 qWarning(
"GEOS exception: %s", e.what() );
738 const double totalCost = polygonIntersectionCostForParts( polygon );
739 const int n = partCount();
740 return std::ceil( totalCost / n );
746 if ( !mOuterBoundsGeos && !
mGeos )
749 if ( !polygon->
mGeos )
755 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
760 catch ( GEOSException &e )
762 qWarning(
"GEOS exception: %s", e.what() );
768 return mNextPart->intersectsWithPolygon( polygon );
776double LabelPosition::polygonIntersectionCostForParts(
PointSet *polygon )
const
779 if ( !mOuterBoundsGeos && !
mGeos )
782 if ( !polygon->
mGeos )
789 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
797 for (
int i = 0; i < 4; ++i )
802 for (
int a = 0; a < 2; ++a )
806 px = (
x[i] +
x[( i + 1 ) % 4] ) / 2.0;
807 py = (
y[i] +
y[( i + 1 ) % 4] ) / 2.0;
811 px = (
x[0] +
x[2] ) / 2.0;
812 py = (
y[0] +
y[2] ) / 2.0;
819 catch ( GEOSException &e )
821 qWarning(
"GEOS exception: %s", e.what() );
830 cost += mNextPart->polygonIntersectionCostForParts( polygon );
838 double angleDiff = 0.0;
839 double angleLast = 0.0;
845 double diff = std::fabs( tmp->
getAlpha() - angleLast );
846 if ( diff > 2 * M_PI )
848 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.
Qgis::LabelOverlapHandling overlapHandling() const
Returns the technique to use for handling overlapping labels for the feature.
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)
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.
Main class to handle 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.
void removeFromIndex(PalRtree< LabelPosition > &index)
Removes the label position from the specified index.
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.
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.
void insertIntoIndex(PalRtree< LabelPosition > &index)
Inserts the label position into the specified index.
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.
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)
struct GEOSGeom_t GEOSGeometry