diff --git a/python/core/qgspallabeling.sip b/python/core/qgspallabeling.sip index e8bbb829917..09c75b99651 100644 --- a/python/core/qgspallabeling.sip +++ b/python/core/qgspallabeling.sip @@ -418,6 +418,10 @@ class QgsPalLayerSettings bool centroidWhole; // whether centroid calculated from whole or visible polygon bool centroidInside; // whether centroid-point calculated must be inside polygon + + /** True if only labels which completely fit within a polygon are allowed. + */ + bool fitInPolygonOnly; double dist; // distance from the feature (in mm) bool distInMapUnits; //true if distance is in map units (otherwise in mm) QgsMapUnitScale distMapUnitScale; diff --git a/src/app/qgslabelinggui.cpp b/src/app/qgslabelinggui.cpp index 850a525b333..2dac9d55987 100644 --- a/src/app/qgslabelinggui.cpp +++ b/src/app/qgslabelinggui.cpp @@ -156,6 +156,7 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, mDirectSymbolsFrame->setVisible( layer->geometryType() == QGis::Line ); mMinSizeFrame->setVisible( layer->geometryType() != QGis::Point ); mPolygonObstacleTypeFrame->setVisible( layer->geometryType() == QGis::Polygon ); + mPolygonFeatureOptionsFrame->setVisible( layer->geometryType() == QGis::Polygon ); // field combo and expression button mFieldExpressionWidget->setLayer( mLayer ); @@ -310,6 +311,7 @@ void QgsLabelingGui::init() // populate placement options mCentroidRadioWhole->setChecked( lyr.centroidWhole ); mCentroidInsideCheckBox->setChecked( lyr.centroidInside ); + mFitInsidePolygonCheckBox->setChecked( lyr.fitInPolygonOnly ); mLineDistanceSpnBx->setValue( lyr.dist ); mLineDistanceUnitWidget->setUnit( lyr.distInMapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM ); mLineDistanceUnitWidget->setMapUnitScale( lyr.distMapUnitScale ); @@ -593,6 +595,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings() QWidget* curPlacementWdgt = stackedPlacement->currentWidget(); lyr.centroidWhole = mCentroidRadioWhole->isChecked(); lyr.centroidInside = mCentroidInsideCheckBox->isChecked(); + lyr.fitInPolygonOnly = mFitInsidePolygonCheckBox->isChecked(); lyr.dist = mLineDistanceSpnBx->value(); lyr.distInMapUnits = ( mLineDistanceUnitWidget->unit() == QgsSymbolV2::MapUnit ); lyr.distMapUnitScale = mLineDistanceUnitWidget->getMapUnitScale(); diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 9b3bcda49f8..0307c1d4ea7 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -237,7 +237,7 @@ namespace pal } } - int FeaturePart::setPositionOverPoint( double x, double y, LabelPosition ***lPos, double angle ) + int FeaturePart::setPositionOverPoint( double x, double y, LabelPosition ***lPos, double angle, PointSet *mapShape ) { int nbp = 1; *lPos = new LabelPosition *[nbp]; @@ -308,20 +308,30 @@ namespace pal double lx = x + xdiff; double ly = y + ydiff; + if ( mapShape && type == GEOS_POLYGON && mFeature->layer->fitInPolygonOnly() ) + { + if ( !mapShape->containsLabelCandidate( lx, ly, labelW, labelH, angle ) ) + { + delete[] *lPos; + *lPos = 0; + return 0; + } + } + ( *lPos )[0] = new LabelPosition( id, lx, ly, labelW, labelH, angle, cost, this, false, quadrantFromOffset() ); return nbp; } - int FeaturePart::setPositionForPoint( double x, double y, LabelPosition ***lPos, double angle ) + int FeaturePart::setPositionForPoint( double x, double y, LabelPosition ***lPos, double angle, PointSet *mapShape ) { #ifdef _DEBUG_ std::cout << "SetPosition (point) : " << layer->name << "/" << uid << std::endl; #endif - double xrm = mFeature->label_x; - double yrm = mFeature->label_y; - double distlabel = mFeature->distlabel; + double labelWidth = mFeature->label_x; + double labelHeight = mFeature->label_y; + double distanceToLabel = mFeature->distlabel; int numberCandidates = mFeature->layer->pal->point_p; @@ -339,10 +349,10 @@ namespace pal double gamma1, gamma2; - if ( distlabel > 0 ) + if ( distanceToLabel > 0 ) { - gamma1 = atan2( yrm / 2, distlabel + xrm / 2 ); - gamma2 = atan2( xrm / 2, distlabel + yrm / 2 ); + gamma1 = atan2( labelHeight / 2, distanceToLabel + labelWidth / 2 ); + gamma2 = atan2( labelWidth / 2, distanceToLabel + labelHeight / 2 ); } else { @@ -361,7 +371,7 @@ namespace pal std::cout << "Oups... label size error..." << std::endl; } - *lPos = new LabelPosition *[numberCandidates]; + QList< LabelPosition* > candidates; int i; double angleToCandidate; @@ -377,59 +387,59 @@ namespace pal if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 ) // on the right { - labelX += distlabel; + labelX += distanceToLabel; double iota = ( angleToCandidate + gamma1 ); if ( iota > a360 - gamma1 ) iota -= a360; //ly += -yrm/2.0 + tan(alpha)*(distlabel + xrm/2); - labelY += -yrm + yrm * iota / ( 2 * gamma1 ); + labelY += -labelHeight + labelHeight * iota / ( 2 * gamma1 ); quadrant = LabelPosition::QuadrantRight; } else if ( angleToCandidate < a90 - gamma2 ) // top-right { - labelX += distlabel * cos( angleToCandidate ); - labelY += distlabel * sin( angleToCandidate ); + labelX += distanceToLabel * cos( angleToCandidate ); + labelY += distanceToLabel * sin( angleToCandidate ); quadrant = LabelPosition::QuadrantAboveRight; } else if ( angleToCandidate < a90 + gamma2 ) // top { //lx += -xrm/2.0 - tan(alpha+a90)*(distlabel + yrm/2); - labelX += -xrm * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 ); - labelY += distlabel; + labelX += -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 ); + labelY += distanceToLabel; quadrant = LabelPosition::QuadrantAbove; } else if ( angleToCandidate < a180 - gamma1 ) // top left { - labelX += distlabel * cos( angleToCandidate ) - xrm; - labelY += distlabel * sin( angleToCandidate ); + labelX += distanceToLabel * cos( angleToCandidate ) - labelWidth; + labelY += distanceToLabel * sin( angleToCandidate ); quadrant = LabelPosition::QuadrantAboveLeft; } else if ( angleToCandidate < a180 + gamma1 ) // left { - labelX += -distlabel - xrm; + labelX += -distanceToLabel - labelWidth; //ly += -yrm/2.0 - tan(alpha)*(distlabel + xrm/2); - labelY += - ( angleToCandidate - a180 + gamma1 ) * yrm / ( 2 * gamma1 ); + labelY += - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 ); quadrant = LabelPosition::QuadrantLeft; } else if ( angleToCandidate < a270 - gamma2 ) // down - left { - labelX += distlabel * cos( angleToCandidate ) - xrm; - labelY += distlabel * sin( angleToCandidate ) - yrm; + labelX += distanceToLabel * cos( angleToCandidate ) - labelWidth; + labelY += distanceToLabel * sin( angleToCandidate ) - labelHeight; quadrant = LabelPosition::QuadrantBelowLeft; } else if ( angleToCandidate < a270 + gamma2 ) // down { - labelY += -distlabel - yrm; + labelY += -distanceToLabel - labelHeight; //lx += -xrm/2.0 + tan(alpha+a90)*(distlabel + yrm/2); - labelX += -xrm + ( angleToCandidate - a270 + gamma2 ) * xrm / ( 2 * gamma2 ); + labelX += -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 ); quadrant = LabelPosition::QuadrantBelow; } else if ( angleToCandidate < a360 ) // down - right { - labelX += distlabel * cos( angleToCandidate ); - labelY += distlabel * sin( angleToCandidate ) - yrm; + labelX += distanceToLabel * cos( angleToCandidate ); + labelY += distanceToLabel * sin( angleToCandidate ) - labelHeight; quadrant = LabelPosition::QuadrantBelowRight; } @@ -440,7 +450,16 @@ namespace pal else cost = 0.0001 + 0.0020 * double( icost ) / double( numberCandidates - 1 ); - ( *lPos )[i] = new LabelPosition( i, labelX, labelY, xrm, yrm, angle, cost, this, false, quadrant ); + + if ( mapShape && type == GEOS_POLYGON && mFeature->layer->fitInPolygonOnly() ) + { + if ( !mapShape->containsLabelCandidate( labelX, labelY, labelWidth, labelHeight, angle ) ) + { + continue; + } + } + + candidates << new LabelPosition( i, labelX, labelY, labelWidth, labelHeight, angle, cost, this, false, quadrant ); icost += inc; @@ -457,10 +476,19 @@ namespace pal } - return numberCandidates; + if ( !candidates.isEmpty() ) + { + *lPos = new LabelPosition *[candidates.count()]; + for ( int i = 0; i < candidates.count(); ++i ) + { + ( *lPos )[i] = candidates.at( i ); + } + } + + return candidates.count(); } -// TODO work with squared distance by remonving call to sqrt or dist_euc2d +// TODO work with squared distance by removing call to sqrt or dist_euc2d int FeaturePart::setPositionForLine( LabelPosition ***lPos, PointSet *mapShape ) { #ifdef _DEBUG_ @@ -969,8 +997,8 @@ namespace pal int i; int j; - double xrm = mFeature->label_x; - double yrm = mFeature->label_y; + double labelWidth = mFeature->label_x; + double labelHeight = mFeature->label_y; //print(); @@ -981,7 +1009,7 @@ namespace pal shapes_toProcess.append( mapShape ); - splitPolygons( shapes_toProcess, shapes_final, xrm, yrm, mFeature->uid ); + splitPolygons( shapes_toProcess, shapes_final, labelWidth, labelHeight, mFeature->uid ); int nbp; @@ -997,7 +1025,7 @@ namespace pal double dy; int bbid; double beta; - double diago = sqrt( xrm * xrm / 4.0 + yrm * yrm / 4 ); + double diago = sqrt( labelWidth * labelWidth / 4.0 + labelHeight * labelHeight / 4 ); double rx, ry; CHullBox **boxes = new CHullBox*[shapes_final.size()]; j = 0; @@ -1015,12 +1043,16 @@ namespace pal } //dx = dy = min( yrm, xrm ) / 2; - dx = xrm / 2.0; - dy = yrm / 2.0; + dx = labelWidth / 2.0; + dy = labelHeight / 2.0; - int num_try = 0; - int max_try = 10; + int numTry = 0; + + //fit in polygon only mode slows down calculation a lot, so if it's enabled + //then use a smaller limit for number of iterations + int maxTry = mFeature->layer->fitInPolygonOnly() ? 7 : 10; + do { for ( bbid = 0; bbid < j; bbid++ ) @@ -1033,11 +1065,21 @@ namespace pal std::cout << " Box size: " << box->length << "/" << box->width << std::endl; std::cout << " Alpha: " << alpha << " " << alpha * 180 / M_PI << std::endl; std::cout << " Dx;Dy: " << dx << " " << dy << std::endl; - std::cout << " LabelSizerm: " << xrm << " " << yrm << std::endl; + std::cout << " LabelSizerm: " << labelWidth << " " << labelHeight << std::endl; std::cout << " LabelSizeUn: " << mFeature->label_x << " " << mFeature->label_y << std::endl; continue; } + if ( mFeature->layer->arrangement() == P_HORIZ && mFeature->layer->fitInPolygonOnly() ) + { + //check width/height of bbox is sufficient for label + if ( box->length < labelWidth || box->width < labelHeight ) + { + //no way label can fit in this box, skip it + continue; + } + } + #ifdef _DEBUG_FULL_ std::cout << "New BBox : " << bbid << std::endl; for ( i = 0; i < 4; i++ ) @@ -1050,16 +1092,16 @@ namespace pal if ( mFeature->layer->arrangement() == P_FREE ) { enoughPlace = true; - px = ( box->x[0] + box->x[2] ) / 2 - xrm; - py = ( box->y[0] + box->y[2] ) / 2 - yrm; + px = ( box->x[0] + box->x[2] ) / 2 - labelWidth; + py = ( box->y[0] + box->y[2] ) / 2 - labelHeight; int i, j; // Virtual label: center on bbox center, label size = 2x original size // alpha = 0. // If all corner are in bbox then place candidates horizontaly - for ( rx = px, i = 0; i < 2; rx = rx + 2 * xrm, i++ ) + for ( rx = px, i = 0; i < 2; rx = rx + 2 * labelWidth, i++ ) { - for ( ry = py, j = 0; j < 2; ry = ry + 2 * yrm, j++ ) + for ( ry = py, j = 0; j < 2; ry = ry + 2 * labelHeight, j++ ) { if ( !mapShape->containsPoint( rx, ry ) ) { @@ -1079,7 +1121,7 @@ namespace pal { alpha = 0.0; // HORIZ } - else if ( box->length > 1.5*xrm && box->width > 1.5*xrm ) + else if ( box->length > 1.5*labelWidth && box->width > 1.5*labelWidth ) { if ( box->alpha <= M_PI / 4 ) { @@ -1099,7 +1141,7 @@ namespace pal alpha = box->alpha; } - beta = atan2( yrm, xrm ) + alpha; + beta = atan2( labelHeight, labelWidth ) + alpha; //alpha = box->alpha; @@ -1108,7 +1150,6 @@ namespace pal dlx = cos( beta ) * diago; dly = sin( beta ) * diago; - double px0, py0; px0 = box->width / 2.0; @@ -1128,11 +1169,13 @@ namespace pal rx += box->x[0]; ry += box->y[0]; - // Only accept candidate that center is in the polygon - if ( mapShape->containsPoint( rx, ry ) ) + bool candidateAcceptable = ( mFeature->layer->fitInPolygonOnly() + ? mapShape->containsLabelCandidate( rx - dlx, ry - dly, labelWidth, labelHeight, alpha ) + : mapShape->containsPoint( rx, ry ) ); + if ( candidateAcceptable ) { // cost is set to minimal value, evaluated later - positions.append( new LabelPosition( id++, rx - dlx, ry - dly, xrm, yrm, alpha, 0.0001, this ) ); // Polygon + positions.append( new LabelPosition( id++, rx - dlx, ry - dly, labelWidth, labelHeight, alpha, 0.0001, this ) ); // Polygon } } } @@ -1143,10 +1186,10 @@ namespace pal { dx /= 2; dy /= 2; - num_try++; + numTry++; } } - while ( nbp == 0 && num_try < max_try ); + while ( nbp == 0 && numTry < maxTry ); nbp = positions.size(); @@ -1245,9 +1288,9 @@ namespace pal double cx, cy; mapShape->getCentroid( cx, cy, mFeature->layer->centroidInside() ); if ( mFeature->layer->arrangement() == P_POINT_OVER ) - nbp = setPositionOverPoint( cx, cy, lPos, angle ); + nbp = setPositionOverPoint( cx, cy, lPos, angle, mapShape ); else - nbp = setPositionForPoint( cx, cy, lPos, angle ); + nbp = setPositionForPoint( cx, cy, lPos, angle, mapShape ); break; case P_LINE: nbp = setPositionForLine( lPos, mapShape ); diff --git a/src/core/pal/feature.h b/src/core/pal/feature.h index 654b0de6811..36d0a7a0b34 100644 --- a/src/core/pal/feature.h +++ b/src/core/pal/feature.h @@ -189,18 +189,20 @@ namespace pal * @param y y coordinate of the point * @param lPos pointer to an array of candidates, will be filled by generated candidates * @param angle orientation of the label + * @param mapShape optional geometry of source polygon * @returns the number of generated candidates */ - int setPositionForPoint( double x, double y, LabelPosition ***lPos, double angle ); + int setPositionForPoint( double x, double y, LabelPosition ***lPos, double angle, PointSet *mapShape = 0 ); /** Generate one candidate over or offset the specified point. * @param x x coordinate of the point * @param y y coordinate of the point * @param lPos pointer to an array of candidates, will be filled by generated candidate * @param angle orientation of the label + * @param mapShape optional geometry of source polygon * @returns the number of generated candidates (always 1) */ - int setPositionOverPoint( double x, double y, LabelPosition ***lPos, double angle ); + int setPositionOverPoint( double x, double y, LabelPosition ***lPos, double angle, PointSet *mapShape = 0 ); /** Generate candidates for line feature. * @param lPos pointer to an array of candidates, will be filled by generated candidates diff --git a/src/core/pal/layer.cpp b/src/core/pal/layer.cpp index f5cb7cc9be4..d3e1aafbf3a 100644 --- a/src/core/pal/layer.cpp +++ b/src/core/pal/layer.cpp @@ -52,6 +52,7 @@ namespace pal , mLabelLayer( toLabel ) , mDisplayAll( displayAll ) , mCentroidInside( false ) + , mFitInPolygon( false ) , mArrangement( arrangement ) , mArrangementFlags( 0 ) , mMode( LabelPerFeature ) diff --git a/src/core/pal/layer.h b/src/core/pal/layer.h index 4be7fc3fb84..d448bcb9a54 100644 --- a/src/core/pal/layer.h +++ b/src/core/pal/layer.h @@ -221,6 +221,21 @@ namespace pal */ bool centroidInside() const { return mCentroidInside; } + /** Sets whether labels which do not fit completely within a polygon feature + * are discarded. + * @param fitInPolygon set to true to discard labels which do not fit within + * polygon features. Set to false to allow labels which partially fall outside + * the polygon. + * @see fitInPolygonOnly + */ + void setFitInPolygonOnly( bool fitInPolygon ) { mFitInPolygon = fitInPolygon; } + + /** Returns whether labels which do not fit completely within a polygon feature + * are discarded. + * @see setFitInPolygonOnly + */ + bool fitInPolygonOnly() const { return mFitInPolygon; } + /** Register a feature in the layer. * @param geom_id unique identifier * @param userGeom user's geometry that implements the PalGeometry interface @@ -277,6 +292,7 @@ namespace pal bool mLabelLayer; bool mDisplayAll; bool mCentroidInside; + bool mFitInPolygon; /** Optional flags used for some placement methods */ Arrangement mArrangement; diff --git a/src/core/pal/pointset.cpp b/src/core/pal/pointset.cpp index 5abd2f647a4..518ce8aa90d 100644 --- a/src/core/pal/pointset.cpp +++ b/src/core/pal/pointset.cpp @@ -277,6 +277,46 @@ namespace pal return result; } + bool PointSet::containsLabelCandidate( double x, double y, double width, double height, double alpha ) const + { + GEOSContextHandle_t geosctxt = geosContext(); + GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 ); + + GEOSCoordSeq_setX_r( geosctxt, coord, 0, x ); + GEOSCoordSeq_setY_r( geosctxt, coord, 0, y ); + if ( !qgsDoubleNear( alpha, 0.0 ) ) + { + double beta = alpha + ( M_PI / 2 ); + double dx1 = cos( alpha ) * width; + double dy1 = sin( alpha ) * width; + double dx2 = cos( beta ) * height; + double dy2 = sin( beta ) * height; + GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + dx1 ); + GEOSCoordSeq_setY_r( geosctxt, coord, 1, y + dy1 ); + GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + dx1 + dx2 ); + GEOSCoordSeq_setY_r( geosctxt, coord, 2, y + dy1 + dy2 ); + GEOSCoordSeq_setX_r( geosctxt, coord, 3, x + dx2 ); + GEOSCoordSeq_setY_r( geosctxt, coord, 3, y + dy2 ); + } + else + { + GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + width ); + GEOSCoordSeq_setY_r( geosctxt, coord, 1, y ); + GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + width ); + GEOSCoordSeq_setY_r( geosctxt, coord, 2, y + height ); + GEOSCoordSeq_setX_r( geosctxt, coord, 3, x ); + GEOSCoordSeq_setY_r( geosctxt, coord, 3, y + height ); + } + //close ring + GEOSCoordSeq_setX_r( geosctxt, coord, 4, x ); + GEOSCoordSeq_setY_r( geosctxt, coord, 4, y ); + + GEOSGeometry* bboxGeos = GEOSGeom_createLinearRing_r( geosctxt, coord ); + bool result = ( GEOSPreparedContains_r( geosctxt, preparedGeom(), bboxGeos ) == 1 ); + GEOSGeom_destroy_r( geosctxt, bboxGeos ); + return result; + } + void PointSet::splitPolygons( QLinkedList &shapes_toProcess, QLinkedList &shapes_final, double xrm, double yrm, const QString& uid ) diff --git a/src/core/pal/pointset.h b/src/core/pal/pointset.h index 7f5662d08bb..2363bbb7fa2 100644 --- a/src/core/pal/pointset.h +++ b/src/core/pal/pointset.h @@ -80,6 +80,16 @@ namespace pal */ bool containsPoint( double x, double y ) const; + /** Tests whether a possible label candidate will fit completely within the shape. + * @param x x-coordinate of label candidate + * @param y y-coordinate of label candidate + * @param width label width + * @param height label height + * @param alpha label angle + * @returns true if point set completely contains candidate label + */ + bool containsLabelCandidate( double x, double y, double width, double height, double alpha = 0 ) const; + CHullBox * compute_chull_bbox(); /** Split a concave shape into several convex shapes. diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index 376a8c78a25..6d2cce559d6 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -169,6 +169,7 @@ QgsPalLayerSettings::QgsPalLayerSettings() placementFlags = AboveLine | MapOrientation; centroidWhole = false; centroidInside = false; + fitInPolygonOnly = false; quadOffset = QuadrantOver; xOffset = 0; yOffset = 0; @@ -379,6 +380,7 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s ) placementFlags = s.placementFlags; centroidWhole = s.centroidWhole; centroidInside = s.centroidInside; + fitInPolygonOnly = s.fitInPolygonOnly; quadOffset = s.quadOffset; xOffset = s.xOffset; yOffset = s.yOffset; @@ -863,6 +865,7 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer ) placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt(); centroidWhole = layer->customProperty( "labeling/centroidWhole", QVariant( false ) ).toBool(); centroidInside = layer->customProperty( "labeling/centroidInside", QVariant( false ) ).toBool(); + fitInPolygonOnly = layer->customProperty( "labeling/fitInPolygonOnly", QVariant( false ) ).toBool(); dist = layer->customProperty( "labeling/dist" ).toDouble(); distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool(); distMapUnitScale.minScale = layer->customProperty( "labeling/distMapUnitMinScale", 0.0 ).toDouble(); @@ -1036,6 +1039,7 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer ) layer->setCustomProperty( "labeling/placementFlags", ( unsigned int )placementFlags ); layer->setCustomProperty( "labeling/centroidWhole", centroidWhole ); layer->setCustomProperty( "labeling/centroidInside", centroidInside ); + layer->setCustomProperty( "labeling/fitInPolygonOnly", fitInPolygonOnly ); layer->setCustomProperty( "labeling/dist", dist ); layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits ); layer->setCustomProperty( "labeling/distMapUnitMinScale", distMapUnitScale.minScale ); @@ -3332,6 +3336,9 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, // set whether location of centroid must be inside of polygons l->setCentroidInside( lyr.centroidInside ); + // set whether labels must fall completely within the polygon + l->setFitInPolygonOnly( lyr.fitInPolygonOnly ); + // set how to show upside-down labels Layer::UpsideDownLabels upsdnlabels; switch ( lyr.upsidedownLabels ) diff --git a/src/core/qgspallabeling.h b/src/core/qgspallabeling.h index cd96fea7ee7..858122a5109 100644 --- a/src/core/qgspallabeling.h +++ b/src/core/qgspallabeling.h @@ -393,6 +393,10 @@ class CORE_EXPORT QgsPalLayerSettings bool centroidWhole; // whether centroid calculated from whole or visible polygon bool centroidInside; // whether centroid-point calculated must be inside polygon + + /** True if only labels which completely fit within a polygon are allowed. + */ + bool fitInPolygonOnly; double dist; // distance from the feature (in mm) bool distInMapUnits; //true if distance is in map units (otherwise in mm) QgsMapUnitScale distMapUnitScale; diff --git a/src/ui/qgslabelingguibase.ui b/src/ui/qgslabelingguibase.ui index f2fc80b3606..0e8aee1faf9 100644 --- a/src/ui/qgslabelingguibase.ui +++ b/src/ui/qgslabelingguibase.ui @@ -524,7 +524,7 @@ 0 0 - 578 + 341 388 @@ -4818,9 +4818,9 @@ font-style: italic; 0 - -292 + -298 578 - 655 + 683 @@ -5439,6 +5439,28 @@ font-style: italic; + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + + + Only draw labels which fit completely within feature + + + + + +