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