Skip to content

Commit dc4049d

Browse files
committedJul 20, 2015
[FEATURE][labeling] Add option to only draw labels which fit
completely within polygon features (fix #12136)
1 parent 42bef4e commit dc4049d

File tree

11 files changed

+208
-56
lines changed

11 files changed

+208
-56
lines changed
 

‎python/core/qgspallabeling.sip

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,10 @@ class QgsPalLayerSettings
418418

419419
bool centroidWhole; // whether centroid calculated from whole or visible polygon
420420
bool centroidInside; // whether centroid-point calculated must be inside polygon
421+
422+
/** True if only labels which completely fit within a polygon are allowed.
423+
*/
424+
bool fitInPolygonOnly;
421425
double dist; // distance from the feature (in mm)
422426
bool distInMapUnits; //true if distance is in map units (otherwise in mm)
423427
QgsMapUnitScale distMapUnitScale;

‎src/app/qgslabelinggui.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
156156
mDirectSymbolsFrame->setVisible( layer->geometryType() == QGis::Line );
157157
mMinSizeFrame->setVisible( layer->geometryType() != QGis::Point );
158158
mPolygonObstacleTypeFrame->setVisible( layer->geometryType() == QGis::Polygon );
159+
mPolygonFeatureOptionsFrame->setVisible( layer->geometryType() == QGis::Polygon );
159160

160161
// field combo and expression button
161162
mFieldExpressionWidget->setLayer( mLayer );
@@ -310,6 +311,7 @@ void QgsLabelingGui::init()
310311
// populate placement options
311312
mCentroidRadioWhole->setChecked( lyr.centroidWhole );
312313
mCentroidInsideCheckBox->setChecked( lyr.centroidInside );
314+
mFitInsidePolygonCheckBox->setChecked( lyr.fitInPolygonOnly );
313315
mLineDistanceSpnBx->setValue( lyr.dist );
314316
mLineDistanceUnitWidget->setUnit( lyr.distInMapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
315317
mLineDistanceUnitWidget->setMapUnitScale( lyr.distMapUnitScale );
@@ -593,6 +595,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
593595
QWidget* curPlacementWdgt = stackedPlacement->currentWidget();
594596
lyr.centroidWhole = mCentroidRadioWhole->isChecked();
595597
lyr.centroidInside = mCentroidInsideCheckBox->isChecked();
598+
lyr.fitInPolygonOnly = mFitInsidePolygonCheckBox->isChecked();
596599
lyr.dist = mLineDistanceSpnBx->value();
597600
lyr.distInMapUnits = ( mLineDistanceUnitWidget->unit() == QgsSymbolV2::MapUnit );
598601
lyr.distMapUnitScale = mLineDistanceUnitWidget->getMapUnitScale();

‎src/core/pal/feature.cpp

Lines changed: 94 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ namespace pal
237237
}
238238
}
239239

240-
int FeaturePart::setPositionOverPoint( double x, double y, LabelPosition ***lPos, double angle )
240+
int FeaturePart::setPositionOverPoint( double x, double y, LabelPosition ***lPos, double angle, PointSet *mapShape )
241241
{
242242
int nbp = 1;
243243
*lPos = new LabelPosition *[nbp];
@@ -308,20 +308,30 @@ namespace pal
308308
double lx = x + xdiff;
309309
double ly = y + ydiff;
310310

311+
if ( mapShape && type == GEOS_POLYGON && mFeature->layer->fitInPolygonOnly() )
312+
{
313+
if ( !mapShape->containsLabelCandidate( lx, ly, labelW, labelH, angle ) )
314+
{
315+
delete[] *lPos;
316+
*lPos = 0;
317+
return 0;
318+
}
319+
}
320+
311321
( *lPos )[0] = new LabelPosition( id, lx, ly, labelW, labelH, angle, cost, this, false, quadrantFromOffset() );
312322
return nbp;
313323
}
314324

315-
int FeaturePart::setPositionForPoint( double x, double y, LabelPosition ***lPos, double angle )
325+
int FeaturePart::setPositionForPoint( double x, double y, LabelPosition ***lPos, double angle, PointSet *mapShape )
316326
{
317327

318328
#ifdef _DEBUG_
319329
std::cout << "SetPosition (point) : " << layer->name << "/" << uid << std::endl;
320330
#endif
321331

322-
double xrm = mFeature->label_x;
323-
double yrm = mFeature->label_y;
324-
double distlabel = mFeature->distlabel;
332+
double labelWidth = mFeature->label_x;
333+
double labelHeight = mFeature->label_y;
334+
double distanceToLabel = mFeature->distlabel;
325335

326336
int numberCandidates = mFeature->layer->pal->point_p;
327337

@@ -339,10 +349,10 @@ namespace pal
339349

340350
double gamma1, gamma2;
341351

342-
if ( distlabel > 0 )
352+
if ( distanceToLabel > 0 )
343353
{
344-
gamma1 = atan2( yrm / 2, distlabel + xrm / 2 );
345-
gamma2 = atan2( xrm / 2, distlabel + yrm / 2 );
354+
gamma1 = atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 );
355+
gamma2 = atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 );
346356
}
347357
else
348358
{
@@ -361,7 +371,7 @@ namespace pal
361371
std::cout << "Oups... label size error..." << std::endl;
362372
}
363373

364-
*lPos = new LabelPosition *[numberCandidates];
374+
QList< LabelPosition* > candidates;
365375

366376
int i;
367377
double angleToCandidate;
@@ -377,59 +387,59 @@ namespace pal
377387

378388
if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 ) // on the right
379389
{
380-
labelX += distlabel;
390+
labelX += distanceToLabel;
381391
double iota = ( angleToCandidate + gamma1 );
382392
if ( iota > a360 - gamma1 )
383393
iota -= a360;
384394

385395
//ly += -yrm/2.0 + tan(alpha)*(distlabel + xrm/2);
386-
labelY += -yrm + yrm * iota / ( 2 * gamma1 );
396+
labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 );
387397

388398
quadrant = LabelPosition::QuadrantRight;
389399
}
390400
else if ( angleToCandidate < a90 - gamma2 ) // top-right
391401
{
392-
labelX += distlabel * cos( angleToCandidate );
393-
labelY += distlabel * sin( angleToCandidate );
402+
labelX += distanceToLabel * cos( angleToCandidate );
403+
labelY += distanceToLabel * sin( angleToCandidate );
394404
quadrant = LabelPosition::QuadrantAboveRight;
395405
}
396406
else if ( angleToCandidate < a90 + gamma2 ) // top
397407
{
398408
//lx += -xrm/2.0 - tan(alpha+a90)*(distlabel + yrm/2);
399-
labelX += -xrm * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
400-
labelY += distlabel;
409+
labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
410+
labelY += distanceToLabel;
401411
quadrant = LabelPosition::QuadrantAbove;
402412
}
403413
else if ( angleToCandidate < a180 - gamma1 ) // top left
404414
{
405-
labelX += distlabel * cos( angleToCandidate ) - xrm;
406-
labelY += distlabel * sin( angleToCandidate );
415+
labelX += distanceToLabel * cos( angleToCandidate ) - labelWidth;
416+
labelY += distanceToLabel * sin( angleToCandidate );
407417
quadrant = LabelPosition::QuadrantAboveLeft;
408418
}
409419
else if ( angleToCandidate < a180 + gamma1 ) // left
410420
{
411-
labelX += -distlabel - xrm;
421+
labelX += -distanceToLabel - labelWidth;
412422
//ly += -yrm/2.0 - tan(alpha)*(distlabel + xrm/2);
413-
labelY += - ( angleToCandidate - a180 + gamma1 ) * yrm / ( 2 * gamma1 );
423+
labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
414424
quadrant = LabelPosition::QuadrantLeft;
415425
}
416426
else if ( angleToCandidate < a270 - gamma2 ) // down - left
417427
{
418-
labelX += distlabel * cos( angleToCandidate ) - xrm;
419-
labelY += distlabel * sin( angleToCandidate ) - yrm;
428+
labelX += distanceToLabel * cos( angleToCandidate ) - labelWidth;
429+
labelY += distanceToLabel * sin( angleToCandidate ) - labelHeight;
420430
quadrant = LabelPosition::QuadrantBelowLeft;
421431
}
422432
else if ( angleToCandidate < a270 + gamma2 ) // down
423433
{
424-
labelY += -distlabel - yrm;
434+
labelY += -distanceToLabel - labelHeight;
425435
//lx += -xrm/2.0 + tan(alpha+a90)*(distlabel + yrm/2);
426-
labelX += -xrm + ( angleToCandidate - a270 + gamma2 ) * xrm / ( 2 * gamma2 );
436+
labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
427437
quadrant = LabelPosition::QuadrantBelow;
428438
}
429439
else if ( angleToCandidate < a360 ) // down - right
430440
{
431-
labelX += distlabel * cos( angleToCandidate );
432-
labelY += distlabel * sin( angleToCandidate ) - yrm;
441+
labelX += distanceToLabel * cos( angleToCandidate );
442+
labelY += distanceToLabel * sin( angleToCandidate ) - labelHeight;
433443
quadrant = LabelPosition::QuadrantBelowRight;
434444
}
435445

@@ -440,7 +450,16 @@ namespace pal
440450
else
441451
cost = 0.0001 + 0.0020 * double( icost ) / double( numberCandidates - 1 );
442452

443-
( *lPos )[i] = new LabelPosition( i, labelX, labelY, xrm, yrm, angle, cost, this, false, quadrant );
453+
454+
if ( mapShape && type == GEOS_POLYGON && mFeature->layer->fitInPolygonOnly() )
455+
{
456+
if ( !mapShape->containsLabelCandidate( labelX, labelY, labelWidth, labelHeight, angle ) )
457+
{
458+
continue;
459+
}
460+
}
461+
462+
candidates << new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost, this, false, quadrant );
444463

445464
icost += inc;
446465

@@ -457,10 +476,19 @@ namespace pal
457476

458477
}
459478

460-
return numberCandidates;
479+
if ( !candidates.isEmpty() )
480+
{
481+
*lPos = new LabelPosition *[candidates.count()];
482+
for ( int i = 0; i < candidates.count(); ++i )
483+
{
484+
( *lPos )[i] = candidates.at( i );
485+
}
486+
}
487+
488+
return candidates.count();
461489
}
462490

463-
// TODO work with squared distance by remonving call to sqrt or dist_euc2d
491+
// TODO work with squared distance by removing call to sqrt or dist_euc2d
464492
int FeaturePart::setPositionForLine( LabelPosition ***lPos, PointSet *mapShape )
465493
{
466494
#ifdef _DEBUG_
@@ -969,8 +997,8 @@ namespace pal
969997
int i;
970998
int j;
971999

972-
double xrm = mFeature->label_x;
973-
double yrm = mFeature->label_y;
1000+
double labelWidth = mFeature->label_x;
1001+
double labelHeight = mFeature->label_y;
9741002

9751003
//print();
9761004

@@ -981,7 +1009,7 @@ namespace pal
9811009

9821010
shapes_toProcess.append( mapShape );
9831011

984-
splitPolygons( shapes_toProcess, shapes_final, xrm, yrm, mFeature->uid );
1012+
splitPolygons( shapes_toProcess, shapes_final, labelWidth, labelHeight, mFeature->uid );
9851013

9861014
int nbp;
9871015

@@ -997,7 +1025,7 @@ namespace pal
9971025
double dy;
9981026
int bbid;
9991027
double beta;
1000-
double diago = sqrt( xrm * xrm / 4.0 + yrm * yrm / 4 );
1028+
double diago = sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 );
10011029
double rx, ry;
10021030
CHullBox **boxes = new CHullBox*[shapes_final.size()];
10031031
j = 0;
@@ -1015,12 +1043,16 @@ namespace pal
10151043
}
10161044

10171045
//dx = dy = min( yrm, xrm ) / 2;
1018-
dx = xrm / 2.0;
1019-
dy = yrm / 2.0;
1046+
dx = labelWidth / 2.0;
1047+
dy = labelHeight / 2.0;
1048+
10201049

1050+
int numTry = 0;
1051+
1052+
//fit in polygon only mode slows down calculation a lot, so if it's enabled
1053+
//then use a smaller limit for number of iterations
1054+
int maxTry = mFeature->layer->fitInPolygonOnly() ? 7 : 10;
10211055

1022-
int num_try = 0;
1023-
int max_try = 10;
10241056
do
10251057
{
10261058
for ( bbid = 0; bbid < j; bbid++ )
@@ -1033,11 +1065,21 @@ namespace pal
10331065
std::cout << " Box size: " << box->length << "/" << box->width << std::endl;
10341066
std::cout << " Alpha: " << alpha << " " << alpha * 180 / M_PI << std::endl;
10351067
std::cout << " Dx;Dy: " << dx << " " << dy << std::endl;
1036-
std::cout << " LabelSizerm: " << xrm << " " << yrm << std::endl;
1068+
std::cout << " LabelSizerm: " << labelWidth << " " << labelHeight << std::endl;
10371069
std::cout << " LabelSizeUn: " << mFeature->label_x << " " << mFeature->label_y << std::endl;
10381070
continue;
10391071
}
10401072

1073+
if ( mFeature->layer->arrangement() == P_HORIZ && mFeature->layer->fitInPolygonOnly() )
1074+
{
1075+
//check width/height of bbox is sufficient for label
1076+
if ( box->length < labelWidth || box->width < labelHeight )
1077+
{
1078+
//no way label can fit in this box, skip it
1079+
continue;
1080+
}
1081+
}
1082+
10411083
#ifdef _DEBUG_FULL_
10421084
std::cout << "New BBox : " << bbid << std::endl;
10431085
for ( i = 0; i < 4; i++ )
@@ -1050,16 +1092,16 @@ namespace pal
10501092
if ( mFeature->layer->arrangement() == P_FREE )
10511093
{
10521094
enoughPlace = true;
1053-
px = ( box->x[0] + box->x[2] ) / 2 - xrm;
1054-
py = ( box->y[0] + box->y[2] ) / 2 - yrm;
1095+
px = ( box->x[0] + box->x[2] ) / 2 - labelWidth;
1096+
py = ( box->y[0] + box->y[2] ) / 2 - labelHeight;
10551097
int i, j;
10561098

10571099
// Virtual label: center on bbox center, label size = 2x original size
10581100
// alpha = 0.
10591101
// If all corner are in bbox then place candidates horizontaly
1060-
for ( rx = px, i = 0; i < 2; rx = rx + 2 * xrm, i++ )
1102+
for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ )
10611103
{
1062-
for ( ry = py, j = 0; j < 2; ry = ry + 2 * yrm, j++ )
1104+
for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ )
10631105
{
10641106
if ( !mapShape->containsPoint( rx, ry ) )
10651107
{
@@ -1079,7 +1121,7 @@ namespace pal
10791121
{
10801122
alpha = 0.0; // HORIZ
10811123
}
1082-
else if ( box->length > 1.5*xrm && box->width > 1.5*xrm )
1124+
else if ( box->length > 1.5*labelWidth && box->width > 1.5*labelWidth )
10831125
{
10841126
if ( box->alpha <= M_PI / 4 )
10851127
{
@@ -1099,7 +1141,7 @@ namespace pal
10991141
alpha = box->alpha;
11001142
}
11011143

1102-
beta = atan2( yrm, xrm ) + alpha;
1144+
beta = atan2( labelHeight, labelWidth ) + alpha;
11031145

11041146

11051147
//alpha = box->alpha;
@@ -1108,7 +1150,6 @@ namespace pal
11081150
dlx = cos( beta ) * diago;
11091151
dly = sin( beta ) * diago;
11101152

1111-
11121153
double px0, py0;
11131154

11141155
px0 = box->width / 2.0;
@@ -1128,11 +1169,13 @@ namespace pal
11281169
rx += box->x[0];
11291170
ry += box->y[0];
11301171

1131-
// Only accept candidate that center is in the polygon
1132-
if ( mapShape->containsPoint( rx, ry ) )
1172+
bool candidateAcceptable = ( mFeature->layer->fitInPolygonOnly()
1173+
? mapShape->containsLabelCandidate( rx - dlx, ry - dly, labelWidth, labelHeight, alpha )
1174+
: mapShape->containsPoint( rx, ry ) );
1175+
if ( candidateAcceptable )
11331176
{
11341177
// cost is set to minimal value, evaluated later
1135-
positions.append( new LabelPosition( id++, rx - dlx, ry - dly, xrm, yrm, alpha, 0.0001, this ) ); // Polygon
1178+
positions.append( new LabelPosition( id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001, this ) ); // Polygon
11361179
}
11371180
}
11381181
}
@@ -1143,10 +1186,10 @@ namespace pal
11431186
{
11441187
dx /= 2;
11451188
dy /= 2;
1146-
num_try++;
1189+
numTry++;
11471190
}
11481191
}
1149-
while ( nbp == 0 && num_try < max_try );
1192+
while ( nbp == 0 && numTry < maxTry );
11501193

11511194
nbp = positions.size();
11521195

@@ -1245,9 +1288,9 @@ namespace pal
12451288
double cx, cy;
12461289
mapShape->getCentroid( cx, cy, mFeature->layer->centroidInside() );
12471290
if ( mFeature->layer->arrangement() == P_POINT_OVER )
1248-
nbp = setPositionOverPoint( cx, cy, lPos, angle );
1291+
nbp = setPositionOverPoint( cx, cy, lPos, angle, mapShape );
12491292
else
1250-
nbp = setPositionForPoint( cx, cy, lPos, angle );
1293+
nbp = setPositionForPoint( cx, cy, lPos, angle, mapShape );
12511294
break;
12521295
case P_LINE:
12531296
nbp = setPositionForLine( lPos, mapShape );

‎src/core/pal/feature.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,20 @@ namespace pal
189189
* @param y y coordinate of the point
190190
* @param lPos pointer to an array of candidates, will be filled by generated candidates
191191
* @param angle orientation of the label
192+
* @param mapShape optional geometry of source polygon
192193
* @returns the number of generated candidates
193194
*/
194-
int setPositionForPoint( double x, double y, LabelPosition ***lPos, double angle );
195+
int setPositionForPoint( double x, double y, LabelPosition ***lPos, double angle, PointSet *mapShape = 0 );
195196

196197
/** Generate one candidate over or offset the specified point.
197198
* @param x x coordinate of the point
198199
* @param y y coordinate of the point
199200
* @param lPos pointer to an array of candidates, will be filled by generated candidate
200201
* @param angle orientation of the label
202+
* @param mapShape optional geometry of source polygon
201203
* @returns the number of generated candidates (always 1)
202204
*/
203-
int setPositionOverPoint( double x, double y, LabelPosition ***lPos, double angle );
205+
int setPositionOverPoint( double x, double y, LabelPosition ***lPos, double angle, PointSet *mapShape = 0 );
204206

205207
/** Generate candidates for line feature.
206208
* @param lPos pointer to an array of candidates, will be filled by generated candidates

‎src/core/pal/layer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ namespace pal
5252
, mLabelLayer( toLabel )
5353
, mDisplayAll( displayAll )
5454
, mCentroidInside( false )
55+
, mFitInPolygon( false )
5556
, mArrangement( arrangement )
5657
, mArrangementFlags( 0 )
5758
, mMode( LabelPerFeature )

‎src/core/pal/layer.h

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

224+
/** Sets whether labels which do not fit completely within a polygon feature
225+
* are discarded.
226+
* @param fitInPolygon set to true to discard labels which do not fit within
227+
* polygon features. Set to false to allow labels which partially fall outside
228+
* the polygon.
229+
* @see fitInPolygonOnly
230+
*/
231+
void setFitInPolygonOnly( bool fitInPolygon ) { mFitInPolygon = fitInPolygon; }
232+
233+
/** Returns whether labels which do not fit completely within a polygon feature
234+
* are discarded.
235+
* @see setFitInPolygonOnly
236+
*/
237+
bool fitInPolygonOnly() const { return mFitInPolygon; }
238+
224239
/** Register a feature in the layer.
225240
* @param geom_id unique identifier
226241
* @param userGeom user's geometry that implements the PalGeometry interface
@@ -277,6 +292,7 @@ namespace pal
277292
bool mLabelLayer;
278293
bool mDisplayAll;
279294
bool mCentroidInside;
295+
bool mFitInPolygon;
280296

281297
/** Optional flags used for some placement methods */
282298
Arrangement mArrangement;

‎src/core/pal/pointset.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,46 @@ namespace pal
277277
return result;
278278
}
279279

280+
bool PointSet::containsLabelCandidate( double x, double y, double width, double height, double alpha ) const
281+
{
282+
GEOSContextHandle_t geosctxt = geosContext();
283+
GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
284+
285+
GEOSCoordSeq_setX_r( geosctxt, coord, 0, x );
286+
GEOSCoordSeq_setY_r( geosctxt, coord, 0, y );
287+
if ( !qgsDoubleNear( alpha, 0.0 ) )
288+
{
289+
double beta = alpha + ( M_PI / 2 );
290+
double dx1 = cos( alpha ) * width;
291+
double dy1 = sin( alpha ) * width;
292+
double dx2 = cos( beta ) * height;
293+
double dy2 = sin( beta ) * height;
294+
GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + dx1 );
295+
GEOSCoordSeq_setY_r( geosctxt, coord, 1, y + dy1 );
296+
GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + dx1 + dx2 );
297+
GEOSCoordSeq_setY_r( geosctxt, coord, 2, y + dy1 + dy2 );
298+
GEOSCoordSeq_setX_r( geosctxt, coord, 3, x + dx2 );
299+
GEOSCoordSeq_setY_r( geosctxt, coord, 3, y + dy2 );
300+
}
301+
else
302+
{
303+
GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + width );
304+
GEOSCoordSeq_setY_r( geosctxt, coord, 1, y );
305+
GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + width );
306+
GEOSCoordSeq_setY_r( geosctxt, coord, 2, y + height );
307+
GEOSCoordSeq_setX_r( geosctxt, coord, 3, x );
308+
GEOSCoordSeq_setY_r( geosctxt, coord, 3, y + height );
309+
}
310+
//close ring
311+
GEOSCoordSeq_setX_r( geosctxt, coord, 4, x );
312+
GEOSCoordSeq_setY_r( geosctxt, coord, 4, y );
313+
314+
GEOSGeometry* bboxGeos = GEOSGeom_createLinearRing_r( geosctxt, coord );
315+
bool result = ( GEOSPreparedContains_r( geosctxt, preparedGeom(), bboxGeos ) == 1 );
316+
GEOSGeom_destroy_r( geosctxt, bboxGeos );
317+
return result;
318+
}
319+
280320
void PointSet::splitPolygons( QLinkedList<PointSet*> &shapes_toProcess,
281321
QLinkedList<PointSet*> &shapes_final,
282322
double xrm, double yrm, const QString& uid )

‎src/core/pal/pointset.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ namespace pal
8080
*/
8181
bool containsPoint( double x, double y ) const;
8282

83+
/** Tests whether a possible label candidate will fit completely within the shape.
84+
* @param x x-coordinate of label candidate
85+
* @param y y-coordinate of label candidate
86+
* @param width label width
87+
* @param height label height
88+
* @param alpha label angle
89+
* @returns true if point set completely contains candidate label
90+
*/
91+
bool containsLabelCandidate( double x, double y, double width, double height, double alpha = 0 ) const;
92+
8393
CHullBox * compute_chull_bbox();
8494

8595
/** Split a concave shape into several convex shapes.

‎src/core/qgspallabeling.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ QgsPalLayerSettings::QgsPalLayerSettings()
169169
placementFlags = AboveLine | MapOrientation;
170170
centroidWhole = false;
171171
centroidInside = false;
172+
fitInPolygonOnly = false;
172173
quadOffset = QuadrantOver;
173174
xOffset = 0;
174175
yOffset = 0;
@@ -379,6 +380,7 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
379380
placementFlags = s.placementFlags;
380381
centroidWhole = s.centroidWhole;
381382
centroidInside = s.centroidInside;
383+
fitInPolygonOnly = s.fitInPolygonOnly;
382384
quadOffset = s.quadOffset;
383385
xOffset = s.xOffset;
384386
yOffset = s.yOffset;
@@ -863,6 +865,7 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
863865
placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
864866
centroidWhole = layer->customProperty( "labeling/centroidWhole", QVariant( false ) ).toBool();
865867
centroidInside = layer->customProperty( "labeling/centroidInside", QVariant( false ) ).toBool();
868+
fitInPolygonOnly = layer->customProperty( "labeling/fitInPolygonOnly", QVariant( false ) ).toBool();
866869
dist = layer->customProperty( "labeling/dist" ).toDouble();
867870
distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool();
868871
distMapUnitScale.minScale = layer->customProperty( "labeling/distMapUnitMinScale", 0.0 ).toDouble();
@@ -1036,6 +1039,7 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
10361039
layer->setCustomProperty( "labeling/placementFlags", ( unsigned int )placementFlags );
10371040
layer->setCustomProperty( "labeling/centroidWhole", centroidWhole );
10381041
layer->setCustomProperty( "labeling/centroidInside", centroidInside );
1042+
layer->setCustomProperty( "labeling/fitInPolygonOnly", fitInPolygonOnly );
10391043
layer->setCustomProperty( "labeling/dist", dist );
10401044
layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits );
10411045
layer->setCustomProperty( "labeling/distMapUnitMinScale", distMapUnitScale.minScale );
@@ -3332,6 +3336,9 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames,
33323336
// set whether location of centroid must be inside of polygons
33333337
l->setCentroidInside( lyr.centroidInside );
33343338

3339+
// set whether labels must fall completely within the polygon
3340+
l->setFitInPolygonOnly( lyr.fitInPolygonOnly );
3341+
33353342
// set how to show upside-down labels
33363343
Layer::UpsideDownLabels upsdnlabels;
33373344
switch ( lyr.upsidedownLabels )

‎src/core/qgspallabeling.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,10 @@ class CORE_EXPORT QgsPalLayerSettings
393393

394394
bool centroidWhole; // whether centroid calculated from whole or visible polygon
395395
bool centroidInside; // whether centroid-point calculated must be inside polygon
396+
397+
/** True if only labels which completely fit within a polygon are allowed.
398+
*/
399+
bool fitInPolygonOnly;
396400
double dist; // distance from the feature (in mm)
397401
bool distInMapUnits; //true if distance is in map units (otherwise in mm)
398402
QgsMapUnitScale distMapUnitScale;

‎src/ui/qgslabelingguibase.ui

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@
524524
<rect>
525525
<x>0</x>
526526
<y>0</y>
527-
<width>578</width>
527+
<width>341</width>
528528
<height>388</height>
529529
</rect>
530530
</property>
@@ -4818,9 +4818,9 @@ font-style: italic;</string>
48184818
<property name="geometry">
48194819
<rect>
48204820
<x>0</x>
4821-
<y>-292</y>
4821+
<y>-298</y>
48224822
<width>578</width>
4823-
<height>655</height>
4823+
<height>683</height>
48244824
</rect>
48254825
</property>
48264826
<layout class="QVBoxLayout" name="verticalLayout_8">
@@ -5439,6 +5439,28 @@ font-style: italic;</string>
54395439
</layout>
54405440
</widget>
54415441
</item>
5442+
<item>
5443+
<widget class="QFrame" name="mPolygonFeatureOptionsFrame">
5444+
<property name="frameShape">
5445+
<enum>QFrame::NoFrame</enum>
5446+
</property>
5447+
<property name="frameShadow">
5448+
<enum>QFrame::Raised</enum>
5449+
</property>
5450+
<layout class="QHBoxLayout" name="horizontalLayout_12">
5451+
<property name="margin">
5452+
<number>0</number>
5453+
</property>
5454+
<item>
5455+
<widget class="QCheckBox" name="mFitInsidePolygonCheckBox">
5456+
<property name="text">
5457+
<string>Only draw labels which fit completely within feature</string>
5458+
</property>
5459+
</widget>
5460+
</item>
5461+
</layout>
5462+
</widget>
5463+
</item>
54425464
<item>
54435465
<widget class="QCheckBox" name="mChkNoObstacle">
54445466
<property name="enabled">

0 commit comments

Comments
 (0)
Please sign in to comment.