1 /*
2  *   libpal - Automated Placement of Labels Library
3  *
4  *   Copyright (C) 2008 Maxence Laurent, MIS-TIC, HEIG-VD
5  *                      University of Applied Sciences, Western Switzerland
6  *                      http://www.hes-so.ch
7  *
8  *   Contact:
9  *      maxence.laurent <at> heig-vd <dot> ch
10  *    or
11  *      eric.taillard <at> heig-vd <dot> ch
12  *
13  * This file is part of libpal.
14  *
15  * libpal is free software: you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation, either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * libpal is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with libpal.  If not, see <http://www.gnu.org/licenses/>.
27  *
28  */
29 
30 #ifndef PAL_H
31 #define PAL_H
32 
33 #define SIP_NO_FILE
34 
35 
36 #include "qgis_core.h"
37 #include "qgsgeometry.h"
38 #include "qgsgeos.h"
39 #include "qgspallabeling.h"
40 #include "qgslabelingenginesettings.h"
41 #include <QList>
42 #include <iostream>
43 #include <ctime>
44 #include <QMutex>
45 #include <QStringList>
46 #include <unordered_map>
47 
48 // TODO ${MAJOR} ${MINOR} etc instead of 0.2
49 
50 class QgsAbstractLabelProvider;
51 
52 namespace pal
53 {
54   class Layer;
55   class LabelPosition;
56   class PalStat;
57   class Problem;
58   class PointSet;
59 
60   //! Search method to use
61   enum SearchMethod
62   {
63     CHAIN = 0, //!< Is the worst but fastest method
64     POPMUSIC_TABU_CHAIN = 1, //!< Is the best but slowest
65     POPMUSIC_TABU = 2, //!< Is a little bit better than CHAIN but slower
66     POPMUSIC_CHAIN = 3, //!< Is slower and best than TABU, worse and faster than TABU_CHAIN
67     FALP = 4 //!< Only initial solution
68   };
69 
70   /**
71    * \ingroup core
72    *  \brief Main Pal labeling class
73    *
74    *  A pal object will contains layers and global information such as which search method
75    *  will be used.
76    *  \class pal::Pal
77    *  \note not available in Python bindings
78    */
79   class CORE_EXPORT Pal
80   {
81       friend class Problem;
82       friend class FeaturePart;
83       friend class Layer;
84 
85     public:
86 
87       /**
88        * \brief Create an new pal instance
89        */
90       Pal();
91 
92       ~Pal();
93 
94       //! Pal cannot be copied.
95       Pal( const Pal &other ) = delete;
96       //! Pal cannot be copied.
97       Pal &operator=( const Pal &other ) = delete;
98 
99       /**
100        * \brief add a new layer
101        *
102        * \param provider Provider associated with the layer
103        * \param layerName layer's name
104        * \param arrangement Howto place candidates
105        * \param defaultPriority layer's prioriry (0 is the best, 1 the worst)
106        * \param active is the layer is active (currently displayed)
107        * \param toLabel the layer will be labeled only if toLablel is TRUE
108        * \param displayAll if TRUE, all features will be labelled even though overlaps occur
109        *
110        * \throws PalException::LayerExists
111        */
112       Layer *addLayer( QgsAbstractLabelProvider *provider, const QString &layerName, QgsPalLayerSettings::Placement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll = false );
113 
114       /**
115        * \brief remove a layer
116        *
117        * \param layer layer to remove
118        */
119       void removeLayer( Layer *layer );
120 
121       typedef bool ( *FnIsCanceled )( void *ctx );
122 
123       //! Register a function that returns whether this job has been canceled - PAL calls it during the computation
124       void registerCancellationCallback( FnIsCanceled fnCanceled, void *context );
125 
126       //! Check whether the job has been canceled
isCanceled()127       inline bool isCanceled() { return fnIsCanceled ? fnIsCanceled( fnIsCanceledContext ) : false; }
128 
129       /**
130        * Extracts the labeling problem for the specified map \a extent - only features within this
131        * extent will be considered. The \a mapBoundary argument specifies the actual geometry of the map
132        * boundary, which will be used to detect whether a label is visible (or partially visible) in
133        * the rendered map. This may differ from \a extent in the case of rotated or non-rectangular
134        * maps.
135        */
136       std::unique_ptr< Problem > extractProblem( const QgsRectangle &extent, const QgsGeometry &mapBoundary );
137 
138       /**
139        * Solves the labeling problem, selecting the best candidate locations for all labels and returns a list of these
140        * calculated label positions.
141        *
142        * If \a displayAll is TRUE, then the best positions for ALL labels will be returned, regardless of whether these
143        * labels overlap other labels.
144        *
145        * If the optional \a unlabeled list is specified, it will be filled with a list of all feature labels which could
146        * not be placed in the returned solution (e.g. due to overlaps or other constraints).
147        *
148        * Ownership of the returned labels is not transferred - it resides with the pal object.
149        */
150       QList<LabelPosition *> solveProblem( Problem *prob, bool displayAll, QList<pal::LabelPosition *> *unlabeled = nullptr );
151 
152       /**
153        * Sets whether partial labels show be allowed.
154        *
155        * \see showPartialLabels()
156        */
157       void setShowPartialLabels( bool show );
158 
159       /**
160        * Returns whether partial labels should be allowed.
161        *
162        * \see setShowPartialLabels()
163        */
164       bool showPartialLabels() const;
165 
166       /**
167        * Returns the maximum number of line label candidate positions per map unit.
168        *
169        * \see setMaximumLineCandidatesPerMapUnit()
170        */
maximumLineCandidatesPerMapUnit()171       double maximumLineCandidatesPerMapUnit() const { return mMaxLineCandidatesPerMapUnit; }
172 
173       /**
174        * Sets the maximum number of line label \a candidates per map unit.
175        *
176        * \see maximumLineCandidatesPerMapUnit()
177        */
setMaximumLineCandidatesPerMapUnit(double candidates)178       void setMaximumLineCandidatesPerMapUnit( double candidates ) { mMaxLineCandidatesPerMapUnit = candidates; }
179 
180       /**
181        * Returns the maximum number of polygon label candidate positions per map unit squared.
182        *
183        * \see setMaximumPolygonCandidatesPerMapUnitSquared()
184        */
maximumPolygonCandidatesPerMapUnitSquared()185       double maximumPolygonCandidatesPerMapUnitSquared() const { return mMaxPolygonCandidatesPerMapUnitSquared; }
186 
187       /**
188        * Sets the maximum number of polygon label \a candidates per map unit squared.
189        *
190        * \see maximumPolygonCandidatesPerMapUnitSquared()
191        */
setMaximumPolygonCandidatesPerMapUnitSquared(double candidates)192       void setMaximumPolygonCandidatesPerMapUnitSquared( double candidates ) { mMaxPolygonCandidatesPerMapUnitSquared = candidates; }
193 
194       /**
195        * Returns the placement engine version, which dictates how the label placement problem is solved.
196        *
197        * \see setPlacementVersion()
198        */
199       QgsLabelingEngineSettings::PlacementEngineVersion placementVersion() const;
200 
201       /**
202        * Sets the placement engine \a version, which dictates how the label placement problem is solved.
203        *
204        * \see placementVersion()
205        */
206       void setPlacementVersion( QgsLabelingEngineSettings::PlacementEngineVersion placementVersion );
207 
208       /**
209        * Returns the global candidates limit for point features, or 0 if no global limit is in effect.
210        *
211        * This is an installation-wide setting which applies to all projects, and is set via QSettings. It can
212        * be used to place global limits on the number of candidates generated for point features in order
213        * to optimise map rendering speeds.
214        *
215        * \see globalCandidatesLimitLine()
216        * \see globalCandidatesLimitPolygon()
217        */
globalCandidatesLimitPoint()218       int globalCandidatesLimitPoint() const { return mGlobalCandidatesLimitPoint; }
219 
220       /**
221        * Returns the global candidates limit for line features, or 0 if no global limit is in effect.
222        *
223        * This is an installation-wide setting which applies to all projects, and is set via QSettings. It can
224        * be used to place global limits on the number of candidates generated for line features in order
225        * to optimise map rendering speeds.
226        *
227        * \see globalCandidatesLimitPolygon()
228        * \see globalCandidatesLimitPoint()
229        */
globalCandidatesLimitLine()230       int globalCandidatesLimitLine() const { return mGlobalCandidatesLimitLine; }
231 
232       /**
233        * Returns the global candidates limit for polygon features, or 0 if no global limit is in effect.
234        *
235        * This is an installation-wide setting which applies to all projects, and is set via QSettings. It can
236        * be used to place global limits on the number of candidates generated for polygon features in order
237        * to optimise map rendering speeds.
238        *
239        * \see globalCandidatesLimitLine()
240        * \see globalCandidatesLimitPoint()
241        */
globalCandidatesLimitPolygon()242       int globalCandidatesLimitPolygon() const { return mGlobalCandidatesLimitPolygon; }
243 
244     private:
245 
246       std::unordered_map< QgsAbstractLabelProvider *, std::unique_ptr< Layer > > mLayers;
247 
248       QMutex mMutex;
249 
250       /*
251        * POPMUSIC Tuning
252        */
253       int mPopmusicR = 30;
254 
255       int mTabuMaxIt = 4;
256       int mTabuMinIt = 2;
257 
258       int mEjChainDeg = 50;
259       int mTenure = 10;
260       double mCandListSize = 0.2;
261 
262       /**
263        * \brief show partial labels (cut-off by the map canvas) or not
264        */
265       bool mShowPartialLabels = true;
266 
267       double mMaxLineCandidatesPerMapUnit = 0;
268       double mMaxPolygonCandidatesPerMapUnitSquared = 0;
269 
270       int mGlobalCandidatesLimitPoint = 0;
271       int mGlobalCandidatesLimitLine = 0;
272       int mGlobalCandidatesLimitPolygon = 0;
273 
274       QgsLabelingEngineSettings::PlacementEngineVersion mPlacementVersion = QgsLabelingEngineSettings::PlacementEngineVersion2;
275 
276       //! Callback that may be called from PAL to check whether the job has not been canceled in meanwhile
277       FnIsCanceled fnIsCanceled = nullptr;
278       //! Application-specific context for the cancellation check function
279       void *fnIsCanceledContext = nullptr;
280 
281       /**
282        * Creates a Problem, by extracting labels and generating candidates from the given \a extent.
283        * The \a mapBoundary geometry specifies the actual visible region of the map, and is used
284        * for pruning candidates which fall outside the visible region.
285        */
286       std::unique_ptr< Problem > extract( const QgsRectangle &extent, const QgsGeometry &mapBoundary );
287 
288       /**
289        * \brief Choose the size of popmusic subpart's
290        * \param r subpart size
291        */
292       void setPopmusicR( int r );
293 
294       /**
295        * \brief minimum # of iteration for search method POPMUSIC_TABU, POPMUSIC_CHAIN and POPMUSIC_TABU_CHAIN
296        * \param min_it Sub part optimization min # of iteration
297        */
298       void setMinIt( int min_it );
299 
300       /**
301        * \brief maximum \# of iteration for search method POPMUSIC_TABU, POPMUSIC_CHAIN and POPMUSIC_TABU_CHAIN
302        * \param max_it Sub part optimization max # of iteration
303        */
304       void setMaxIt( int max_it );
305 
306       /**
307        * \brief For tabu search : how many iteration a feature will be tabu
308        * \param tenure consiser a feature as tabu for tenure iteration after updating feature in solution
309        */
310       void setTenure( int tenure );
311 
312       /**
313        * \brief For *CHAIN, select the max size of a transformation chain
314        * \param degree maximum soze of a transformation chain
315        */
316       void setEjChainDeg( int degree );
317 
318       /**
319        * \brief How many candidates will be tested by a tabu iteration
320        * \param fact the ration (0..1) of candidates to test
321        */
322       void setCandListSize( double fact );
323 
324 
325       /**
326        * Returns the minimum number of iterations used for POPMUSIC_TABU, POPMUSIC_CHAIN and POPMUSIC_TABU_CHAIN.
327        * \see getMaxIt()
328        */
329       int getMinIt();
330 
331       /**
332        * Returns the maximum number of iterations allowed for POPMUSIC_TABU, POPMUSIC_CHAIN and POPMUSIC_TABU_CHAIN.
333        * \see getMinIt()
334        */
335       int getMaxIt();
336 
337   };
338 
339 } // end namespace pal
340 
341 #endif
342