QGIS API Documentation  2.4.0-Chugiak
qgsmaptopixelgeometrysimplifier.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaptopixelgeometrysimplifier.cpp
3  ---------------------
4  begin : December 2013
5  copyright : (C) 2013 by Alvaro Huarte
6  email : http://wiki.osgeo.org/wiki/Alvaro_Huarte
7
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16
17 #include <limits>
19 #include "qgsapplication.h"
20
21 QgsMapToPixelSimplifier::QgsMapToPixelSimplifier( int simplifyFlags, double tolerance )
22  : mSimplifyFlags( simplifyFlags )
23  , mTolerance( tolerance )
24 {
25 }
27 {
28 }
29
31 // Helper simplification methods
32
34 float QgsMapToPixelSimplifier::calculateLengthSquared2D( double x1, double y1, double x2, double y2 )
35 {
36  float vx = ( float )( x2 - x1 );
37  float vy = ( float )( y2 - y1 );
38
39  return vx*vx + vy*vy;
40 }
41
43 inline static QgsRectangle calculateBoundingBox( QGis::WkbType wkbType, unsigned char* wkb, size_t numPoints )
44 {
45  double xmin = std::numeric_limits<double>::max(), x, y;
46  double ymin = std::numeric_limits<double>::max();
47  double xmax = -std::numeric_limits<double>::max();
48  double ymax = -std::numeric_limits<double>::max();
49
50  int sizeOfDoubleX = sizeof( double );
51  int sizeOfDoubleY = QGis::wkbDimensions( wkbType ) == 3 /*hasZValue*/ ? 2 * sizeof( double ) : sizeof( double );
52
53  for ( size_t i = 0; i < numPoints; ++i )
54  {
55  memcpy( &x, wkb, sizeof( double ) ); wkb += sizeOfDoubleX;
56  memcpy( &y, wkb, sizeof( double ) ); wkb += sizeOfDoubleY;
57
58  if ( xmin > x ) xmin = x;
59  if ( ymin > y ) ymin = y;
60  if ( xmax < x ) xmax = x;
61  if ( ymax < y ) ymax = y;
62  }
63
64  return QgsRectangle( xmin, ymin, xmax, ymax );
65 }
66
68 inline static bool generalizeWkbGeometry( QGis::WkbType wkbType, unsigned char* sourceWkb, size_t sourceWkbSize, unsigned char* targetWkb, size_t& targetWkbSize, const QgsRectangle& envelope, bool writeHeader )
69 {
70  Q_UNUSED( sourceWkb );
71  unsigned char* wkb2 = targetWkb;
72  unsigned int geometryType = QGis::singleType( QGis::flatType( wkbType ) );
73
74  int sizeOfDoubleX = sizeof( double );
75  int sizeOfDoubleY = QGis::wkbDimensions( wkbType ) == 3 /*hasZValue*/ ? 2 * sizeof( double ) : sizeof( double );
76
77  // Skip the unnecesary generalization because of is a very single geometry
78  size_t minimumSize = ( geometryType == QGis::WKBLineString ? 4 + 2 * ( sizeOfDoubleX + sizeOfDoubleY ) : 8 + 5 * ( sizeOfDoubleX + sizeOfDoubleY ) );
79  if ( writeHeader ) minimumSize += 5;
80  if ( sourceWkbSize <= minimumSize )
81  {
82  targetWkbSize = 0;
83  return false;
84  }
85
86  double x1 = envelope.xMinimum();
87  double y1 = envelope.yMinimum();
88  double x2 = envelope.xMaximum();
89  double y2 = envelope.yMaximum();
90
91  // Write the main header of the geometry
93  {
94  char byteOrder = QgsApplication::endian(); // byteOrder
95  memcpy( targetWkb, &byteOrder, 1 );
96  targetWkb += 1;
97
98  memcpy( targetWkb, &geometryType, 4 ); // type
99  targetWkb += 4;
100
101  if ( geometryType == QGis::WKBPolygon ) // numRings
102  {
103  int numRings = 1;
104  memcpy( targetWkb, &numRings, 4 );
105  targetWkb += 4;
106  }
107  }
108
109  // Write the generalized geometry
110  if ( geometryType == QGis::WKBLineString )
111  {
112  int numPoints = 2;
113  memcpy( targetWkb, &numPoints, 4 ); // numPoints;
114  targetWkb += 4;
115
116  memcpy( targetWkb, &x1, sizeof( double ) ); targetWkb += sizeof( double );
117  memcpy( targetWkb, &y1, sizeof( double ) ); targetWkb += sizeof( double );
118  memcpy( targetWkb, &x2, sizeof( double ) ); targetWkb += sizeof( double );
119  memcpy( targetWkb, &y2, sizeof( double ) ); targetWkb += sizeof( double );
120  }
121  else
122  {
123  int numPoints = 5;
124  memcpy( targetWkb, &numPoints, 4 ); // numPoints;
125  targetWkb += 4;
126
127  memcpy( targetWkb, &x1, sizeof( double ) ); targetWkb += sizeof( double );
128  memcpy( targetWkb, &y1, sizeof( double ) ); targetWkb += sizeof( double );
129  memcpy( targetWkb, &x2, sizeof( double ) ); targetWkb += sizeof( double );
130  memcpy( targetWkb, &y1, sizeof( double ) ); targetWkb += sizeof( double );
131  memcpy( targetWkb, &x2, sizeof( double ) ); targetWkb += sizeof( double );
132  memcpy( targetWkb, &y2, sizeof( double ) ); targetWkb += sizeof( double );
133  memcpy( targetWkb, &x1, sizeof( double ) ); targetWkb += sizeof( double );
134  memcpy( targetWkb, &y2, sizeof( double ) ); targetWkb += sizeof( double );
135  memcpy( targetWkb, &x1, sizeof( double ) ); targetWkb += sizeof( double );
136  memcpy( targetWkb, &y1, sizeof( double ) ); targetWkb += sizeof( double );
137  }
138  targetWkbSize += targetWkb - wkb2;
139
140  return true;
141 }
142
144 bool QgsMapToPixelSimplifier::simplifyWkbGeometry( int simplifyFlags, QGis::WkbType wkbType, unsigned char* sourceWkb, size_t sourceWkbSize, unsigned char* targetWkb, size_t& targetWkbSize, const QgsRectangle& envelope, double map2pixelTol, bool writeHeader, bool isaLinearRing )
145 {
146  bool canbeGeneralizable = true;
147  bool hasZValue = QGis::wkbDimensions( wkbType ) == 3;
148  bool result = false;
149
150  // Save initial WKB settings to use when the simplification creates invalid geometries
151  unsigned char* sourcePrevWkb = sourceWkb;
152  unsigned char* targetPrevWkb = targetWkb;
153  size_t targetWkbPrevSize = targetWkbSize;
154
155  // Can replace the geometry by its BBOX ?
156  if (( simplifyFlags & QgsMapToPixelSimplifier::SimplifyEnvelope ) && ( envelope.xMaximum() - envelope.xMinimum() ) < map2pixelTol && ( envelope.yMaximum() - envelope.yMinimum() ) < map2pixelTol )
157  {
158  canbeGeneralizable = generalizeWkbGeometry( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, envelope, writeHeader );
159  if ( canbeGeneralizable ) return true;
160  }
161  if ( !( simplifyFlags & QgsMapToPixelSimplifier::SimplifyGeometry ) ) canbeGeneralizable = false;
162
163  // Write the main header of the geometry
165  {
166  targetWkb[0] = sourceWkb[0]; // byteOrder
167  sourceWkb += 1;
168  targetWkb += 1;
169
170  int geometryType;
171  memcpy( &geometryType, sourceWkb, 4 );
172  int flatType = QGis::flatType(( QGis::WkbType )geometryType );
173  memcpy( targetWkb, &flatType, 4 ); // type
174  sourceWkb += 4;
175  targetWkb += 4;
176
177  targetWkbSize += 5;
178  }
179
180  unsigned char* wkb1 = sourceWkb;
181  unsigned char* wkb2 = targetWkb;
182  unsigned int flatType = QGis::flatType( wkbType );
183
184  // Write the geometry
185  if ( flatType == QGis::WKBLineString || isaLinearRing )
186  {
187  double x, y, lastX = 0, lastY = 0;
188
189  double xmin = std::numeric_limits<double>::max();
190  double ymin = std::numeric_limits<double>::max();
191  double xmax = -std::numeric_limits<double>::max();
192  double ymax = -std::numeric_limits<double>::max();
193
194  int sizeOfDoubleX = sizeof( double );
195  int sizeOfDoubleY = QGis::wkbDimensions( wkbType ) == 3 /*hasZValue*/ ? 2 * sizeof( double ) : sizeof( double );
196
197  int numPoints;
198  memcpy( &numPoints, sourceWkb, 4 );
199  sourceWkb += 4;
200  if ( numPoints <= ( isaLinearRing ? 5 : 2 ) ) canbeGeneralizable = false;
201
202  int numTargetPoints = 0;
203  memcpy( targetWkb, &numTargetPoints, 4 );
204  targetWkb += 4;
205  targetWkbSize += 4;
206
207  double* ptr = ( double* )targetWkb;
208  map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.
209
210  // Check whether the LinearRing is really closed.
211  if ( isaLinearRing )
212  {
213  double x1, y1, x2, y2;
214
215  unsigned char* startWkbX = sourceWkb;
216  unsigned char* startWkbY = startWkbX + sizeOfDoubleX;
217  unsigned char* finalWkbX = sourceWkb + ( numPoints - 1 ) * ( sizeOfDoubleX + sizeOfDoubleY );
218  unsigned char* finalWkbY = finalWkbX + sizeOfDoubleX;
219
220  memcpy( &x1, startWkbX, sizeof( double ) );
221  memcpy( &y1, startWkbY, sizeof( double ) );
222  memcpy( &x2, finalWkbX, sizeof( double ) );
223  memcpy( &y2, finalWkbY, sizeof( double ) );
224
225  isaLinearRing = ( x1 == x2 ) && ( y1 == y2 );
226  }
227
228  // Process each vertex...
229  for ( int i = 0, numPoints_i = ( isaLinearRing ? numPoints - 1 : numPoints ); i < numPoints_i; ++i )
230  {
231  memcpy( &x, sourceWkb, sizeof( double ) ); sourceWkb += sizeOfDoubleX;
232  memcpy( &y, sourceWkb, sizeof( double ) ); sourceWkb += sizeOfDoubleY;
233
234  if ( i == 0 || !canbeGeneralizable || QgsMapToPixelSimplifier::calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol || ( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
235  {
236  memcpy( ptr, &x, sizeof( double ) ); lastX = x; ptr++;
237  memcpy( ptr, &y, sizeof( double ) ); lastY = y; ptr++;
238  numTargetPoints++;
239  }
240  if ( xmin > x ) xmin = x;
241  if ( ymin > y ) ymin = y;
242  if ( xmax < x ) xmax = x;
243  if ( ymax < y ) ymax = y;
244  }
245  targetWkb = wkb2 + 4;
246
247  // Fix the topology of the geometry
248  if ( numTargetPoints <= ( isaLinearRing ? 2 : 1 ) )
249  {
250  unsigned char* targetTempWkb = targetWkb;
251  int targetWkbTempSize = targetWkbSize;
252
253  sourceWkb = sourcePrevWkb;
254  targetWkb = targetPrevWkb;
255  targetWkbSize = targetWkbPrevSize;
256  bool isok = generalizeWkbGeometry( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, QgsRectangle( xmin, ymin, xmax, ymax ), writeHeader );
257  if ( isok ) return true;
258
259  targetWkb = targetTempWkb;
260  targetWkbSize = targetWkbTempSize;
261  }
262  if ( isaLinearRing )
263  {
264  memcpy( &x, targetWkb + 0, sizeof( double ) );
265  memcpy( &y, targetWkb + 8, sizeof( double ) );
266  memcpy( ptr, &x, sizeof( double ) ); ptr++;
267  memcpy( ptr, &y, sizeof( double ) ); ptr++;
268  numTargetPoints++;
269  }
270  targetWkbSize += numTargetPoints * 16;
271  targetWkb = wkb2;
272
273  memcpy( targetWkb, &numTargetPoints, 4 );
274  result = numPoints != numTargetPoints;
275  }
276  else
277  if ( flatType == QGis::WKBPolygon )
278  {
279  int numRings;
280  memcpy( &numRings, sourceWkb, 4 );
281  sourceWkb += 4;
282
283  memcpy( targetWkb, &numRings, 4 );
284  targetWkb += 4;
285  targetWkbSize += 4;
286
287  for ( int i = 0; i < numRings; ++i )
288  {
289  int numPoints_i;
290  memcpy( &numPoints_i, sourceWkb, 4 );
291  QgsRectangle envelope_i = numRings == 1 ? envelope : calculateBoundingBox( wkbType, sourceWkb + 4, numPoints_i );
292
293  size_t sourceWkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );
294  size_t targetWkbSize_i = 0;
295
296  result |= simplifyWkbGeometry( simplifyFlags, wkbType, sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope_i, map2pixelTol, false, true );
297  sourceWkb += sourceWkbSize_i;
298  targetWkb += targetWkbSize_i;
299
300  targetWkbSize += targetWkbSize_i;
301  }
302  }
303  else
304  if ( flatType == QGis::WKBMultiLineString || flatType == QGis::WKBMultiPolygon )
305  {
306  int numGeoms;
307  memcpy( &numGeoms, sourceWkb, 4 );
308  sourceWkb += 4;
309  wkb1 += 4;
310
311  memcpy( targetWkb, &numGeoms, 4 );
312  targetWkb += 4;
313  targetWkbSize += 4;
314
315  for ( int i = 0; i < numGeoms; ++i )
316  {
317  size_t sourceWkbSize_i = 0;
318  size_t targetWkbSize_i = 0;
319
320  // ... calculate the wkb-size of the current child complex geometry
321  if ( flatType == QGis::WKBMultiLineString )
322  {
323  int numPoints_i;
324  memcpy( &numPoints_i, wkb1 + 5, 4 );
325  int wkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );
326
327  sourceWkbSize_i += 5 + wkbSize_i;
328  wkb1 += 5 + wkbSize_i;
329  }
330  else
331  {
332  int numPrings_i;
333  memcpy( &numPrings_i, wkb1 + 5, 4 );
334  sourceWkbSize_i = 9;
335  wkb1 += 9;
336
337  for ( int j = 0; j < numPrings_i; ++j )
338  {
339  int numPoints_i;
340  memcpy( &numPoints_i, wkb1, 4 );
341  int wkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );
342
343  sourceWkbSize_i += wkbSize_i;
344  wkb1 += wkbSize_i;
345  }
346  }
347  result |= simplifyWkbGeometry( simplifyFlags, QGis::singleType( wkbType ), sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope, map2pixelTol, true, false );
348  sourceWkb += sourceWkbSize_i;
349  targetWkb += targetWkbSize_i;
350
351  targetWkbSize += targetWkbSize_i;
352  }
353  }
354  return result;
355 }
356
358
361 {
362  // Can replace the geometry by its BBOX ?
363  if (( envelope.xMaximum() - envelope.xMinimum() ) < map2pixelTol && ( envelope.yMaximum() - envelope.yMinimum() ) < map2pixelTol )
364  {
365  return true;
366  }
367  return false;
368 }
369
372 {
373  QgsGeometry* g = new QgsGeometry();
374
375  size_t wkbSize = geometry->wkbSize();
376  unsigned char* wkb = ( unsigned char* )malloc( wkbSize );
377  memcpy( wkb, geometry->asWkb(), wkbSize );
378  g->fromWkb( wkb, wkbSize );
380
381  return g;
382 }
383
385 bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double tolerance )
386 {
387  size_t targetWkbSize = 0;
388
389  // Check whether the geometry can be simplified using the map2pixel context
390  QGis::GeometryType geometryType = geometry->type();
391  if ( !( geometryType == QGis::Line || geometryType == QGis::Polygon ) ) return false;
392
393  QgsRectangle envelope = geometry->boundingBox();
394  QGis::WkbType wkbType = geometry->wkbType();
395
396  unsigned char* wkb = ( unsigned char* )geometry->asWkb( );
397  size_t wkbSize = geometry->wkbSize( );
398
399  // Simplify the geometry rewriting temporally its WKB-stream for saving calloc's.
400  if ( simplifyWkbGeometry( simplifyFlags, wkbType, wkb, wkbSize, wkb, targetWkbSize, envelope, tolerance ) )
401  {
402  unsigned char* targetWkb = new unsigned char[targetWkbSize];
403  memcpy( targetWkb, wkb, targetWkbSize );
404  geometry->fromWkb( targetWkb, targetWkbSize );
405  return true;
406  }
407  return false;
408 }
409
412 {
413  return simplifyGeometry( geometry, mSimplifyFlags, mTolerance );
414 }
static WkbType singleType(WkbType type)
Definition: qgis.h:71
A rectangle specified with double values.
Definition: qgsrectangle.h:35
GeometryType
Definition: qgis.h:155
size_t wkbSize() const
Returns the size of the WKB in asWkb().
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:194
static bool generalizeWkbGeometry(QGis::WkbType wkbType, unsigned char *sourceWkb, size_t sourceWkbSize, unsigned char *targetWkb, size_t &targetWkbSize, const QgsRectangle &envelope, bool writeHeader)
Generalize the WKB-geometry using the BBOX of the original geometry.
QGis::GeometryType type()
Returns type of the vector.
static QgsRectangle calculateBoundingBox(QGis::WkbType wkbType, unsigned char *wkb, size_t numPoints)
Returns the BBOX of the specified WKB-point stream.
WkbType
Used for symbology operations.
Definition: qgis.h:53
static endian_t endian()
Returns whether this machine uses big or little endian.
double ANALYSIS_EXPORT max(double x, double y)
returns the maximum of two doubles or the first argument if both are equal
static WkbType flatType(WkbType type)
Definition: qgis.h:99
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:199
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:184
The geometries can be fully simplified by its BoundingBox.
static bool canbeGeneralizedByMapBoundingBox(const QgsRectangle &envelope, double map2pixelTol)
Returns whether the envelope can be replaced by its BBOX when is applied the specified map2pixel cont...
QgsMapToPixelSimplifier(int simplifyFlags, double tolerance)
static float calculateLengthSquared2D(double x1, double y1, double x2, double y2)
Returns the squared 2D-distance of the vector defined by the two points specified.
QGis::WkbType wkbType() const
Returns type of wkb (point / linestring / polygon etc.)
double mTolerance
Distance tolerance for the simplification.
virtual QgsGeometry * simplify(QgsGeometry *geometry) const
Returns a simplified version the specified geometry.
QgsRectangle boundingBox()
Returns the bounding box of this feature.
static int wkbDimensions(WkbType type)
Definition: qgis.h:139
virtual bool simplifyGeometry(QgsGeometry *geometry) const
Simplifies the specified geometry.
void fromWkb(unsigned char *wkb, size_t length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length...
static bool simplifyWkbGeometry(int simplifyFlags, QGis::WkbType wkbType, unsigned char *sourceWkb, size_t sourceWkbSize, unsigned char *targetWkb, size_t &targetWkbSize, const QgsRectangle &envelope, double map2pixelTol, bool writeHeader=true, bool isaLinearRing=false)
Simplify the WKB-geometry using the specified tolerance.
int mSimplifyFlags
Current simplification flags.
The geometries can be simplified using the current map2pixel context state.
const unsigned char * asWkb() const
Returns the buffer containing this geometry in WKB format.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:189