Skip to content

Commit fe42b00

Browse files
committedMay 29, 2014
Merge pull request #1238 from ahuarte47/Issue_9480
Fix bug #9480: Labels for polygon centroids should always originate inside a polygon
2 parents 34f3378 + 6cb4cbb commit fe42b00

20 files changed

+178
-14
lines changed
 

‎python/core/qgsgeometry.sip

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@ class QgsGeometry
322322
* and for point based geometries, the point itself is returned */
323323
QgsGeometry* centroid() /Factory/;
324324

325+
/** Returns a point within a geometry */
326+
QgsGeometry* pointOnSurface() /Factory/;
327+
325328
/** Returns the smallest convex polygon that contains all the points in the geometry. */
326329
QgsGeometry* convexHull() /Factory/;
327330

‎python/core/qgspallabeling.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ class QgsPalLayerSettings
394394
unsigned int placementFlags;
395395

396396
bool centroidWhole; // whether centroid calculated from whole or visible polygon
397+
bool centroidInside; // whether centroid-point calculated must be inside polygon
397398
double dist; // distance from the feature (in mm)
398399
bool distInMapUnits; //true if distance is in map units (otherwise in mm)
399400
QgsMapUnitScale distMapUnitScale;

‎src/app/qgslabelinggui.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ void QgsLabelingGui::init()
259259

260260
// populate placement options
261261
mCentroidRadioWhole->setChecked( lyr.centroidWhole );
262+
mCentroidInsideCheckBox->setChecked( lyr.centroidInside );
262263
switch ( lyr.placement )
263264
{
264265
case QgsPalLayerSettings::AroundPoint:
@@ -545,6 +546,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
545546

546547
QWidget* curPlacementWdgt = stackedPlacement->currentWidget();
547548
lyr.centroidWhole = mCentroidRadioWhole->isChecked();
549+
lyr.centroidInside = mCentroidInsideCheckBox->isChecked();
548550
if (( curPlacementWdgt == pagePoint && radAroundPoint->isChecked() )
549551
|| ( curPlacementWdgt == pagePolygon && radAroundCentroid->isChecked() ) )
550552
{

‎src/core/pal/feature.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1344,7 +1344,7 @@ namespace pal
13441344
case P_POINT:
13451345
case P_POINT_OVER:
13461346
double cx, cy;
1347-
mapShape->getCentroid( cx, cy );
1347+
mapShape->getCentroid( cx, cy, f->layer->getCentroidInside() );
13481348
if ( f->layer->getArrangement() == P_POINT_OVER )
13491349
nbp = setPositionOverPoint( cx, cy, scale, lPos, delta, angle );
13501350
else

‎src/core/pal/layer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ namespace pal
6161

6262
Layer::Layer( const char *lyrName, double min_scale, double max_scale, Arrangement arrangement, Units label_unit, double defaultPriority, bool obstacle, bool active, bool toLabel, Pal *pal, bool displayAll )
6363
: pal( pal ), obstacle( obstacle ), active( active ),
64-
toLabel( toLabel ), displayAll( displayAll ), label_unit( label_unit ),
64+
toLabel( toLabel ), displayAll( displayAll ), centroidInside( false ), label_unit( label_unit ),
6565
min_scale( min_scale ), max_scale( max_scale ),
6666
arrangement( arrangement ), arrangementFlags( 0 ), mode( LabelPerFeature ), mergeLines( false )
6767
{

‎src/core/pal/layer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ namespace pal
101101
bool active;
102102
bool toLabel;
103103
bool displayAll;
104+
bool centroidInside;
104105

105106
Units label_unit;
106107

@@ -291,6 +292,9 @@ namespace pal
291292
void setUpsidedownLabels( UpsideDownLabels ud ) { upsidedownLabels = ud; }
292293
UpsideDownLabels getUpsidedownLabels() const { return upsidedownLabels; }
293294

295+
void setCentroidInside( bool forceInside ) { centroidInside = forceInside; }
296+
bool getCentroidInside() const { return centroidInside; }
297+
294298
/**
295299
* \brief register a feature in the layer
296300
*

‎src/core/pal/pointset.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ namespace pal
956956

957957

958958

959-
void PointSet::getCentroid( double &px, double &py )
959+
void PointSet::getCentroid( double &px, double &py, bool forceInside )
960960
{
961961
// for explanation see this page:
962962
// http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
@@ -984,6 +984,35 @@ namespace pal
984984
px = cx / ( 3 * A ) + x[0];
985985
py = cy / ( 3 * A ) + y[0];
986986
}
987+
988+
// check if centroid inside in polygon
989+
if ( forceInside && !isPointInPolygon( nbPoints, x, y, px, py ) )
990+
{
991+
GEOSCoordSequence *coord = GEOSCoordSeq_create( nbPoints, 2 );
992+
993+
for ( int i = 0; i < nbPoints; ++i )
994+
{
995+
GEOSCoordSeq_setX( coord, i, x[i] );
996+
GEOSCoordSeq_setY( coord, i, y[i] );
997+
}
998+
999+
GEOSGeometry *geom = GEOSGeom_createPolygon( GEOSGeom_createLinearRing( coord ), 0, 0 );
1000+
1001+
if ( geom )
1002+
{
1003+
GEOSGeometry *pointGeom = GEOSPointOnSurface( geom );
1004+
1005+
if ( pointGeom )
1006+
{
1007+
const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq( pointGeom );
1008+
GEOSCoordSeq_getX( coordSeq, 0, &px );
1009+
GEOSCoordSeq_getY( coordSeq, 0, &py );
1010+
1011+
GEOSGeom_destroy( pointGeom );
1012+
}
1013+
GEOSGeom_destroy( geom );
1014+
}
1015+
}
9871016
}
9881017

9891018
} // end namespace

‎src/core/pal/pointset.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ namespace pal
160160

161161
//double getDistInside(double px, double py);
162162

163-
void getCentroid( double &px, double &py );
163+
void getCentroid( double &px, double &py, bool forceInside = false );
164164

165165

166166
int getGeosType() const { return type; }

‎src/core/qgsgeometry.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ static GEOSInit geosinit;
145145
#define GEOSArea(g, a) GEOSArea( (GEOSGeometry*) g, a )
146146
#define GEOSTopologyPreserveSimplify(g, t) GEOSTopologyPreserveSimplify( (GEOSGeometry*) g, t )
147147
#define GEOSGetCentroid(g) GEOSGetCentroid( (GEOSGeometry*) g )
148+
#define GEOSPointOnSurface(g) GEOSPointOnSurface( (GEOSGeometry*) g )
148149

149150
#define GEOSCoordSeq_getSize(cs,n) GEOSCoordSeq_getSize( (GEOSCoordSequence *) cs, n )
150151
#define GEOSCoordSeq_getX(cs,i,x) GEOSCoordSeq_getX( (GEOSCoordSequence *)cs, i, x )
@@ -5623,6 +5624,21 @@ QgsGeometry* QgsGeometry::centroid()
56235624
CATCH_GEOS( 0 )
56245625
}
56255626

5627+
QgsGeometry* QgsGeometry::pointOnSurface()
5628+
{
5629+
if ( mDirtyGeos )
5630+
exportWkbToGeos();
5631+
5632+
if ( !mGeos )
5633+
return 0;
5634+
5635+
try
5636+
{
5637+
return fromGeosGeom( GEOSPointOnSurface( mGeos ) );
5638+
}
5639+
CATCH_GEOS( 0 )
5640+
}
5641+
56265642
QgsGeometry* QgsGeometry::convexHull()
56275643
{
56285644
if ( mDirtyGeos )

‎src/core/qgsgeometry.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,9 @@ class CORE_EXPORT QgsGeometry
363363
* and for point based geometries, the point itself is returned */
364364
QgsGeometry* centroid();
365365

366+
/** Returns a point within a geometry */
367+
QgsGeometry* pointOnSurface();
368+
366369
/** Returns the smallest convex polygon that contains all the points in the geometry. */
367370
QgsGeometry* convexHull();
368371

‎src/core/qgspallabeling.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ QgsPalLayerSettings::QgsPalLayerSettings()
311311
placement = AroundPoint;
312312
placementFlags = 0;
313313
centroidWhole = false;
314+
centroidInside = false;
314315
quadOffset = QuadrantOver;
315316
xOffset = 0;
316317
yOffset = 0;
@@ -502,6 +503,7 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
502503
placement = s.placement;
503504
placementFlags = s.placementFlags;
504505
centroidWhole = s.centroidWhole;
506+
centroidInside = s.centroidInside;
505507
quadOffset = s.quadOffset;
506508
xOffset = s.xOffset;
507509
yOffset = s.yOffset;
@@ -997,6 +999,7 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
997999
placement = ( Placement )layer->customProperty( "labeling/placement" ).toInt();
9981000
placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
9991001
centroidWhole = layer->customProperty( "labeling/centroidWhole", QVariant( false ) ).toBool();
1002+
centroidInside = layer->customProperty( "labeling/centroidInside", QVariant( false ) ).toBool();
10001003
dist = layer->customProperty( "labeling/dist" ).toDouble();
10011004
distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool();
10021005
distMapUnitScale.minScale = layer->customProperty( "labeling/distMapUnitMinScale", 0.0 ).toDouble();
@@ -1167,6 +1170,7 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
11671170
layer->setCustomProperty( "labeling/placement", placement );
11681171
layer->setCustomProperty( "labeling/placementFlags", ( unsigned int )placementFlags );
11691172
layer->setCustomProperty( "labeling/centroidWhole", centroidWhole );
1173+
layer->setCustomProperty( "labeling/centroidInside", centroidInside );
11701174
layer->setCustomProperty( "labeling/dist", dist );
11711175
layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits );
11721176
layer->setCustomProperty( "labeling/distMapUnitMinScale", distMapUnitScale.minScale );
@@ -3389,6 +3393,9 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames,
33893393
// set whether adjacent lines should be merged
33903394
l->setMergeConnectedLines( lyr.mergeLines );
33913395

3396+
// set whether location of centroid must be inside of polygons
3397+
l->setCentroidInside( lyr.centroidInside );
3398+
33923399
// set how to show upside-down labels
33933400
Layer::UpsideDownLabels upsdnlabels;
33943401
switch ( lyr.upsidedownLabels )

‎src/core/qgspallabeling.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ class CORE_EXPORT QgsPalLayerSettings
374374
unsigned int placementFlags;
375375

376376
bool centroidWhole; // whether centroid calculated from whole or visible polygon
377+
bool centroidInside; // whether centroid-point calculated must be inside polygon
377378
double dist; // distance from the feature (in mm)
378379
bool distInMapUnits; //true if distance is in map units (otherwise in mm)
379380
QgsMapUnitScale distMapUnitScale;

‎src/core/symbology-ng/qgsfillsymbollayerv2.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3166,7 +3166,7 @@ QSet<QString> QgsPointPatternFillSymbolLayer::usedAttributes() const
31663166
//////////////
31673167

31683168

3169-
QgsCentroidFillSymbolLayerV2::QgsCentroidFillSymbolLayerV2(): mMarker( NULL )
3169+
QgsCentroidFillSymbolLayerV2::QgsCentroidFillSymbolLayerV2(): mMarker( NULL ), mPointOnSurface( false )
31703170
{
31713171
setSubSymbol( new QgsMarkerSymbolV2() );
31723172
}
@@ -3178,8 +3178,12 @@ QgsCentroidFillSymbolLayerV2::~QgsCentroidFillSymbolLayerV2()
31783178

31793179
QgsSymbolLayerV2* QgsCentroidFillSymbolLayerV2::create( const QgsStringMap& properties )
31803180
{
3181-
Q_UNUSED( properties );
3182-
return new QgsCentroidFillSymbolLayerV2();
3181+
QgsCentroidFillSymbolLayerV2* sl = new QgsCentroidFillSymbolLayerV2();
3182+
3183+
if ( properties.contains( "point_on_surface" ) )
3184+
sl->setPointOnSurface( properties["point_on_surface"].toInt() != 0 );
3185+
3186+
return sl;
31833187
}
31843188

31853189
QString QgsCentroidFillSymbolLayerV2::layerType() const
@@ -3208,13 +3212,15 @@ void QgsCentroidFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList
32083212
{
32093213
Q_UNUSED( rings );
32103214

3211-
QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
3215+
QPointF centroid = mPointOnSurface ? QgsSymbolLayerV2Utils::polygonPointOnSurface( points ) : QgsSymbolLayerV2Utils::polygonCentroid( points );
32123216
mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
32133217
}
32143218

32153219
QgsStringMap QgsCentroidFillSymbolLayerV2::properties() const
32163220
{
3217-
return QgsStringMap();
3221+
QgsStringMap map;
3222+
map["point_on_surface"] = QString::number( mPointOnSurface );
3223+
return map;
32183224
}
32193225

32203226
QgsSymbolLayerV2* QgsCentroidFillSymbolLayerV2::clone() const
@@ -3223,6 +3229,7 @@ QgsSymbolLayerV2* QgsCentroidFillSymbolLayerV2::clone() const
32233229
x->mAngle = mAngle;
32243230
x->mColor = mColor;
32253231
x->setSubSymbol( mMarker->clone() );
3232+
x->setPointOnSurface( mPointOnSurface );
32263233
return x;
32273234
}
32283235

@@ -3246,9 +3253,9 @@ QgsSymbolLayerV2* QgsCentroidFillSymbolLayerV2::createFromSld( QDomElement &elem
32463253
layers.append( l );
32473254
QgsMarkerSymbolV2 *marker = new QgsMarkerSymbolV2( layers );
32483255

3249-
QgsCentroidFillSymbolLayerV2* x = new QgsCentroidFillSymbolLayerV2();
3250-
x->setSubSymbol( marker );
3251-
return x;
3256+
QgsCentroidFillSymbolLayerV2* sl = new QgsCentroidFillSymbolLayerV2();
3257+
sl->setSubSymbol( marker );
3258+
return sl;
32523259
}
32533260

32543261

‎src/core/symbology-ng/qgsfillsymbollayerv2.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,8 +904,12 @@ class CORE_EXPORT QgsCentroidFillSymbolLayerV2 : public QgsFillSymbolLayerV2
904904

905905
virtual QSet<QString> usedAttributes() const;
906906

907+
void setPointOnSurface( bool pointOnSurface ) { mPointOnSurface = pointOnSurface; }
908+
bool pointOnSurface() const { return mPointOnSurface; }
909+
907910
protected:
908911
QgsMarkerSymbolV2* mMarker;
912+
bool mPointOnSurface;
909913
};
910914

911915
#endif

‎src/core/symbology-ng/qgssymbollayerv2utils.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3179,6 +3179,62 @@ QPointF QgsSymbolLayerV2Utils::polygonCentroid( const QPolygonF& points )
31793179
return QPointF( cx, cy );
31803180
}
31813181

3182+
QPointF QgsSymbolLayerV2Utils::polygonPointOnSurface( const QPolygonF& points )
3183+
{
3184+
QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
3185+
3186+
// check if centroid inside in polygon
3187+
if ( !QgsSymbolLayerV2Utils::pointInPolygon( points, centroid ) )
3188+
{
3189+
unsigned int i, pointCount = points.count();
3190+
3191+
QgsPolyline polyline( pointCount );
3192+
for ( i = 0; i < pointCount; ++i ) polyline[i] = QgsPoint( points[i].x(), points[i].y() );
3193+
3194+
QgsGeometry* geom = QgsGeometry::fromPolygon( QgsPolygon() << polyline );
3195+
if ( geom )
3196+
{
3197+
QgsGeometry* pointOnSurfaceGeom = geom->pointOnSurface();
3198+
3199+
if ( pointOnSurfaceGeom )
3200+
{
3201+
QgsPoint point = pointOnSurfaceGeom->asPoint();
3202+
delete pointOnSurfaceGeom;
3203+
delete geom;
3204+
3205+
return QPointF( point.x(), point.y() );
3206+
}
3207+
delete geom;
3208+
}
3209+
}
3210+
return centroid;
3211+
}
3212+
3213+
bool QgsSymbolLayerV2Utils::pointInPolygon( const QPolygonF &points, const QPointF &point )
3214+
{
3215+
bool inside = false;
3216+
3217+
double x = point.x();
3218+
double y = point.y();
3219+
3220+
for ( int i = 0, j = points.count() - 1; i < points.count(); i++ )
3221+
{
3222+
const QPointF& p1 = points[i];
3223+
const QPointF& p2 = points[j];
3224+
3225+
if ( p1.x() == x && p1.y() == y )
3226+
return true;
3227+
3228+
if ( ( p1.y() < y && p2.y() >= y ) || ( p2.y() < y && p1.y() >= y ) )
3229+
{
3230+
if ( p1.x() + ( y - p1.y() ) / ( p2.y() - p1.y() )*( p2.x() - p1.x() ) <= x )
3231+
inside = !inside;
3232+
}
3233+
3234+
j = i;
3235+
}
3236+
return inside;
3237+
}
31823238

31833239
QgsExpression* QgsSymbolLayerV2Utils::fieldOrExpressionToExpression( const QString& fieldOrExpression )
31843240
{

‎src/core/symbology-ng/qgssymbollayerv2utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ class CORE_EXPORT QgsSymbolLayerV2Utils
287287
//! Calculate the centroid point of a QPolygonF
288288
static QPointF polygonCentroid( const QPolygonF& points );
289289

290+
//! Calculate a point within of a QPolygonF
291+
static QPointF polygonPointOnSurface( const QPolygonF& points );
292+
293+
//! Calculate whether a point is within of a QPolygonF
294+
static bool pointInPolygon( const QPolygonF &points, const QPointF &point );
295+
290296
/** Return a new valid expression instance for given field or expression string.
291297
* If the input is not a valid expression, it is assumed that it is a field name and gets properly quoted.
292298
* If the string is empty, returns null pointer.

‎src/gui/symbology-ng/qgssymbollayerv2widget.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2794,12 +2794,20 @@ void QgsCentroidFillSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* layer
27942794

27952795
// layer type is correct, we can do the cast
27962796
mLayer = static_cast<QgsCentroidFillSymbolLayerV2*>( layer );
2797+
2798+
// set values
2799+
mDrawInsideCheckBox->blockSignals( true );
2800+
mDrawInsideCheckBox->setChecked( mLayer->pointOnSurface() );
2801+
mDrawInsideCheckBox->blockSignals( false );
27972802
}
27982803

27992804
QgsSymbolLayerV2* QgsCentroidFillSymbolLayerV2Widget::symbolLayer()
28002805
{
28012806
return mLayer;
28022807
}
28032808

2804-
2805-
2809+
void QgsCentroidFillSymbolLayerV2Widget::on_mDrawInsideCheckBox_stateChanged( int state )
2810+
{
2811+
mLayer->setPointOnSurface( state == Qt::Checked );
2812+
emit changed();
2813+
}

‎src/gui/symbology-ng/qgssymbollayerv2widget.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,9 @@ class GUI_EXPORT QgsCentroidFillSymbolLayerV2Widget : public QgsSymbolLayerV2Wid
482482
virtual void setSymbolLayer( QgsSymbolLayerV2* layer );
483483
virtual QgsSymbolLayerV2* symbolLayer();
484484

485+
public slots:
486+
void on_mDrawInsideCheckBox_stateChanged( int state );
487+
485488
protected:
486489
QgsCentroidFillSymbolLayerV2* mLayer;
487490
};

‎src/ui/qgslabelingguibase.ui

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4101,6 +4101,13 @@ font-style: italic;</string>
41014101
</property>
41024102
</widget>
41034103
</item>
4104+
<item row="1" column="1">
4105+
<widget class="QCheckBox" name="mCentroidInsideCheckBox">
4106+
<property name="text">
4107+
<string>Force point inside polygon</string>
4108+
</property>
4109+
</widget>
4110+
</item>
41044111
</layout>
41054112
</widget>
41064113
</item>

‎src/ui/symbollayer/widget_centroidfill.ui

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@
1616
<layout class="QVBoxLayout" name="verticalLayout">
1717
<item>
1818
<layout class="QHBoxLayout" name="horizontalLayout">
19+
<item>
20+
<widget class="QCheckBox" name="mDrawInsideCheckBox">
21+
<property name="text">
22+
<string>Force point inside polygon</string>
23+
</property>
24+
</widget>
25+
</item>
1926
<item>
2027
<spacer>
2128
<property name="orientation">

0 commit comments

Comments
 (0)
Please sign in to comment.