Skip to content

Commit 9e965f4

Browse files
committedAug 10, 2016
Fix "label only inside polygon" mode when used with perimeter placement
The option was not working with perimeter placements as perimeter placements alter the label feature geometry to be a boundary linestring - hence no labels where ever inside this boundary. Accordingly this refactors how the force label inside polygon option functions. Now QgsLabelFeatures can have a permissible zone geometry set, such that any label candidates outside of this permissible zone are discarded. This approach is more flexible as it could also be used for more labeling options in future, eg discarding label candidates which are too far from a centroid or something. Sponsored by Andreas Neumann (cherry-picked from c234d80)
1 parent 5ac4d0e commit 9e965f4

13 files changed

+194
-97
lines changed
 

‎src/core/pal/feature.cpp

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ LabelPosition::Quadrant FeaturePart::quadrantFromOffset() const
227227
}
228228
}
229229

230-
int FeaturePart::createCandidatesOverPoint( double x, double y, QList< LabelPosition*>& lPos, double angle, PointSet *mapShape )
230+
int FeaturePart::createCandidatesOverPoint( double x, double y, QList< LabelPosition*>& lPos, double angle )
231231
{
232232
int nbp = 1;
233233

@@ -294,9 +294,9 @@ int FeaturePart::createCandidatesOverPoint( double x, double y, QList< LabelPosi
294294
double lx = x + xdiff;
295295
double ly = y + ydiff;
296296

297-
if ( mapShape && type == GEOS_POLYGON && mLF->layer()->fitInPolygonOnly() )
297+
if ( mLF->permissibleZonePrepared() )
298298
{
299-
if ( !mapShape->containsLabelCandidate( lx, ly, labelW, labelH, angle ) )
299+
if ( !GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), lx, ly, labelW, labelH, angle ) )
300300
{
301301
return 0;
302302
}
@@ -419,18 +419,20 @@ int FeaturePart::createCandidatesAtOrderedPositionsOverPoint( double x, double y
419419
double labelX = referenceX + deltaX;
420420
double labelY = referenceY + deltaY;
421421

422-
lPos << new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost, this, false, quadrant );
423-
424-
//TODO - tweak
425-
cost += 0.001;
422+
if ( ! mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), labelX, labelY, labelWidth, labelHeight, angle ) )
423+
{
424+
lPos << new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost, this, false, quadrant );
425+
//TODO - tweak
426+
cost += 0.001;
427+
}
426428

427429
++i;
428430
}
429431

430432
return lPos.count();
431433
}
432434

433-
int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPosition* >& lPos, double angle, PointSet *mapShape )
435+
int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPosition* >& lPos, double angle )
434436
{
435437
double labelWidth = getLabelWidth();
436438
double labelHeight = getLabelHeight();
@@ -547,9 +549,9 @@ int FeaturePart::createCandidatesAroundPoint( double x, double y, QList< LabelPo
547549
cost = 0.0001 + 0.0020 * double( icost ) / double( numberCandidates - 1 );
548550

549551

550-
if ( mapShape && type == GEOS_POLYGON && mLF->layer()->fitInPolygonOnly() )
552+
if ( mLF->permissibleZonePrepared() )
551553
{
552-
if ( !mapShape->containsLabelCandidate( labelX, labelY, labelWidth, labelHeight, angle ) )
554+
if ( !GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), labelX, labelY, labelWidth, labelHeight, angle ) )
553555
{
554556
continue;
555557
}
@@ -698,17 +700,17 @@ int FeaturePart::createCandidatesAlongLine( QList< LabelPosition* >& lPos, Point
698700

699701
if ( aboveLine )
700702
{
701-
if ( !mLF->layer()->fitInPolygonOnly() || mapShape->containsLabelCandidate( bx + cos( beta ) *distlabel, by + sin( beta ) *distlabel, xrm, yrm, alpha ) )
703+
if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), bx + cos( beta ) *distlabel, by + sin( beta ) *distlabel, xrm, yrm, alpha ) )
702704
positions.append( new LabelPosition( i, bx + cos( beta ) *distlabel, by + sin( beta ) *distlabel, xrm, yrm, alpha, cost, this, isRightToLeft ) ); // Line
703705
}
704706
if ( belowLine )
705707
{
706-
if ( !mLF->layer()->fitInPolygonOnly() || mapShape->containsLabelCandidate( bx - cos( beta ) *( distlabel + yrm ), by - sin( beta ) *( distlabel + yrm ), xrm, yrm, alpha ) )
708+
if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), bx - cos( beta ) *( distlabel + yrm ), by - sin( beta ) *( distlabel + yrm ), xrm, yrm, alpha ) )
707709
positions.append( new LabelPosition( i, bx - cos( beta ) *( distlabel + yrm ), by - sin( beta ) *( distlabel + yrm ), xrm, yrm, alpha, cost, this, isRightToLeft ) ); // Line
708710
}
709711
if ( flags & FLAG_ON_LINE )
710712
{
711-
if ( !mLF->layer()->fitInPolygonOnly() || mapShape->containsLabelCandidate( bx - yrm*cos( beta ) / 2, by - yrm*sin( beta ) / 2, xrm, yrm, alpha ) )
713+
if ( !mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), bx - yrm*cos( beta ) / 2, by - yrm*sin( beta ) / 2, xrm, yrm, alpha ) )
712714
positions.append( new LabelPosition( i, bx - yrm*cos( beta ) / 2, by - yrm*sin( beta ) / 2, xrm, yrm, alpha, cost, this, isRightToLeft ) ); // Line
713715
}
714716
}
@@ -879,6 +881,7 @@ LabelPosition* FeaturePart::curvedPlacementAtOffset( PointSet* path_positions, d
879881
delete slp;
880882
return nullptr;
881883
}
884+
882885
// Shift the character downwards since the draw position is specified at the baseline
883886
// and we're calculating the mean line here
884887
double dist = 0.9 * li->label_height / 2;
@@ -1047,14 +1050,36 @@ int FeaturePart::createCurvedCandidatesAlongLine( QList< LabelPosition* >& lPos,
10471050

10481051
// average angle is calculated with respect to periodicity of angles
10491052
double angle_avg = atan2( sin_avg / li->char_num, cos_avg / li->char_num );
1050-
// displacement
1051-
if (( !reversed && ( flags & FLAG_ABOVE_LINE ) ) || ( reversed && ( flags & FLAG_BELOW_LINE ) ) )
1052-
positions.append( _createCurvedCandidate( slp, angle_avg, mLF->distLabel() + li->label_height / 2 ) );
1053-
if ( flags & FLAG_ON_LINE )
1054-
positions.append( _createCurvedCandidate( slp, angle_avg, 0 ) );
1055-
if (( !reversed && ( flags & FLAG_BELOW_LINE ) ) || ( reversed && ( flags & FLAG_ABOVE_LINE ) ) )
1056-
positions.append( _createCurvedCandidate( slp, angle_avg, -li->label_height / 2 - mLF->distLabel() ) );
1053+
// displacement - we loop through 3 times, generating above, online then below line placements successively
1054+
for ( int i = 0; i <= 2; ++i )
1055+
{
1056+
LabelPosition* p = nullptr;
1057+
if ( i == 0 && (( !reversed && ( flags & FLAG_ABOVE_LINE ) ) || ( reversed && ( flags & FLAG_BELOW_LINE ) ) ) )
1058+
p = _createCurvedCandidate( slp, angle_avg, mLF->distLabel() + li->label_height / 2 );
1059+
if ( i == 1 && flags & FLAG_ON_LINE )
1060+
p = _createCurvedCandidate( slp, angle_avg, 0 );
1061+
if ( i == 2 && (( !reversed && ( flags & FLAG_BELOW_LINE ) ) || ( reversed && ( flags & FLAG_ABOVE_LINE ) ) ) )
1062+
p = _createCurvedCandidate( slp, angle_avg, -li->label_height / 2 - mLF->distLabel() );
1063+
1064+
if ( p && mLF->permissibleZonePrepared() )
1065+
{
1066+
bool within = true;
1067+
LabelPosition* currentPos = p;
1068+
while ( within && currentPos )
1069+
{
1070+
within = GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), currentPos->getX(), currentPos->getY(), currentPos->getWidth(), currentPos->getHeight(), currentPos->getAlpha() );
1071+
currentPos = currentPos->getNextPart();
1072+
}
1073+
if ( !within )
1074+
{
1075+
delete p;
1076+
p = nullptr;
1077+
}
1078+
}
10571079

1080+
if ( p )
1081+
positions.append( p );
1082+
}
10581083
// delete original candidate
10591084
delete slp;
10601085
}
@@ -1144,7 +1169,7 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition*>& lPos, Point
11441169

11451170
//fit in polygon only mode slows down calculation a lot, so if it's enabled
11461171
//then use a smaller limit for number of iterations
1147-
int maxTry = mLF->layer()->fitInPolygonOnly() ? 7 : 10;
1172+
int maxTry = mLF->permissibleZonePrepared() ? 7 : 10;
11481173

11491174
do
11501175
{
@@ -1158,10 +1183,11 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition*>& lPos, Point
11581183
continue;
11591184
}
11601185

1161-
if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Horizontal && mLF->layer()->fitInPolygonOnly() )
1186+
if ( mLF->layer()->arrangement() == QgsPalLayerSettings::Horizontal && mLF->permissibleZonePrepared() )
11621187
{
11631188
//check width/height of bbox is sufficient for label
1164-
if ( box->length < labelWidth || box->width < labelHeight )
1189+
if ( mLF->permissibleZone().boundingBox().width() < labelWidth ||
1190+
mLF->permissibleZone().boundingBox().height() < labelHeight )
11651191
{
11661192
//no way label can fit in this box, skip it
11671193
continue;
@@ -1249,8 +1275,8 @@ int FeaturePart::createCandidatesForPolygon( QList< LabelPosition*>& lPos, Point
12491275
rx += box->x[0];
12501276
ry += box->y[0];
12511277

1252-
bool candidateAcceptable = ( mLF->layer()->fitInPolygonOnly()
1253-
? mapShape->containsLabelCandidate( rx - dlx, ry - dly, labelWidth, labelHeight, alpha )
1278+
bool candidateAcceptable = ( mLF->permissibleZonePrepared()
1279+
? GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), rx - dlx, ry - dly, labelWidth, labelHeight, alpha )
12541280
: mapShape->containsPoint( rx, ry ) );
12551281
if ( candidateAcceptable )
12561282
{
@@ -1337,9 +1363,9 @@ int FeaturePart::createCandidates( QList< LabelPosition*>& lPos,
13371363
double cx, cy;
13381364
mapShape->getCentroid( cx, cy, mLF->layer()->centroidInside() );
13391365
if ( mLF->layer()->arrangement() == QgsPalLayerSettings::OverPoint )
1340-
createCandidatesOverPoint( cx, cy, lPos, angle, mapShape );
1366+
createCandidatesOverPoint( cx, cy, lPos, angle );
13411367
else
1342-
createCandidatesAroundPoint( cx, cy, lPos, angle, mapShape );
1368+
createCandidatesAroundPoint( cx, cy, lPos, angle );
13431369
break;
13441370
case QgsPalLayerSettings::Line:
13451371
createCandidatesAlongLine( lPos, mapShape );

‎src/core/pal/feature.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,20 +131,18 @@ namespace pal
131131
* @param y y coordinate of the point
132132
* @param lPos pointer to an array of candidates, will be filled by generated candidates
133133
* @param angle orientation of the label
134-
* @param mapShape optional geometry of source polygon
135134
* @returns the number of generated candidates
136135
*/
137-
int createCandidatesAroundPoint( double x, double y, QList<LabelPosition *> &lPos, double angle, PointSet *mapShape = nullptr );
136+
int createCandidatesAroundPoint( double x, double y, QList<LabelPosition *> &lPos, double angle );
138137

139138
/** Generate one candidate over or offset the specified point.
140139
* @param x x coordinate of the point
141140
* @param y y coordinate of the point
142141
* @param lPos pointer to an array of candidates, will be filled by generated candidate
143142
* @param angle orientation of the label
144-
* @param mapShape optional geometry of source polygon
145143
* @returns the number of generated candidates (always 1)
146144
*/
147-
int createCandidatesOverPoint( double x, double y, QList<LabelPosition *> &lPos, double angle, PointSet *mapShape = nullptr );
145+
int createCandidatesOverPoint( double x, double y, QList<LabelPosition *> &lPos, double angle );
148146

149147
/** Generates candidates following a prioritised list of predefined positions around a point.
150148
* @param x x coordinate of the point

‎src/core/pal/geomfunction.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "feature.h"
3232
#include "util.h"
3333
#include "qgis.h"
34+
#include "pal.h"
35+
#include "qgsmessagelog.h"
3436

3537
using namespace pal;
3638

@@ -315,6 +317,57 @@ int GeomFunction::reorderPolygon( int nbPoints, double *x, double *y )
315317
return 0;
316318
}
317319

320+
bool GeomFunction::containsCandidate( const GEOSPreparedGeometry *geom, double x, double y, double width, double height, double alpha )
321+
{
322+
if ( !geom )
323+
return false;
324+
325+
GEOSContextHandle_t geosctxt = geosContext();
326+
GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
327+
328+
GEOSCoordSeq_setX_r( geosctxt, coord, 0, x );
329+
GEOSCoordSeq_setY_r( geosctxt, coord, 0, y );
330+
if ( !qgsDoubleNear( alpha, 0.0 ) )
331+
{
332+
double beta = alpha + ( M_PI / 2 );
333+
double dx1 = cos( alpha ) * width;
334+
double dy1 = sin( alpha ) * width;
335+
double dx2 = cos( beta ) * height;
336+
double dy2 = sin( beta ) * height;
337+
GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + dx1 );
338+
GEOSCoordSeq_setY_r( geosctxt, coord, 1, y + dy1 );
339+
GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + dx1 + dx2 );
340+
GEOSCoordSeq_setY_r( geosctxt, coord, 2, y + dy1 + dy2 );
341+
GEOSCoordSeq_setX_r( geosctxt, coord, 3, x + dx2 );
342+
GEOSCoordSeq_setY_r( geosctxt, coord, 3, y + dy2 );
343+
}
344+
else
345+
{
346+
GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + width );
347+
GEOSCoordSeq_setY_r( geosctxt, coord, 1, y );
348+
GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + width );
349+
GEOSCoordSeq_setY_r( geosctxt, coord, 2, y + height );
350+
GEOSCoordSeq_setX_r( geosctxt, coord, 3, x );
351+
GEOSCoordSeq_setY_r( geosctxt, coord, 3, y + height );
352+
}
353+
//close ring
354+
GEOSCoordSeq_setX_r( geosctxt, coord, 4, x );
355+
GEOSCoordSeq_setY_r( geosctxt, coord, 4, y );
356+
357+
try
358+
{
359+
GEOSGeometry* bboxGeos = GEOSGeom_createLinearRing_r( geosctxt, coord );
360+
bool result = ( GEOSPreparedContainsProperly_r( geosctxt, geom, bboxGeos ) == 1 );
361+
GEOSGeom_destroy_r( geosctxt, bboxGeos );
362+
return result;
363+
}
364+
catch ( GEOSException &e )
365+
{
366+
QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
367+
return false;
368+
}
369+
}
370+
318371
void GeomFunction::findLineCircleIntersection( double cx, double cy, double radius,
319372
double x1, double y1, double x2, double y2,
320373
double& xRes, double& yRes )

‎src/core/pal/geomfunction.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define PAL_GEOM_FUNCTION
3232

3333
#include "util.h"
34+
#include "qgsgeos.h"
3435

3536
namespace pal
3637
{
@@ -98,6 +99,17 @@ namespace pal
9899
//! Reorder points to have cross prod ((x,y)[i], (x,y)[i+1), point) > 0 when point is outside
99100
static int reorderPolygon( int nbPoints, double *x, double *y );
100101

102+
/** Returns true if a GEOS prepared geometry totally contains a label candidate.
103+
* @param geom GEOS prepared geometry
104+
* @param x candidate x
105+
* @param y candidate y
106+
* @param width candidate width
107+
* @param height candidate height
108+
* @param alpha candidate angle
109+
* @returns true if candidate is totally contained
110+
*/
111+
static bool containsCandidate( const GEOSPreparedGeometry* geom, double x, double y, double width, double height, double alpha );
112+
101113
};
102114
} //namespace
103115

‎src/core/pal/layer.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ Layer::Layer( QgsAbstractLabelProvider* provider, const QString& name, QgsPalLay
4949
, mLabelLayer( toLabel )
5050
, mDisplayAll( displayAll )
5151
, mCentroidInside( false )
52-
, mFitInPolygon( false )
5352
, mArrangement( arrangement )
5453
, mArrangementFlags( nullptr )
5554
, mMode( LabelPerFeature )

‎src/core/pal/layer.h

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -210,21 +210,6 @@ namespace pal
210210
*/
211211
bool centroidInside() const { return mCentroidInside; }
212212

213-
/** Sets whether labels which do not fit completely within a polygon feature
214-
* are discarded.
215-
* @param fitInPolygon set to true to discard labels which do not fit within
216-
* polygon features. Set to false to allow labels which partially fall outside
217-
* the polygon.
218-
* @see fitInPolygonOnly
219-
*/
220-
void setFitInPolygonOnly( bool fitInPolygon ) { mFitInPolygon = fitInPolygon; }
221-
222-
/** Returns whether labels which do not fit completely within a polygon feature
223-
* are discarded.
224-
* @see setFitInPolygonOnly
225-
*/
226-
bool fitInPolygonOnly() const { return mFitInPolygon; }
227-
228213
/** Register a feature in the layer.
229214
*
230215
* Does not take ownership of the label feature (it is owned by its provider).
@@ -266,7 +251,6 @@ namespace pal
266251
bool mLabelLayer;
267252
bool mDisplayAll;
268253
bool mCentroidInside;
269-
bool mFitInPolygon;
270254

271255
/** Optional flags used for some placement methods */
272256
QgsPalLayerSettings::Placement mArrangement;

‎src/core/pal/pointset.cpp

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -289,50 +289,7 @@ bool PointSet::containsPoint( double x, double y ) const
289289

290290
bool PointSet::containsLabelCandidate( double x, double y, double width, double height, double alpha ) const
291291
{
292-
GEOSContextHandle_t geosctxt = geosContext();
293-
GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
294-
295-
GEOSCoordSeq_setX_r( geosctxt, coord, 0, x );
296-
GEOSCoordSeq_setY_r( geosctxt, coord, 0, y );
297-
if ( !qgsDoubleNear( alpha, 0.0 ) )
298-
{
299-
double beta = alpha + ( M_PI / 2 );
300-
double dx1 = cos( alpha ) * width;
301-
double dy1 = sin( alpha ) * width;
302-
double dx2 = cos( beta ) * height;
303-
double dy2 = sin( beta ) * height;
304-
GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + dx1 );
305-
GEOSCoordSeq_setY_r( geosctxt, coord, 1, y + dy1 );
306-
GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + dx1 + dx2 );
307-
GEOSCoordSeq_setY_r( geosctxt, coord, 2, y + dy1 + dy2 );
308-
GEOSCoordSeq_setX_r( geosctxt, coord, 3, x + dx2 );
309-
GEOSCoordSeq_setY_r( geosctxt, coord, 3, y + dy2 );
310-
}
311-
else
312-
{
313-
GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + width );
314-
GEOSCoordSeq_setY_r( geosctxt, coord, 1, y );
315-
GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + width );
316-
GEOSCoordSeq_setY_r( geosctxt, coord, 2, y + height );
317-
GEOSCoordSeq_setX_r( geosctxt, coord, 3, x );
318-
GEOSCoordSeq_setY_r( geosctxt, coord, 3, y + height );
319-
}
320-
//close ring
321-
GEOSCoordSeq_setX_r( geosctxt, coord, 4, x );
322-
GEOSCoordSeq_setY_r( geosctxt, coord, 4, y );
323-
324-
try
325-
{
326-
GEOSGeometry* bboxGeos = GEOSGeom_createLinearRing_r( geosctxt, coord );
327-
bool result = ( GEOSPreparedContainsProperly_r( geosctxt, preparedGeom(), bboxGeos ) == 1 );
328-
GEOSGeom_destroy_r( geosctxt, bboxGeos );
329-
return result;
330-
}
331-
catch ( GEOSException &e )
332-
{
333-
QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
334-
return false;
335-
}
292+
return GeomFunction::containsCandidate( preparedGeom(), x, y, width, height, alpha );
336293
}
337294

338295
void PointSet::splitPolygons( QLinkedList<PointSet*> &shapes_toProcess,

‎src/core/qgslabelfeature.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ QgsLabelFeature::QgsLabelFeature( QgsFeatureId id, GEOSGeometry* geometry, QSize
3535
, mIsObstacle( false )
3636
, mObstacleFactor( 1 )
3737
, mInfo( nullptr )
38+
, mPermissibleZoneGeosPrepared( nullptr )
3839
{
3940
}
4041

@@ -46,6 +47,9 @@ QgsLabelFeature::~QgsLabelFeature()
4647
if ( mObstacleGeometry )
4748
GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), mObstacleGeometry );
4849

50+
if ( mPermissibleZoneGeosPrepared )
51+
GEOSPreparedGeom_destroy_r( QgsGeometry::getGEOSHandler(), mPermissibleZoneGeosPrepared );
52+
4953
delete mInfo;
5054
}
5155

@@ -56,3 +60,23 @@ void QgsLabelFeature::setObstacleGeometry( GEOSGeometry* obstacleGeom )
5660

5761
mObstacleGeometry = obstacleGeom;
5862
}
63+
64+
void QgsLabelFeature::setPermissibleZone( const QgsGeometry &geometry )
65+
{
66+
mPermissibleZone = geometry;
67+
68+
if ( mPermissibleZoneGeosPrepared )
69+
{
70+
GEOSPreparedGeom_destroy_r( QgsGeometry::getGEOSHandler(), mPermissibleZoneGeosPrepared );
71+
mPermissibleZoneGeosPrepared = nullptr;
72+
}
73+
74+
if ( mPermissibleZone.isEmpty() )
75+
return;
76+
77+
const GEOSGeometry* zoneGeos = mPermissibleZone.asGeos();
78+
if ( !zoneGeos )
79+
return;
80+
81+
mPermissibleZoneGeosPrepared = GEOSPrepare_r( QgsGeometry::getGEOSHandler(), zoneGeos );
82+
}

‎src/core/qgslabelfeature.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,32 @@ class CORE_EXPORT QgsLabelFeature
107107
*/
108108
GEOSGeometry* obstacleGeometry() const { return mObstacleGeometry; }
109109

110+
/** Sets the label's permissible zone geometry. If set, the feature's label MUST be fully contained
111+
* within this zone, and the feature will not be labeled if no candidates can be generated which
112+
* are not contained within the zone.
113+
* @param geometry permissible zone geometry. If an invalid QgsGeometry is passed then no zone limit
114+
* will be applied to the label candidates (this is the default behaviour).
115+
* @note added in QGIS 3.0
116+
* @see permissibleZone()
117+
*/
118+
void setPermissibleZone( const QgsGeometry& geometry );
119+
120+
/** Returns the label's permissible zone geometry. If a valid geometry is returned, the feature's label
121+
* MUST be fully contained within this zone, and the feature will not be labeled if no candidates can be
122+
* generated which are not contained within the zone.
123+
* @note added in QGIS 3.0
124+
* @see setPermissibleZone()
125+
* @see permissibleZonePrepared()
126+
*/
127+
QgsGeometry permissibleZone() const { return mPermissibleZone; }
128+
129+
/** Returns a GEOS prepared geometry representing the label's permissibleZone().
130+
* @see permissibleZone()
131+
* @note added in QGIS 3.0
132+
*/
133+
//TODO - remove when QgsGeometry caches GEOS preparedness
134+
const GEOSPreparedGeometry* permissibleZonePrepared() const { return mPermissibleZoneGeosPrepared; }
135+
110136
//! Size of the label (in map units)
111137
QSizeF size() const { return mSize; }
112138

@@ -316,6 +342,8 @@ class CORE_EXPORT QgsLabelFeature
316342
GEOSGeometry* mGeometry;
317343
//! Optional geometry to use for label obstacles, if different to mGeometry
318344
GEOSGeometry* mObstacleGeometry;
345+
//! Optional geometry to use for label's permissible zone
346+
QgsGeometry mPermissibleZone;
319347
//! Width and height of the label
320348
QSizeF mSize;
321349
//! Visual margin of label contents
@@ -358,6 +386,12 @@ class CORE_EXPORT QgsLabelFeature
358386
QString mLabelText;
359387
//! extra information for curved labels (may be null)
360388
pal::LabelInfo* mInfo;
389+
390+
private:
391+
392+
// TODO - not required when QgsGeometry caches geos preparedness
393+
const GEOSPreparedGeometry* mPermissibleZoneGeosPrepared;
394+
361395
};
362396

363397
#endif // QGSLABELFEATURE_H

‎src/core/qgslabelingenginev2.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,6 @@ void QgsLabelingEngineV2::processProvider( QgsAbstractLabelProvider* provider, Q
126126
// set whether location of centroid must be inside of polygons
127127
l->setCentroidInside( flags.testFlag( QgsAbstractLabelProvider::CentroidMustBeInside ) );
128128

129-
// set whether labels must fall completely within the polygon
130-
l->setFitInPolygonOnly( flags.testFlag( QgsAbstractLabelProvider::FitInPolygonOnly ) );
131-
132129
// set how to show upside-down labels
133130
pal::Layer::UpsideDownLabels upsdnlabels;
134131
switch ( provider->upsidedownLabels() )

‎src/core/qgslabelingenginev2.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ class CORE_EXPORT QgsAbstractLabelProvider
5757
DrawAllLabels = 1 << 2, //!< whether all features will be labelled even though overlaps occur
5858
MergeConnectedLines = 1 << 3, //!< whether adjacent lines (with the same label text) should be merged
5959
CentroidMustBeInside = 1 << 4, //!< whether location of centroid must be inside of polygons
60-
FitInPolygonOnly = 1 << 5, //!< whether labels must fall completely within the polygon
6160
LabelPerFeaturePart = 1 << 6, //!< whether to label each part of multi-part features separately
6261
};
6362
Q_DECLARE_FLAGS( Flags, Flag )

‎src/core/qgspallabeling.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2436,6 +2436,20 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &cont
24362436
doClip = true;
24372437
}
24382438

2439+
// if using fitInPolygonOnly option, generate the permissible zone (must happen before geometry is modified - eg
2440+
// as a result of using perimeter based labeling and the geometry is converted to a boundary)
2441+
QgsGeometry permissibleZone;
2442+
if ( geom->type() == QGis::Polygon && fitInPolygonOnly )
2443+
{
2444+
permissibleZone = *geom;
2445+
if ( QgsPalLabeling::geometryRequiresPreparation( &permissibleZone, context, ct, doClip ? extentGeom : nullptr ) )
2446+
{
2447+
QgsGeometry* preparedZone = QgsPalLabeling::prepareGeometry( &permissibleZone, context, ct, doClip ? extentGeom : nullptr );
2448+
permissibleZone = *preparedZone;
2449+
delete preparedZone;
2450+
}
2451+
}
2452+
24392453
const GEOSGeometry* geos_geom = nullptr;
24402454
const QgsGeometry* preparedGeom = geom;
24412455
QScopedPointer<QgsGeometry> scopedPreparedGeom;
@@ -2834,6 +2848,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &cont
28342848
( *labelFeature )->setAlwaysShow( alwaysShow );
28352849
( *labelFeature )->setRepeatDistance( repeatDist );
28362850
( *labelFeature )->setLabelText( labelText );
2851+
( *labelFeature )->setPermissibleZone( permissibleZone );
28372852
if ( geosObstacleGeomClone )
28382853
{
28392854
( *labelFeature )->setObstacleGeometry( geosObstacleGeomClone );

‎src/core/qgsvectorlayerlabelprovider.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ void QgsVectorLayerLabelProvider::init()
101101
if ( mSettings.displayAll ) mFlags |= DrawAllLabels;
102102
if ( mSettings.mergeLines ) mFlags |= MergeConnectedLines;
103103
if ( mSettings.centroidInside ) mFlags |= CentroidMustBeInside;
104-
if ( mSettings.fitInPolygonOnly ) mFlags |= FitInPolygonOnly;
105104
if ( mSettings.labelPerPart ) mFlags |= LabelPerFeaturePart;
106105
mPriority = 1 - mSettings.priority / 10.0; // convert 0..10 --> 1..0
107106

0 commit comments

Comments
 (0)
Please sign in to comment.