Branch data Line data Source code
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 LABELPOSITION_H
31 : : #define LABELPOSITION_H
32 : :
33 : : #define SIP_NO_FILE
34 : :
35 : :
36 : : #include "qgis_core.h"
37 : : #include "pointset.h"
38 : : #include "palrtree.h"
39 : : #include <fstream>
40 : :
41 : : namespace pal
42 : : {
43 : :
44 : : class FeaturePart;
45 : : class Pal;
46 : : class Label;
47 : :
48 : :
49 : : /**
50 : : * \ingroup core
51 : : * \brief LabelPosition is a candidate feature label position
52 : : * \class pal::LabelPosition
53 : : * \note not available in Python bindings
54 : : */
55 : 0 : class CORE_EXPORT LabelPosition : public PointSet
56 : : {
57 : : friend class CostCalculator;
58 : : friend class PolygonCostCalculator;
59 : :
60 : : public:
61 : :
62 : : /**
63 : : * \brief Position of label candidate relative to feature.
64 : : */
65 : : enum Quadrant
66 : : {
67 : : QuadrantAboveLeft,
68 : : QuadrantAbove,
69 : : QuadrantAboveRight,
70 : : QuadrantLeft,
71 : : QuadrantOver,
72 : : QuadrantRight,
73 : : QuadrantBelowLeft,
74 : : QuadrantBelow,
75 : : QuadrantBelowRight
76 : : };
77 : :
78 : : /**
79 : : * \brief create a new LabelPosition
80 : : *
81 : : * \param id id of this labelposition
82 : : * \param x1 down-left x coordinate
83 : : * \param y1 down-left y coordinate
84 : : * \param w label width
85 : : * \param h label height
86 : : * \param alpha rotation in rad
87 : : * \param cost geographic cost
88 : : * \param feature labelpos owners
89 : : * \param isReversed label is reversed
90 : : * \param quadrant relative position of label to feature
91 : : */
92 : : LabelPosition( int id, double x1, double y1,
93 : : double w, double h,
94 : : double alpha, double cost,
95 : : FeaturePart *feature, bool isReversed = false, Quadrant quadrant = QuadrantOver );
96 : :
97 : : //! Copy constructor
98 : : LabelPosition( const LabelPosition &other );
99 : :
100 : : /**
101 : : * \brief Is the labelposition in the bounding-box ? (intersect or inside????)
102 : : *
103 : : *\param bbox the bounding-box double[4] = {xmin, ymin, xmax, ymax}
104 : : */
105 : : bool isIn( double *bbox );
106 : :
107 : : /**
108 : : * \brief Is the labelposition intersect the bounding-box ?
109 : : *
110 : : *\param bbox the bounding-box double[4] = {xmin, ymin, xmax, ymax}
111 : : */
112 : : bool isIntersect( double *bbox );
113 : :
114 : : /**
115 : : * Returns TRUE if the label position intersects a \a geometry.
116 : : */
117 : : bool intersects( const GEOSPreparedGeometry *geometry );
118 : :
119 : : /**
120 : : * Returns TRUE if the label position is within a \a geometry.
121 : : */
122 : : bool within( const GEOSPreparedGeometry *geometry );
123 : :
124 : : /**
125 : : * \brief Is the labelposition inside the bounding-box ?
126 : : *
127 : : *\param bbox the bounding-box double[4] = {xmin, ymin, xmax, ymax}
128 : : */
129 : : bool isInside( double *bbox );
130 : :
131 : : /**
132 : : * \brief Check whether or not this overlap with another labelPosition
133 : : *
134 : : * \param ls other labelposition
135 : : * \returns TRUE or FALSE
136 : : */
137 : : bool isInConflict( const LabelPosition *ls ) const;
138 : :
139 : : //! Returns bounding box - amin: xmin,ymin - amax: xmax,ymax
140 : : void getBoundingBox( double amin[2], double amax[2] ) const;
141 : :
142 : : //! Gets distance from this label to a point. If point lies inside, returns negative number.
143 : : double getDistanceToPoint( double xp, double yp ) const;
144 : :
145 : : //! Returns TRUE if this label crosses the specified line
146 : : bool crossesLine( PointSet *line ) const;
147 : :
148 : : //! Returns TRUE if this label crosses the boundary of the specified polygon
149 : : bool crossesBoundary( PointSet *polygon ) const;
150 : :
151 : : /**
152 : : * Returns cost of position intersection with polygon (testing area of intersection and center).
153 : : * Cost ranges between 0 and 12, with extra cost if center of label position is covered.
154 : : */
155 : : int polygonIntersectionCost( PointSet *polygon ) const;
156 : :
157 : : /**
158 : : * Returns TRUE if any intersection between polygon and position exists.
159 : : */
160 : : bool intersectsWithPolygon( PointSet *polygon ) const;
161 : :
162 : : //! Shift the label by specified offset
163 : : void offsetPosition( double xOffset, double yOffset );
164 : :
165 : : /**
166 : : * Returns the id
167 : : */
168 : : int getId() const;
169 : :
170 : :
171 : : /**
172 : : * Returns the feature corresponding to this labelposition
173 : : */
174 : : FeaturePart *getFeaturePart() const;
175 : :
176 : 0 : int getNumOverlaps() const { return nbOverlap; }
177 : 0 : void resetNumOverlaps() { nbOverlap = 0; } // called from problem.cpp, pal.cpp
178 : :
179 : : /**
180 : : * Increases the number of overlaps recorded against this position by 1.
181 : : */
182 : 0 : void incrementNumOverlaps() { nbOverlap++; }
183 : :
184 : : /**
185 : : * Decreases the number of overlaps recorded against this position by 1.
186 : : */
187 : 0 : void decrementNumOverlaps() { nbOverlap++; }
188 : :
189 : 0 : int getProblemFeatureId() const { return probFeat; }
190 : :
191 : : /**
192 : : * Set problem feature ID and assigned label candidate ID.
193 : : * called from pal.cpp during extraction.
194 : : */
195 : 0 : void setProblemIds( int probFid, int lpId )
196 : : {
197 : 0 : probFeat = probFid;
198 : 0 : id = lpId;
199 : 0 : if ( mNextPart ) mNextPart->setProblemIds( probFid, lpId );
200 : 0 : }
201 : :
202 : : /**
203 : : * Returns the candidate label position's geographical cost.
204 : : * \see setCost
205 : : */
206 : 0 : double cost() const { return mCost; }
207 : :
208 : : /**
209 : : * Sets the candidate label position's geographical cost.
210 : : * \param newCost new cost for position
211 : : * \see cost
212 : : */
213 : 0 : void setCost( double newCost ) { mCost = newCost; }
214 : :
215 : : /**
216 : : * Sets whether the position is marked as conflicting with an obstacle feature.
217 : : * \param conflicts set to TRUE to mark candidate as being in conflict
218 : : * \note This method applies to all label parts for the candidate position.
219 : : * \see conflictsWithObstacle
220 : : */
221 : : void setConflictsWithObstacle( bool conflicts );
222 : :
223 : : /**
224 : : * Returns whether the position is marked as conflicting with an obstacle feature.
225 : : * \see setConflictsWithObstacle
226 : : */
227 : 0 : bool conflictsWithObstacle() const { return mHasObstacleConflict; }
228 : :
229 : : /**
230 : : * Sets whether the position is marked as having a hard conflict with an obstacle feature.
231 : : * A hard conflict means that the placement should (usually) not be considered, because the candidate
232 : : * conflicts with a obstacle of sufficient weight.
233 : : * \see hasHardObstacleConflict()
234 : : */
235 : : void setHasHardObstacleConflict( bool conflicts );
236 : :
237 : : /**
238 : : * Returns whether the position is marked as having a hard conflict with an obstacle feature.
239 : : * A hard conflict means that the placement should (usually) not be considered, because the candidate
240 : : * conflicts with a obstacle of sufficient weight.
241 : : * \see setHasHardObstacleConflict()
242 : : */
243 : 0 : bool hasHardObstacleConflict() const { return mHasHardConflict; }
244 : :
245 : : //! Make sure the cost is less than 1
246 : : void validateCost();
247 : :
248 : : /**
249 : : * Returns the down-left x coordinate.
250 : : * \see getY()
251 : : */
252 : : double getX( int i = 0 ) const;
253 : :
254 : : /**
255 : : * Returns the down-left y coordinate.
256 : : * \see getX()
257 : : */
258 : : double getY( int i = 0 ) const;
259 : :
260 : 0 : double getWidth() const { return w; }
261 : 0 : double getHeight() const { return h; }
262 : :
263 : : /**
264 : : * Returns the angle to rotate text (in rad).
265 : : */
266 : : double getAlpha() const;
267 : 0 : bool getReversed() const { return reversed; }
268 : 0 : bool getUpsideDown() const { return upsideDown; }
269 : :
270 : 0 : Quadrant getQuadrant() const { return quadrant; }
271 : :
272 : : /**
273 : : * Returns the next part of this label position (i.e. the next character for a curved label).
274 : : *
275 : : * \see setNextPart()
276 : : */
277 : 0 : LabelPosition *nextPart() const { return mNextPart.get(); }
278 : :
279 : : /**
280 : : * Sets the \a next part of this label position (i.e. the next character for a curved label).
281 : : *
282 : : * \see nextPart()
283 : : */
284 : 0 : void setNextPart( std::unique_ptr< LabelPosition > next ) { mNextPart = std::move( next ); }
285 : :
286 : : // -1 if not multi-part
287 : 0 : int getPartId() const { return partId; }
288 : 0 : void setPartId( int id ) { partId = id; }
289 : :
290 : : /**
291 : : * Sets the \a count of upside down characters for this label position.
292 : : *
293 : : * \see upsideDownCharCount()
294 : : */
295 : 0 : void setUpsideDownCharCount( int count ) { mUpsideDownCharCount = count ; }
296 : :
297 : : /**
298 : : * Returns the number of upside down characters for this label position.
299 : : *
300 : : * \see setUpsideDownCharCount()
301 : : */
302 : : int upsideDownCharCount() const { return mUpsideDownCharCount; }
303 : :
304 : : /**
305 : : * Removes the label position from the specified \a index.
306 : : */
307 : : void removeFromIndex( PalRtree<LabelPosition> &index );
308 : :
309 : : /**
310 : : * Inserts the label position into the specified \a index.
311 : : */
312 : : void insertIntoIndex( PalRtree<LabelPosition> &index );
313 : :
314 : : /**
315 : : * Returns a prepared GEOS representation of all label parts as a multipolygon.
316 : : *
317 : : * \since QGIS 3.20
318 : : */
319 : : const GEOSPreparedGeometry *preparedMultiPartGeom() const;
320 : :
321 : : /**
322 : : * Returns the global ID for the candidate, which is unique for a single run of the pal
323 : : * labelling engine.
324 : : *
325 : : * A return value of 0 means that the ID has not been assigned.
326 : : *
327 : : * \see setGlobalId()
328 : : */
329 : 0 : unsigned int globalId() const { return mGlobalId; }
330 : :
331 : : /**
332 : : * Sets the global \a id for the candidate, which is unique for a single run of the pal
333 : : * labelling engine.
334 : : *
335 : : * \see globalId()
336 : : */
337 : 0 : void setGlobalId( unsigned int id ) { mGlobalId = id; }
338 : :
339 : : protected:
340 : :
341 : : int id;
342 : :
343 : : FeaturePart *feature = nullptr;
344 : :
345 : : // bug # 1 (maxence 10/23/2008)
346 : : int probFeat;
347 : :
348 : : int nbOverlap;
349 : :
350 : : double alpha;
351 : : double w;
352 : : double h;
353 : :
354 : : int partId;
355 : :
356 : : //True if label direction is the same as line / polygon ring direction.
357 : : //Could be used by the application to draw a directional arrow ('<' or '>')
358 : : //if the layer arrangement is P_LINE
359 : : bool reversed;
360 : :
361 : : bool upsideDown;
362 : :
363 : : LabelPosition::Quadrant quadrant;
364 : :
365 : : private:
366 : :
367 : : unsigned int mGlobalId = 0;
368 : : std::unique_ptr< LabelPosition > mNextPart;
369 : :
370 : : double mCost;
371 : : bool mHasObstacleConflict;
372 : : bool mHasHardConflict = false;
373 : : int mUpsideDownCharCount;
374 : :
375 : : /**
376 : : * Calculates the total number of parts for this label position
377 : : */
378 : : int partCount() const;
379 : :
380 : : /**
381 : : * Calculates the polygon intersection cost for a single label position part
382 : : * \returns double between 0 - 12
383 : : */
384 : : double polygonIntersectionCostForParts( PointSet *polygon ) const;
385 : :
386 : : /**
387 : : * Creates a GEOS representation of all label parts as a multipolygon.
388 : : */
389 : : void createMultiPartGeosGeom() const;
390 : :
391 : : bool isInConflictMultiPart( const LabelPosition *lp ) const;
392 : :
393 : : LabelPosition &operator=( const LabelPosition & ) = delete;
394 : : };
395 : :
396 : : } // end namespace
397 : :
398 : : #endif
|