QGIS API Documentation 3.29.0-Master (da8bb1db43)
labelposition.cpp
Go to the documentation of this file.
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#include "layer.h"
31#include "costcalculator.h"
32#include "feature.h"
33#include "labelposition.h"
34#include "geomfunction.h"
35#include "qgsgeos.h"
36#include "qgsmessagelog.h"
37#include <cmath>
38#include <cfloat>
39
40using namespace pal;
41
42LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, bool isReversed, Quadrant quadrant )
43 : id( id )
44 , feature( feature )
45 , probFeat( 0 )
46 , nbOverlap( 0 )
47 , alpha( alpha )
48 , w( w )
49 , h( h )
50 , partId( -1 )
51 , reversed( isReversed )
52 , upsideDown( false )
53 , quadrant( quadrant )
54 , mCost( cost )
55 , mHasObstacleConflict( false )
56 , mUpsideDownCharCount( 0 )
57{
58 type = GEOS_POLYGON;
59 nbPoints = 4;
60 x.resize( nbPoints );
61 y.resize( nbPoints );
62
63 // alpha take his value bw 0 and 2*pi rad
64 while ( this->alpha > 2 * M_PI )
65 this->alpha -= 2 * M_PI;
66
67 while ( this->alpha < 0 )
68 this->alpha += 2 * M_PI;
69
70 const double beta = this->alpha + M_PI_2;
71
72 double dx1, dx2, dy1, dy2;
73
74 dx1 = std::cos( this->alpha ) * w;
75 dy1 = std::sin( this->alpha ) * w;
76
77 dx2 = std::cos( beta ) * h;
78 dy2 = std::sin( beta ) * h;
79
80 x[0] = x1;
81 y[0] = y1;
82
83 x[1] = x1 + dx1;
84 y[1] = y1 + dy1;
85
86 x[2] = x1 + dx1 + dx2;
87 y[2] = y1 + dy1 + dy2;
88
89 x[3] = x1 + dx2;
90 y[3] = y1 + dy2;
91
92 // upside down ? (curved labels are always correct)
93 if ( !feature->layer()->isCurved() &&
94 this->alpha > M_PI_2 && this->alpha <= 3 * M_PI_2 )
95 {
97 {
98 // Turn label upsidedown by inverting boundary points
99 double tx, ty;
100
101 tx = x[0];
102 ty = y[0];
103
104 x[0] = x[2];
105 y[0] = y[2];
106
107 x[2] = tx;
108 y[2] = ty;
109
110 tx = x[1];
111 ty = y[1];
112
113 x[1] = x[3];
114 y[1] = y[3];
115
116 x[3] = tx;
117 y[3] = ty;
118
119 if ( this->alpha < M_PI )
120 this->alpha += M_PI;
121 else
122 this->alpha -= M_PI;
123
124 // labels with text shown upside down are not classified as upsideDown,
125 // only those whose boundary points have been inverted
126 upsideDown = true;
127 }
128 }
129
130 for ( int i = 0; i < nbPoints; ++i )
131 {
132 xmin = std::min( xmin, x[i] );
133 xmax = std::max( xmax, x[i] );
134 ymin = std::min( ymin, y[i] );
135 ymax = std::max( ymax, y[i] );
136 }
137
138 createOuterBoundsGeom();
139}
140
142 : PointSet( other )
143{
144 id = other.id;
145 mCost = other.mCost;
146 feature = other.feature;
147 probFeat = other.probFeat;
148 nbOverlap = other.nbOverlap;
149
150 alpha = other.alpha;
151 w = other.w;
152 h = other.h;
153
154 if ( other.mNextPart )
155 mNextPart = std::make_unique< LabelPosition >( *other.mNextPart );
156
157 partId = other.partId;
158 upsideDown = other.upsideDown;
159 reversed = other.reversed;
160 quadrant = other.quadrant;
161 mHasObstacleConflict = other.mHasObstacleConflict;
162 mUpsideDownCharCount = other.mUpsideDownCharCount;
163
164 createOuterBoundsGeom();
165}
166
168{
169 if ( mPreparedOuterBoundsGeos )
170 {
171 GEOSPreparedGeom_destroy_r( QgsGeos::getGEOSHandler(), mPreparedOuterBoundsGeos );
172 mPreparedOuterBoundsGeos = nullptr;
173 }
174}
175
176bool LabelPosition::intersects( const GEOSPreparedGeometry *geometry )
177{
178 // this method considers the label's outer bounds
179 if ( !mOuterBoundsGeos && !mGeos )
181
182 try
183 {
184 if ( GEOSPreparedIntersects_r( QgsGeos::getGEOSHandler(), geometry, mOuterBoundsGeos ? mOuterBoundsGeos.get() : mGeos ) == 1 )
185 {
186 return true;
187 }
188 else if ( mNextPart )
189 {
190 return mNextPart->intersects( geometry );
191 }
192 }
193 catch ( GEOSException &e )
194 {
195 qWarning( "GEOS exception: %s", e.what() );
196 QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
197 return false;
198 }
199
200 return false;
201}
202
203bool LabelPosition::within( const GEOSPreparedGeometry *geometry )
204{
205 // this method considers the label's outer bounds
206 if ( !mOuterBoundsGeos && !mGeos )
208
209 try
210 {
211 if ( GEOSPreparedContains_r( QgsGeos::getGEOSHandler(), geometry, mOuterBoundsGeos ? mOuterBoundsGeos.get() : mGeos ) != 1 )
212 {
213 return false;
214 }
215 else if ( mNextPart )
216 {
217 return mNextPart->within( geometry );
218 }
219 }
220 catch ( GEOSException &e )
221 {
222 qWarning( "GEOS exception: %s", e.what() );
223 QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
224 return false;
225 }
226
227 return true;
228}
229
231{
232 // this method considers the label's outer bounds
233
234 if ( this->probFeat == lp->probFeat ) // bugfix #1
235 return false; // always overlaping itself !
236
237 // if either this label doesn't cause collisions, or the other one doesn't, then we don't conflict!
240 return false;
241
242 if ( !nextPart() && !lp->nextPart() )
243 {
244 if ( qgsDoubleNear( alpha, 0 ) && qgsDoubleNear( lp->alpha, 0 ) )
245 {
246 // simple case -- both candidates are oriented to axis, so shortcut with easy calculation
247 if ( mOuterBoundsGeos )
248 {
249 return outerBoundingBoxIntersects( lp );
250 }
251 else
252 {
253 return boundingBoxIntersects( lp );
254 }
255 }
256 else
257 {
258 // this method considers the label's outer bounds
259 if ( !mOuterBoundsGeos && !mGeos )
261
262 GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
263 try
264 {
265 const bool result = ( GEOSPreparedIntersects_r( geosctxt, lp->preparedOuterBoundsGeom() ? lp->preparedOuterBoundsGeom() : lp->preparedGeom(),
266 mOuterBoundsGeos ? mOuterBoundsGeos.get() : mGeos ) == 1 );
267 return result;
268 }
269 catch ( GEOSException &e )
270 {
271 qWarning( "GEOS exception: %s", e.what() );
272 QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
273 return false;
274 }
275
276 return false;
277 }
278 }
279
280 return isInConflictMultiPart( lp );
281}
282
283bool LabelPosition::isInConflictMultiPart( const LabelPosition *lp ) const
284{
285 if ( !mMultipartGeos )
286 createMultiPartGeosGeom();
287
288 if ( !lp->mMultipartGeos )
289 lp->createMultiPartGeosGeom();
290
291 GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
292 try
293 {
294 const bool result = ( GEOSPreparedIntersects_r( geosctxt, preparedMultiPartGeom(), lp->mMultipartGeos ) == 1 );
295 return result;
296 }
297 catch ( GEOSException &e )
298 {
299 qWarning( "GEOS exception: %s", e.what() );
300 QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
301 return false;
302 }
303
304 return false;
305}
306
307void LabelPosition::createOuterBoundsGeom()
308{
309 const QRectF outerBounds = getFeaturePart()->feature()->outerBounds();
310 if ( outerBounds.isNull() )
311 return;
312
313 GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
314
315 const double beta = this->alpha + M_PI_2;
316
317 const double dx1 = std::cos( this->alpha ) * outerBounds.width();
318 const double dy1 = std::sin( this->alpha ) * outerBounds.width();
319
320 const double dx2 = std::cos( beta ) * outerBounds.height();
321 const double dy2 = std::sin( beta ) * outerBounds.height();
322
323 mOuterBoundsX.resize( 5 );
324 mOuterBoundsY.resize( 5 );
325
326 const double x1 = x[0] + outerBounds.left();
327 const double y1 = y[0] + outerBounds.top();
328
329 mOuterBoundsX[0] = x1;
330 mOuterBoundsY[0] = y1;
331
332 mOuterBoundsX[1] = x1 + dx1;
333 mOuterBoundsY[1] = y1 + dy1;
334
335 mOuterBoundsX[2] = x1 + dx1 + dx2;
336 mOuterBoundsY[2] = y1 + dy1 + dy2;
337
338 mOuterBoundsX[3] = x1 + dx2;
339 mOuterBoundsY[3] = y1 + dy2;
340
341 mOuterBoundsX[4] = mOuterBoundsX[0];
342 mOuterBoundsY[4] = mOuterBoundsY[0];
343
344 GEOSCoordSequence *coord = nullptr;
345#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
346 // use optimised method if we don't have to force close an open ring
347 coord = GEOSCoordSeq_copyFromArrays_r( geosctxt, mOuterBoundsX.data(), mOuterBoundsY.data(), nullptr, nullptr, 5 );
348#else
349 coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
350 for ( int i = 0; i < nbPoints; ++i )
351 {
352 GEOSCoordSeq_setXY_r( geosctxt, coord, i, mOuterBoundsX[i], mOuterBoundsY[i] );
353 }
354#endif
355
356 mOuterBoundsGeos.reset( GEOSGeom_createPolygon_r( geosctxt, GEOSGeom_createLinearRing_r( geosctxt, coord ), nullptr, 0 ) );
357
358 mPreparedOuterBoundsGeos = GEOSPrepare_r( QgsGeos::getGEOSHandler(), mOuterBoundsGeos.get() );
359
360 auto xminmax = std::minmax_element( mOuterBoundsX.begin(), mOuterBoundsX.end() );
361 mOuterBoundsXMin = *xminmax.first;
362 mOuterBoundsXMax = *xminmax.second;
363 auto yminmax = std::minmax_element( mOuterBoundsY.begin(), mOuterBoundsY.end() );
364 mOuterBoundsYMin = *yminmax.first;
365 mOuterBoundsYMax = *yminmax.second;
366}
367
368int LabelPosition::partCount() const
369{
370 if ( mNextPart )
371 return mNextPart->partCount() + 1;
372 else
373 return 1;
374}
375
377{
378 return id;
379}
380
381double LabelPosition::getX( int i ) const
382{
383 return ( i >= 0 && i < 4 ? x[i] : -1 );
384}
385
386double LabelPosition::getY( int i ) const
387{
388 return ( i >= 0 && i < 4 ? y[i] : -1 );
389}
390
392{
393 return alpha;
394}
395
397{
398 if ( mCost >= 1 )
399 {
400 mCost -= int ( mCost ); // label cost up to 1
401 }
402}
403
405{
406 return feature;
407}
408
409void LabelPosition::getBoundingBox( double amin[2], double amax[2] ) const
410{
411 // this method considers the label's outer bounds
412 if ( mNextPart )
413 {
414 mNextPart->getBoundingBox( amin, amax );
415 }
416 else
417 {
418 amin[0] = std::numeric_limits<double>::max();
419 amax[0] = std::numeric_limits<double>::lowest();
420 amin[1] = std::numeric_limits<double>::max();
421 amax[1] = std::numeric_limits<double>::lowest();
422 }
423 for ( int c = 0; c < 4; c++ )
424 {
425 if ( mOuterBoundsGeos )
426 {
427 if ( mOuterBoundsX[c] < amin[0] )
428 amin[0] = mOuterBoundsX[c];
429 if ( mOuterBoundsX[c] > amax[0] )
430 amax[0] = mOuterBoundsX[c];
431 if ( mOuterBoundsY[c] < amin[1] )
432 amin[1] = mOuterBoundsY[c];
433 if ( mOuterBoundsY[c] > amax[1] )
434 amax[1] = mOuterBoundsY[c];
435 }
436 else
437 {
438 if ( x[c] < amin[0] )
439 amin[0] = x[c];
440 if ( x[c] > amax[0] )
441 amax[0] = x[c];
442 if ( y[c] < amin[1] )
443 amin[1] = y[c];
444 if ( y[c] > amax[1] )
445 amax[1] = y[c];
446 }
447 }
448}
449
451{
452 if ( other->mOuterBoundsGeos )
453 {
454 const double x1 = ( mOuterBoundsXMin > other->mOuterBoundsXMin ? mOuterBoundsXMin : other->mOuterBoundsXMin );
455 const double x2 = ( mOuterBoundsXMax < other->mOuterBoundsXMax ? mOuterBoundsXMax : other->mOuterBoundsXMax );
456 if ( x1 > x2 )
457 return false;
458 const double y1 = ( mOuterBoundsYMin > other->mOuterBoundsYMin ? mOuterBoundsYMin : other->mOuterBoundsYMin );
459 const double y2 = ( mOuterBoundsYMax < other->mOuterBoundsYMax ? mOuterBoundsYMax : other->mOuterBoundsYMax );
460 return y1 <= y2;
461 }
462 else
463 {
464 const double x1 = ( mOuterBoundsXMin > other->xmin ? mOuterBoundsXMin : other->xmin );
465 const double x2 = ( mOuterBoundsXMax < other->xmax ? mOuterBoundsXMax : other->xmax );
466 if ( x1 > x2 )
467 return false;
468 const double y1 = ( mOuterBoundsYMin > other->ymin ? mOuterBoundsYMin : other->ymin );
469 const double y2 = ( mOuterBoundsYMax < other->ymax ? mOuterBoundsYMax : other->ymax );
470 return y1 <= y2;
471 }
472}
473
475{
476 mHasObstacleConflict = conflicts;
477 if ( mNextPart )
478 mNextPart->setConflictsWithObstacle( conflicts );
479}
480
482{
483 mHasHardConflict = conflicts;
484 if ( mNextPart )
485 mNextPart->setHasHardObstacleConflict( conflicts );
486}
487
489{
490 double amin[2];
491 double amax[2];
492 getBoundingBox( amin, amax );
493 index.remove( this, QgsRectangle( amin[0], amin[1], amax[0], amax[1] ) );
494}
495
497{
498 double amin[2];
499 double amax[2];
500 getBoundingBox( amin, amax );
501 index.insert( this, QgsRectangle( amin[0], amin[1], amax[0], amax[1] ) );
502}
503
504
505void LabelPosition::createMultiPartGeosGeom() const
506{
507 GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
508
509 std::vector< const GEOSGeometry * > geometries;
510 const LabelPosition *tmp1 = this;
511 while ( tmp1 )
512 {
513 const GEOSGeometry *partGeos = tmp1->geos();
514 if ( !GEOSisEmpty_r( geosctxt, partGeos ) )
515 geometries.emplace_back( partGeos );
516 tmp1 = tmp1->nextPart();
517 }
518
519 const std::size_t partCount = geometries.size();
520 GEOSGeometry **geomarr = new GEOSGeometry*[ partCount ];
521 for ( std::size_t i = 0; i < partCount; ++i )
522 {
523 geomarr[i ] = GEOSGeom_clone_r( geosctxt, geometries[i] );
524 }
525
526 mMultipartGeos = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomarr, partCount );
527 delete [] geomarr;
528}
529
530const GEOSPreparedGeometry *LabelPosition::preparedMultiPartGeom() const
531{
532 if ( !mMultipartGeos )
533 createMultiPartGeosGeom();
534
535 if ( !mMultipartPreparedGeos )
536 {
537 mMultipartPreparedGeos = GEOSPrepare_r( QgsGeos::getGEOSHandler(), mMultipartGeos );
538 }
539 return mMultipartPreparedGeos;
540}
541
542const GEOSPreparedGeometry *LabelPosition::preparedOuterBoundsGeom() const
543{
544 return mPreparedOuterBoundsGeos;
545}
546
547double LabelPosition::getDistanceToPoint( double xp, double yp, bool useOuterBounds ) const
548{
549 // this method may consider the label's outer bounds!
550
551 GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
552
553 //first check if inside, if so then distance is -1
554 bool contains = false;
555 if ( alpha == 0 )
556 {
557 // easy case -- horizontal label
558 if ( useOuterBounds && mOuterBoundsGeos )
559 {
560 contains = mOuterBoundsX[0] <= xp && mOuterBoundsX[1] >= xp && mOuterBoundsY[0] <= yp && mOuterBoundsY[2] >= yp;
561 }
562 else
563 {
564 contains = x[0] <= xp && x[1] >= xp && y[0] <= yp && y[2] >= yp;
565 }
566 }
567 else
568 {
569 if ( useOuterBounds && mPreparedOuterBoundsGeos )
570 {
571 try
572 {
573 geos::unique_ptr point( GEOSGeom_createPointFromXY_r( geosctxt, xp, yp ) );
574 contains = ( GEOSPreparedContainsProperly_r( geosctxt, mPreparedOuterBoundsGeos, point.get() ) == 1 );
575 }
576 catch ( GEOSException &e )
577 {
578 contains = false;
579 }
580 }
581 else
582 {
583 contains = containsPoint( xp, yp );
584 }
585 }
586
587 double distance = -1;
588 if ( !contains )
589 {
590 if ( alpha == 0 )
591 {
592 if ( useOuterBounds && mOuterBoundsGeos )
593 {
594 const double dx = std::max( std::max( mOuterBoundsX[0] - xp, 0.0 ), xp - mOuterBoundsX[1] );
595 const double dy = std::max( std::max( mOuterBoundsY[0] - yp, 0.0 ), yp - mOuterBoundsY[2] );
596 distance = std::sqrt( dx * dx + dy * dy );
597 }
598 else
599 {
600 const double dx = std::max( std::max( x[0] - xp, 0.0 ), xp - x[1] );
601 const double dy = std::max( std::max( y[0] - yp, 0.0 ), yp - y[2] );
602 distance = std::sqrt( dx * dx + dy * dy );
603 }
604 }
605 else
606 {
607 if ( useOuterBounds && mPreparedOuterBoundsGeos )
608 {
609 try
610 {
611 geos::unique_ptr geosPt( GEOSGeom_createPointFromXY_r( geosctxt, xp, yp ) );
612
613 const geos::coord_sequence_unique_ptr nearestCoord( GEOSPreparedNearestPoints_r( geosctxt, mPreparedOuterBoundsGeos, geosPt.get() ) );
614 double nx;
615 double ny;
616 unsigned int nPoints = 0;
617 GEOSCoordSeq_getSize_r( geosctxt, nearestCoord.get(), &nPoints );
618 if ( nPoints == 0 )
619 {
620 distance = -1;
621 }
622 else
623 {
624 ( void )GEOSCoordSeq_getXY_r( geosctxt, nearestCoord.get(), 0, &nx, &ny );
625 distance = GeomFunction::dist_euc2d_sq( xp, yp, nx, ny );
626 }
627 }
628 catch ( GEOSException &e )
629 {
630 qWarning( "GEOS exception: %s", e.what() );
631 QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
632 distance = -1;
633 }
634 }
635 else
636 {
637 distance = std::sqrt( minDistanceToPoint( xp, yp ) );
638 }
639 }
640 }
641
642 if ( mNextPart && distance > 0 )
643 return std::min( distance, mNextPart->getDistanceToPoint( xp, yp, useOuterBounds ) );
644
645 return distance;
646}
647
649{
650 // this method considers the label's outer bounds
651 if ( !mOuterBoundsGeos && !mGeos )
653
654 if ( !line->mGeos )
655 line->createGeosGeom();
656
657 GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
658 try
659 {
660 if ( GEOSPreparedIntersects_r( geosctxt, line->preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() : mGeos ) == 1 )
661 {
662 return true;
663 }
664 else if ( mNextPart )
665 {
666 return mNextPart->crossesLine( line );
667 }
668 }
669 catch ( GEOSException &e )
670 {
671 qWarning( "GEOS exception: %s", e.what() );
672 QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
673 return false;
674 }
675
676 return false;
677}
678
680{
681 // this method considers the label's outer bounds
682 if ( !mOuterBoundsGeos && !mGeos )
684
685 if ( !polygon->mGeos )
686 polygon->createGeosGeom();
687
688 GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
689 try
690 {
691 if ( GEOSPreparedIntersects_r( geosctxt, polygon->preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() : mGeos ) == 1
692 && GEOSPreparedContains_r( geosctxt, polygon->preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() : mGeos ) != 1 )
693 {
694 return true;
695 }
696 else if ( mNextPart )
697 {
698 return mNextPart->crossesBoundary( polygon );
699 }
700 }
701 catch ( GEOSException &e )
702 {
703 qWarning( "GEOS exception: %s", e.what() );
704 QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
705 return false;
706 }
707
708 return false;
709}
710
712{
713 //effectively take the average polygon intersection cost for all label parts
714 const double totalCost = polygonIntersectionCostForParts( polygon );
715 const int n = partCount();
716 return std::ceil( totalCost / n );
717}
718
720{
721 // this method considers the label's outer bounds
722 if ( !mOuterBoundsGeos && !mGeos )
724
725 if ( !polygon->mGeos )
726 polygon->createGeosGeom();
727
728 GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
729 try
730 {
731 if ( GEOSPreparedIntersects_r( geosctxt, polygon->preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() : mGeos ) == 1 )
732 {
733 return true;
734 }
735 }
736 catch ( GEOSException &e )
737 {
738 qWarning( "GEOS exception: %s", e.what() );
739 QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
740 }
741
742 if ( mNextPart )
743 {
744 return mNextPart->intersectsWithPolygon( polygon );
745 }
746 else
747 {
748 return false;
749 }
750}
751
752double LabelPosition::polygonIntersectionCostForParts( PointSet *polygon ) const
753{
754 // this method considers the label's outer bounds
755 if ( !mOuterBoundsGeos && !mGeos )
757
758 if ( !polygon->mGeos )
759 polygon->createGeosGeom();
760
761 GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
762 double cost = 0;
763 try
764 {
765 if ( GEOSPreparedIntersects_r( geosctxt, polygon->preparedGeom(), mOuterBoundsGeos ? mOuterBoundsGeos.get() : mGeos ) == 1 )
766 {
767 //at least a partial intersection
768 cost += 1;
769
770 double px, py;
771
772 // check each corner
773 for ( int i = 0; i < 4; ++i )
774 {
775 px = x[i];
776 py = y[i];
777
778 for ( int a = 0; a < 2; ++a ) // and each middle of segment
779 {
780 if ( polygon->containsPoint( px, py ) )
781 cost++;
782 px = ( x[i] + x[( i + 1 ) % 4] ) / 2.0;
783 py = ( y[i] + y[( i + 1 ) % 4] ) / 2.0;
784 }
785 }
786
787 px = ( x[0] + x[2] ) / 2.0;
788 py = ( y[0] + y[2] ) / 2.0;
789
790 //check the label center. if covered by polygon, cost of 4
791 if ( polygon->containsPoint( px, py ) )
792 cost += 4;
793 }
794 }
795 catch ( GEOSException &e )
796 {
797 qWarning( "GEOS exception: %s", e.what() );
798 QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
799 }
800
801 //maintain scaling from 0 -> 12
802 cost = 12.0 * cost / 13.0;
803
804 if ( mNextPart )
805 {
806 cost += mNextPart->polygonIntersectionCostForParts( polygon );
807 }
808
809 return cost;
810}
811
813{
814 double angleDiff = 0.0;
815 double angleLast = 0.0;
816 LabelPosition *tmp = this;
817 while ( tmp )
818 {
819 if ( tmp != this ) // not first?
820 {
821 double diff = std::fabs( tmp->getAlpha() - angleLast );
822 if ( diff > 2 * M_PI )
823 diff -= 2 * M_PI;
824 diff = std::min( diff, 2 * M_PI - diff ); // difference 350 deg is actually just 10 deg...
825 angleDiff += diff;
826 }
827
828 angleLast = tmp->getAlpha();
829 tmp = tmp->nextPart();
830 }
831 return angleDiff;
832}
A rtree spatial index for use in the pal labeling engine.
Definition: palrtree.h:36
void insert(T *data, const QgsRectangle &bounds)
Inserts new data into the spatial index, with the specified bounds.
Definition: palrtree.h:59
void remove(T *data, const QgsRectangle &bounds)
Removes existing data from the spatial index, with the specified bounds.
Definition: palrtree.h:78
@ AllowOverlapAtNoCost
Labels may freely overlap other labels, at no cost.
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:3446
Qgis::LabelOverlapHandling overlapHandling() const
Returns the technique to use for handling overlapping labels for the feature.
QRectF outerBounds() const
Returns the extreme outer bounds of the label feature, including any surrounding content like borders...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Main class to handle feature.
Definition: feature.h:65
bool onlyShowUprightLabels() const
Returns true if feature's label must be displayed upright.
Definition: feature.cpp:2376
QgsLabelFeature * feature()
Returns the parent feature.
Definition: feature.h:94
Layer * layer()
Returns the layer that feature belongs to.
Definition: feature.cpp:159
static double dist_euc2d_sq(double x1, double y1, double x2, double y2)
Definition: geomfunction.h:72
LabelPosition is a candidate feature label position.
Definition: labelposition.h:56
bool outerBoundingBoxIntersects(const LabelPosition *other) const
Returns true if the outer bounding box of this pointset intersects the outer bounding box of another ...
bool intersectsWithPolygon(PointSet *polygon) const
Returns true if any intersection between polygon and position exists.
bool isInConflict(const LabelPosition *ls) const
Check whether or not this overlap with another labelPosition.
double getAlpha() const
Returns the angle to rotate text (in rad).
Quadrant
Position of label candidate relative to feature.
Definition: labelposition.h:66
LabelPosition::Quadrant quadrant
double angleDifferential()
Returns the angle differential of all LabelPosition parts.
void removeFromIndex(PalRtree< LabelPosition > &index)
Removes the label position from the specified index.
~LabelPosition() override
bool crossesLine(PointSet *line) const
Returns true if this label crosses the specified line.
int getId() const
Returns the id.
FeaturePart * feature
const GEOSPreparedGeometry * preparedOuterBoundsGeom() const
Returns the prepared outer boundary geometry.
void validateCost()
Make sure the cost is less than 1.
double cost() const
Returns the candidate label position's geographical cost.
void setConflictsWithObstacle(bool conflicts)
Sets whether the position is marked as conflicting with an obstacle feature.
bool intersects(const GEOSPreparedGeometry *geometry)
Returns true if the label position intersects a geometry.
void setHasHardObstacleConflict(bool conflicts)
Sets whether the position is marked as having a hard conflict with an obstacle feature.
bool crossesBoundary(PointSet *polygon) const
Returns true if this label crosses the boundary of the specified polygon.
double getDistanceToPoint(double xp, double yp, bool useOuterBounds) const
Gets distance from this label to a point.
FeaturePart * getFeaturePart() const
Returns the feature corresponding to this labelposition.
const GEOSPreparedGeometry * preparedMultiPartGeom() const
Returns a prepared GEOS representation of all label parts as a multipolygon.
double getX(int i=0) const
Returns the down-left x coordinate.
void getBoundingBox(double amin[2], double amax[2]) const
Returns bounding box - amin: xmin,ymin - amax: xmax,ymax.
double getY(int i=0) const
Returns the down-left y coordinate.
bool within(const GEOSPreparedGeometry *geometry)
Returns true if the label position is within a geometry.
void insertIntoIndex(PalRtree< LabelPosition > &index)
Inserts the label position into the specified index.
int polygonIntersectionCost(PointSet *polygon) const
Returns cost of position intersection with polygon (testing area of intersection and center).
LabelPosition * nextPart() const
Returns the next part of this label position (i.e.
bool isCurved() const
Returns true if the layer has curved labels.
Definition: layer.h:173
The underlying raw pal geometry class.
Definition: pointset.h:77
friend class LabelPosition
Definition: pointset.h:79
double ymax
Definition: pointset.h:261
double ymin
Definition: pointset.h:260
void createGeosGeom() const
Definition: pointset.cpp:99
std::vector< double > y
Definition: pointset.h:231
bool boundingBoxIntersects(const PointSet *other) const
Returns true if the bounding box of this pointset intersects the bounding box of another pointset.
Definition: pointset.cpp:964
std::vector< double > x
Definition: pointset.h:230
const GEOSPreparedGeometry * preparedGeom() const
Definition: pointset.cpp:154
GEOSGeometry * mGeos
Definition: pointset.h:234
double xmin
Definition: pointset.h:258
const GEOSGeometry * geos() const
Returns the point set's GEOS geometry.
Definition: pointset.cpp:1052
double minDistanceToPoint(double px, double py, double *rx=nullptr, double *ry=nullptr) const
Returns the squared minimum distance between the point set geometry and the point (px,...
Definition: pointset.cpp:860
double xmax
Definition: pointset.h:259
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
Definition: pointset.cpp:270
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:74
std::unique_ptr< GEOSCoordSequence, GeosDeleter > coord_sequence_unique_ptr
Scoped GEOS coordinate sequence pointer.
Definition: qgsgeos.h:89
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3090