QGIS API Documentation  3.14.0-Pi (9f7028fd23)
pointset.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 "pointset.h"
31 #include "util.h"
32 #include "pal.h"
33 #include "geomfunction.h"
34 #include "qgsgeos.h"
35 #include "qgsmessagelog.h"
36 #include "qgsgeometryutils.h"
37 #include <qglobal.h>
38 
39 using namespace pal;
40 
42 {
43  nbPoints = cHullSize = 0;
44  cHull = nullptr;
45  type = -1;
46 }
47 
48 PointSet::PointSet( int nbPoints, double *x, double *y )
49  : nbPoints( nbPoints )
50  , type( GEOS_POLYGON )
51 {
52  this->x.resize( nbPoints );
53  this->y.resize( nbPoints );
54  int i;
55 
56  for ( i = 0; i < nbPoints; i++ )
57  {
58  this->x[i] = x[i];
59  this->y[i] = y[i];
60  }
61 
62 }
63 
64 PointSet::PointSet( double aX, double aY )
65  : type( GEOS_POINT )
66  , xmin( aX )
67  , xmax( aY )
68  , ymin( aX )
69  , ymax( aY )
70 
71 {
72  nbPoints = cHullSize = 1;
73  x.resize( 1 );
74  y.resize( 1 );
75  x[0] = aX;
76  y[0] = aY;
77 }
78 
80  : xmin( ps.xmin )
81  , xmax( ps.xmax )
82  , ymin( ps.ymin )
83  , ymax( ps.ymax )
84 {
85  int i;
86 
87  nbPoints = ps.nbPoints;
88  x = ps.x;
89  y = ps.y;
90 
91  if ( ps.cHull )
92  {
93  cHullSize = ps.cHullSize;
94  cHull = new int[cHullSize];
95  for ( i = 0; i < cHullSize; i++ )
96  {
97  cHull[i] = ps.cHull[i];
98  }
99  }
100  else
101  {
102  cHull = nullptr;
103  cHullSize = 0;
104  }
105 
106  type = ps.type;
107 
108  holeOf = ps.holeOf;
109 
110  if ( ps.mGeos )
111  {
112  mGeos = GEOSGeom_clone_r( QgsGeos::getGEOSHandler(), ps.mGeos );
113  mOwnsGeom = true;
114  }
115 }
116 
118 {
119  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
120 
121  bool needClose = false;
122  if ( type == GEOS_POLYGON && ( !qgsDoubleNear( x[0], x[ nbPoints - 1] ) || !qgsDoubleNear( y[0], y[ nbPoints - 1 ] ) ) )
123  {
124  needClose = true;
125  }
126 
127  GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, nbPoints + ( needClose ? 1 : 0 ), 2 );
128  for ( int i = 0; i < nbPoints; ++i )
129  {
130 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
131  GEOSCoordSeq_setXY_r( geosctxt, coord, i, x[i], y[i] );
132 #else
133  GEOSCoordSeq_setX_r( geosctxt, coord, i, x[i] );
134  GEOSCoordSeq_setY_r( geosctxt, coord, i, y[i] );
135 #endif
136  }
137 
138  //close ring if needed
139  if ( needClose )
140  {
141 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
142  GEOSCoordSeq_setXY_r( geosctxt, coord, nbPoints, x[0], y[0] );
143 #else
144  GEOSCoordSeq_setX_r( geosctxt, coord, nbPoints, x[0] );
145  GEOSCoordSeq_setY_r( geosctxt, coord, nbPoints, y[0] );
146 #endif
147  }
148 
149  switch ( type )
150  {
151  case GEOS_POLYGON:
152  mGeos = GEOSGeom_createPolygon_r( geosctxt, GEOSGeom_createLinearRing_r( geosctxt, coord ), nullptr, 0 );
153  break;
154 
155  case GEOS_LINESTRING:
156  mGeos = GEOSGeom_createLineString_r( geosctxt, coord );
157  break;
158 
159  case GEOS_POINT:
160  mGeos = GEOSGeom_createPoint_r( geosctxt, coord );
161  break;
162  }
163 
164  mOwnsGeom = true;
165 }
166 
167 const GEOSPreparedGeometry *PointSet::preparedGeom() const
168 {
169  if ( !mGeos )
170  createGeosGeom();
171 
172  if ( !mPreparedGeom )
173  {
174  mPreparedGeom = GEOSPrepare_r( QgsGeos::getGEOSHandler(), mGeos );
175  }
176  return mPreparedGeom;
177 }
178 
180 {
181  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
182  if ( mOwnsGeom ) // delete old geometry if we own it
183  GEOSGeom_destroy_r( geosctxt, mGeos );
184  GEOSPreparedGeom_destroy_r( geosctxt, mPreparedGeom );
185  mOwnsGeom = false;
186  mGeos = nullptr;
187  mPreparedGeom = nullptr;
188  mLength = -1;
189  mArea = -1;
190 }
191 
193 {
194  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
195 
196  if ( mGeos && mOwnsGeom )
197  {
198  GEOSGeom_destroy_r( geosctxt, mGeos );
199  mGeos = nullptr;
200  }
201  GEOSPreparedGeom_destroy_r( geosctxt, mPreparedGeom );
202 
203  deleteCoords();
204 
205  delete[] cHull;
206 }
207 
209 {
210  x.clear();
211  y.clear();
212 }
213 
214 std::unique_ptr<PointSet> PointSet::extractShape( int nbPtSh, int imin, int imax, int fps, int fpe, double fptx, double fpty )
215 {
216  int i, j;
217 
218  std::unique_ptr<PointSet> newShape = qgis::make_unique< PointSet >();
219  newShape->type = GEOS_POLYGON;
220  newShape->nbPoints = nbPtSh;
221  newShape->x.resize( newShape->nbPoints );
222  newShape->y.resize( newShape->nbPoints );
223 
224  // new shape # 1 from imin to imax
225  for ( j = 0, i = imin; i != ( imax + 1 ) % nbPoints; i = ( i + 1 ) % nbPoints, j++ )
226  {
227  newShape->x[j] = x[i];
228  newShape->y[j] = y[i];
229  }
230  // is the cutting point a new one ?
231  if ( fps != fpe )
232  {
233  // yes => so add it
234  newShape->x[j] = fptx;
235  newShape->y[j] = fpty;
236  }
237 
238  return newShape;
239 }
240 
241 std::unique_ptr<PointSet> PointSet::clone() const
242 {
243  return std::unique_ptr< PointSet>( new PointSet( *this ) );
244 }
245 
246 bool PointSet::containsPoint( double x, double y ) const
247 {
248  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
249  try
250  {
251 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
252  geos::unique_ptr point( GEOSGeom_createPointFromXY_r( geosctxt, x, y ) );
253 #else
254  GEOSCoordSequence *seq = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
255  GEOSCoordSeq_setX_r( geosctxt, seq, 0, x );
256  GEOSCoordSeq_setY_r( geosctxt, seq, 0, y );
257  geos::unique_ptr point( GEOSGeom_createPoint_r( geosctxt, seq ) );
258 #endif
259  bool result = ( GEOSPreparedContainsProperly_r( geosctxt, preparedGeom(), point.get() ) == 1 );
260 
261  return result;
262  }
263  catch ( GEOSException &e )
264  {
265  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
266  return false;
267  }
268 
269 }
270 
271 bool PointSet::containsLabelCandidate( double x, double y, double width, double height, double alpha ) const
272 {
273  return GeomFunction::containsCandidate( preparedGeom(), x, y, width, height, alpha );
274 }
275 
276 void PointSet::splitPolygons( QLinkedList<PointSet *> &inputShapes,
277  QLinkedList<PointSet *> &outputShapes,
278  double xrm, double yrm )
279 {
280  int i, j;
281 
282  int nbp;
283  std::vector< double > x;
284  std::vector< double > y;
285 
286  int *pts = nullptr;
287 
288  int *cHull = nullptr;
289  int cHullSize;
290 
291  double cp;
292  double bestcp = 0;
293 
294  double bestArea = 0;
295  double area;
296 
297  double base;
298  double b, c;
299  double s;
300 
301  int ihs;
302  int ihn;
303 
304  int ips;
305  int ipn;
306 
307  int holeS = -1; // hole start and end points
308  int holeE = -1;
309 
310  int retainedPt = -1;
311  int pt = 0;
312 
313  double labelArea = xrm * yrm;
314 
315  PointSet *shape = nullptr;
316 
317  while ( !inputShapes.isEmpty() )
318  {
319  shape = inputShapes.takeFirst();
320 
321  x = shape->x;
322  y = shape->y;
323  nbp = shape->nbPoints;
324  pts = new int[nbp];
325 
326  for ( i = 0; i < nbp; i++ )
327  {
328  pts[i] = i;
329  }
330 
331  // conpute convex hull
332  shape->cHullSize = GeomFunction::convexHullId( pts, x, y, nbp, shape->cHull );
333 
334  cHull = shape->cHull;
335  cHullSize = shape->cHullSize;
336 
337  bestArea = 0;
338  retainedPt = -1;
339 
340  // lookup for a hole
341  for ( ihs = 0; ihs < cHullSize; ihs++ )
342  {
343  // ihs->ihn => cHull'seg
344  ihn = ( ihs + 1 ) % cHullSize;
345 
346  ips = cHull[ihs];
347  ipn = ( ips + 1 ) % nbp;
348  if ( ipn != cHull[ihn] ) // next point on shape is not the next point on cHull => there is a hole here !
349  {
350  bestcp = 0;
351  pt = -1;
352  // lookup for the deepest point in the hole
353  for ( i = ips; i != cHull[ihn]; i = ( i + 1 ) % nbp )
354  {
355  cp = std::fabs( GeomFunction::cross_product( x[cHull[ihs]], y[cHull[ihs]],
356  x[cHull[ihn]], y[cHull[ihn]],
357  x[i], y[i] ) );
358  if ( cp - bestcp > EPSILON )
359  {
360  bestcp = cp;
361  pt = i;
362  }
363  }
364 
365  if ( pt != -1 )
366  {
367  // compute the ihs->ihn->pt triangle's area
368  base = GeomFunction::dist_euc2d( x[cHull[ihs]], y[cHull[ihs]],
369  x[cHull[ihn]], y[cHull[ihn]] );
370 
371  b = GeomFunction::dist_euc2d( x[cHull[ihs]], y[cHull[ihs]],
372  x[pt], y[pt] );
373 
374  c = GeomFunction::dist_euc2d( x[cHull[ihn]], y[cHull[ihn]],
375  x[pt], y[pt] );
376 
377  s = ( base + b + c ) / 2; // s = half perimeter
378  area = s * ( s - base ) * ( s - b ) * ( s - c );
379  if ( area < 0 )
380  area = -area;
381 
382  // retain the biggest area
383  if ( area - bestArea > EPSILON )
384  {
385  bestArea = area;
386  retainedPt = pt;
387  holeS = ihs;
388  holeE = ihn;
389  }
390  }
391  }
392  }
393 
394  // we have a hole, its area, and the deppest point in hole
395  // we're going to find the second point to cup the shape
396  // holeS = hole starting point
397  // holeE = hole ending point
398  // retainedPt = deppest point in hole
399  // bestArea = area of triangle HoleS->holeE->retainedPoint
400  bestArea = std::sqrt( bestArea );
401  double cx, cy, dx, dy, ex, ey, fx, fy, seg_length, ptx = 0, pty = 0, fptx = 0, fpty = 0;
402  int ps = -1, pe = -1, fps = -1, fpe = -1;
403  if ( retainedPt >= 0 && bestArea > labelArea ) // there is a hole so we'll cut the shape in two new shape (only if hole area is bigger than twice labelArea)
404  {
405  c = std::numeric_limits<double>::max();
406 
407  // iterate on all shape points except points which are in the hole
408  bool isValid;
409  int k, l;
410  for ( i = ( cHull[holeE] + 1 ) % nbp; i != ( cHull[holeS] - 1 + nbp ) % nbp; i = j )
411  {
412  j = ( i + 1 ) % nbp; // i->j is shape segment not in hole
413 
414  // compute distance between retainedPoint and segment
415  // whether perpendicular distance (if retaindPoint is fronting segment i->j)
416  // or distance between retainedPt and i or j (choose the nearest)
417  seg_length = GeomFunction::dist_euc2d( x[i], y[i], x[j], y[j] );
418  cx = ( x[i] + x[j] ) / 2.0;
419  cy = ( y[i] + y[j] ) / 2.0;
420  dx = cy - y[i];
421  dy = cx - x[i];
422 
423  ex = cx - dx;
424  ey = cy + dy;
425  fx = cx + dx;
426  fy = cy - dy;
427 
428  if ( seg_length < EPSILON || std::fabs( ( b = GeomFunction::cross_product( ex, ey, fx, fy, x[retainedPt], y[retainedPt] ) / ( seg_length ) ) ) > ( seg_length / 2 ) ) // retainedPt is not fronting i->j
429  {
430  if ( ( ex = GeomFunction::dist_euc2d_sq( x[i], y[i], x[retainedPt], y[retainedPt] ) ) < ( ey = GeomFunction::dist_euc2d_sq( x[j], y[j], x[retainedPt], y[retainedPt] ) ) )
431  {
432  b = ex;
433  ps = i;
434  pe = i;
435  }
436  else
437  {
438  b = ey;
439  ps = j;
440  pe = j;
441  }
442  }
443  else // point fronting i->j => compute pependicular distance => create a new point
444  {
445  b = GeomFunction::cross_product( x[i], y[i], x[j], y[j], x[retainedPt], y[retainedPt] ) / seg_length;
446  b *= b;
447  ps = i;
448  pe = j;
449 
450  if ( !GeomFunction::computeLineIntersection( x[i], y[i], x[j], y[j], x[retainedPt], y[retainedPt], x[retainedPt] - dx, y[retainedPt] + dy, &ptx, &pty ) )
451  {
452  //error - it should intersect the line
453  }
454  }
455 
456  isValid = true;
457  double pointX, pointY;
458  if ( ps == pe )
459  {
460  pointX = x[pe];
461  pointY = y[pe];
462  }
463  else
464  {
465  pointX = ptx;
466  pointY = pty;
467  }
468 
469  for ( k = cHull[holeS]; k != cHull[holeE]; k = ( k + 1 ) % nbp )
470  {
471  l = ( k + 1 ) % nbp;
472  if ( GeomFunction::isSegIntersects( x[retainedPt], y[retainedPt], pointX, pointY, x[k], y[k], x[l], y[l] ) )
473  {
474  isValid = false;
475  break;
476  }
477  }
478 
479 
480  if ( isValid && b < c )
481  {
482  c = b;
483  fps = ps;
484  fpe = pe;
485  fptx = ptx;
486  fpty = pty;
487  }
488  } // for point which are not in hole
489 
490  // we will cut the shapeu in two new shapes, one from [retainedPoint] to [newPoint] and one form [newPoint] to [retainedPoint]
491  int imin = retainedPt;
492  int imax = ( ( ( fps < retainedPt && fpe < retainedPt ) || ( fps > retainedPt && fpe > retainedPt ) ) ? std::min( fps, fpe ) : std::max( fps, fpe ) );
493 
494  int nbPtSh1, nbPtSh2; // how many points in new shapes ?
495  if ( imax > imin )
496  nbPtSh1 = imax - imin + 1 + ( fpe != fps );
497  else
498  nbPtSh1 = imax + nbp - imin + 1 + ( fpe != fps );
499 
500  if ( ( imax == fps ? fpe : fps ) < imin )
501  nbPtSh2 = imin - ( imax == fps ? fpe : fps ) + 1 + ( fpe != fps );
502  else
503  nbPtSh2 = imin + nbp - ( imax == fps ? fpe : fps ) + 1 + ( fpe != fps );
504 
505  if ( retainedPt == -1 || fps == -1 || fpe == -1 )
506  {
507  if ( shape->parent )
508  delete shape;
509  }
510  // check for useless splitting
511  else if ( imax == imin || nbPtSh1 <= 2 || nbPtSh2 <= 2 || nbPtSh1 == nbp || nbPtSh2 == nbp )
512  {
513  outputShapes.append( shape );
514  }
515  else
516  {
517 
518  PointSet *newShape = shape->extractShape( nbPtSh1, imin, imax, fps, fpe, fptx, fpty ).release();
519 
520  if ( shape->parent )
521  newShape->parent = shape->parent;
522  else
523  newShape->parent = shape;
524 
525  inputShapes.append( newShape );
526 
527  if ( imax == fps )
528  imax = fpe;
529  else
530  imax = fps;
531 
532  newShape = shape->extractShape( nbPtSh2, imax, imin, fps, fpe, fptx, fpty ).release();
533 
534  if ( shape->parent )
535  newShape->parent = shape->parent;
536  else
537  newShape->parent = shape;
538 
539  inputShapes.append( newShape );
540 
541  if ( shape->parent )
542  delete shape;
543  }
544  }
545  else
546  {
547  outputShapes.append( shape );
548  }
549  delete[] pts;
550  }
551 }
552 
553 void PointSet::extendLineByDistance( double startDistance, double endDistance, double smoothDistance )
554 {
555  if ( nbPoints < 2 )
556  return;
557 
558  double x0 = x[0];
559  double y0 = y[0];
560  if ( startDistance > 0 )
561  {
562  // trace forward by smoothDistance
563  double x1 = x[1];
564  double y1 = y[1];
565 
566  double distanceConsumed = 0;
567  double lastX = x0;
568  double lastY = y0;
569  for ( int i = 1; i < nbPoints; ++i )
570  {
571  double thisX = x[i];
572  double thisY = y[i];
573  const double thisSegmentLength = std::sqrt( ( thisX - lastX ) * ( thisX - lastX ) + ( thisY - lastY ) * ( thisY - lastY ) );
574  distanceConsumed += thisSegmentLength;
575  if ( distanceConsumed >= smoothDistance )
576  {
577  double c = ( distanceConsumed - smoothDistance ) / thisSegmentLength;
578  x1 = lastX + c * ( thisX - lastX );
579  y1 = lastY + c * ( thisY - lastY );
580  break;
581  }
582  lastX = thisX;
583  lastY = thisY;
584  }
585 
586  const double distance = std::sqrt( ( x1 - x0 ) * ( x1 - x0 ) + ( y1 - y0 ) * ( y1 - y0 ) );
587  const double extensionFactor = ( startDistance + distance ) / distance;
588  const QgsPointXY newStart = QgsGeometryUtils::interpolatePointOnLine( x1, y1, x0, y0, extensionFactor );
589  x0 = newStart.x();
590  y0 = newStart.y();
591  // defer actually changing the stored start until we've calculated the new end point
592  }
593 
594  if ( endDistance > 0 )
595  {
596  double xend0 = x[nbPoints - 1];
597  double yend0 = y[nbPoints - 1];
598  double xend1 = x[nbPoints - 2];
599  double yend1 = y[nbPoints - 2];
600 
601  // trace backward by smoothDistance
602  double distanceConsumed = 0;
603  double lastX = x0;
604  double lastY = y0;
605  for ( int i = nbPoints - 2; i >= 0; --i )
606  {
607  double thisX = x[i];
608  double thisY = y[i];
609  const double thisSegmentLength = std::sqrt( ( thisX - lastX ) * ( thisX - lastX ) + ( thisY - lastY ) * ( thisY - lastY ) );
610  distanceConsumed += thisSegmentLength;
611  if ( distanceConsumed >= smoothDistance )
612  {
613  double c = ( distanceConsumed - smoothDistance ) / thisSegmentLength;
614  xend1 = lastX + c * ( thisX - lastX );
615  yend1 = lastY + c * ( thisY - lastY );
616  break;
617  }
618  lastX = thisX;
619  lastY = thisY;
620  }
621 
622  const double distance = std::sqrt( ( xend1 - xend0 ) * ( xend1 - xend0 ) + ( yend1 - yend0 ) * ( yend1 - yend0 ) );
623  const double extensionFactor = ( endDistance + distance ) / distance;
624  const QgsPointXY newEnd = QgsGeometryUtils::interpolatePointOnLine( xend1, yend1, xend0, yend0, extensionFactor );
625  x.emplace_back( newEnd.x() );
626  y.emplace_back( newEnd.y() );
627  nbPoints++;
628  }
629 
630  if ( startDistance > 0 )
631  {
632  x.insert( x.begin(), x0 );
633  y.insert( y.begin(), y0 );
634  nbPoints++;
635  }
636 
637  invalidateGeos();
638 }
639 
641 {
642  int i;
643  int j;
644 
645  double bbox[4]; // xmin, ymin, xmax, ymax
646 
647  double alpha;
648  int alpha_d;
649 
650  double alpha_seg;
651 
652  double dref;
653  double d1, d2;
654 
655  double bb[16]; // {ax, ay, bx, by, cx, cy, dx, dy, ex, ey, fx, fy, gx, gy, hx, hy}}
656 
657  double cp;
658  double best_cp;
659  double distNearestPoint;
660 
661  double area;
662  double width;
663  double length;
664 
665  double best_area = std::numeric_limits<double>::max();
666  double best_alpha = -1;
667  double best_bb[16];
668  double best_length = 0;
669  double best_width = 0;
670 
671 
672  bbox[0] = std::numeric_limits<double>::max();
673  bbox[1] = std::numeric_limits<double>::max();
674  bbox[2] = std::numeric_limits<double>::lowest();
675  bbox[3] = std::numeric_limits<double>::lowest();
676 
677  for ( i = 0; i < cHullSize; i++ )
678  {
679  if ( x[cHull[i]] < bbox[0] )
680  bbox[0] = x[cHull[i]];
681 
682  if ( x[cHull[i]] > bbox[2] )
683  bbox[2] = x[cHull[i]];
684 
685  if ( y[cHull[i]] < bbox[1] )
686  bbox[1] = y[cHull[i]];
687 
688  if ( y[cHull[i]] > bbox[3] )
689  bbox[3] = y[cHull[i]];
690  }
691 
692 
693  dref = bbox[2] - bbox[0];
694 
695  for ( alpha_d = 0; alpha_d < 90; alpha_d++ )
696  {
697  alpha = alpha_d * M_PI / 180.0;
698  d1 = std::cos( alpha ) * dref;
699  d2 = std::sin( alpha ) * dref;
700 
701  bb[0] = bbox[0];
702  bb[1] = bbox[3]; // ax, ay
703 
704  bb[4] = bbox[0];
705  bb[5] = bbox[1]; // cx, cy
706 
707  bb[8] = bbox[2];
708  bb[9] = bbox[1]; // ex, ey
709 
710  bb[12] = bbox[2];
711  bb[13] = bbox[3]; // gx, gy
712 
713 
714  bb[2] = bb[0] + d1;
715  bb[3] = bb[1] + d2; // bx, by
716  bb[6] = bb[4] - d2;
717  bb[7] = bb[5] + d1; // dx, dy
718  bb[10] = bb[8] - d1;
719  bb[11] = bb[9] - d2; // fx, fy
720  bb[14] = bb[12] + d2;
721  bb[15] = bb[13] - d1; // hx, hy
722 
723  // adjust all points
724  for ( i = 0; i < 16; i += 4 )
725  {
726 
727  alpha_seg = ( ( i / 4 > 0 ? ( i / 4 ) - 1 : 3 ) ) * M_PI_2 + alpha;
728 
729  best_cp = std::numeric_limits<double>::max();
730  for ( j = 0; j < nbPoints; j++ )
731  {
732  cp = GeomFunction::cross_product( bb[i + 2], bb[i + 3], bb[i], bb[i + 1], x[cHull[j]], y[cHull[j]] );
733  if ( cp < best_cp )
734  {
735  best_cp = cp;
736  }
737  }
738 
739  distNearestPoint = best_cp / dref;
740 
741  d1 = std::cos( alpha_seg ) * distNearestPoint;
742  d2 = std::sin( alpha_seg ) * distNearestPoint;
743 
744  bb[i] += d1; // x
745  bb[i + 1] += d2; // y
746  bb[i + 2] += d1; // x
747  bb[i + 3] += d2; // y
748  }
749 
750  // compute and compare AREA
751  width = GeomFunction::cross_product( bb[6], bb[7], bb[4], bb[5], bb[12], bb[13] ) / dref;
752  length = GeomFunction::cross_product( bb[2], bb[3], bb[0], bb[1], bb[8], bb[9] ) / dref;
753 
754  area = width * length;
755 
756  if ( area < 0 )
757  area *= -1;
758 
759 
760  if ( best_area - area > EPSILON )
761  {
762  best_area = area;
763  best_length = length;
764  best_width = width;
765  best_alpha = alpha;
766  memcpy( best_bb, bb, sizeof( double ) * 16 );
767  }
768  }
769 
770  // best bbox is defined
771 
772  CHullBox finalBb;
773 
774  for ( i = 0; i < 16; i = i + 4 )
775  {
776  GeomFunction::computeLineIntersection( best_bb[i], best_bb[i + 1], best_bb[i + 2], best_bb[i + 3],
777  best_bb[( i + 4 ) % 16], best_bb[( i + 5 ) % 16], best_bb[( i + 6 ) % 16], best_bb[( i + 7 ) % 16],
778  &finalBb.x[int ( i / 4 )], &finalBb.y[int ( i / 4 )] );
779  }
780 
781  finalBb.alpha = best_alpha;
782  finalBb.width = best_width;
783  finalBb.length = best_length;
784 
785  return finalBb;
786 }
787 
788 double PointSet::minDistanceToPoint( double px, double py, double *rx, double *ry ) const
789 {
790  if ( !mGeos )
791  createGeosGeom();
792 
793  if ( !mGeos )
794  return 0;
795 
796  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
797  try
798  {
799 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
800  geos::unique_ptr geosPt( GEOSGeom_createPointFromXY_r( geosctxt, px, py ) );
801 #else
802  GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
803  GEOSCoordSeq_setX_r( geosctxt, coord, 0, px );
804  GEOSCoordSeq_setY_r( geosctxt, coord, 0, py );
805  geos::unique_ptr geosPt( GEOSGeom_createPoint_r( geosctxt, coord ) );
806 #endif
807  int type = GEOSGeomTypeId_r( geosctxt, mGeos );
808  const GEOSGeometry *extRing = nullptr;
809  if ( type != GEOS_POLYGON )
810  {
811  extRing = mGeos;
812  }
813  else
814  {
815  //for polygons, we want distance to exterior ring (not an interior point)
816  extRing = GEOSGetExteriorRing_r( geosctxt, mGeos );
817  }
818  geos::coord_sequence_unique_ptr nearestCoord( GEOSNearestPoints_r( geosctxt, extRing, geosPt.get() ) );
819  double nx;
820  double ny;
821 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
822  unsigned int nPoints = 0;
823  GEOSCoordSeq_getSize_r( geosctxt, nearestCoord.get(), &nPoints );
824  if ( nPoints == 0 )
825  return 0;
826 
827  ( void )GEOSCoordSeq_getXY_r( geosctxt, nearestCoord.get(), 0, &nx, &ny );
828 #else
829  ( void )GEOSCoordSeq_getX_r( geosctxt, nearestCoord.get(), 0, &nx );
830  ( void )GEOSCoordSeq_getY_r( geosctxt, nearestCoord.get(), 0, &ny );
831 #endif
832 
833  if ( rx )
834  *rx = nx;
835  if ( ry )
836  *ry = ny;
837 
838  return GeomFunction::dist_euc2d_sq( px, py, nx, ny );
839  }
840  catch ( GEOSException &e )
841  {
842  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
843  return 0;
844  }
845 }
846 
847 void PointSet::getCentroid( double &px, double &py, bool forceInside ) const
848 {
849  if ( !mGeos )
850  createGeosGeom();
851 
852  if ( !mGeos )
853  return;
854 
855  try
856  {
857  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
858  geos::unique_ptr centroidGeom( GEOSGetCentroid_r( geosctxt, mGeos ) );
859  if ( centroidGeom )
860  {
861  const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, centroidGeom.get() );
862 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
863  unsigned int nPoints = 0;
864  GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
865  if ( nPoints == 0 )
866  return;
867  GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
868 #else
869  GEOSCoordSeq_getX_r( geosctxt, coordSeq, 0, &px );
870  GEOSCoordSeq_getY_r( geosctxt, coordSeq, 0, &py );
871 #endif
872  }
873 
874  // check if centroid inside in polygon
875  if ( forceInside && !containsPoint( px, py ) )
876  {
877  geos::unique_ptr pointGeom( GEOSPointOnSurface_r( geosctxt, mGeos ) );
878 
879  if ( pointGeom )
880  {
881  const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
882 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
883  unsigned int nPoints = 0;
884  GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
885  if ( nPoints == 0 )
886  return;
887 
888  GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
889 #else
890  GEOSCoordSeq_getX_r( geosctxt, coordSeq, 0, &px );
891  GEOSCoordSeq_getY_r( geosctxt, coordSeq, 0, &py );
892 #endif
893  }
894  }
895  }
896  catch ( GEOSException &e )
897  {
898  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
899  return;
900  }
901 }
902 
903 bool PointSet::boundingBoxIntersects( const PointSet *other ) const
904 {
905  double x1 = ( xmin > other->xmin ? xmin : other->xmin );
906  double x2 = ( xmax < other->xmax ? xmax : other->xmax );
907  if ( x1 > x2 )
908  return false;
909  double y1 = ( ymin > other->ymin ? ymin : other->ymin );
910  double y2 = ( ymax < other->ymax ? ymax : other->ymax );
911  return y1 <= y2;
912 }
913 
914 void PointSet::getPointByDistance( double *d, double *ad, double dl, double *px, double *py )
915 {
916  int i;
917  double dx, dy, di;
918  double distr;
919 
920  i = 0;
921  if ( dl >= 0 )
922  {
923  while ( i < nbPoints && ad[i] <= dl ) i++;
924  i--;
925  }
926 
927  if ( i < nbPoints - 1 )
928  {
929  if ( dl < 0 )
930  {
931  dx = x[nbPoints - 1] - x[0];
932  dy = y[nbPoints - 1] - y[0];
933  di = std::sqrt( dx * dx + dy * dy );
934  }
935  else
936  {
937  dx = x[i + 1] - x[i];
938  dy = y[i + 1] - y[i];
939  di = d[i];
940  }
941 
942  distr = dl - ad[i];
943  *px = x[i] + dx * distr / di;
944  *py = y[i] + dy * distr / di;
945  }
946  else // just select last point...
947  {
948  *px = x[i];
949  *py = y[i];
950  }
951 }
952 
953 const GEOSGeometry *PointSet::geos() const
954 {
955  if ( !mGeos )
956  createGeosGeom();
957 
958  return mGeos;
959 }
960 
961 double PointSet::length() const
962 {
963  if ( mLength >= 0 )
964  return mLength;
965 
966  if ( !mGeos )
967  createGeosGeom();
968 
969  if ( !mGeos )
970  return -1;
971 
972  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
973 
974  try
975  {
976  ( void )GEOSLength_r( geosctxt, mGeos, &mLength );
977  return mLength;
978  }
979  catch ( GEOSException &e )
980  {
981  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
982  return -1;
983  }
984 }
985 
986 double PointSet::area() const
987 {
988  if ( mArea >= 0 )
989  return mArea;
990 
991  if ( !mGeos )
992  createGeosGeom();
993 
994  if ( !mGeos )
995  return -1;
996 
997  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
998 
999  try
1000  {
1001  ( void )GEOSArea_r( geosctxt, mGeos, &mArea );
1002  mArea = std::fabs( mArea );
1003  return mArea;
1004  }
1005  catch ( GEOSException &e )
1006  {
1007  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
1008  return -1;
1009  }
1010 }
1011 
1013 {
1014  return qgsDoubleNear( x[0], x[nbPoints - 1] ) && qgsDoubleNear( y[0], y[nbPoints - 1] );
1015 }
pal::PointSet::mArea
double mArea
Definition: pointset.h:216
pal::PointSet::cHullSize
int cHullSize
Definition: pointset.h:209
pal::PointSet::length
double length() const
Returns length of line geometry.
Definition: pointset.cpp:961
pal::PointSet::xmax
double xmax
Definition: pointset.h:230
pal::GeomFunction::dist_euc2d
static double dist_euc2d(double x1, double y1, double x2, double y2)
Definition: geomfunction.h:66
QgsPointXY::y
double y
Definition: qgspointxy.h:48
pal::CHullBox::width
double width
Definition: pointset.h:62
pal::GeomFunction::dist_euc2d_sq
static double dist_euc2d_sq(double x1, double y1, double x2, double y2)
Definition: geomfunction.h:71
EPSILON
#define EPSILON
Definition: util.h:78
pal::PointSet::compute_chull_bbox
CHullBox compute_chull_bbox()
Computes a con???? hull.
Definition: pointset.cpp:640
pal::PointSet::~PointSet
virtual ~PointSet()
Definition: pointset.cpp:192
QgsGeometryUtils::interpolatePointOnLine
static QgsPointXY interpolatePointOnLine(double x1, double y1, double x2, double y2, double fraction)
Interpolates the position of a point a fraction of the way along the line from (x1,...
Definition: qgsgeometryutils.cpp:1396
pal::GeomFunction::convexHullId
static int convexHullId(int *id, const std::vector< double > &x, const std::vector< double > &y, int n, int *&cHull)
Compute the convex hull in O(n·log(n))
Definition: geomfunction.cpp:166
pal::PointSet::mOwnsGeom
bool mOwnsGeom
Definition: pointset.h:206
pal::PointSet::deleteCoords
void deleteCoords()
Definition: pointset.cpp:208
pal
Definition: qgsdiagramrenderer.h:49
pal::GeomFunction::computeLineIntersection
static bool computeLineIntersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, double *x, double *y)
Compute the point where two lines intersect.
Definition: geomfunction.cpp:136
pal::PointSet::containsPoint
bool containsPoint(double x, double y) const
Tests whether point set contains a specified point.
Definition: pointset.cpp:246
pal::PointSet::holeOf
PointSet * holeOf
Definition: pointset.h:213
pal::GeomFunction::containsCandidate
static bool containsCandidate(const GEOSPreparedGeometry *geom, double x, double y, double width, double height, double alpha)
Returns true if a GEOS prepared geometry totally contains a label candidate.
Definition: geomfunction.cpp:321
pal::PointSet::isClosed
bool isClosed() const
Returns true if pointset is closed.
Definition: pointset.cpp:1012
geos::unique_ptr
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
pal::CHullBox::alpha
double alpha
Definition: pointset.h:60
pal::PointSet::geos
const GEOSGeometry * geos() const
Returns the point set's GEOS geometry.
Definition: pointset.cpp:953
pal::PointSet::ymin
double ymin
Definition: pointset.h:231
pal::PointSet::minDistanceToPoint
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:788
pal::PointSet::extendLineByDistance
void extendLineByDistance(double startDistance, double endDistance, double smoothDistance)
Extends linestrings by the specified amount at the start and end of the line, by extending the existi...
Definition: pointset.cpp:553
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
pal::PointSet::boundingBoxIntersects
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:903
pal::PointSet::x
std::vector< double > x
Definition: pointset.h:201
pal::PointSet::type
int type
Definition: pointset.h:211
geomfunction.h
pointset.h
pal::CHullBox::length
double length
Definition: pointset.h:63
pal::PointSet::clone
std::unique_ptr< PointSet > clone() const
Returns a copy of the point set.
Definition: pointset.cpp:241
pal::PointSet::extractShape
std::unique_ptr< PointSet > extractShape(int nbPtSh, int imin, int imax, int fps, int fpe, double fptx, double fpty)
Does...
Definition: pointset.cpp:214
pal::PointSet::cHull
int * cHull
Definition: pointset.h:208
pal::PointSet::invalidateGeos
void invalidateGeos()
Definition: pointset.cpp:179
QgsGeos::getGEOSHandler
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:2873
qgsgeometryutils.h
pal::CHullBox
Definition: pointset.h:55
pal::PointSet::createGeosGeom
void createGeosGeom() const
Definition: pointset.cpp:117
QgsPointXY
Definition: qgspointxy.h:43
pal::PointSet::containsLabelCandidate
bool containsLabelCandidate(double x, double y, double width, double height, double alpha=0) const
Tests whether a possible label candidate will fit completely within the shape.
Definition: pointset.cpp:271
pal::PointSet::mGeos
GEOSGeometry * mGeos
Definition: pointset.h:205
pal::PointSet::y
std::vector< double > y
Definition: pointset.h:202
pal::PointSet
Definition: pointset.h:71
pal::PointSet::parent
PointSet * parent
Definition: pointset.h:214
pal::GeomFunction::isSegIntersects
static bool isSegIntersects(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
Returns true if the two segments intersect.
Definition: geomfunction.cpp:129
pal::PointSet::xmin
double xmin
Definition: pointset.h:229
pal::PointSet::preparedGeom
const GEOSPreparedGeometry * preparedGeom() const
Definition: pointset.cpp:167
c
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
Definition: porting_processing.dox:1
pal::CHullBox::x
double x[4]
Definition: pointset.h:57
pal::PointSet::PointSet
PointSet()
Definition: pointset.cpp:41
QgsPointXY::x
double x
Definition: qgspointxy.h:47
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
pal::PointSet::getCentroid
void getCentroid(double &px, double &py, bool forceInside=false) const
Definition: pointset.cpp:847
geos::coord_sequence_unique_ptr
std::unique_ptr< GEOSCoordSequence, GeosDeleter > coord_sequence_unique_ptr
Scoped GEOS coordinate sequence pointer.
Definition: qgsgeos.h:94
pal.h
pal::GeomFunction::cross_product
static double cross_product(double x1, double y1, double x2, double y2, double x3, double y3)
Definition: geomfunction.h:61
pal::PointSet::mLength
double mLength
Definition: pointset.h:217
util.h
pal::PointSet::nbPoints
int nbPoints
Definition: pointset.h:200
pal::PointSet::splitPolygons
static void splitPolygons(QLinkedList< PointSet * > &inputShapes, QLinkedList< PointSet * > &outputShapes, double xrm, double yrm)
Split a concave shape into several convex shapes.
Definition: pointset.cpp:276
pal::PointSet::area
double area() const
Returns area of polygon geometry.
Definition: pointset.cpp:986
qgsgeos.h
pal::PointSet::getPointByDistance
void getPointByDistance(double *d, double *ad, double dl, double *px, double *py)
Gets a point a set distance along a line geometry.
Definition: pointset.cpp:914
qgsmessagelog.h
pal::CHullBox::y
double y[4]
Definition: pointset.h:58
pal::PointSet::ymax
double ymax
Definition: pointset.h:232