QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 *
11  * it under the terms of the GNU General Public License as published by *
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
92  if ( writeHeader )
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  // Can replace the geometry by its BBOX ?
151  if (( simplifyFlags & QgsMapToPixelSimplifier::SimplifyEnvelope ) && ( envelope.xMaximum() - envelope.xMinimum() ) < map2pixelTol && ( envelope.yMaximum() - envelope.yMinimum() ) < map2pixelTol )
152  {
153  canbeGeneralizable = generalizeWkbGeometry( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, envelope, writeHeader );
154  if ( canbeGeneralizable ) return true;
155  }
156  if ( !( simplifyFlags & QgsMapToPixelSimplifier::SimplifyGeometry ) ) canbeGeneralizable = false;
157 
158  // Write the main header of the geometry
159  if ( writeHeader )
160  {
161  memcpy( targetWkb, sourceWkb, 1 ); // byteOrder
162  sourceWkb += 1;
163  targetWkb += 1;
164 
165  int geometryType;
166  memcpy( &geometryType, sourceWkb, 4 );
167  int flatType = QGis::flatType(( QGis::WkbType )geometryType );
168  memcpy( targetWkb, &flatType, 4 ); // type
169  sourceWkb += 4;
170  targetWkb += 4;
171 
172  targetWkbSize += 5;
173  }
174 
175  unsigned char* wkb1 = sourceWkb;
176  unsigned char* wkb2 = targetWkb;
177  unsigned int flatType = QGis::flatType( wkbType );
178 
179  // Write the geometry
180  if ( flatType == QGis::WKBLineString || isaLinearRing )
181  {
182  double x, y, lastX = 0, lastY = 0;
183 
184  int sizeOfDoubleX = sizeof( double );
185  int sizeOfDoubleY = QGis::wkbDimensions( wkbType ) == 3 /*hasZValue*/ ? 2 * sizeof( double ) : sizeof( double );
186 
187  int numPoints;
188  memcpy( &numPoints, sourceWkb, 4 );
189  sourceWkb += 4;
190  if ( numPoints <= ( isaLinearRing ? 5 : 2 ) ) canbeGeneralizable = false;
191 
192  int numTargetPoints = 0;
193  memcpy( targetWkb, &numTargetPoints, 4 );
194  targetWkb += 4;
195  targetWkbSize += 4;
196 
197  double* ptr = ( double* )targetWkb;
198  map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.
199 
200  // Process each vertex...
201  for ( int i = 0, numPoints_i = ( isaLinearRing ? numPoints - 1 : numPoints ); i < numPoints_i; ++i )
202  {
203  memcpy( &x, sourceWkb, sizeof( double ) ); sourceWkb += sizeOfDoubleX;
204  memcpy( &y, sourceWkb, sizeof( double ) ); sourceWkb += sizeOfDoubleY;
205 
206  if ( i == 0 || !canbeGeneralizable || QgsMapToPixelSimplifier::calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol || ( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
207  {
208  memcpy( ptr, &x, sizeof( double ) ); lastX = x; ptr++;
209  memcpy( ptr, &y, sizeof( double ) ); lastY = y; ptr++;
210  numTargetPoints++;
211  }
212  }
213  targetWkb = wkb2 + 4;
214 
215  // Fix the topology of the geometry
216  if ( isaLinearRing )
217  {
218  memcpy( &x, targetWkb + 0, sizeof( double ) );
219  memcpy( &y, targetWkb + 8, sizeof( double ) );
220  memcpy( ptr, &x, sizeof( double ) ); ptr++;
221  memcpy( ptr, &y, sizeof( double ) ); ptr++;
222  numTargetPoints++;
223  }
224  targetWkbSize += numTargetPoints * 16;
225  targetWkb = wkb2;
226 
227  memcpy( targetWkb, &numTargetPoints, 4 );
228  result = numPoints != numTargetPoints;
229  }
230  else
231  if ( flatType == QGis::WKBPolygon )
232  {
233  int numRings;
234  memcpy( &numRings, sourceWkb, 4 );
235  sourceWkb += 4;
236 
237  memcpy( targetWkb, &numRings, 4 );
238  targetWkb += 4;
239  targetWkbSize += 4;
240 
241  for ( int i = 0; i < numRings; ++i )
242  {
243  int numPoints_i;
244  memcpy( &numPoints_i, sourceWkb, 4 );
245  QgsRectangle envelope_i = numRings == 1 ? envelope : calculateBoundingBox( wkbType, sourceWkb + 4, numPoints_i );
246 
247  size_t sourceWkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );
248  size_t targetWkbSize_i = 0;
249 
250  result |= simplifyWkbGeometry( simplifyFlags, wkbType, sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope_i, map2pixelTol, false, true );
251  sourceWkb += sourceWkbSize_i;
252  targetWkb += targetWkbSize_i;
253 
254  targetWkbSize += targetWkbSize_i;
255  }
256  }
257  else
258  if ( flatType == QGis::WKBMultiLineString || flatType == QGis::WKBMultiPolygon )
259  {
260  int numGeoms;
261  memcpy( &numGeoms, sourceWkb, 4 );
262  sourceWkb += 4;
263  wkb1 += 4;
264 
265  memcpy( targetWkb, &numGeoms, 4 );
266  targetWkb += 4;
267  targetWkbSize += 4;
268 
269  for ( int i = 0; i < numGeoms; ++i )
270  {
271  size_t sourceWkbSize_i = 0;
272  size_t targetWkbSize_i = 0;
273 
274  // ... calculate the wkb-size of the current child complex geometry
275  if ( flatType == QGis::WKBMultiLineString )
276  {
277  int numPoints_i;
278  memcpy( &numPoints_i, wkb1 + 5, 4 );
279  int wkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );
280 
281  sourceWkbSize_i += 5 + wkbSize_i;
282  wkb1 += 5 + wkbSize_i;
283  }
284  else
285  {
286  int numPrings_i;
287  memcpy( &numPrings_i, wkb1 + 5, 4 );
288  sourceWkbSize_i = 9;
289  wkb1 += 9;
290 
291  for ( int j = 0; j < numPrings_i; ++j )
292  {
293  int numPoints_i;
294  memcpy( &numPoints_i, wkb1, 4 );
295  int wkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );
296 
297  sourceWkbSize_i += wkbSize_i;
298  wkb1 += wkbSize_i;
299  }
300  }
301  result |= simplifyWkbGeometry( simplifyFlags, QGis::singleType( wkbType ), sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope, map2pixelTol, true, false );
302  sourceWkb += sourceWkbSize_i;
303  targetWkb += targetWkbSize_i;
304 
305  targetWkbSize += targetWkbSize_i;
306  }
307  }
308  return result;
309 }
310 
312 
315 {
316  // Can replace the geometry by its BBOX ?
317  if (( envelope.xMaximum() - envelope.xMinimum() ) < map2pixelTol && ( envelope.yMaximum() - envelope.yMinimum() ) < map2pixelTol )
318  {
319  return true;
320  }
321  return false;
322 }
323 
326 {
327  QgsGeometry* g = new QgsGeometry();
328 
329  size_t wkbSize = geometry->wkbSize();
330  unsigned char* wkb = ( unsigned char* )malloc( wkbSize );
331  memcpy( wkb, geometry->asWkb(), wkbSize );
332  g->fromWkb( wkb, wkbSize );
334 
335  return g;
336 }
337 
339 bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double tolerance )
340 {
341  size_t targetWkbSize = 0;
342 
343  // Check whether the geometry can be simplified using the map2pixel context
344  QGis::GeometryType geometryType = geometry->type();
345  if ( !( geometryType == QGis::Line || geometryType == QGis::Polygon ) ) return false;
346 
347  QgsRectangle envelope = geometry->boundingBox();
348  QGis::WkbType wkbType = geometry->wkbType();
349 
350  unsigned char* wkb = ( unsigned char* )geometry->asWkb( );
351  size_t wkbSize = geometry->wkbSize( );
352 
353  // Simplify the geometry rewriting temporally its WKB-stream for saving calloc's.
354  if ( simplifyWkbGeometry( simplifyFlags, wkbType, wkb, wkbSize, wkb, targetWkbSize, envelope, tolerance ) )
355  {
356  unsigned char* targetWkb = ( unsigned char* )malloc( targetWkbSize );
357  memcpy( targetWkb, wkb, targetWkbSize );
358  geometry->fromWkb( targetWkb, targetWkbSize );
359  return true;
360  }
361  return false;
362 }
363 
366 {
367  return simplifyGeometry( geometry, mSimplifyFlags, mTolerance );
368 }