52 , reversed( isReversed )
54 , quadrant( quadrant )
56 , mHasObstacleConflict( false )
57 , mUpsideDownCharCount( 0 )
65 while ( this->alpha > 2 * M_PI )
66 this->alpha -= 2 * M_PI;
68 while ( this->alpha < 0 )
69 this->alpha += 2 * M_PI;
71 const double beta = this->alpha + M_PI_2;
73 double dx1, dx2, dy1, dy2;
75 dx1 = std::cos( this->alpha ) *
w;
76 dy1 = std::sin( this->alpha ) *
w;
78 dx2 = std::cos( beta ) *
h;
79 dy2 = std::sin( beta ) *
h;
87 x[2] = x1 + dx1 + dx2;
88 y[2] = y1 + dy1 + dy2;
95 this->alpha > M_PI_2 && this->alpha <= 3 * M_PI_2 )
120 if ( this->alpha < M_PI )
131 for (
int i = 0; i <
nbPoints; ++i )
139 createOuterBoundsGeom();
155 if ( other.mNextPart )
156 mNextPart = std::make_unique< LabelPosition >( *other.mNextPart );
162 mHasObstacleConflict = other.mHasObstacleConflict;
163 mUpsideDownCharCount = other.mUpsideDownCharCount;
165 createOuterBoundsGeom();
170 if ( mPreparedOuterBoundsGeos )
173 mPreparedOuterBoundsGeos =
nullptr;
180 if ( !mOuterBoundsGeos && !
mGeos )
189 else if ( mNextPart )
191 return mNextPart->intersects( geometry );
194 catch ( GEOSException &e )
196 qWarning(
"GEOS exception: %s", e.what() );
207 if ( !mOuterBoundsGeos && !
mGeos )
216 else if ( mNextPart )
218 return mNextPart->within( geometry );
221 catch ( GEOSException &e )
223 qWarning(
"GEOS exception: %s", e.what() );
248 if ( mOuterBoundsGeos )
260 if ( !mOuterBoundsGeos && !
mGeos )
267 mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 );
270 catch ( GEOSException &e )
272 qWarning(
"GEOS exception: %s", e.what() );
281 return isInConflictMultiPart( lp );
284bool LabelPosition::isInConflictMultiPart(
const LabelPosition *lp )
const
286 if ( !mMultipartGeos )
287 createMultiPartGeosGeom();
289 if ( !lp->mMultipartGeos )
290 lp->createMultiPartGeosGeom();
295 const bool result = ( GEOSPreparedIntersects_r( geosctxt,
preparedMultiPartGeom(), lp->mMultipartGeos ) == 1 );
298 catch ( GEOSException &e )
300 qWarning(
"GEOS exception: %s", e.what() );
308void LabelPosition::createOuterBoundsGeom()
311 if ( outerBounds.isNull() )
316 const double beta = this->
alpha + M_PI_2;
318 const double dx1 = std::cos( this->
alpha ) * outerBounds.width();
319 const double dy1 = std::sin( this->
alpha ) * outerBounds.width();
321 const double dx2 = std::cos( beta ) * outerBounds.height();
322 const double dy2 = std::sin( beta ) * outerBounds.height();
324 mOuterBoundsX.resize( 5 );
325 mOuterBoundsY.resize( 5 );
327 const double x1 =
x[0] + outerBounds.left();
328 const double y1 =
y[0] + outerBounds.top();
330 mOuterBoundsX[0] = x1;
331 mOuterBoundsY[0] = y1;
333 mOuterBoundsX[1] = x1 + dx1;
334 mOuterBoundsY[1] = y1 + dy1;
336 mOuterBoundsX[2] = x1 + dx1 + dx2;
337 mOuterBoundsY[2] = y1 + dy1 + dy2;
339 mOuterBoundsX[3] = x1 + dx2;
340 mOuterBoundsY[3] = y1 + dy2;
342 mOuterBoundsX[4] = mOuterBoundsX[0];
343 mOuterBoundsY[4] = mOuterBoundsY[0];
345 GEOSCoordSequence *coord =
nullptr;
346#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
348 coord = GEOSCoordSeq_copyFromArrays_r( geosctxt, mOuterBoundsX.data(), mOuterBoundsY.data(),
nullptr,
nullptr, 5 );
350 coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
351 for (
int i = 0; i <
nbPoints; ++i )
353 GEOSCoordSeq_setXY_r( geosctxt, coord, i, mOuterBoundsX[i], mOuterBoundsY[i] );
357 mOuterBoundsGeos.reset( GEOSGeom_createPolygon_r( geosctxt, GEOSGeom_createLinearRing_r( geosctxt, coord ),
nullptr, 0 ) );
361 auto xminmax = std::minmax_element( mOuterBoundsX.begin(), mOuterBoundsX.end() );
362 mOuterBoundsXMin = *xminmax.first;
363 mOuterBoundsXMax = *xminmax.second;
364 auto yminmax = std::minmax_element( mOuterBoundsY.begin(), mOuterBoundsY.end() );
365 mOuterBoundsYMin = *yminmax.first;
366 mOuterBoundsYMax = *yminmax.second;
369int LabelPosition::partCount()
const
372 return mNextPart->partCount() + 1;
384 return ( i >= 0 && i < 4 ?
x[i] : -1 );
389 return ( i >= 0 && i < 4 ?
y[i] : -1 );
401 mCost -= int ( mCost );
415 mNextPart->getBoundingBox( amin, amax );
419 amin[0] = std::numeric_limits<double>::max();
420 amax[0] = std::numeric_limits<double>::lowest();
421 amin[1] = std::numeric_limits<double>::max();
422 amax[1] = std::numeric_limits<double>::lowest();
424 for (
int c = 0;
c < 4;
c++ )
426 if ( mOuterBoundsGeos )
428 if ( mOuterBoundsX[
c] < amin[0] )
429 amin[0] = mOuterBoundsX[
c];
430 if ( mOuterBoundsX[
c] > amax[0] )
431 amax[0] = mOuterBoundsX[
c];
432 if ( mOuterBoundsY[
c] < amin[1] )
433 amin[1] = mOuterBoundsY[
c];
434 if ( mOuterBoundsY[
c] > amax[1] )
435 amax[1] = mOuterBoundsY[
c];
439 if (
x[
c] < amin[0] )
441 if (
x[
c] > amax[0] )
443 if (
y[
c] < amin[1] )
445 if (
y[
c] > amax[1] )
453 if ( other->mOuterBoundsGeos )
455 const double x1 = ( mOuterBoundsXMin > other->mOuterBoundsXMin ? mOuterBoundsXMin : other->mOuterBoundsXMin );
456 const double x2 = ( mOuterBoundsXMax < other->mOuterBoundsXMax ? mOuterBoundsXMax : other->mOuterBoundsXMax );
459 const double y1 = ( mOuterBoundsYMin > other->mOuterBoundsYMin ? mOuterBoundsYMin : other->mOuterBoundsYMin );
460 const double y2 = ( mOuterBoundsYMax < other->mOuterBoundsYMax ? mOuterBoundsYMax : other->mOuterBoundsYMax );
465 const double x1 = ( mOuterBoundsXMin > other->
xmin ? mOuterBoundsXMin : other->
xmin );
466 const double x2 = ( mOuterBoundsXMax < other->
xmax ? mOuterBoundsXMax : other->
xmax );
469 const double y1 = ( mOuterBoundsYMin > other->
ymin ? mOuterBoundsYMin : other->
ymin );
470 const double y2 = ( mOuterBoundsYMax < other->
ymax ? mOuterBoundsYMax : other->
ymax );
477 mHasObstacleConflict = conflicts;
479 mNextPart->setConflictsWithObstacle( conflicts );
484 mHasHardConflict = conflicts;
486 mNextPart->setHasHardObstacleConflict( conflicts );
506void LabelPosition::createMultiPartGeosGeom()
const
510 std::vector< const GEOSGeometry * > geometries;
515 if ( !GEOSisEmpty_r( geosctxt, partGeos ) )
516 geometries.emplace_back( partGeos );
520 const std::size_t partCount = geometries.size();
522 for ( std::size_t i = 0; i < partCount; ++i )
524 geomarr[i ] = GEOSGeom_clone_r( geosctxt, geometries[i] );
527 mMultipartGeos = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomarr, partCount );
533 if ( !mMultipartGeos )
534 createMultiPartGeosGeom();
536 if ( !mMultipartPreparedGeos )
540 return mMultipartPreparedGeos;
545 return mPreparedOuterBoundsGeos;
555 bool contains =
false;
559 if ( useOuterBounds && mOuterBoundsGeos )
561 contains = mOuterBoundsX[0] <= xp && mOuterBoundsX[1] >= xp && mOuterBoundsY[0] <= yp && mOuterBoundsY[2] >= yp;
565 contains =
x[0] <= xp &&
x[1] >= xp &&
y[0] <= yp &&
y[2] >= yp;
570 if ( useOuterBounds && mPreparedOuterBoundsGeos )
575 contains = ( GEOSPreparedContainsProperly_r( geosctxt, mPreparedOuterBoundsGeos, point.get() ) == 1 );
577 catch ( GEOSException &e )
588 double distance = -1;
593 if ( useOuterBounds && mOuterBoundsGeos )
595 const double dx = std::max( std::max( mOuterBoundsX[0] - xp, 0.0 ), xp - mOuterBoundsX[1] );
596 const double dy = std::max( std::max( mOuterBoundsY[0] - yp, 0.0 ), yp - mOuterBoundsY[2] );
597 distance = std::sqrt( dx * dx + dy * dy );
601 const double dx = std::max( std::max(
x[0] - xp, 0.0 ), xp -
x[1] );
602 const double dy = std::max( std::max(
y[0] - yp, 0.0 ), yp -
y[2] );
603 distance = std::sqrt( dx * dx + dy * dy );
608 if ( useOuterBounds && mPreparedOuterBoundsGeos )
612 geos::unique_ptr geosPt( GEOSGeom_createPointFromXY_r( geosctxt, xp, yp ) );
617 unsigned int nPoints = 0;
618 GEOSCoordSeq_getSize_r( geosctxt, nearestCoord.get(), &nPoints );
625 ( void )GEOSCoordSeq_getXY_r( geosctxt, nearestCoord.get(), 0, &nx, &ny );
629 catch ( GEOSException &e )
631 qWarning(
"GEOS exception: %s", e.what() );
643 if ( mNextPart && distance > 0 )
644 return std::min( distance, mNextPart->getDistanceToPoint( xp, yp, useOuterBounds ) );
652 if ( !mOuterBoundsGeos && !
mGeos )
661 if ( GEOSPreparedIntersects_r( geosctxt, line->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
665 else if ( mNextPart )
667 return mNextPart->crossesLine( line );
670 catch ( GEOSException &e )
672 qWarning(
"GEOS exception: %s", e.what() );
683 if ( !mOuterBoundsGeos && !
mGeos )
686 if ( !polygon->
mGeos )
692 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1
693 && GEOSPreparedContains_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) != 1 )
697 else if ( mNextPart )
699 return mNextPart->crossesBoundary( polygon );
702 catch ( GEOSException &e )
704 qWarning(
"GEOS exception: %s", e.what() );
715 const double totalCost = polygonIntersectionCostForParts( polygon );
716 const int n = partCount();
717 return std::ceil( totalCost / n );
723 if ( !mOuterBoundsGeos && !
mGeos )
726 if ( !polygon->
mGeos )
732 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
737 catch ( GEOSException &e )
739 qWarning(
"GEOS exception: %s", e.what() );
745 return mNextPart->intersectsWithPolygon( polygon );
753double LabelPosition::polygonIntersectionCostForParts(
PointSet *polygon )
const
756 if ( !mOuterBoundsGeos && !
mGeos )
759 if ( !polygon->
mGeos )
766 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
774 for (
int i = 0; i < 4; ++i )
779 for (
int a = 0; a < 2; ++a )
783 px = (
x[i] +
x[( i + 1 ) % 4] ) / 2.0;
784 py = (
y[i] +
y[( i + 1 ) % 4] ) / 2.0;
788 px = (
x[0] +
x[2] ) / 2.0;
789 py = (
y[0] +
y[2] ) / 2.0;
796 catch ( GEOSException &e )
798 qWarning(
"GEOS exception: %s", e.what() );
807 cost += mNextPart->polygonIntersectionCostForParts( polygon );
815 double angleDiff = 0.0;
816 double angleLast = 0.0;
822 double diff = std::fabs( tmp->
getAlpha() - angleLast );
823 if ( diff > 2 * M_PI )
825 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.
@ AllowOverlapAtNoCost
Labels may freely overlap other labels, at no cost.
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 getGEOSHandler()
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.
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 ...
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).
Quadrant
Position of label candidate relative to feature.
LabelPosition::Quadrant quadrant
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() 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.
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.
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.
LabelPosition(int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, bool isReversed=false, Quadrant quadrant=QuadrantOver)
create a new LabelPosition
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.
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)
struct GEOSGeom_t GEOSGeometry