QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
geomfunction.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 "geomfunction.h"
31 #include "feature.h"
32 #include "util.h"
33 #include "qgis.h"
34 #include "pal.h"
35 #include "qgsmessagelog.h"
36 #include <vector>
37 
38 using namespace pal;
39 
40 void heapsort( int *sid, int *id, const std::vector< double > &x, int N )
41 {
42  unsigned int n = N, i = n / 2, parent, child;
43  int tx;
44  for ( ;; )
45  {
46  if ( i > 0 )
47  {
48  i--;
49  tx = sid[i];
50  }
51  else
52  {
53  n--;
54  if ( n == 0 ) return;
55  tx = sid[n];
56  sid[n] = sid[0];
57  }
58  parent = i;
59  child = i * 2 + 1;
60  while ( child < n )
61  {
62  if ( child + 1 < n && x[id[sid[child + 1]]] > x[id[sid[child]]] )
63  {
64  child++;
65  }
66  if ( x[id[sid[child]]] > x[id[tx]] )
67  {
68  sid[parent] = sid[child];
69  parent = child;
70  child = parent * 2 + 1;
71  }
72  else
73  {
74  break;
75  }
76  }
77  sid[parent] = tx;
78  }
79 }
80 
81 
82 void heapsort2( int *x, double *heap, int N )
83 {
84  unsigned int n = N, i = n / 2, parent, child;
85  double t;
86  int tx;
87  for ( ;; )
88  {
89  if ( i > 0 )
90  {
91  i--;
92  t = heap[i];
93  tx = x[i];
94  }
95  else
96  {
97  n--;
98  if ( n == 0 ) return;
99  t = heap[n];
100  tx = x[n];
101  heap[n] = heap[0];
102  x[n] = x[0];
103  }
104  parent = i;
105  child = i * 2 + 1;
106  while ( child < n )
107  {
108  if ( child + 1 < n && heap[child + 1] > heap[child] )
109  {
110  child++;
111  }
112  if ( heap[child] > t )
113  {
114  heap[parent] = heap[child];
115  x[parent] = x[child];
116  parent = child;
117  child = parent * 2 + 1;
118  }
119  else
120  {
121  break;
122  }
123  }
124  heap[parent] = t;
125  x[parent] = tx;
126  }
127 }
128 
129 bool GeomFunction::isSegIntersects( double x1, double y1, double x2, double y2, // 1st segment
130  double x3, double y3, double x4, double y4 ) // 2nd segment
131 {
132  return ( cross_product( x1, y1, x2, y2, x3, y3 ) * cross_product( x1, y1, x2, y2, x4, y4 ) < 0
133  && cross_product( x3, y3, x4, y4, x1, y1 ) * cross_product( x3, y3, x4, y4, x2, y2 ) < 0 );
134 }
135 
136 bool GeomFunction::computeLineIntersection( double x1, double y1, double x2, double y2, // 1st line (segment)
137  double x3, double y3, double x4, double y4, // 2nd line segment
138  double *x, double *y )
139 {
140 
141  double a1, a2, b1, b2, c1, c2;
142  double denom;
143 
144  a1 = y2 - y1;
145  b1 = x1 - x2;
146  c1 = x2 * y1 - x1 * y2;
147 
148  a2 = y4 - y3;
149  b2 = x3 - x4;
150  c2 = x4 * y3 - x3 * y4;
151 
152  denom = a1 * b2 - a2 * b1;
153  if ( qgsDoubleNear( denom, 0.0 ) )
154  {
155  return false;
156  }
157  else
158  {
159  *x = ( b1 * c2 - b2 * c1 ) / denom;
160  *y = ( a2 * c1 - a1 * c2 ) / denom;
161  }
162 
163  return true;
164 }
165 
166 int GeomFunction::convexHullId( int *id, const std::vector< double > &x, const std::vector< double > &y, int n, int *&cHull )
167 {
168  int i;
169 
170  cHull = new int[n];
171  for ( i = 0; i < n; i++ )
172  {
173  cHull[i] = i;
174  }
175 
176 
177  if ( n <= 3 ) return n;
178 
179  int *stack = new int[n];
180  double *tan = new double [n];
181  int ref;
182 
183  int second, top;
184  double result;
185 
186  // find the lowest y value
187  heapsort( cHull, id, y, n );
188 
189  // find the lowest x value from the lowest y
190  ref = 1;
191  while ( ref < n && qgsDoubleNear( y[id[cHull[ref]]], y[id[cHull[0]]] ) ) ref++;
192 
193  heapsort( cHull, id, x, ref );
194 
195  // the first point is now for sure in the hull as well as the ref one
196  for ( i = ref; i < n; i++ )
197  {
198  if ( qgsDoubleNear( y[id[cHull[i]]], y[id[cHull[0]]] ) )
199  tan[i] = FLT_MAX;
200  else
201  tan[i] = ( x[id[cHull[0]]] - x[id[cHull[i]]] ) / ( y[id[cHull[i]]] - y[id[cHull[0]]] );
202  }
203 
204  if ( ref < n )
205  heapsort2( cHull + ref, tan + ref, n - ref );
206 
207  // the second point is in too
208  stack[0] = cHull[0];
209  if ( ref == 1 )
210  {
211  stack[1] = cHull[1];
212  ref++;
213  }
214  else
215  stack[1] = cHull[ref - 1];
216 
217 
218  top = 1;
219  second = 0;
220 
221  for ( i = ref; i < n; i++ )
222  {
223  result = cross_product( x[id[stack[second]]], y[id[stack[second]]],
224  x[id[stack[top]]], y[id[stack[top]]], x[id[cHull[i]]], y[id[cHull[i]]] );
225  // Coolineaire !! garder le plus éloigné
226  if ( qgsDoubleNear( result, 0.0 ) )
227  {
228  if ( dist_euc2d_sq( x[id[stack[second]]], y[id[stack[second]]], x[id[cHull[i]]], y[id[cHull[i]]] )
229  > dist_euc2d_sq( x[id[stack[second]]], y[id[stack[second]]], x[id[stack[top]]], y[id[stack[top]]] ) )
230  {
231  stack[top] = cHull[i];
232  }
233  }
234  else if ( result > 0 ) //convexe
235  {
236  second++;
237  top++;
238  stack[top] = cHull[i];
239  }
240  else
241  {
242  while ( result < 0 && top > 1 )
243  {
244  second--;
245  top--;
246  result = cross_product( x[id[stack[second]]],
247  y[id[stack[second]]], x[id[stack[top]]],
248  y[id[stack[top]]], x[id[cHull[i]]], y[id[cHull[i]]] );
249  }
250  second++;
251  top++;
252  stack[top] = cHull[i];
253  }
254  }
255 
256  for ( i = 0; i <= top; i++ )
257  {
258  cHull[i] = stack[i];
259  }
260 
261  delete[] stack;
262  delete[] tan;
263 
264  return top + 1;
265 }
266 
267 int GeomFunction::reorderPolygon( int nbPoints, std::vector<double> &x, std::vector<double> &y )
268 {
269  int inc = 0;
270  int *cHull = nullptr;
271  int i;
272 
273  int *pts = new int[nbPoints];
274  for ( i = 0; i < nbPoints; i++ )
275  pts[i] = i;
276 
277  ( void )convexHullId( pts, x, y, nbPoints, cHull );
278 
279  if ( pts[cHull[0]] < pts[cHull[1]] && pts[cHull[1]] < pts[cHull[2]] )
280  inc = 1;
281  else if ( pts[cHull[0]] > pts[cHull[1]] && pts[cHull[1]] > pts[cHull[2]] )
282  inc = -1;
283  else if ( pts[cHull[0]] > pts[cHull[1]] && pts[cHull[1]] < pts[cHull[2]] && pts[cHull[2]] < pts[cHull[0]] )
284  inc = 1;
285  else if ( pts[cHull[0]] > pts[cHull[1]] && pts[cHull[1]] < pts[cHull[2]] && pts[cHull[2]] > pts[cHull[0]] )
286  inc = -1;
287  else if ( pts[cHull[0]] < pts[cHull[1]] && pts[cHull[1]] > pts[cHull[2]] && pts[cHull[2]] > pts[cHull[0]] )
288  inc = -1;
289  else if ( pts[cHull[0]] < pts[cHull[1]] && pts[cHull[1]] > pts[cHull[2]] && pts[cHull[2]] < pts[cHull[0]] )
290  inc = 1;
291  else
292  {
293  // wrong cHull
294  delete[] cHull;
295  delete[] pts;
296  return -1;
297  }
298 
299  if ( inc == -1 ) // re-order points
300  {
301  double tmp;
302  int j;
303  for ( i = 0, j = nbPoints - 1; i <= j; i++, j-- )
304  {
305  tmp = x[i];
306  x[i] = x[j];
307  x[j] = tmp;
308 
309  tmp = y[i];
310  y[i] = y[j];
311  y[j] = tmp;
312  }
313  }
314 
315  delete[] cHull;
316  delete[] pts;
317 
318  return 0;
319 }
320 
321 bool GeomFunction::containsCandidate( const GEOSPreparedGeometry *geom, double x, double y, double width, double height, double alpha )
322 {
323  if ( !geom )
324  return false;
325 
326  GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
327  GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 5, 2 );
328 
329  GEOSCoordSeq_setX_r( geosctxt, coord, 0, x );
330  GEOSCoordSeq_setY_r( geosctxt, coord, 0, y );
331  if ( !qgsDoubleNear( alpha, 0.0 ) )
332  {
333  double beta = alpha + M_PI_2;
334  double dx1 = std::cos( alpha ) * width;
335  double dy1 = std::sin( alpha ) * width;
336  double dx2 = std::cos( beta ) * height;
337  double dy2 = std::sin( beta ) * height;
338  GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + dx1 );
339  GEOSCoordSeq_setY_r( geosctxt, coord, 1, y + dy1 );
340  GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + dx1 + dx2 );
341  GEOSCoordSeq_setY_r( geosctxt, coord, 2, y + dy1 + dy2 );
342  GEOSCoordSeq_setX_r( geosctxt, coord, 3, x + dx2 );
343  GEOSCoordSeq_setY_r( geosctxt, coord, 3, y + dy2 );
344  }
345  else
346  {
347  GEOSCoordSeq_setX_r( geosctxt, coord, 1, x + width );
348  GEOSCoordSeq_setY_r( geosctxt, coord, 1, y );
349  GEOSCoordSeq_setX_r( geosctxt, coord, 2, x + width );
350  GEOSCoordSeq_setY_r( geosctxt, coord, 2, y + height );
351  GEOSCoordSeq_setX_r( geosctxt, coord, 3, x );
352  GEOSCoordSeq_setY_r( geosctxt, coord, 3, y + height );
353  }
354  //close ring
355  GEOSCoordSeq_setX_r( geosctxt, coord, 4, x );
356  GEOSCoordSeq_setY_r( geosctxt, coord, 4, y );
357 
358  try
359  {
360  geos::unique_ptr bboxGeos( GEOSGeom_createLinearRing_r( geosctxt, coord ) );
361  bool result = ( GEOSPreparedContainsProperly_r( geosctxt, geom, bboxGeos.get() ) == 1 );
362  return result;
363  }
364  catch ( GEOSException &e )
365  {
367  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
368  return false;
370  }
371 }
372 
373 void GeomFunction::findLineCircleIntersection( double cx, double cy, double radius,
374  double x1, double y1, double x2, double y2,
375  double &xRes, double &yRes )
376 {
377  double dx = x2 - x1;
378  double dy = y2 - y1;
379 
380  double A = dx * dx + dy * dy;
381  double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
382  double C = ( x1 - cx ) * ( x1 - cx ) + ( y1 - cy ) * ( y1 - cy ) - radius * radius;
383 
384  double det = B * B - 4 * A * C;
385  if ( A <= 0.000000000001 || det < 0 )
386  // Should never happen, No real solutions.
387  return;
388 
389  if ( qgsDoubleNear( det, 0.0 ) )
390  {
391  // Could potentially happen.... One solution.
392  double t = -B / ( 2 * A );
393  xRes = x1 + t * dx;
394  yRes = y1 + t * dy;
395  }
396  else
397  {
398  // Two solutions.
399  // Always use the 1st one
400  // We only really have one solution here, as we know the line segment will start in the circle and end outside
401  double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
402  xRes = x1 + t * dx;
403  yRes = y1 + t * dy;
404  }
405 }
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.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
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.
static void findLineCircleIntersection(double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes)
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))
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:2825
static double cross_product(double x1, double y1, double x2, double y2, double x3, double y3)
Definition: geomfunction.h:61
void heapsort(int *sid, int *id, const std::vector< double > &x, int N)
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.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
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).
#define Q_NOWARN_UNREACHABLE_POP
Definition: qgis.h:652
static int reorderPolygon(int nbPoints, std::vector< double > &x, std::vector< double > &y)
Reorder points to have cross prod ((x,y)[i], (x,y)[i+1), point) > 0 when point is outside...
void heapsort2(int *x, double *heap, int N)
#define Q_NOWARN_UNREACHABLE_PUSH
Definition: qgis.h:651
static double dist_euc2d_sq(double x1, double y1, double x2, double y2)
Definition: geomfunction.h:71