QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgsclipper.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsclipper.cpp - a class that clips line
3 segments and polygons
4 -------------------
5 begin : March 2004
6 copyright : (C) 2005 by Gavin Macaulay
7 email :
8 ***************************************************************************/
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19#include "qgsclipper.h"
20
21#include "qgsgeometry.h"
22#include "qgslogger.h"
23
24// Where has all the code gone?
25
26// It's been inlined, so its in the qgsclipper.h file.
27
28// But the static members must be initialized outside the class! (or GCC 4 dies)
29
30// Qt also does clipping when the coordinates go over +/- 32767
31// moreover from Qt 4.6, Qt clips also when the width/height of a painter path
32// is more than 32767. Since we want to avoid clipping by Qt (because it is slow)
33// we set coordinate limit to less than 32767 / 2
34const double QgsClipper::MAX_X = 16000;
35const double QgsClipper::MIN_X = -16000;
36const double QgsClipper::MAX_Y = 16000;
37const double QgsClipper::MIN_Y = -16000;
38
39const double QgsClipper::SMALL_NUM = 1e-12;
40
41void QgsClipper::clipped3dLine( const QVector< double > &xIn, const QVector< double > &yIn, const QVector<double> &zIn, QVector<double> &x, QVector<double> &y, QVector<double> &z, const QgsBox3D &clipExtent )
42{
43 double p0x, p0y, p0z, p1x = 0.0, p1y = 0.0, p1z = 0.0; //original coordinates
44 double p1x_c, p1y_c, p1z_c; //clipped end coordinates
45 double lastClipX = 0.0, lastClipY = 0.0, lastClipZ = 0.0; // last successfully clipped coordinates
46
47 const int nPoints = xIn.size();
48
49 x.reserve( nPoints );
50 y.reserve( nPoints );
51 z.reserve( nPoints );
52
53 const double *sourceX = xIn.data();
54 const double *sourceY = yIn.data();
55 const double *sourceZ = zIn.data();
56
57 for ( int i = 0; i < nPoints; ++i )
58 {
59 if ( i == 0 )
60 {
61 p1x = *sourceX++;
62 p1y = *sourceY++;
63 p1z = *sourceZ++;
64 }
65 else
66 {
67 p0x = p1x;
68 p0y = p1y;
69 p0z = p1z;
70
71 p1x = *sourceX++;
72 p1y = *sourceY++;
73 p1z = *sourceZ++;
74
75 p1x_c = p1x;
76 p1y_c = p1y;
77 p1z_c = p1z;
78
79 // TODO: should be in 3D
80 if ( clipLineSegment( clipExtent, p0x, p0y, p0z, p1x_c, p1y_c, p1z_c ) )
81 {
82 bool newLine = !x.isEmpty() && ( !qgsDoubleNear( p0x, lastClipX ) || !qgsDoubleNear( p0y, lastClipY ) || !qgsDoubleNear( p0z, lastClipZ ) );
83 if ( newLine )
84 {
85 //add edge points to connect old and new line
86 // TODO: should be (really) in 3D
87 connectSeparatedLines( lastClipX, lastClipY, lastClipZ, p0x, p0y, p0z, clipExtent, x, y, z );
88 }
89 if ( x.isEmpty() || newLine )
90 {
91 //add first point
92 x << p0x;
93 y << p0y;
94 z << p0z;
95 }
96
97 //add second point
98 lastClipX = p1x_c;
99 lastClipY = p1y_c;
100 lastClipZ = p1z_c;
101 x << p1x_c;
102 y << p1y_c;
103 z << p1z_c;
104 }
105 }
106 }
107}
108
109QPolygonF QgsClipper::clippedLine( const QgsCurve &curve, const QgsRectangle &clipExtent )
110{
111 return clippedLine( curve.asQPolygonF(), clipExtent );
112}
113
114QPolygonF QgsClipper::clippedLine( const QPolygonF &curve, const QgsRectangle &clipExtent )
115{
116 const int nPoints = curve.size();
117
118 double p0x, p0y, p1x = 0.0, p1y = 0.0; //original coordinates
119 double p1x_c, p1y_c; //clipped end coordinates
120 double lastClipX = 0.0, lastClipY = 0.0; //last successfully clipped coords
121
122 QPolygonF line;
123 line.reserve( nPoints + 1 );
124
125 const QPointF *curveData = curve.data();
126
127 for ( int i = 0; i < nPoints; ++i )
128 {
129 if ( i == 0 )
130 {
131 p1x = curveData->x();
132 p1y = curveData->y();
133 }
134 else
135 {
136 p0x = p1x;
137 p0y = p1y;
138
139 p1x = curveData->x();
140 p1y = curveData->y();
141
142 p1x_c = p1x;
143 p1y_c = p1y;
144 if ( clipLineSegment( clipExtent.xMinimum(), clipExtent.xMaximum(), clipExtent.yMinimum(), clipExtent.yMaximum(), p0x, p0y, p1x_c, p1y_c ) )
145 {
146 const bool newLine = !line.isEmpty() && ( !qgsDoubleNear( p0x, lastClipX ) || !qgsDoubleNear( p0y, lastClipY ) );
147 if ( newLine )
148 {
149 //add edge points to connect old and new line
150 connectSeparatedLines( lastClipX, lastClipY, p0x, p0y, clipExtent, line );
151 }
152 if ( line.empty() || newLine )
153 {
154 //add first point
155 line << QPointF( p0x, p0y );
156 }
157
158 //add second point
159 lastClipX = p1x_c;
160 lastClipY = p1y_c;
161 line << QPointF( p1x_c, p1y_c );
162 }
163 }
164 curveData++;
165 }
166 return line;
167}
168
169void QgsClipper::connectSeparatedLines( double x0, double y0, double x1, double y1, const QgsRectangle &clipRect, QPolygonF &pts )
170{
171 //test the different edge combinations...
172 if ( qgsDoubleNear( x0, clipRect.xMinimum() ) )
173 {
174 if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
175 {
176 return;
177 }
178 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
179 {
180 pts << QPointF( clipRect.xMinimum(), clipRect.yMaximum() );
181 return;
182 }
183 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
184 {
185 pts << QPointF( clipRect.xMinimum(), clipRect.yMaximum() );
186 pts << QPointF( clipRect.xMaximum(), clipRect.yMaximum() );
187 return;
188 }
189 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
190 {
191 pts << QPointF( clipRect.xMinimum(), clipRect.yMinimum() );
192 return;
193 }
194 }
195 else if ( qgsDoubleNear( y0, clipRect.yMaximum() ) )
196 {
197 if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
198 {
199 return;
200 }
201 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
202 {
203 pts << QPointF( clipRect.xMaximum(), clipRect.yMaximum() );
204 return;
205 }
206 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
207 {
208 pts << QPointF( clipRect.xMaximum(), clipRect.yMaximum() );
209 pts << QPointF( clipRect.xMaximum(), clipRect.yMinimum() );
210 return;
211 }
212 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
213 {
214 pts << QPointF( clipRect.xMinimum(), clipRect.yMaximum() );
215 return;
216 }
217 }
218 else if ( qgsDoubleNear( x0, clipRect.xMaximum() ) )
219 {
220 if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
221 {
222 return;
223 }
224 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
225 {
226 pts << QPointF( clipRect.xMaximum(), clipRect.yMinimum() );
227 return;
228 }
229 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
230 {
231 pts << QPointF( clipRect.xMaximum(), clipRect.yMinimum() );
232 pts << QPointF( clipRect.xMinimum(), clipRect.yMinimum() );
233 return;
234 }
235 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
236 {
237 pts << QPointF( clipRect.xMaximum(), clipRect.yMaximum() );
238 return;
239 }
240 }
241 else if ( qgsDoubleNear( y0, clipRect.yMinimum() ) )
242 {
243 if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
244 {
245 return;
246 }
247 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
248 {
249 pts << QPointF( clipRect.xMinimum(), clipRect.yMinimum() );
250 return;
251 }
252 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
253 {
254 pts << QPointF( clipRect.xMinimum(), clipRect.yMinimum() );
255 pts << QPointF( clipRect.xMinimum(), clipRect.yMaximum() );
256 return;
257 }
258 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
259 {
260 pts << QPointF( clipRect.xMaximum(), clipRect.yMinimum() );
261 return;
262 }
263 }
264}
265
266void QgsClipper::connectSeparatedLines( double x0, double y0, double z0, double x1, double y1, double z1, const QgsBox3D &clipRect, QVector< double > &ptsX, QVector< double > &ptsY, QVector<double> &ptsZ )
267{
268 // TODO: really relevant and sufficient?
269 double meanZ = ( z0 + z1 ) / 2.0;
270
271 //test the different edge combinations...
272 if ( qgsDoubleNear( x0, clipRect.xMinimum() ) )
273 {
274 if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
275 {
276 return;
277 }
278 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
279 {
280 ptsX << clipRect.xMinimum();
281 ptsY << clipRect.yMaximum();
282 ptsZ << meanZ;
283 return;
284 }
285 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
286 {
287 ptsX << clipRect.xMinimum();
288 ptsY << clipRect.yMaximum();
289 ptsZ << meanZ;
290 ptsX << clipRect.xMaximum();
291 ptsY << clipRect.yMaximum();
292 ptsZ << meanZ;
293 return;
294 }
295 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
296 {
297 ptsX << clipRect.xMinimum();
298 ptsY << clipRect.yMinimum();
299 ptsZ << meanZ;
300 return;
301 }
302 }
303 else if ( qgsDoubleNear( y0, clipRect.yMaximum() ) )
304 {
305 if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
306 {
307 return;
308 }
309 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
310 {
311 ptsX << clipRect.xMaximum();
312 ptsY << clipRect.yMaximum();
313 ptsZ << meanZ;
314 return;
315 }
316 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
317 {
318 ptsX << clipRect.xMaximum();
319 ptsY << clipRect.yMaximum();
320 ptsZ << meanZ;
321 ptsX << clipRect.xMaximum();
322 ptsY << clipRect.yMinimum();
323 ptsZ << meanZ;
324 return;
325 }
326 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
327 {
328 ptsX << clipRect.xMinimum();
329 ptsY << clipRect.yMaximum();
330 ptsZ << meanZ;
331 return;
332 }
333 }
334 else if ( qgsDoubleNear( x0, clipRect.xMaximum() ) )
335 {
336 if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
337 {
338 return;
339 }
340 else if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
341 {
342 ptsX << clipRect.xMaximum();
343 ptsY << clipRect.yMinimum();
344 ptsZ << meanZ;
345 return;
346 }
347 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
348 {
349 ptsX << clipRect.xMaximum();
350 ptsY << clipRect.yMinimum();
351 ptsZ << meanZ;
352 ptsX << clipRect.xMinimum();
353 ptsY << clipRect.yMinimum();
354 ptsZ << meanZ;
355 return;
356 }
357 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
358 {
359 ptsX << clipRect.xMaximum();
360 ptsY << clipRect.yMaximum();
361 ptsZ << meanZ;
362 return;
363 }
364 }
365 else if ( qgsDoubleNear( y0, clipRect.yMinimum() ) )
366 {
367 if ( qgsDoubleNear( y1, clipRect.yMinimum() ) )
368 {
369 return;
370 }
371 else if ( qgsDoubleNear( x1, clipRect.xMinimum() ) )
372 {
373 ptsX << clipRect.xMinimum();
374 ptsY << clipRect.yMinimum();
375 ptsZ << meanZ;
376 return;
377 }
378 else if ( qgsDoubleNear( y1, clipRect.yMaximum() ) )
379 {
380 ptsX << clipRect.xMinimum();
381 ptsY << clipRect.yMinimum();
382 ptsZ << meanZ;
383 ptsX << clipRect.xMinimum();
384 ptsY << clipRect.yMaximum();
385 ptsZ << meanZ;
386 return;
387 }
388 else if ( qgsDoubleNear( x1, clipRect.xMaximum() ) )
389 {
390 ptsX << clipRect.xMaximum();
391 ptsY << clipRect.yMinimum();
392 ptsZ << meanZ;
393 return;
394 }
395 }
396}
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
double yMaximum() const
Returns the maximum y value.
Definition qgsbox3d.h:240
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:205
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:212
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:233
static bool clipLineSegment(double left, double right, double bottom, double top, double &x0, double &y0, double &x1, double &y1)
Clips a line segment to a rectangle.
Definition qgsclipper.h:749
static const double MAX_X
Maximum X-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:71
static const double MIN_Y
Minimum Y-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:77
static const double MAX_Y
Maximum Y-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:75
static QPolygonF clippedLine(const QgsCurve &curve, const QgsRectangle &clipExtent)
Takes a linestring and clips it to clipExtent.
static const double MIN_X
Minimum X-coordinate of the rectangular box used for clipping.
Definition qgsclipper.h:73
static void clipped3dLine(const QVector< double > &xIn, const QVector< double > &yIn, const QVector< double > &zIn, QVector< double > &x, QVector< double > &y, QVector< double > &z, const QgsBox3D &clipExtent)
Takes a line with 3D coordinates and clips it to clipExtent.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
virtual QPolygonF asQPolygonF() const
Returns a QPolygonF representing the points.
Definition qgscurve.cpp:266
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
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