QGIS API Documentation  3.27.0-Master (e113457133)
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 "pal.h"
32 #include "costcalculator.h"
33 #include "feature.h"
34 #include "geomfunction.h"
35 #include "labelposition.h"
36 #include "qgsgeos.h"
37 #include "qgsmessagelog.h"
38 #include <cmath>
39 #include <cfloat>
40 
41 using namespace pal;
42 
43 LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, bool isReversed, Quadrant quadrant )
44  : id( id )
45  , feature( feature )
46  , probFeat( 0 )
47  , nbOverlap( 0 )
48  , alpha( alpha )
49  , w( w )
50  , h( h )
51  , partId( -1 )
52  , reversed( isReversed )
53  , upsideDown( false )
54  , quadrant( quadrant )
55  , mCost( cost )
56  , mHasObstacleConflict( false )
57  , mUpsideDownCharCount( 0 )
58 {
59  type = GEOS_POLYGON;
60  nbPoints = 4;
61  x.resize( nbPoints );
62  y.resize( nbPoints );
63 
64  // alpha take his value bw 0 and 2*pi rad
65  while ( this->alpha > 2 * M_PI )
66  this->alpha -= 2 * M_PI;
67 
68  while ( this->alpha < 0 )
69  this->alpha += 2 * M_PI;
70 
71  const double beta = this->alpha + M_PI_2;
72 
73  double dx1, dx2, dy1, dy2;
74 
75  dx1 = std::cos( this->alpha ) * w;
76  dy1 = std::sin( this->alpha ) * w;
77 
78  dx2 = std::cos( beta ) * h;
79  dy2 = std::sin( beta ) * h;
80 
81  x[0] = x1;
82  y[0] = y1;
83 
84  x[1] = x1 + dx1;
85  y[1] = y1 + dy1;
86 
87  x[2] = x1 + dx1 + dx2;
88  y[2] = y1 + dy1 + dy2;
89 
90  x[3] = x1 + dx2;
91  y[3] = y1 + dy2;
92 
93  // upside down ? (curved labels are always correct)
94  if ( !feature->layer()->isCurved() &&
95  this->alpha > M_PI_2 && this->alpha <= 3 * M_PI_2 )
96  {
98  {
99  // Turn label upsidedown by inverting boundary points
100  double tx, ty;
101 
102  tx = x[0];
103  ty = y[0];
104 
105  x[0] = x[2];
106  y[0] = y[2];
107 
108  x[2] = tx;
109  y[2] = ty;
110 
111  tx = x[1];
112  ty = y[1];
113 
114  x[1] = x[3];
115  y[1] = y[3];
116 
117  x[3] = tx;
118  y[3] = ty;
119 
120  if ( this->alpha < M_PI )
121  this->alpha += M_PI;
122  else
123  this->alpha -= M_PI;
124 
125  // labels with text shown upside down are not classified as upsideDown,
126  // only those whose boundary points have been inverted
127  upsideDown = true;
128  }
129  }
130 
131  for ( int i = 0; i < nbPoints; ++i )
132  {
133  xmin = std::min( xmin, x[i] );
134  xmax = std::max( xmax, x[i] );
135  ymin = std::min( ymin, y[i] );
136  ymax = std::max( ymax, y[i] );
137  }
138 }
139 
141  : PointSet( other )
142 {
143  id = other.id;
144  mCost = other.mCost;
145  feature = other.feature;
146  probFeat = other.probFeat;
147  nbOverlap = other.nbOverlap;
148 
149  alpha = other.alpha;
150  w = other.w;
151  h = other.h;
152 
153  if ( other.mNextPart )
154  mNextPart = std::make_unique< LabelPosition >( *other.mNextPart );
155 
156  partId = other.partId;
157  upsideDown = other.upsideDown;
158  reversed = other.reversed;
159  quadrant = other.quadrant;
160  mHasObstacleConflict = other.mHasObstacleConflict;
161  mUpsideDownCharCount = other.mUpsideDownCharCount;
162 }
163 
164 bool LabelPosition::isIn( double *bbox )
165 {
166  int i;
167 
168  for ( i = 0; i < 4; i++ )
169  {
170  if ( x[i] >= bbox[0] && x[i] <= bbox[2] &&
171  y[i] >= bbox[1] && y[i] <= bbox[3] )
172  return true;
173  }
174 
175  if ( mNextPart )
176  return mNextPart->isIn( bbox );
177  else
178  return false;
179 }
180 
181 bool LabelPosition::isIntersect( double *bbox )
182 {
183  int i;
184 
185  for ( i = 0; i < 4; i++ )
186  {
187  if ( x[i] >= bbox[0] && x[i] <= bbox[2] &&
188  y[i] >= bbox[1] && y[i] <= bbox[3] )
189  return true;
190  }
191 
192  if ( mNextPart )
193  return mNextPart->isIntersect( bbox );
194  else
195  return false;
196 }
197 
198 bool LabelPosition::intersects( const GEOSPreparedGeometry *geometry )
199 {
200  if ( !mGeos )
201  createGeosGeom();
202 
203  try
204  {
205  if ( GEOSPreparedIntersects_r( QgsGeos::getGEOSHandler(), geometry, mGeos ) == 1 )
206  {
207  return true;
208  }
209  else if ( mNextPart )
210  {
211  return mNextPart->intersects( geometry );
212  }
213  }
214  catch ( GEOSException &e )
215  {
216  qWarning( "GEOS exception: %s", e.what() );
217  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
218  return false;
219  }
220 
221  return false;
222 }
223 
224 bool LabelPosition::within( const GEOSPreparedGeometry *geometry )
225 {
226  if ( !mGeos )
227  createGeosGeom();
228 
229  try
230  {
231  if ( GEOSPreparedContains_r( QgsGeos::getGEOSHandler(), geometry, mGeos ) != 1 )
232  {
233  return false;
234  }
235  else if ( mNextPart )
236  {
237  return mNextPart->within( geometry );
238  }
239  }
240  catch ( GEOSException &e )
241  {
242  qWarning( "GEOS exception: %s", e.what() );
243  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
244  return false;
245  }
246 
247  return true;
248 }
249 
250 bool LabelPosition::isInside( double *bbox )
251 {
252  for ( int i = 0; i < 4; i++ )
253  {
254  if ( !( x[i] >= bbox[0] && x[i] <= bbox[2] &&
255  y[i] >= bbox[1] && y[i] <= bbox[3] ) )
256  return false;
257  }
258 
259  if ( mNextPart )
260  return mNextPart->isInside( bbox );
261  else
262  return true;
263 }
264 
266 {
267  if ( this->probFeat == lp->probFeat ) // bugfix #1
268  return false; // always overlaping itself !
269 
270  // if either this label doesn't cause collisions, or the other one doesn't, then we don't conflict!
273  return false;
274 
275  if ( !nextPart() && !lp->nextPart() )
276  {
277  if ( qgsDoubleNear( alpha, 0 ) && qgsDoubleNear( lp->alpha, 0 ) )
278  {
279  // simple case -- both candidates are oriented to axis, so shortcut with easy calculation
280  return boundingBoxIntersects( lp );
281  }
282  }
283 
284  return isInConflictMultiPart( lp );
285 }
286 
287 bool LabelPosition::isInConflictMultiPart( const LabelPosition *lp ) const
288 {
289  if ( !mMultipartGeos )
290  createMultiPartGeosGeom();
291 
292  if ( !lp->mMultipartGeos )
293  lp->createMultiPartGeosGeom();
294 
295  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
296  try
297  {
298  const bool result = ( GEOSPreparedIntersects_r( geosctxt, preparedMultiPartGeom(), lp->mMultipartGeos ) == 1 );
299  return result;
300  }
301  catch ( GEOSException &e )
302  {
303  qWarning( "GEOS exception: %s", e.what() );
304  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
305  return false;
306  }
307 
308  return false;
309 }
310 
311 int LabelPosition::partCount() const
312 {
313  if ( mNextPart )
314  return mNextPart->partCount() + 1;
315  else
316  return 1;
317 }
318 
319 void LabelPosition::offsetPosition( double xOffset, double yOffset )
320 {
321  for ( int i = 0; i < 4; i++ )
322  {
323  x[i] += xOffset;
324  y[i] += yOffset;
325  }
326 
327  if ( mNextPart )
328  mNextPart->offsetPosition( xOffset, yOffset );
329 
330  invalidateGeos();
331 }
332 
334 {
335  return id;
336 }
337 
338 double LabelPosition::getX( int i ) const
339 {
340  return ( i >= 0 && i < 4 ? x[i] : -1 );
341 }
342 
343 double LabelPosition::getY( int i ) const
344 {
345  return ( i >= 0 && i < 4 ? y[i] : -1 );
346 }
347 
349 {
350  return alpha;
351 }
352 
354 {
355  if ( mCost >= 1 )
356  {
357  mCost -= int ( mCost ); // label cost up to 1
358  }
359 }
360 
362 {
363  return feature;
364 }
365 
366 void LabelPosition::getBoundingBox( double amin[2], double amax[2] ) const
367 {
368  if ( mNextPart )
369  {
370  mNextPart->getBoundingBox( amin, amax );
371  }
372  else
373  {
374  amin[0] = std::numeric_limits<double>::max();
375  amax[0] = std::numeric_limits<double>::lowest();
376  amin[1] = std::numeric_limits<double>::max();
377  amax[1] = std::numeric_limits<double>::lowest();
378  }
379  for ( int c = 0; c < 4; c++ )
380  {
381  if ( x[c] < amin[0] )
382  amin[0] = x[c];
383  if ( x[c] > amax[0] )
384  amax[0] = x[c];
385  if ( y[c] < amin[1] )
386  amin[1] = y[c];
387  if ( y[c] > amax[1] )
388  amax[1] = y[c];
389  }
390 }
391 
393 {
394  mHasObstacleConflict = conflicts;
395  if ( mNextPart )
396  mNextPart->setConflictsWithObstacle( conflicts );
397 }
398 
400 {
401  mHasHardConflict = conflicts;
402  if ( mNextPart )
403  mNextPart->setHasHardObstacleConflict( conflicts );
404 }
405 
407 {
408  double amin[2];
409  double amax[2];
410  getBoundingBox( amin, amax );
411  index.remove( this, QgsRectangle( amin[0], amin[1], amax[0], amax[1] ) );
412 }
413 
415 {
416  double amin[2];
417  double amax[2];
418  getBoundingBox( amin, amax );
419  index.insert( this, QgsRectangle( amin[0], amin[1], amax[0], amax[1] ) );
420 }
421 
422 
423 void LabelPosition::createMultiPartGeosGeom() const
424 {
425  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
426 
427  std::vector< const GEOSGeometry * > geometries;
428  const LabelPosition *tmp1 = this;
429  while ( tmp1 )
430  {
431  const GEOSGeometry *partGeos = tmp1->geos();
432  if ( !GEOSisEmpty_r( geosctxt, partGeos ) )
433  geometries.emplace_back( partGeos );
434  tmp1 = tmp1->nextPart();
435  }
436 
437  const std::size_t partCount = geometries.size();
438  GEOSGeometry **geomarr = new GEOSGeometry*[ partCount ];
439  for ( std::size_t i = 0; i < partCount; ++i )
440  {
441  geomarr[i ] = GEOSGeom_clone_r( geosctxt, geometries[i] );
442  }
443 
444  mMultipartGeos = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomarr, partCount );
445  delete [] geomarr;
446 }
447 
448 const GEOSPreparedGeometry *LabelPosition::preparedMultiPartGeom() const
449 {
450  if ( !mMultipartGeos )
451  createMultiPartGeosGeom();
452 
453  if ( !mMultipartPreparedGeos )
454  {
455  mMultipartPreparedGeos = GEOSPrepare_r( QgsGeos::getGEOSHandler(), mMultipartGeos );
456  }
457  return mMultipartPreparedGeos;
458 }
459 
460 double LabelPosition::getDistanceToPoint( double xp, double yp ) const
461 {
462  //first check if inside, if so then distance is -1
463  bool contains = false;
464  if ( alpha == 0 )
465  {
466  // easy case -- horizontal label
467  contains = x[0] <= xp && x[1] >= xp && y[0] <= yp && y[2] >= yp;
468  }
469  else
470  {
471  contains = containsPoint( xp, yp );
472  }
473 
474  double distance = -1;
475  if ( !contains )
476  {
477  if ( alpha == 0 )
478  {
479  const double dx = std::max( std::max( x[0] - xp, 0.0 ), xp - x[1] );
480  const double dy = std::max( std::max( y[0] - yp, 0.0 ), yp - y[2] );
481  distance = std::sqrt( dx * dx + dy * dy );
482  }
483  else
484  {
485  distance = std::sqrt( minDistanceToPoint( xp, yp ) );
486  }
487  }
488 
489  if ( mNextPart && distance > 0 )
490  return std::min( distance, mNextPart->getDistanceToPoint( xp, yp ) );
491 
492  return distance;
493 }
494 
496 {
497  if ( !mGeos )
498  createGeosGeom();
499 
500  if ( !line->mGeos )
501  line->createGeosGeom();
502 
503  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
504  try
505  {
506  if ( GEOSPreparedIntersects_r( geosctxt, line->preparedGeom(), mGeos ) == 1 )
507  {
508  return true;
509  }
510  else if ( mNextPart )
511  {
512  return mNextPart->crossesLine( line );
513  }
514  }
515  catch ( GEOSException &e )
516  {
517  qWarning( "GEOS exception: %s", e.what() );
518  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
519  return false;
520  }
521 
522  return false;
523 }
524 
526 {
527  if ( !mGeos )
528  createGeosGeom();
529 
530  if ( !polygon->mGeos )
531  polygon->createGeosGeom();
532 
533  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
534  try
535  {
536  if ( GEOSPreparedIntersects_r( geosctxt, polygon->preparedGeom(), mGeos ) == 1
537  && GEOSPreparedContains_r( geosctxt, polygon->preparedGeom(), mGeos ) != 1 )
538  {
539  return true;
540  }
541  else if ( mNextPart )
542  {
543  return mNextPart->crossesBoundary( polygon );
544  }
545  }
546  catch ( GEOSException &e )
547  {
548  qWarning( "GEOS exception: %s", e.what() );
549  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
550  return false;
551  }
552 
553  return false;
554 }
555 
557 {
558  //effectively take the average polygon intersection cost for all label parts
559  const double totalCost = polygonIntersectionCostForParts( polygon );
560  const int n = partCount();
561  return std::ceil( totalCost / n );
562 }
563 
565 {
566  if ( !mGeos )
567  createGeosGeom();
568 
569  if ( !polygon->mGeos )
570  polygon->createGeosGeom();
571 
572  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
573  try
574  {
575  if ( GEOSPreparedIntersects_r( geosctxt, polygon->preparedGeom(), mGeos ) == 1 )
576  {
577  return true;
578  }
579  }
580  catch ( GEOSException &e )
581  {
582  qWarning( "GEOS exception: %s", e.what() );
583  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
584  }
585 
586  if ( mNextPart )
587  {
588  return mNextPart->intersectsWithPolygon( polygon );
589  }
590  else
591  {
592  return false;
593  }
594 }
595 
596 double LabelPosition::polygonIntersectionCostForParts( PointSet *polygon ) const
597 {
598  if ( !mGeos )
599  createGeosGeom();
600 
601  if ( !polygon->mGeos )
602  polygon->createGeosGeom();
603 
604  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
605  double cost = 0;
606  try
607  {
608  if ( GEOSPreparedIntersects_r( geosctxt, polygon->preparedGeom(), mGeos ) == 1 )
609  {
610  //at least a partial intersection
611  cost += 1;
612 
613  double px, py;
614 
615  // check each corner
616  for ( int i = 0; i < 4; ++i )
617  {
618  px = x[i];
619  py = y[i];
620 
621  for ( int a = 0; a < 2; ++a ) // and each middle of segment
622  {
623  if ( polygon->containsPoint( px, py ) )
624  cost++;
625  px = ( x[i] + x[( i + 1 ) % 4] ) / 2.0;
626  py = ( y[i] + y[( i + 1 ) % 4] ) / 2.0;
627  }
628  }
629 
630  px = ( x[0] + x[2] ) / 2.0;
631  py = ( y[0] + y[2] ) / 2.0;
632 
633  //check the label center. if covered by polygon, cost of 4
634  if ( polygon->containsPoint( px, py ) )
635  cost += 4;
636  }
637  }
638  catch ( GEOSException &e )
639  {
640  qWarning( "GEOS exception: %s", e.what() );
641  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
642  }
643 
644  //maintain scaling from 0 -> 12
645  cost = 12.0 * cost / 13.0;
646 
647  if ( mNextPart )
648  {
649  cost += mNextPart->polygonIntersectionCostForParts( polygon );
650  }
651 
652  return cost;
653 }
654 
656 {
657  double angleDiff = 0.0;
658  double angleLast = 0.0;
659  LabelPosition *tmp = this;
660  while ( tmp )
661  {
662  if ( tmp != this ) // not first?
663  {
664  double diff = std::fabs( tmp->getAlpha() - angleLast );
665  if ( diff > 2 * M_PI )
666  diff -= 2 * M_PI;
667  diff = std::min( diff, 2 * M_PI - diff ); // difference 350 deg is actually just 10 deg...
668  angleDiff += diff;
669  }
670 
671  angleLast = tmp->getAlpha();
672  tmp = tmp->nextPart();
673  }
674  return angleDiff;
675 }
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:3369
Qgis::LabelOverlapHandling overlapHandling() const
Returns the technique to use for handling overlapping labels for the feature.
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:2370
Layer * layer()
Returns the layer that feature belongs to.
Definition: feature.cpp:156
QgsLabelFeature * feature()
Returns the parent feature.
Definition: feature.h:94
LabelPosition is a candidate feature label position.
Definition: labelposition.h:56
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.
bool isIntersect(double *bbox)
Is the labelposition intersect the bounding-box ?
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.
bool isInside(double *bbox)
Is the labelposition inside the bounding-box ?
bool crossesLine(PointSet *line) const
Returns true if this label crosses the specified line.
int getId() const
Returns the id.
FeaturePart * feature
void validateCost()
Make sure the cost is less than 1.
bool isIn(double *bbox)
Is the labelposition in the bounding-box ? (intersect or inside????)
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.
LabelPosition * nextPart() const
Returns the next part of this label position (i.e.
void offsetPosition(double xOffset, double yOffset)
Shift the label by specified offset.
double getDistanceToPoint(double xp, double yp) const
Gets distance from this label to a point. If point lies inside, returns negative number.
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).
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:100
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:989
std::vector< double > x
Definition: pointset.h:230
const GEOSPreparedGeometry * preparedGeom() const
Definition: pointset.cpp:150
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:1077
void invalidateGeos() const
Definition: pointset.cpp:162
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:853
double xmax
Definition: pointset.h:259
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
Definition: pointset.cpp:266
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:2260