QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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  qWarning( "GEOS exception: %s", e.what() );
266  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
267  return false;
268  }
269 
270 }
271 
272 bool PointSet::containsLabelCandidate( double x, double y, double width, double height, double alpha ) const
273 {
274  return GeomFunction::containsCandidate( preparedGeom(), x, y, width, height, alpha );
275 }
276 
277 void PointSet::splitPolygons( QLinkedList<PointSet *> &inputShapes,
278  QLinkedList<PointSet *> &outputShapes,
279  double xrm, double yrm )
280 {
281  int i, j;
282 
283  int nbp;
284  std::vector< double > x;
285  std::vector< double > y;
286 
287  int *pts = nullptr;
288 
289  int *cHull = nullptr;
290  int cHullSize;
291 
292  double cp;
293  double bestcp = 0;
294 
295  double bestArea = 0;
296  double area;
297 
298  double base;
299  double b, c;
300  double s;
301 
302  int ihs;
303  int ihn;
304 
305  int ips;
306  int ipn;
307 
308  int holeS = -1; // hole start and end points
309  int holeE = -1;
310 
311  int retainedPt = -1;
312  int pt = 0;
313 
314  double labelArea = xrm * yrm;
315 
316  PointSet *shape = nullptr;
317 
318  while ( !inputShapes.isEmpty() )
319  {
320  shape = inputShapes.takeFirst();
321 
322  x = shape->x;
323  y = shape->y;
324  nbp = shape->nbPoints;
325  pts = new int[nbp];
326 
327  for ( i = 0; i < nbp; i++ )
328  {
329  pts[i] = i;
330  }
331 
332  // conpute convex hull
333  shape->cHullSize = GeomFunction::convexHullId( pts, x, y, nbp, shape->cHull );
334 
335  cHull = shape->cHull;
336  cHullSize = shape->cHullSize;
337 
338  bestArea = 0;
339  retainedPt = -1;
340 
341  // lookup for a hole
342  for ( ihs = 0; ihs < cHullSize; ihs++ )
343  {
344  // ihs->ihn => cHull'seg
345  ihn = ( ihs + 1 ) % cHullSize;
346 
347  ips = cHull[ihs];
348  ipn = ( ips + 1 ) % nbp;
349  if ( ipn != cHull[ihn] ) // next point on shape is not the next point on cHull => there is a hole here !
350  {
351  bestcp = 0;
352  pt = -1;
353  // lookup for the deepest point in the hole
354  for ( i = ips; i != cHull[ihn]; i = ( i + 1 ) % nbp )
355  {
356  cp = std::fabs( GeomFunction::cross_product( x[cHull[ihs]], y[cHull[ihs]],
357  x[cHull[ihn]], y[cHull[ihn]],
358  x[i], y[i] ) );
359  if ( cp - bestcp > EPSILON )
360  {
361  bestcp = cp;
362  pt = i;
363  }
364  }
365 
366  if ( pt != -1 )
367  {
368  // compute the ihs->ihn->pt triangle's area
369  base = GeomFunction::dist_euc2d( x[cHull[ihs]], y[cHull[ihs]],
370  x[cHull[ihn]], y[cHull[ihn]] );
371 
372  b = GeomFunction::dist_euc2d( x[cHull[ihs]], y[cHull[ihs]],
373  x[pt], y[pt] );
374 
375  c = GeomFunction::dist_euc2d( x[cHull[ihn]], y[cHull[ihn]],
376  x[pt], y[pt] );
377 
378  s = ( base + b + c ) / 2; // s = half perimeter
379  area = s * ( s - base ) * ( s - b ) * ( s - c );
380  if ( area < 0 )
381  area = -area;
382 
383  // retain the biggest area
384  if ( area - bestArea > EPSILON )
385  {
386  bestArea = area;
387  retainedPt = pt;
388  holeS = ihs;
389  holeE = ihn;
390  }
391  }
392  }
393  }
394 
395  // we have a hole, its area, and the deppest point in hole
396  // we're going to find the second point to cup the shape
397  // holeS = hole starting point
398  // holeE = hole ending point
399  // retainedPt = deppest point in hole
400  // bestArea = area of triangle HoleS->holeE->retainedPoint
401  bestArea = std::sqrt( bestArea );
402  double cx, cy, dx, dy, ex, ey, fx, fy, seg_length, ptx = 0, pty = 0, fptx = 0, fpty = 0;
403  int ps = -1, pe = -1, fps = -1, fpe = -1;
404  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)
405  {
406  c = std::numeric_limits<double>::max();
407 
408  // iterate on all shape points except points which are in the hole
409  bool isValid;
410  int k, l;
411  for ( i = ( cHull[holeE] + 1 ) % nbp; i != ( cHull[holeS] - 1 + nbp ) % nbp; i = j )
412  {
413  j = ( i + 1 ) % nbp; // i->j is shape segment not in hole
414 
415  // compute distance between retainedPoint and segment
416  // whether perpendicular distance (if retaindPoint is fronting segment i->j)
417  // or distance between retainedPt and i or j (choose the nearest)
418  seg_length = GeomFunction::dist_euc2d( x[i], y[i], x[j], y[j] );
419  cx = ( x[i] + x[j] ) / 2.0;
420  cy = ( y[i] + y[j] ) / 2.0;
421  dx = cy - y[i];
422  dy = cx - x[i];
423 
424  ex = cx - dx;
425  ey = cy + dy;
426  fx = cx + dx;
427  fy = cy - dy;
428 
429  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
430  {
431  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] ) ) )
432  {
433  b = ex;
434  ps = i;
435  pe = i;
436  }
437  else
438  {
439  b = ey;
440  ps = j;
441  pe = j;
442  }
443  }
444  else // point fronting i->j => compute pependicular distance => create a new point
445  {
446  b = GeomFunction::cross_product( x[i], y[i], x[j], y[j], x[retainedPt], y[retainedPt] ) / seg_length;
447  b *= b;
448  ps = i;
449  pe = j;
450 
451  if ( !GeomFunction::computeLineIntersection( x[i], y[i], x[j], y[j], x[retainedPt], y[retainedPt], x[retainedPt] - dx, y[retainedPt] + dy, &ptx, &pty ) )
452  {
453  //error - it should intersect the line
454  }
455  }
456 
457  isValid = true;
458  double pointX, pointY;
459  if ( ps == pe )
460  {
461  pointX = x[pe];
462  pointY = y[pe];
463  }
464  else
465  {
466  pointX = ptx;
467  pointY = pty;
468  }
469 
470  for ( k = cHull[holeS]; k != cHull[holeE]; k = ( k + 1 ) % nbp )
471  {
472  l = ( k + 1 ) % nbp;
473  if ( GeomFunction::isSegIntersects( x[retainedPt], y[retainedPt], pointX, pointY, x[k], y[k], x[l], y[l] ) )
474  {
475  isValid = false;
476  break;
477  }
478  }
479 
480 
481  if ( isValid && b < c )
482  {
483  c = b;
484  fps = ps;
485  fpe = pe;
486  fptx = ptx;
487  fpty = pty;
488  }
489  } // for point which are not in hole
490 
491  // we will cut the shapeu in two new shapes, one from [retainedPoint] to [newPoint] and one form [newPoint] to [retainedPoint]
492  int imin = retainedPt;
493  int imax = ( ( ( fps < retainedPt && fpe < retainedPt ) || ( fps > retainedPt && fpe > retainedPt ) ) ? std::min( fps, fpe ) : std::max( fps, fpe ) );
494 
495  int nbPtSh1, nbPtSh2; // how many points in new shapes ?
496  if ( imax > imin )
497  nbPtSh1 = imax - imin + 1 + ( fpe != fps );
498  else
499  nbPtSh1 = imax + nbp - imin + 1 + ( fpe != fps );
500 
501  if ( ( imax == fps ? fpe : fps ) < imin )
502  nbPtSh2 = imin - ( imax == fps ? fpe : fps ) + 1 + ( fpe != fps );
503  else
504  nbPtSh2 = imin + nbp - ( imax == fps ? fpe : fps ) + 1 + ( fpe != fps );
505 
506  if ( retainedPt == -1 || fps == -1 || fpe == -1 )
507  {
508  if ( shape->parent )
509  delete shape;
510  }
511  // check for useless splitting
512  else if ( imax == imin || nbPtSh1 <= 2 || nbPtSh2 <= 2 || nbPtSh1 == nbp || nbPtSh2 == nbp )
513  {
514  outputShapes.append( shape );
515  }
516  else
517  {
518 
519  PointSet *newShape = shape->extractShape( nbPtSh1, imin, imax, fps, fpe, fptx, fpty ).release();
520 
521  if ( shape->parent )
522  newShape->parent = shape->parent;
523  else
524  newShape->parent = shape;
525 
526  inputShapes.append( newShape );
527 
528  if ( imax == fps )
529  imax = fpe;
530  else
531  imax = fps;
532 
533  newShape = shape->extractShape( nbPtSh2, imax, imin, fps, fpe, fptx, fpty ).release();
534 
535  if ( shape->parent )
536  newShape->parent = shape->parent;
537  else
538  newShape->parent = shape;
539 
540  inputShapes.append( newShape );
541 
542  if ( shape->parent )
543  delete shape;
544  }
545  }
546  else
547  {
548  outputShapes.append( shape );
549  }
550  delete[] pts;
551  }
552 }
553 
554 void PointSet::extendLineByDistance( double startDistance, double endDistance, double smoothDistance )
555 {
556  if ( nbPoints < 2 )
557  return;
558 
559  double x0 = x[0];
560  double y0 = y[0];
561  if ( startDistance > 0 )
562  {
563  // trace forward by smoothDistance
564  double x1 = x[1];
565  double y1 = y[1];
566 
567  double distanceConsumed = 0;
568  double lastX = x0;
569  double lastY = y0;
570  for ( int i = 1; i < nbPoints; ++i )
571  {
572  double thisX = x[i];
573  double thisY = y[i];
574  const double thisSegmentLength = std::sqrt( ( thisX - lastX ) * ( thisX - lastX ) + ( thisY - lastY ) * ( thisY - lastY ) );
575  distanceConsumed += thisSegmentLength;
576  if ( distanceConsumed >= smoothDistance )
577  {
578  double c = ( distanceConsumed - smoothDistance ) / thisSegmentLength;
579  x1 = lastX + c * ( thisX - lastX );
580  y1 = lastY + c * ( thisY - lastY );
581  break;
582  }
583  lastX = thisX;
584  lastY = thisY;
585  }
586 
587  const double distance = std::sqrt( ( x1 - x0 ) * ( x1 - x0 ) + ( y1 - y0 ) * ( y1 - y0 ) );
588  const double extensionFactor = ( startDistance + distance ) / distance;
589  const QgsPointXY newStart = QgsGeometryUtils::interpolatePointOnLine( x1, y1, x0, y0, extensionFactor );
590  x0 = newStart.x();
591  y0 = newStart.y();
592  // defer actually changing the stored start until we've calculated the new end point
593  }
594 
595  if ( endDistance > 0 )
596  {
597  double xend0 = x[nbPoints - 1];
598  double yend0 = y[nbPoints - 1];
599  double xend1 = x[nbPoints - 2];
600  double yend1 = y[nbPoints - 2];
601 
602  // trace backward by smoothDistance
603  double distanceConsumed = 0;
604  double lastX = x0;
605  double lastY = y0;
606  for ( int i = nbPoints - 2; i >= 0; --i )
607  {
608  double thisX = x[i];
609  double thisY = y[i];
610  const double thisSegmentLength = std::sqrt( ( thisX - lastX ) * ( thisX - lastX ) + ( thisY - lastY ) * ( thisY - lastY ) );
611  distanceConsumed += thisSegmentLength;
612  if ( distanceConsumed >= smoothDistance )
613  {
614  double c = ( distanceConsumed - smoothDistance ) / thisSegmentLength;
615  xend1 = lastX + c * ( thisX - lastX );
616  yend1 = lastY + c * ( thisY - lastY );
617  break;
618  }
619  lastX = thisX;
620  lastY = thisY;
621  }
622 
623  const double distance = std::sqrt( ( xend1 - xend0 ) * ( xend1 - xend0 ) + ( yend1 - yend0 ) * ( yend1 - yend0 ) );
624  const double extensionFactor = ( endDistance + distance ) / distance;
625  const QgsPointXY newEnd = QgsGeometryUtils::interpolatePointOnLine( xend1, yend1, xend0, yend0, extensionFactor );
626  x.emplace_back( newEnd.x() );
627  y.emplace_back( newEnd.y() );
628  nbPoints++;
629  }
630 
631  if ( startDistance > 0 )
632  {
633  x.insert( x.begin(), x0 );
634  y.insert( y.begin(), y0 );
635  nbPoints++;
636  }
637 
638  invalidateGeos();
639 }
640 
642 {
643  int i;
644  int j;
645 
646  double bbox[4]; // xmin, ymin, xmax, ymax
647 
648  double alpha;
649  int alpha_d;
650 
651  double alpha_seg;
652 
653  double dref;
654  double d1, d2;
655 
656  double bb[16]; // {ax, ay, bx, by, cx, cy, dx, dy, ex, ey, fx, fy, gx, gy, hx, hy}}
657 
658  double cp;
659  double best_cp;
660  double distNearestPoint;
661 
662  double area;
663  double width;
664  double length;
665 
666  double best_area = std::numeric_limits<double>::max();
667  double best_alpha = -1;
668  double best_bb[16];
669  double best_length = 0;
670  double best_width = 0;
671 
672 
673  bbox[0] = std::numeric_limits<double>::max();
674  bbox[1] = std::numeric_limits<double>::max();
675  bbox[2] = std::numeric_limits<double>::lowest();
676  bbox[3] = std::numeric_limits<double>::lowest();
677 
678  for ( i = 0; i < cHullSize; i++ )
679  {
680  if ( x[cHull[i]] < bbox[0] )
681  bbox[0] = x[cHull[i]];
682 
683  if ( x[cHull[i]] > bbox[2] )
684  bbox[2] = x[cHull[i]];
685 
686  if ( y[cHull[i]] < bbox[1] )
687  bbox[1] = y[cHull[i]];
688 
689  if ( y[cHull[i]] > bbox[3] )
690  bbox[3] = y[cHull[i]];
691  }
692 
693 
694  dref = bbox[2] - bbox[0];
695 
696  for ( alpha_d = 0; alpha_d < 90; alpha_d++ )
697  {
698  alpha = alpha_d * M_PI / 180.0;
699  d1 = std::cos( alpha ) * dref;
700  d2 = std::sin( alpha ) * dref;
701 
702  bb[0] = bbox[0];
703  bb[1] = bbox[3]; // ax, ay
704 
705  bb[4] = bbox[0];
706  bb[5] = bbox[1]; // cx, cy
707 
708  bb[8] = bbox[2];
709  bb[9] = bbox[1]; // ex, ey
710 
711  bb[12] = bbox[2];
712  bb[13] = bbox[3]; // gx, gy
713 
714 
715  bb[2] = bb[0] + d1;
716  bb[3] = bb[1] + d2; // bx, by
717  bb[6] = bb[4] - d2;
718  bb[7] = bb[5] + d1; // dx, dy
719  bb[10] = bb[8] - d1;
720  bb[11] = bb[9] - d2; // fx, fy
721  bb[14] = bb[12] + d2;
722  bb[15] = bb[13] - d1; // hx, hy
723 
724  // adjust all points
725  for ( i = 0; i < 16; i += 4 )
726  {
727 
728  alpha_seg = ( ( i / 4 > 0 ? ( i / 4 ) - 1 : 3 ) ) * M_PI_2 + alpha;
729 
730  best_cp = std::numeric_limits<double>::max();
731  for ( j = 0; j < nbPoints; j++ )
732  {
733  cp = GeomFunction::cross_product( bb[i + 2], bb[i + 3], bb[i], bb[i + 1], x[cHull[j]], y[cHull[j]] );
734  if ( cp < best_cp )
735  {
736  best_cp = cp;
737  }
738  }
739 
740  distNearestPoint = best_cp / dref;
741 
742  d1 = std::cos( alpha_seg ) * distNearestPoint;
743  d2 = std::sin( alpha_seg ) * distNearestPoint;
744 
745  bb[i] += d1; // x
746  bb[i + 1] += d2; // y
747  bb[i + 2] += d1; // x
748  bb[i + 3] += d2; // y
749  }
750 
751  // compute and compare AREA
752  width = GeomFunction::cross_product( bb[6], bb[7], bb[4], bb[5], bb[12], bb[13] ) / dref;
753  length = GeomFunction::cross_product( bb[2], bb[3], bb[0], bb[1], bb[8], bb[9] ) / dref;
754 
755  area = width * length;
756 
757  if ( area < 0 )
758  area *= -1;
759 
760 
761  if ( best_area - area > EPSILON )
762  {
763  best_area = area;
764  best_length = length;
765  best_width = width;
766  best_alpha = alpha;
767  memcpy( best_bb, bb, sizeof( double ) * 16 );
768  }
769  }
770 
771  // best bbox is defined
772 
773  CHullBox finalBb;
774 
775  for ( i = 0; i < 16; i = i + 4 )
776  {
777  GeomFunction::computeLineIntersection( best_bb[i], best_bb[i + 1], best_bb[i + 2], best_bb[i + 3],
778  best_bb[( i + 4 ) % 16], best_bb[( i + 5 ) % 16], best_bb[( i + 6 ) % 16], best_bb[( i + 7 ) % 16],
779  &finalBb.x[int ( i / 4 )], &finalBb.y[int ( i / 4 )] );
780  }
781 
782  finalBb.alpha = best_alpha;
783  finalBb.width = best_width;
784  finalBb.length = best_length;
785 
786  return finalBb;
787 }
788 
789 double PointSet::minDistanceToPoint( double px, double py, double *rx, double *ry ) const
790 {
791  if ( !mGeos )
792  createGeosGeom();
793 
794  if ( !mGeos )
795  return 0;
796 
797  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
798  try
799  {
800 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
801  geos::unique_ptr geosPt( GEOSGeom_createPointFromXY_r( geosctxt, px, py ) );
802 #else
803  GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
804  GEOSCoordSeq_setX_r( geosctxt, coord, 0, px );
805  GEOSCoordSeq_setY_r( geosctxt, coord, 0, py );
806  geos::unique_ptr geosPt( GEOSGeom_createPoint_r( geosctxt, coord ) );
807 #endif
808  int type = GEOSGeomTypeId_r( geosctxt, mGeos );
809  const GEOSGeometry *extRing = nullptr;
810  if ( type != GEOS_POLYGON )
811  {
812  extRing = mGeos;
813  }
814  else
815  {
816  //for polygons, we want distance to exterior ring (not an interior point)
817  extRing = GEOSGetExteriorRing_r( geosctxt, mGeos );
818  }
819  geos::coord_sequence_unique_ptr nearestCoord( GEOSNearestPoints_r( geosctxt, extRing, geosPt.get() ) );
820  double nx;
821  double ny;
822 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
823  unsigned int nPoints = 0;
824  GEOSCoordSeq_getSize_r( geosctxt, nearestCoord.get(), &nPoints );
825  if ( nPoints == 0 )
826  return 0;
827 
828  ( void )GEOSCoordSeq_getXY_r( geosctxt, nearestCoord.get(), 0, &nx, &ny );
829 #else
830  ( void )GEOSCoordSeq_getX_r( geosctxt, nearestCoord.get(), 0, &nx );
831  ( void )GEOSCoordSeq_getY_r( geosctxt, nearestCoord.get(), 0, &ny );
832 #endif
833 
834  if ( rx )
835  *rx = nx;
836  if ( ry )
837  *ry = ny;
838 
839  return GeomFunction::dist_euc2d_sq( px, py, nx, ny );
840  }
841  catch ( GEOSException &e )
842  {
843  qWarning( "GEOS exception: %s", e.what() );
844  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
845  return 0;
846  }
847 }
848 
849 void PointSet::getCentroid( double &px, double &py, bool forceInside ) const
850 {
851  if ( !mGeos )
852  createGeosGeom();
853 
854  if ( !mGeos )
855  return;
856 
857  try
858  {
859  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
860  geos::unique_ptr centroidGeom( GEOSGetCentroid_r( geosctxt, mGeos ) );
861  if ( centroidGeom )
862  {
863  const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, centroidGeom.get() );
864 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
865  unsigned int nPoints = 0;
866  GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
867  if ( nPoints == 0 )
868  return;
869  GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
870 #else
871  GEOSCoordSeq_getX_r( geosctxt, coordSeq, 0, &px );
872  GEOSCoordSeq_getY_r( geosctxt, coordSeq, 0, &py );
873 #endif
874  }
875 
876  // check if centroid inside in polygon
877  if ( forceInside && !containsPoint( px, py ) )
878  {
879  geos::unique_ptr pointGeom( GEOSPointOnSurface_r( geosctxt, mGeos ) );
880 
881  if ( pointGeom )
882  {
883  const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosctxt, pointGeom.get() );
884 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
885  unsigned int nPoints = 0;
886  GEOSCoordSeq_getSize_r( geosctxt, coordSeq, &nPoints );
887  if ( nPoints == 0 )
888  return;
889 
890  GEOSCoordSeq_getXY_r( geosctxt, coordSeq, 0, &px, &py );
891 #else
892  GEOSCoordSeq_getX_r( geosctxt, coordSeq, 0, &px );
893  GEOSCoordSeq_getY_r( geosctxt, coordSeq, 0, &py );
894 #endif
895  }
896  }
897  }
898  catch ( GEOSException &e )
899  {
900  qWarning( "GEOS exception: %s", e.what() );
901  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
902  return;
903  }
904 }
905 
906 bool PointSet::boundingBoxIntersects( const PointSet *other ) const
907 {
908  double x1 = ( xmin > other->xmin ? xmin : other->xmin );
909  double x2 = ( xmax < other->xmax ? xmax : other->xmax );
910  if ( x1 > x2 )
911  return false;
912  double y1 = ( ymin > other->ymin ? ymin : other->ymin );
913  double y2 = ( ymax < other->ymax ? ymax : other->ymax );
914  return y1 <= y2;
915 }
916 
917 void PointSet::getPointByDistance( double *d, double *ad, double dl, double *px, double *py )
918 {
919  int i;
920  double dx, dy, di;
921  double distr;
922 
923  i = 0;
924  if ( dl >= 0 )
925  {
926  while ( i < nbPoints && ad[i] <= dl ) i++;
927  i--;
928  }
929 
930  if ( i < nbPoints - 1 )
931  {
932  if ( dl < 0 )
933  {
934  dx = x[nbPoints - 1] - x[0];
935  dy = y[nbPoints - 1] - y[0];
936  di = std::sqrt( dx * dx + dy * dy );
937  }
938  else
939  {
940  dx = x[i + 1] - x[i];
941  dy = y[i + 1] - y[i];
942  di = d[i];
943  }
944 
945  distr = dl - ad[i];
946  *px = x[i] + dx * distr / di;
947  *py = y[i] + dy * distr / di;
948  }
949  else // just select last point...
950  {
951  *px = x[i];
952  *py = y[i];
953  }
954 }
955 
956 const GEOSGeometry *PointSet::geos() const
957 {
958  if ( !mGeos )
959  createGeosGeom();
960 
961  return mGeos;
962 }
963 
964 double PointSet::length() const
965 {
966  if ( mLength >= 0 )
967  return mLength;
968 
969  if ( !mGeos )
970  createGeosGeom();
971 
972  if ( !mGeos )
973  return -1;
974 
975  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
976 
977  try
978  {
979  ( void )GEOSLength_r( geosctxt, mGeos, &mLength );
980  return mLength;
981  }
982  catch ( GEOSException &e )
983  {
984  qWarning( "GEOS exception: %s", e.what() );
985  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
986  return -1;
987  }
988 }
989 
990 double PointSet::area() const
991 {
992  if ( mArea >= 0 )
993  return mArea;
994 
995  if ( !mGeos )
996  createGeosGeom();
997 
998  if ( !mGeos )
999  return -1;
1000 
1001  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
1002 
1003  try
1004  {
1005  ( void )GEOSArea_r( geosctxt, mGeos, &mArea );
1006  mArea = std::fabs( mArea );
1007  return mArea;
1008  }
1009  catch ( GEOSException &e )
1010  {
1011  qWarning( "GEOS exception: %s", e.what() );
1012  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
1013  return -1;
1014  }
1015 }
1016 
1018 {
1019  return qgsDoubleNear( x[0], x[nbPoints - 1] ) && qgsDoubleNear( y[0], y[nbPoints - 1] );
1020 }
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:964
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
QgsPointXY::x
Q_GADGET double x
Definition: qgspointxy.h:47
EPSILON
#define EPSILON
Definition: util.h:78
pal::PointSet::compute_chull_bbox
CHullBox compute_chull_bbox()
Computes a con???? hull.
Definition: pointset.cpp:641
pal::PointSet::~PointSet
virtual ~PointSet()
Definition: pointset.cpp:192
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:1017
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:956
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:789
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:554
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:906
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:2888
qgsgeometryutils.h
pal::CHullBox
Definition: pointset.h:56
pal::PointSet::createGeosGeom
void createGeosGeom() const
Definition: pointset.cpp:117
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:44
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:272
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:72
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
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:849
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:277
QgsGeometryUtils::interpolatePointOnLine
static QgsPointXY interpolatePointOnLine(double x1, double y1, double x2, double y2, double fraction) SIP_HOLDGIL
Interpolates the position of a point a fraction of the way along the line from (x1,...
Definition: qgsgeometryutils.cpp:1411
pal::PointSet::area
double area() const
Returns area of polygon geometry.
Definition: pointset.cpp:990
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:917
qgsmessagelog.h
pal::CHullBox::y
double y[4]
Definition: pointset.h:58
pal::PointSet::ymax
double ymax
Definition: pointset.h:232