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