43LabelPosition::LabelPosition(
int id,
double x1,
double y1,
double w,
double h,
double alpha,
double cost,
FeaturePart *feature,
bool isReversed,
Quadrant quadrant )
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 )
153 if ( other.mNextPart )
154 mNextPart = std::make_unique< LabelPosition >( *other.mNextPart );
160 mHasObstacleConflict = other.mHasObstacleConflict;
161 mUpsideDownCharCount = other.mUpsideDownCharCount;
168 for ( i = 0; i < 4; i++ )
170 if (
x[i] >= bbox[0] &&
x[i] <= bbox[2] &&
171 y[i] >= bbox[1] &&
y[i] <= bbox[3] )
176 return mNextPart->isIn( bbox );
185 for ( i = 0; i < 4; i++ )
187 if (
x[i] >= bbox[0] &&
x[i] <= bbox[2] &&
188 y[i] >= bbox[1] &&
y[i] <= bbox[3] )
193 return mNextPart->isIntersect( bbox );
209 else if ( mNextPart )
211 return mNextPart->intersects( geometry );
214 catch ( GEOSException &e )
216 qWarning(
"GEOS exception: %s", e.what() );
235 else if ( mNextPart )
237 return mNextPart->within( geometry );
240 catch ( GEOSException &e )
242 qWarning(
"GEOS exception: %s", e.what() );
252 for (
int i = 0; i < 4; i++ )
254 if ( !(
x[i] >= bbox[0] &&
x[i] <= bbox[2] &&
255 y[i] >= bbox[1] &&
y[i] <= bbox[3] ) )
260 return mNextPart->isInside( bbox );
284 return isInConflictMultiPart( lp );
287bool LabelPosition::isInConflictMultiPart(
const LabelPosition *lp )
const
289 if ( !mMultipartGeos )
290 createMultiPartGeosGeom();
292 if ( !lp->mMultipartGeos )
293 lp->createMultiPartGeosGeom();
298 const bool result = ( GEOSPreparedIntersects_r( geosctxt,
preparedMultiPartGeom(), lp->mMultipartGeos ) == 1 );
301 catch ( GEOSException &e )
303 qWarning(
"GEOS exception: %s", e.what() );
311int LabelPosition::partCount()
const
314 return mNextPart->partCount() + 1;
321 for (
int i = 0; i < 4; i++ )
328 mNextPart->offsetPosition( xOffset, yOffset );
340 return ( i >= 0 && i < 4 ?
x[i] : -1 );
345 return ( i >= 0 && i < 4 ?
y[i] : -1 );
357 mCost -= int ( mCost );
370 mNextPart->getBoundingBox( amin, amax );
374 amin[0] = std::numeric_limits<double>::max();
375 amax[0] = std::numeric_limits<double>::lowest();
376 amin[1] = std::numeric_limits<double>::max();
377 amax[1] = std::numeric_limits<double>::lowest();
379 for (
int c = 0;
c < 4;
c++ )
381 if (
x[
c] < amin[0] )
383 if (
x[
c] > amax[0] )
385 if (
y[
c] < amin[1] )
387 if (
y[
c] > amax[1] )
394 mHasObstacleConflict = conflicts;
396 mNextPart->setConflictsWithObstacle( conflicts );
401 mHasHardConflict = conflicts;
403 mNextPart->setHasHardObstacleConflict( conflicts );
423void LabelPosition::createMultiPartGeosGeom()
const
427 std::vector< const GEOSGeometry * > geometries;
431 const GEOSGeometry *partGeos = tmp1->
geos();
432 if ( !GEOSisEmpty_r( geosctxt, partGeos ) )
433 geometries.emplace_back( partGeos );
437 const std::size_t partCount = geometries.size();
438 GEOSGeometry **geomarr =
new GEOSGeometry*[ partCount ];
439 for ( std::size_t i = 0; i < partCount; ++i )
441 geomarr[i ] = GEOSGeom_clone_r( geosctxt, geometries[i] );
444 mMultipartGeos = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomarr, partCount );
450 if ( !mMultipartGeos )
451 createMultiPartGeosGeom();
453 if ( !mMultipartPreparedGeos )
457 return mMultipartPreparedGeos;
463 bool contains =
false;
467 contains =
x[0] <= xp &&
x[1] >= xp &&
y[0] <= yp &&
y[2] >= yp;
474 double distance = -1;
479 const double dx = std::max( std::max(
x[0] - xp, 0.0 ), xp -
x[1] );
480 const double dy = std::max( std::max(
y[0] - yp, 0.0 ), yp -
y[2] );
481 distance = std::sqrt( dx * dx + dy * dy );
489 if ( mNextPart && distance > 0 )
490 return std::min( distance, mNextPart->getDistanceToPoint( xp, yp ) );
510 else if ( mNextPart )
512 return mNextPart->crossesLine( line );
515 catch ( GEOSException &e )
517 qWarning(
"GEOS exception: %s", e.what() );
530 if ( !polygon->
mGeos )
541 else if ( mNextPart )
543 return mNextPart->crossesBoundary( polygon );
546 catch ( GEOSException &e )
548 qWarning(
"GEOS exception: %s", e.what() );
559 const double totalCost = polygonIntersectionCostForParts( polygon );
560 const int n = partCount();
561 return std::ceil( totalCost / n );
569 if ( !polygon->
mGeos )
580 catch ( GEOSException &e )
582 qWarning(
"GEOS exception: %s", e.what() );
588 return mNextPart->intersectsWithPolygon( polygon );
596double LabelPosition::polygonIntersectionCostForParts(
PointSet *polygon )
const
601 if ( !polygon->
mGeos )
616 for (
int i = 0; i < 4; ++i )
621 for (
int a = 0; a < 2; ++a )
625 px = (
x[i] +
x[( i + 1 ) % 4] ) / 2.0;
626 py = (
y[i] +
y[( i + 1 ) % 4] ) / 2.0;
630 px = (
x[0] +
x[2] ) / 2.0;
631 py = (
y[0] +
y[2] ) / 2.0;
638 catch ( GEOSException &e )
640 qWarning(
"GEOS exception: %s", e.what() );
649 cost += mNextPart->polygonIntersectionCostForParts( polygon );
657 double angleDiff = 0.0;
658 double angleLast = 0.0;
664 double diff = std::fabs( tmp->
getAlpha() - angleLast );
665 if ( diff > 2 * M_PI )
667 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.
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 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.
bool isIntersect(double *bbox)
Is the labelposition intersect the bounding-box ?
double getAlpha() const
Returns the angle to rotate text (in rad).
Quadrant
Position of label candidate relative to feature.
LabelPosition::Quadrant quadrant
double angleDifferential()
Returns the angle differential of all LabelPosition parts.
void removeFromIndex(PalRtree< LabelPosition > &index)
Removes the label position from the specified index.
bool isInside(double *bbox)
Is the labelposition inside the bounding-box ?
bool crossesLine(PointSet *line) const
Returns true if this label crosses the specified line.
int getId() const
Returns the id.
void validateCost()
Make sure the cost is less than 1.
bool isIn(double *bbox)
Is the labelposition in the bounding-box ? (intersect or inside????)
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.
void offsetPosition(double xOffset, double yOffset)
Shift the label by specified offset.
double getDistanceToPoint(double xp, double yp) const
Gets distance from this label to a point. If point lies inside, returns negative number.
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.
void invalidateGeos() const
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)