QGIS API Documentation  2.12.0-Lyon
qgslabelingenginev2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslabelingenginev2.cpp
3  --------------------------------------
4  Date : September 2015
5  Copyright : (C) 2015 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgslabelingenginev2.h"
17 
18 #include "qgslogger.h"
19 #include "qgsproject.h"
20 
21 #include "feature.h"
22 #include "labelposition.h"
23 #include "layer.h"
24 #include "pal.h"
25 #include "problem.h"
26 
27 
28 
29 // helper function for checking for job cancellation within PAL
30 static bool _palIsCancelled( void* ctx )
31 {
32  return (( QgsRenderContext* ) ctx )->renderingStopped();
33 }
34 
35 
37  : mFlags( RenderOutlineLabels | UsePartialCandidates )
38  , mSearchMethod( QgsPalLabeling::Chain )
39  , mCandPoint( 8 )
40  , mCandLine( 8 )
41  , mCandPolygon( 8 )
42  , mResults( 0 )
43 {
45 }
46 
48 {
49  delete mResults;
50  qDeleteAll( mProviders );
51  qDeleteAll( mSubProviders );
52 }
53 
55 {
56  provider->setEngine( this );
57  mProviders << provider;
58 }
59 
61 {
62  int idx = mProviders.indexOf( provider );
63  if ( idx >= 0 )
64  {
65  delete mProviders.takeAt( idx );
66  }
67 }
68 
70 {
71  // how to place the labels
72  pal::Arrangement arrangement;
73  switch ( provider->placement() )
74  {
75  case QgsPalLayerSettings::AroundPoint: arrangement = pal::P_POINT; break;
76  case QgsPalLayerSettings::OverPoint: arrangement = pal::P_POINT_OVER; break;
77  case QgsPalLayerSettings::Line: arrangement = pal::P_LINE; break;
78  case QgsPalLayerSettings::Curved: arrangement = pal::P_CURVED; break;
79  case QgsPalLayerSettings::Horizontal: arrangement = pal::P_HORIZ; break;
80  case QgsPalLayerSettings::Free: arrangement = pal::P_FREE; break;
81  default: Q_ASSERT( "unsupported placement" && 0 ); return;
82  }
83 
84  QgsAbstractLabelProvider::Flags flags = provider->flags();
85 
86  // create the pal layer
87  pal::Layer* l = p.addLayer( provider,
88  provider->name(),
89  arrangement,
90  provider->priority(),
91  true,
92  flags.testFlag( QgsAbstractLabelProvider::DrawLabels ),
93  flags.testFlag( QgsAbstractLabelProvider::DrawAllLabels ) );
94 
95  // extra flags for placement of labels for linestrings
96  l->setArrangementFlags(( pal::LineArrangementFlags ) provider->linePlacementFlags() );
97 
98  // set label mode (label per feature is the default)
100 
101  // set whether adjacent lines should be merged
103 
104  // set obstacle type
105  switch ( provider->obstacleType() )
106  {
109  break;
112  break;
113  }
114 
115  // set whether location of centroid must be inside of polygons
117 
118  // set whether labels must fall completely within the polygon
120 
121  // set how to show upside-down labels
122  pal::Layer::UpsideDownLabels upsdnlabels;
123  switch ( provider->upsidedownLabels() )
124  {
125  case QgsPalLayerSettings::Upright: upsdnlabels = pal::Layer::Upright; break;
127  case QgsPalLayerSettings::ShowAll: upsdnlabels = pal::Layer::ShowAll; break;
128  default: Q_ASSERT( "unsupported upside-down label setting" && 0 ); return;
129  }
130  l->setUpsidedownLabels( upsdnlabels );
131 
132 
133  QList<QgsLabelFeature*> features = provider->labelFeatures( context );
134 
135  Q_FOREACH ( QgsLabelFeature* feature, features )
136  {
137  try
138  {
139  l->registerFeature( feature );
140  }
141  catch ( std::exception &e )
142  {
143  Q_UNUSED( e );
144  QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
145  continue;
146  }
147  }
148 
149  // any sub-providers?
150  Q_FOREACH ( QgsAbstractLabelProvider* subProvider, provider->subProviders() )
151  {
152  mSubProviders << subProvider;
153  processProvider( subProvider, context, p );
154  }
155 }
156 
157 
159 {
160  pal::Pal p;
161 
163  switch ( mSearchMethod )
164  {
165  default:
166  case QgsPalLabeling::Chain: s = pal::CHAIN; break;
170  case QgsPalLabeling::Falp: s = pal::FALP; break;
171  }
172  p.setSearch( s );
173 
174  // set number of candidates generated per feature
175  p.setPointP( mCandPoint );
176  p.setLineP( mCandLine );
177  p.setPolyP( mCandPolygon );
178 
179  p.setShowPartial( mFlags.testFlag( UsePartialCandidates ) );
180 
181 
182  // for each provider: get labels and register them in PAL
183  Q_FOREACH ( QgsAbstractLabelProvider* provider, mProviders )
184  {
185  processProvider( provider, context, p );
186  }
187 
188 
189  // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
190 
191  QPainter* painter = context.painter();
192 
194  if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
195  {
196  //PAL features are prerotated, so extent also needs to be unrotated
198  }
199 
200  QgsRectangle extent = extentGeom->boundingBox();
201  delete extentGeom;
202 
203  p.registerCancellationCallback( &_palIsCancelled, ( void* ) &context );
204 
205  QTime t;
206  t.start();
207 
208  // do the labeling itself
209  double bbox[] = { extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() };
210 
211  std::list<pal::LabelPosition*>* labels;
212  pal::Problem *problem;
213  try
214  {
215  problem = p.extractProblem( bbox );
216  }
217  catch ( std::exception& e )
218  {
219  Q_UNUSED( e );
220  QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
221  return;
222  }
223 
224 
225  if ( context.renderingStopped() )
226  {
227  delete problem;
228  return; // it has been cancelled
229  }
230 
231 #if 1 // XXX strk
232  // features are pre-rotated but not scaled/translated,
233  // so we only disable rotation here. Ideally, they'd be
234  // also pre-scaled/translated, as suggested here:
235  // http://hub.qgis.org/issues/11856
237  xform.setMapRotation( 0, 0, 0 );
238 #else
239  const QgsMapToPixel& xform = mMapSettings->mapToPixel();
240 #endif
241 
242  // draw rectangles with all candidates
243  // this is done before actual solution of the problem
244  // before number of candidates gets reduced
245  // TODO mCandidates.clear();
246  if ( mFlags.testFlag( DrawCandidates ) && problem )
247  {
248  painter->setBrush( Qt::NoBrush );
249  for ( int i = 0; i < problem->getNumFeatures(); i++ )
250  {
251  for ( int j = 0; j < problem->getFeatureCandidateCount( i ); j++ )
252  {
253  pal::LabelPosition* lp = problem->getFeatureCandidate( i, j );
254 
255  QgsPalLabeling::drawLabelCandidateRect( lp, painter, &xform );
256  }
257  }
258  }
259 
260  // find the solution
261  labels = p.solveProblem( problem, mFlags.testFlag( UseAllLabels ) );
262 
263  QgsDebugMsgLevel( QString( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( labels->size() ), 4 );
264  t.restart();
265 
266  if ( context.renderingStopped() )
267  {
268  delete problem;
269  delete labels;
270  return;
271  }
272  painter->setRenderHint( QPainter::Antialiasing );
273 
274  // draw the labels
275  std::list<pal::LabelPosition*>::iterator it = labels->begin();
276  for ( ; it != labels->end(); ++it )
277  {
278  if ( context.renderingStopped() )
279  break;
280 
281  QgsLabelFeature* lf = ( *it )->getFeaturePart()->feature();
282  if ( !lf )
283  {
284  continue;
285  }
286 
287  lf->provider()->drawLabel( context, *it );
288  }
289 
290  // Reset composition mode for further drawing operations
291  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
292 
293  QgsDebugMsgLevel( QString( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
294 
295  delete problem;
296  delete labels;
297 
298 
299 }
300 
302 {
304  mResults = 0;
305  return res;
306 }
307 
308 
310 {
311  bool saved = false;
313  mSearchMethod = ( QgsPalLabeling::Search )( prj->readNumEntry( "PAL", "/SearchMethod", ( int ) QgsPalLabeling::Chain, &saved ) );
314  mCandPoint = prj->readNumEntry( "PAL", "/CandidatesPoint", 8, &saved );
315  mCandLine = prj->readNumEntry( "PAL", "/CandidatesLine", 8, &saved );
316  mCandPolygon = prj->readNumEntry( "PAL", "/CandidatesPolygon", 8, &saved );
317 
318  mFlags = 0;
319  if ( prj->readBoolEntry( "PAL", "/ShowingCandidates", false, &saved ) ) mFlags |= DrawCandidates;
320  if ( prj->readBoolEntry( "PAL", "/DrawRectOnly", false, &saved ) ) mFlags |= DrawLabelRectOnly;
321  if ( prj->readBoolEntry( "PAL", "/ShowingShadowRects", false, &saved ) ) mFlags |= DrawShadowRects;
322  if ( prj->readBoolEntry( "PAL", "/ShowingAllLabels", false, &saved ) ) mFlags |= UseAllLabels;
323  if ( prj->readBoolEntry( "PAL", "/ShowingPartialsLabels", true, &saved ) ) mFlags |= UsePartialCandidates;
324  if ( prj->readBoolEntry( "PAL", "/DrawOutlineLabels", true, &saved ) ) mFlags |= RenderOutlineLabels;
325 }
326 
328 {
329  QgsProject::instance()->writeEntry( "PAL", "/SearchMethod", ( int )mSearchMethod );
330  QgsProject::instance()->writeEntry( "PAL", "/CandidatesPoint", mCandPoint );
331  QgsProject::instance()->writeEntry( "PAL", "/CandidatesLine", mCandLine );
332  QgsProject::instance()->writeEntry( "PAL", "/CandidatesPolygon", mCandPolygon );
333 
334  QgsProject::instance()->writeEntry( "PAL", "/ShowingCandidates", mFlags.testFlag( DrawCandidates ) );
335  QgsProject::instance()->writeEntry( "PAL", "/DrawRectOnly", mFlags.testFlag( DrawLabelRectOnly ) );
336  QgsProject::instance()->writeEntry( "PAL", "/ShowingShadowRects", mFlags.testFlag( DrawShadowRects ) );
337  QgsProject::instance()->writeEntry( "PAL", "/ShowingAllLabels", mFlags.testFlag( UseAllLabels ) );
338  QgsProject::instance()->writeEntry( "PAL", "/ShowingPartialsLabels", mFlags.testFlag( UsePartialCandidates ) );
339  QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", mFlags.testFlag( RenderOutlineLabels ) );
340 }
341 
342 
343 
345 
346 
347 
348 QgsLabelFeature::QgsLabelFeature( QgsFeatureId id, GEOSGeometry* geometry, const QSizeF& size )
349  : mLayer( 0 )
350  , mId( id )
351  , mGeometry( geometry )
352  , mSize( size )
353  , mPriority( -1 )
354  , mHasFixedPosition( false )
355  , mHasFixedAngle( false )
356  , mFixedAngle( 0 )
357  , mHasFixedQuadrant( false )
358  , mDistLabel( 0 )
359  , mRepeatDistance( 0 )
360  , mAlwaysShow( false )
361  , mIsObstacle( false )
362  , mObstacleFactor( 1 )
363  , mInfo( 0 )
364 {
365 }
366 
368 {
369  if ( mGeometry )
370  GEOSGeom_destroy_r( QgsGeometry::getGEOSHandler(), mGeometry );
371 
372  delete mInfo;
373 }
374 
376 {
377  return mLayer ? mLayer->provider() : 0;
378 
379 }
380 
382  : mEngine( 0 )
383  , mFlags( DrawLabels )
384  , mPlacement( QgsPalLayerSettings::AroundPoint )
385  , mLinePlacementFlags( 0 )
386  , mPriority( 0.5 )
387  , mObstacleType( QgsPalLayerSettings::PolygonInterior )
388  , mUpsidedownLabels( QgsPalLayerSettings::Upright )
389 {
390 }
QgsLabelFeature(QgsFeatureId id, GEOSGeometry *geometry, const QSizeF &size)
Create label feature, takes ownership of the geometry instance.
QString name() const
Name of the layer (for statistics, debugging etc.) - does not need to be unique.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
void processProvider(QgsAbstractLabelProvider *provider, QgsRenderContext &context, pal::Pal &p)
std::list< LabelPosition * > * solveProblem(Problem *prob, bool displayAll)
Definition: pal.cpp:583
void setMapRotation(double degrees, double cx, double cy)
Set map rotation in degrees (clockwise)
static bool _palIsCancelled(void *ctx)
Definition: pal.h:70
void setCompositionMode(CompositionMode mode)
int getFeatureCandidateCount(int i)
Definition: problem.h:115
unsigned int linePlacementFlags() const
For layers with linestring geometries - extra placement flags (or-ed combination of QgsPalLayerSettin...
void setRenderHint(RenderHint hint, bool on)
virtual ~QgsLabelFeature()
Clean up geometry and curved label info (if present)
arranges candidates around a point (centroid for polygon)
Definition: pal.h:79
~QgsLabelingEngineV2()
Clean up everything (especially the registered providers)
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:196
Whether to show debugging rectangles for drop shadows.
QgsAbstractLabelProvider * provider() const
Return provider of this instance.
A layer of spacial entites.
Definition: layer.h:57
void setPointP(int point_p)
set # candidates to generate for points features Higher the value is, longer Pal::labeller will spend...
Definition: pal.cpp:608
void addProvider(QgsAbstractLabelProvider *provider)
Add provider of label features. Takes ownership of the provider.
void setObstacleType(ObstacleType obstacleType)
Sets the obstacle type, which controls how features within the layer act as obstacles for labels...
Definition: layer.h:154
void setPolyP(int poly_p)
set maximum # candidates to generate for polygon features Higher the value is, longer Pal::labeller w...
Definition: pal.cpp:620
QgsRectangle boundingBox() const
Returns the bounding box of this feature.
pal::LabelInfo * mInfo
extra information for curved labels (may be null)
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
Pal main class.
Definition: pal.h:111
is slower and best than TABU, worse and faster than TABU_CHAIN
Definition: pal.h:69
UpsideDownLabels
Definition: layer.h:69
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
int mCandPoint
Number of candedate positions that will be generated for features.
void setUpsidedownLabels(UpsideDownLabels ud)
Sets how upside down labels will be handled within the layer.
Definition: layer.h:195
T takeAt(int i)
const QgsMapToPixel & mapToPixel() const
whether to label each part of multi-part features separately
double rotation() const
Return the rotation of the resulting map image Units are clockwise degrees.
void readSettingsFromProject()
Read configuration of the labeling engine from the current project file.
Only for polygon, arranges candidates with respect of polygon orientation.
Definition: pal.h:84
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=0) const
QgsPalLayerSettings::UpsideDownLabels upsidedownLabels() const
How to handle labels that would be upside down.
virtual QList< QgsAbstractLabelProvider * > subProviders()
Return list of child providers - useful if the provider needs to put labels into more layers with dif...
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=0) const
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:268
void setCentroidInside(bool forceInside)
Sets whether labels placed at the centroid of features within the layer are forced to be placed insid...
Definition: layer.h:208
is a little bit better than CHAIN but slower
Definition: pal.h:68
void setFitInPolygonOnly(bool fitInPolygon)
Sets whether labels which do not fit completely within a polygon feature are discarded.
Definition: layer.h:223
whether adjacent lines (with the same label text) should be merged
int indexOf(const T &value, int from) const
void removeProvider(QgsAbstractLabelProvider *provider)
Remove provider if the provider's initialization failed. Provider instance is deleted.
virtual QList< QgsLabelFeature * > labelFeatures(QgsRenderContext &context)=0
Return list of label features (they are owned by the provider and thus deleted on its destruction) ...
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
bool writeEntry(const QString &scope, const QString &key, bool value)
int elapsed() const
QgsPalLayerSettings::Placement placement() const
What placement strategy to use for the labels.
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:201
void setSearch(SearchMethod method)
Select the search method to use.
Definition: pal.cpp:700
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:186
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
QgsMapSettings mMapSettings
Associated map settings instance.
QList< QgsAbstractLabelProvider * > mProviders
List of providers (the are owned by the labeling engine)
bool renderingStopped() const
Whether to draw rectangles of generated candidates (good for debugging)
is the best but slowest
Definition: pal.h:67
struct pal::_chain Chain
Flags flags() const
Get flags of the labeling engine.
GEOSGeometry * mGeometry
Geometry of the feature to be labelled.
whether location of centroid must be inside of polygons
QgsPalLabeling::Search mSearchMethod
search method to use for removal collisions between labels
void setBrush(const QBrush &brush)
double priority() const
Default priority of labels (may be overridden by individual labels)
int restart()
Problem * extractProblem(double bbox[4])
Definition: pal.cpp:578
Reads and writes project states.
Definition: qgsproject.h:69
QgsLabelingResults * mResults
Resulting labeling layout.
Flags flags() const
Flags associated with the provider.
void setEngine(const QgsLabelingEngineV2 *engine)
Associate provider with a labeling engine (should be only called internally from QgsLabelingEngineV2)...
QgsAbstractLabelProvider * provider() const
Returns pointer to the associated provider.
Definition: layer.h:85
whether labels must fall completely within the polygon
whether all features will be labelled even though overlaps occur
QgsAbstractLabelProvider()
Construct the provider with default values.
QgsLabelingEngineV2()
Construct the labeling engine with default settings.
The QgsAbstractLabelProvider class is an interface class.
void setArrangementFlags(const LineArrangementFlags &flags)
Sets the layer's arrangement flags.
Definition: layer.h:111
void writeSettingsToProject()
Write configuration of the labeling engine to the current project file.
void setShowPartial(bool show)
Set flag show partial label.
Definition: pal.cpp:660
Only for lines, labels along the line.
Definition: pal.h:83
void run(QgsRenderContext &context)
compute the labeling with given map settings and providers
Whether to draw all labels even if there would be collisions.
Whether to use also label candidates that are partially outside of the map view.
Arrangement
The way to arrange labels against spatial entities.
Definition: pal.h:77
Contains information about the context of a rendering operation.
QPainter * painter()
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
Whether to only draw the label rect and not the actual label text (used for unit tests) ...
static GEOSContextHandle_t getGEOSHandler()
Return GEOS context handle.
QgsPalLayerSettings::ObstacleType obstacleType() const
How the feature geometries will work as obstacles.
bool registerFeature(QgsLabelFeature *label)
Register a feature in the layer.
Definition: layer.cpp:100
int getNumFeatures()
Definition: problem.h:113
int rotate(double rotation, const QgsPoint &center)
Rotate this geometry around the Z axis.
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:353
void setLabelMode(LabelMode mode)
Sets the layer's labeling mode.
Definition: layer.h:173
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, Arrangement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll=false)
add a new layer
Definition: pal.cpp:121
static QgsGeometry * fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
LabelPosition is a candidate feature label position.
Definition: labelposition.h:48
qint64 QgsFeatureId
Definition: qgsfeature.h:31
pal::Layer * mLayer
Pointer to PAL layer (assigned when registered to PAL)
QString fromLatin1(const char *str, int size)
Represent a problem.
Definition: problem.h:93
void start()
void setLineP(int line_p)
set maximum # candidates to generate for lines features Higher the value is, longer Pal::labeller wil...
Definition: pal.cpp:614
whether the labels should be rendered
static void drawLabelCandidateRect(pal::LabelPosition *lp, QPainter *painter, const QgsMapToPixel *xform, QList< QgsLabelCandidate > *candidates=0)
LabelPosition * getFeatureCandidate(int fi, int ci)
Definition: problem.h:117
Class that stores computed placement from labeling engine.
SearchMethod
Search method to use.
Definition: pal.h:64
virtual void drawLabel(QgsRenderContext &context, pal::LabelPosition *label) const =0
draw this label at the position determined by the labeling engine
is the worst but fastest method
Definition: pal.h:66
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:191
Whether to render labels as text or outlines.
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:216
QList< QgsAbstractLabelProvider * > mSubProviders
QgsLabelingResults * takeResults()
Return pointer to recently computed results and pass the ownership of results to the caller...
arranges candidates over a point (centroid for polygon)
Definition: pal.h:81
void registerCancellationCallback(FnIsCancelled fnCancelled, void *context)
Register a function that returns whether this job has been cancelled - PAL calls it during the comput...
Definition: pal.cpp:572
void setMergeConnectedLines(bool merge)
Sets whether connected lines should be merged before labeling.
Definition: layer.h:184
QgsFeatureId id() const
Identifier of the label (unique within the parent label provider)