51 , reversed( isReversed )
53 , quadrant( quadrant )
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 );
161 mHasObstacleConflict = other.mHasObstacleConflict;
162 mUpsideDownCharCount = other.mUpsideDownCharCount;
164 createOuterBoundsGeom();
169 if ( mPreparedOuterBoundsGeos )
172 mPreparedOuterBoundsGeos =
nullptr;
179 if ( !mOuterBoundsGeos && !
mGeos )
188 else if ( mNextPart )
190 return mNextPart->intersects( geometry );
193 catch ( GEOSException &e )
195 qWarning(
"GEOS exception: %s", e.what() );
206 if ( !mOuterBoundsGeos && !
mGeos )
215 else if ( mNextPart )
217 return mNextPart->within( geometry );
220 catch ( GEOSException &e )
222 qWarning(
"GEOS exception: %s", e.what() );
247 if ( mOuterBoundsGeos )
259 if ( !mOuterBoundsGeos && !
mGeos )
266 mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 );
269 catch ( GEOSException &e )
271 qWarning(
"GEOS exception: %s", e.what() );
280 return isInConflictMultiPart( lp );
283bool LabelPosition::isInConflictMultiPart(
const LabelPosition *lp )
const
285 if ( !mMultipartGeos )
286 createMultiPartGeosGeom();
288 if ( !lp->mMultipartGeos )
289 lp->createMultiPartGeosGeom();
294 const bool result = ( GEOSPreparedIntersects_r( geosctxt,
preparedMultiPartGeom(), lp->mMultipartGeos ) == 1 );
297 catch ( GEOSException &e )
299 qWarning(
"GEOS exception: %s", e.what() );
307void LabelPosition::createOuterBoundsGeom()
310 if ( outerBounds.isNull() )
315 const double beta = this->
alpha + M_PI_2;
317 const double dx1 = std::cos( this->
alpha ) * outerBounds.width();
318 const double dy1 = std::sin( this->
alpha ) * outerBounds.width();
320 const double dx2 = std::cos( beta ) * outerBounds.height();
321 const double dy2 = std::sin( beta ) * outerBounds.height();
323 mOuterBoundsX.resize( 5 );
324 mOuterBoundsY.resize( 5 );
326 const double x1 =
x[0] + outerBounds.left();
327 const double y1 =
y[0] + outerBounds.top();
329 mOuterBoundsX[0] = x1;
330 mOuterBoundsY[0] = y1;
332 mOuterBoundsX[1] = x1 + dx1;
333 mOuterBoundsY[1] = y1 + dy1;
335 mOuterBoundsX[2] = x1 + dx1 + dx2;
336 mOuterBoundsY[2] = y1 + dy1 + dy2;
338 mOuterBoundsX[3] = x1 + dx2;
339 mOuterBoundsY[3] = y1 + dy2;
341 mOuterBoundsX[4] = mOuterBoundsX[0];
342 mOuterBoundsY[4] = mOuterBoundsY[0];
344 GEOSCoordSequence *coord =
nullptr;
345#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
347 coord = GEOSCoordSeq_copyFromArrays_r( geosctxt, mOuterBoundsX.data(), mOuterBoundsY.data(),
nullptr,
nullptr, 5 );
349 coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
350 for (
int i = 0; i <
nbPoints; ++i )
352 GEOSCoordSeq_setXY_r( geosctxt, coord, i, mOuterBoundsX[i], mOuterBoundsY[i] );
356 mOuterBoundsGeos.reset( GEOSGeom_createPolygon_r( geosctxt, GEOSGeom_createLinearRing_r( geosctxt, coord ),
nullptr, 0 ) );
360 auto xminmax = std::minmax_element( mOuterBoundsX.begin(), mOuterBoundsX.end() );
361 mOuterBoundsXMin = *xminmax.first;
362 mOuterBoundsXMax = *xminmax.second;
363 auto yminmax = std::minmax_element( mOuterBoundsY.begin(), mOuterBoundsY.end() );
364 mOuterBoundsYMin = *yminmax.first;
365 mOuterBoundsYMax = *yminmax.second;
368int LabelPosition::partCount()
const
371 return mNextPart->partCount() + 1;
383 return ( i >= 0 && i < 4 ?
x[i] : -1 );
388 return ( i >= 0 && i < 4 ?
y[i] : -1 );
400 mCost -= int ( mCost );
414 mNextPart->getBoundingBox( amin, amax );
418 amin[0] = std::numeric_limits<double>::max();
419 amax[0] = std::numeric_limits<double>::lowest();
420 amin[1] = std::numeric_limits<double>::max();
421 amax[1] = std::numeric_limits<double>::lowest();
423 for (
int c = 0;
c < 4;
c++ )
425 if ( mOuterBoundsGeos )
427 if ( mOuterBoundsX[
c] < amin[0] )
428 amin[0] = mOuterBoundsX[
c];
429 if ( mOuterBoundsX[
c] > amax[0] )
430 amax[0] = mOuterBoundsX[
c];
431 if ( mOuterBoundsY[
c] < amin[1] )
432 amin[1] = mOuterBoundsY[
c];
433 if ( mOuterBoundsY[
c] > amax[1] )
434 amax[1] = mOuterBoundsY[
c];
438 if (
x[
c] < amin[0] )
440 if (
x[
c] > amax[0] )
442 if (
y[
c] < amin[1] )
444 if (
y[
c] > amax[1] )
452 if ( other->mOuterBoundsGeos )
454 const double x1 = ( mOuterBoundsXMin > other->mOuterBoundsXMin ? mOuterBoundsXMin : other->mOuterBoundsXMin );
455 const double x2 = ( mOuterBoundsXMax < other->mOuterBoundsXMax ? mOuterBoundsXMax : other->mOuterBoundsXMax );
458 const double y1 = ( mOuterBoundsYMin > other->mOuterBoundsYMin ? mOuterBoundsYMin : other->mOuterBoundsYMin );
459 const double y2 = ( mOuterBoundsYMax < other->mOuterBoundsYMax ? mOuterBoundsYMax : other->mOuterBoundsYMax );
464 const double x1 = ( mOuterBoundsXMin > other->
xmin ? mOuterBoundsXMin : other->
xmin );
465 const double x2 = ( mOuterBoundsXMax < other->
xmax ? mOuterBoundsXMax : other->
xmax );
468 const double y1 = ( mOuterBoundsYMin > other->
ymin ? mOuterBoundsYMin : other->
ymin );
469 const double y2 = ( mOuterBoundsYMax < other->
ymax ? mOuterBoundsYMax : other->
ymax );
476 mHasObstacleConflict = conflicts;
478 mNextPart->setConflictsWithObstacle( conflicts );
483 mHasHardConflict = conflicts;
485 mNextPart->setHasHardObstacleConflict( conflicts );
505void LabelPosition::createMultiPartGeosGeom()
const
509 std::vector< const GEOSGeometry * > geometries;
514 if ( !GEOSisEmpty_r( geosctxt, partGeos ) )
515 geometries.emplace_back( partGeos );
519 const std::size_t partCount = geometries.size();
521 for ( std::size_t i = 0; i < partCount; ++i )
523 geomarr[i ] = GEOSGeom_clone_r( geosctxt, geometries[i] );
526 mMultipartGeos = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomarr, partCount );
532 if ( !mMultipartGeos )
533 createMultiPartGeosGeom();
535 if ( !mMultipartPreparedGeos )
539 return mMultipartPreparedGeos;
544 return mPreparedOuterBoundsGeos;
554 bool contains =
false;
558 if ( useOuterBounds && mOuterBoundsGeos )
560 contains = mOuterBoundsX[0] <= xp && mOuterBoundsX[1] >= xp && mOuterBoundsY[0] <= yp && mOuterBoundsY[2] >= yp;
564 contains =
x[0] <= xp &&
x[1] >= xp &&
y[0] <= yp &&
y[2] >= yp;
569 if ( useOuterBounds && mPreparedOuterBoundsGeos )
574 contains = ( GEOSPreparedContainsProperly_r( geosctxt, mPreparedOuterBoundsGeos, point.get() ) == 1 );
576 catch ( GEOSException &e )
587 double distance = -1;
592 if ( useOuterBounds && mOuterBoundsGeos )
594 const double dx = std::max( std::max( mOuterBoundsX[0] - xp, 0.0 ), xp - mOuterBoundsX[1] );
595 const double dy = std::max( std::max( mOuterBoundsY[0] - yp, 0.0 ), yp - mOuterBoundsY[2] );
596 distance = std::sqrt( dx * dx + dy * dy );
600 const double dx = std::max( std::max(
x[0] - xp, 0.0 ), xp -
x[1] );
601 const double dy = std::max( std::max(
y[0] - yp, 0.0 ), yp -
y[2] );
602 distance = std::sqrt( dx * dx + dy * dy );
607 if ( useOuterBounds && mPreparedOuterBoundsGeos )
611 geos::unique_ptr geosPt( GEOSGeom_createPointFromXY_r( geosctxt, xp, yp ) );
616 unsigned int nPoints = 0;
617 GEOSCoordSeq_getSize_r( geosctxt, nearestCoord.get(), &nPoints );
624 ( void )GEOSCoordSeq_getXY_r( geosctxt, nearestCoord.get(), 0, &nx, &ny );
628 catch ( GEOSException &e )
630 qWarning(
"GEOS exception: %s", e.what() );
642 if ( mNextPart && distance > 0 )
643 return std::min( distance, mNextPart->getDistanceToPoint( xp, yp, useOuterBounds ) );
651 if ( !mOuterBoundsGeos && !
mGeos )
660 if ( GEOSPreparedIntersects_r( geosctxt, line->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
664 else if ( mNextPart )
666 return mNextPart->crossesLine( line );
669 catch ( GEOSException &e )
671 qWarning(
"GEOS exception: %s", e.what() );
682 if ( !mOuterBoundsGeos && !
mGeos )
685 if ( !polygon->
mGeos )
691 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1
692 && GEOSPreparedContains_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) != 1 )
696 else if ( mNextPart )
698 return mNextPart->crossesBoundary( polygon );
701 catch ( GEOSException &e )
703 qWarning(
"GEOS exception: %s", e.what() );
714 const double totalCost = polygonIntersectionCostForParts( polygon );
715 const int n = partCount();
716 return std::ceil( totalCost / n );
722 if ( !mOuterBoundsGeos && !
mGeos )
725 if ( !polygon->
mGeos )
731 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
736 catch ( GEOSException &e )
738 qWarning(
"GEOS exception: %s", e.what() );
744 return mNextPart->intersectsWithPolygon( polygon );
752double LabelPosition::polygonIntersectionCostForParts(
PointSet *polygon )
const
755 if ( !mOuterBoundsGeos && !
mGeos )
758 if ( !polygon->
mGeos )
765 if ( GEOSPreparedIntersects_r( geosctxt, polygon->
preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() :
mGeos ) == 1 )
773 for (
int i = 0; i < 4; ++i )
778 for (
int a = 0; a < 2; ++a )
782 px = (
x[i] +
x[( i + 1 ) % 4] ) / 2.0;
783 py = (
y[i] +
y[( i + 1 ) % 4] ) / 2.0;
787 px = (
x[0] +
x[2] ) / 2.0;
788 py = (
y[0] +
y[2] ) / 2.0;
795 catch ( GEOSException &e )
797 qWarning(
"GEOS exception: %s", e.what() );
806 cost += mNextPart->polygonIntersectionCostForParts( polygon );
814 double angleDiff = 0.0;
815 double angleLast = 0.0;
821 double diff = std::fabs( tmp->
getAlpha() - angleLast );
822 if ( diff > 2 * M_PI )
824 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 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.
static double dist_euc2d_sq(double x1, double y1, double x2, double y2)
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