QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsgml.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgml.cpp
3  ---------------------
4  begin : February 2013
5  copyright : (C) 2013 by Radim Blazek
6  email : radim dot blazek at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsgml.h"
16 #include "qgsrectangle.h"
18 #include "qgsgeometry.h"
19 #include "qgslogger.h"
20 #include "qgsmessagelog.h"
22 #include <QBuffer>
23 #include <QList>
24 #include <QNetworkRequest>
25 #include <QNetworkReply>
26 #include <QProgressDialog>
27 #include <QSet>
28 #include <QSettings>
29 #include <QUrl>
30 
31 #include <limits>
32 
33 const char NS_SEPARATOR = '?';
34 const QString GML_NAMESPACE = "http://www.opengis.net/gml";
35 
37  const QString& typeName,
38  const QString& geometryAttribute,
39  const QgsFields & fields )
40  : QObject()
41  , mTypeName( typeName )
42  , mGeometryAttribute( geometryAttribute )
43  , mWkbType( NULL )
44  , mFinished( false )
45  , mCurrentFeature( 0 )
46  , mFeatureCount( 0 )
47  , mCurrentWKB( NULL )
48  , mCurrentWKBSize( 0 )
49  , mDimension( 2 )
50  , mCoorMode( QgsGml::coordinate )
51  , mEpsg( 0 )
52 {
53  mThematicAttributes.clear();
54  for ( int i = 0; i < fields.size(); i++ )
55  {
56  mThematicAttributes.insert( fields[i].name(), qMakePair( i, fields[i] ) );
57  }
58 
59  mEndian = QgsApplication::endian();
60 
61  int index = mTypeName.indexOf( ":" );
62  if ( index != -1 && index < mTypeName.length() )
63  {
64  mTypeName = mTypeName.mid( index + 1 );
65  }
66 }
67 
69 {
70 }
71 
72 int QgsGml::getFeatures( const QString& uri, QGis::WkbType* wkbType, QgsRectangle* extent, const QString& userName, const QString& password )
73 {
74  mUri = uri;
75  mWkbType = wkbType;
76 
77  XML_Parser p = XML_ParserCreateNS( NULL, NS_SEPARATOR );
78  XML_SetUserData( p, this );
79  XML_SetElementHandler( p, QgsGml::start, QgsGml::end );
80  XML_SetCharacterDataHandler( p, QgsGml::chars );
81 
82  //start with empty extent
83  mExtent.setMinimal();
84 
85  QNetworkRequest request( mUri );
86  if ( !userName.isNull() || !password.isNull() )
87  {
88  request.setRawHeader( "Authorization", "Basic " + QString( "%1:%2" ).arg( userName ).arg( password ).toAscii().toBase64() );
89  }
90  QNetworkReply* reply = QgsNetworkAccessManager::instance()->get( request );
91 
92  connect( reply, SIGNAL( finished() ), this, SLOT( setFinished() ) );
93  connect( reply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( handleProgressEvent( qint64, qint64 ) ) );
94 
95  //find out if there is a QGIS main window. If yes, display a progress dialog
96  QProgressDialog* progressDialog = 0;
97  QWidget* mainWindow = 0;
98  QWidgetList topLevelWidgets = qApp->topLevelWidgets();
99  for ( QWidgetList::iterator it = topLevelWidgets.begin(); it != topLevelWidgets.end(); ++it )
100  {
101  if (( *it )->objectName() == "QgisApp" )
102  {
103  mainWindow = *it;
104  break;
105  }
106  }
107  if ( mainWindow )
108  {
109  progressDialog = new QProgressDialog( tr( "Loading GML data\n%1" ).arg( mTypeName ), tr( "Abort" ), 0, 0, mainWindow );
110  progressDialog->setWindowModality( Qt::ApplicationModal );
111  connect( this, SIGNAL( dataReadProgress( int ) ), progressDialog, SLOT( setValue( int ) ) );
112  connect( this, SIGNAL( totalStepsUpdate( int ) ), progressDialog, SLOT( setMaximum( int ) ) );
113  connect( progressDialog, SIGNAL( canceled() ), this, SLOT( setFinished() ) );
114  progressDialog->show();
115  }
116 
117  int atEnd = 0;
118  while ( !atEnd )
119  {
120  if ( mFinished )
121  {
122  atEnd = 1;
123  }
124  QByteArray readData = reply->readAll();
125  if ( readData.size() > 0 )
126  {
127  if ( XML_Parse( p, readData.constData(), readData.size(), atEnd ) == 0 )
128  {
129  XML_Error errorCode = XML_GetErrorCode( p );
130  QString errorString = tr( "Error: %1 on line %2, column %3" )
131  .arg( XML_ErrorString( errorCode ) )
132  .arg( XML_GetCurrentLineNumber( p ) )
133  .arg( XML_GetCurrentColumnNumber( p ) );
134  QgsMessageLog::logMessage( errorString, tr( "WFS" ) );
135  }
136  }
137  QCoreApplication::processEvents();
138  }
139 
140  QNetworkReply::NetworkError replyError = reply->error();
141  QString replyErrorString = reply->errorString();
142 
143  delete reply;
144  delete progressDialog;
145 
146  if ( replyError )
147  {
149  tr( "GML Getfeature network request failed with error: %1" ).arg( replyErrorString ),
150  tr( "Network" ),
152  );
153  return 1;
154  }
155 
156  if ( *mWkbType != QGis::WKBNoGeometry )
157  {
158  if ( mExtent.isEmpty() )
159  {
160  //reading of bbox from the server failed, so we calculate it less efficiently by evaluating the features
161  calculateExtentFromFeatures();
162  }
163  }
164 
165  XML_ParserFree( p );
166 
167  if ( extent )
168  *extent = mExtent;
169 
170  return 0;
171 }
172 
173 int QgsGml::getFeatures( const QByteArray &data, QGis::WkbType* wkbType, QgsRectangle* extent )
174 {
175  mWkbType = wkbType;
176  mExtent.setMinimal();
177 
178  XML_Parser p = XML_ParserCreateNS( NULL, NS_SEPARATOR );
179  XML_SetUserData( p, this );
180  XML_SetElementHandler( p, QgsGml::start, QgsGml::end );
181  XML_SetCharacterDataHandler( p, QgsGml::chars );
182  int atEnd = 1;
183  XML_Parse( p, data.constData(), data.size(), atEnd );
184 
185  if ( extent )
186  *extent = mExtent;
187 
188  return 0;
189 }
190 
191 void QgsGml::setFinished()
192 {
193  mFinished = true;
194 }
195 
196 void QgsGml::handleProgressEvent( qint64 progress, qint64 totalSteps )
197 {
198  if ( totalSteps < 0 )
199  {
200  totalSteps = 0;
201  progress = 0;
202  }
203  emit totalStepsUpdate( totalSteps );
204  emit dataReadProgress( progress );
205  emit dataProgressAndSteps( progress, totalSteps );
206 }
207 
208 void QgsGml::startElement( const XML_Char* el, const XML_Char** attr )
209 {
210  QString elementName( QString::fromUtf8( el ) );
211  ParseMode theParseMode( mParseModeStack.isEmpty() ? none : mParseModeStack.top() );
212  QStringList splitName = elementName.split( NS_SEPARATOR );
213  QString localName = splitName.last();
214  QString ns = splitName.size() > 1 ? splitName.first() : "";
215 
216  if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "coordinates" )
217  {
218  mParseModeStack.push( QgsGml::coordinate );
219  mCoorMode = QgsGml::coordinate;
220  mStringCash.clear();
221  mCoordinateSeparator = readAttribute( "cs", attr );
222  if ( mCoordinateSeparator.isEmpty() )
223  {
224  mCoordinateSeparator = ",";
225  }
226  mTupleSeparator = readAttribute( "ts", attr );
227  if ( mTupleSeparator.isEmpty() )
228  {
229  mTupleSeparator = " ";
230  }
231  }
232  if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "pos"
233  || elementName == GML_NAMESPACE + NS_SEPARATOR + "posList" )
234  {
235  mParseModeStack.push( QgsGml::posList );
236  mCoorMode = QgsGml::posList;
237  mStringCash.clear();
238  QString dimension = readAttribute( "srsDimension", attr );
239  bool ok;
240  mDimension = dimension.toInt( &ok );
241  if ( dimension.isEmpty() || !ok )
242  {
243  mDimension = 2;
244  }
245  }
246  else if ( localName == mGeometryAttribute )
247  {
248  mParseModeStack.push( QgsGml::geometry );
249  }
250  //else if ( mParseModeStack.size() == 0 && elementName == GML_NAMESPACE + NS_SEPARATOR + "boundedBy" )
251  else if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "boundedBy" )
252  {
253  mParseModeStack.push( QgsGml::boundingBox );
254  }
255  else if ( theParseMode == none && localName == mTypeName )
256  {
257  Q_ASSERT( !mCurrentFeature );
258  mCurrentFeature = new QgsFeature( mFeatureCount );
259  QgsAttributes attributes( mThematicAttributes.size() ); //add empty attributes
260  mCurrentFeature->setAttributes( attributes );
261  mParseModeStack.push( QgsGml::feature );
262  mCurrentFeatureId = readAttribute( "fid", attr );
263  }
264 
265  else if ( theParseMode == boundingBox && elementName == GML_NAMESPACE + NS_SEPARATOR + "Box" )
266  {
267  //read attribute srsName="EPSG:26910"
268  int epsgNr;
269  if ( readEpsgFromAttribute( epsgNr, attr ) != 0 )
270  {
271  QgsDebugMsg( "error, could not get epsg id" );
272  }
273  }
274  else if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "Polygon" )
275  {
276  mCurrentWKBFragments.push_back( QList<unsigned char*>() );
277  mCurrentWKBFragmentSizes.push_back( QList<int>() );
278  }
279  else if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiPoint" )
280  {
281  mParseModeStack.push( QgsGml::multiPoint );
282  //we need one nested list for intermediate WKB
283  mCurrentWKBFragments.push_back( QList<unsigned char*>() );
284  mCurrentWKBFragmentSizes.push_back( QList<int>() );
285  }
286  else if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiLineString" )
287  {
288  mParseModeStack.push( QgsGml::multiLine );
289  //we need one nested list for intermediate WKB
290  mCurrentWKBFragments.push_back( QList<unsigned char*>() );
291  mCurrentWKBFragmentSizes.push_back( QList<int>() );
292  }
293  else if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiPolygon" )
294  {
295  mParseModeStack.push( QgsGml::multiPolygon );
296  }
297  else if ( theParseMode == feature && mThematicAttributes.contains( localName ) )
298  {
299  mParseModeStack.push( QgsGml::attribute );
300  mAttributeName = localName;
301  mStringCash.clear();
302  }
303  // QGIS server (2.2) is using:
304  // <Attribute value="My description" name="desc"/>
305  else if ( theParseMode == feature
306  && localName.compare( "attribute", Qt::CaseInsensitive ) == 0 )
307  {
308  QString name = readAttribute( "name", attr );
309  if ( mThematicAttributes.contains( name ) )
310  {
311  QString value = readAttribute( "value", attr );
312  setAttribute( name, value );
313  }
314  }
315 
316  if ( mEpsg == 0 && ( localName == "Point" || localName == "MultiPoint" ||
317  localName == "LineString" || localName == "MultiLineString" ||
318  localName == "Polygon" || localName == "MultiPolygon" ) )
319  {
320  if ( readEpsgFromAttribute( mEpsg, attr ) != 0 )
321  {
322  QgsDebugMsg( "error, could not get epsg id" );
323  }
324  else
325  {
326  QgsDebugMsg( QString( "mEpsg = %1" ).arg( mEpsg ) );
327  }
328  }
329 }
330 
331 void QgsGml::endElement( const XML_Char* el )
332 {
333  QString elementName( QString::fromUtf8( el ) );
334  ParseMode theParseMode( mParseModeStack.isEmpty() ? none : mParseModeStack.top() );
335  QStringList splitName = elementName.split( NS_SEPARATOR );
336  QString localName = splitName.last();
337  QString ns = splitName.size() > 1 ? splitName.first() : "";
338 
339  if (( theParseMode == coordinate && elementName == GML_NAMESPACE + NS_SEPARATOR + "coordinates" )
340  || ( theParseMode == posList && (
341  elementName == GML_NAMESPACE + NS_SEPARATOR + "pos"
342  || elementName == GML_NAMESPACE + NS_SEPARATOR + "posList" ) ) )
343  {
344  mParseModeStack.pop();
345  }
346  else if ( theParseMode == attribute && localName == mAttributeName ) //add a thematic attribute to the feature
347  {
348  mParseModeStack.pop();
349 
350  setAttribute( mAttributeName, mStringCash );
351  }
352  else if ( theParseMode == geometry && localName == mGeometryAttribute )
353  {
354  mParseModeStack.pop();
355  }
356  else if ( theParseMode == boundingBox && elementName == GML_NAMESPACE + NS_SEPARATOR + "boundedBy" )
357  {
358  //create bounding box from mStringCash
359  if ( createBBoxFromCoordinateString( mCurrentExtent, mStringCash ) != 0 )
360  {
361  QgsDebugMsg( "creation of bounding box failed" );
362  }
363 
364  mParseModeStack.pop();
365  }
366  else if ( theParseMode == feature && localName == mTypeName )
367  {
368  Q_ASSERT( mCurrentFeature );
369  if ( mCurrentWKBSize > 0 )
370  {
371  mCurrentFeature->setGeometryAndOwnership( mCurrentWKB, mCurrentWKBSize );
372  }
373  else if ( !mCurrentExtent.isEmpty() )
374  {
375  mCurrentFeature->setGeometry( QgsGeometry::fromRect( mCurrentExtent ) );
376  }
377  else
378  {
379  mCurrentFeature->setGeometry( 0 );
380  }
381  mCurrentFeature->setValid( true );
382 
383  mFeatures.insert( mCurrentFeature->id(), mCurrentFeature );
384  if ( !mCurrentFeatureId.isEmpty() )
385  {
386  mIdMap.insert( mCurrentFeature->id(), mCurrentFeatureId );
387  }
388  mCurrentFeature = 0;
389  ++mFeatureCount;
390  mParseModeStack.pop();
391  }
392  else if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "Point" )
393  {
394  QList<QgsPoint> pointList;
395  if ( pointsFromString( pointList, mStringCash ) != 0 )
396  {
397  //error
398  }
399 
400  if ( pointList.count() == 0 )
401  return; // error
402 
403  if ( theParseMode == QgsGml::geometry )
404  {
405  //directly add WKB point to the feature
406  if ( getPointWKB( &mCurrentWKB, &mCurrentWKBSize, *( pointList.begin() ) ) != 0 )
407  {
408  //error
409  }
410 
411  if ( *mWkbType != QGis::WKBMultiPoint ) //keep multitype in case of geometry type mix
412  {
413  *mWkbType = QGis::WKBPoint;
414  }
415  }
416  else //multipoint, add WKB as fragment
417  {
418  unsigned char* wkb = 0;
419  int wkbSize = 0;
420  QList<unsigned char*> wkbList;
421  QList<int> wkbSizeList;
422  if ( getPointWKB( &wkb, &wkbSize, *( pointList.begin() ) ) != 0 )
423  {
424  //error
425  }
426  if ( !mCurrentWKBFragments.isEmpty() )
427  {
428  mCurrentWKBFragments.last().push_back( wkb );
429  mCurrentWKBFragmentSizes.last().push_back( wkbSize );
430  }
431  else
432  {
433  QgsDebugMsg( "No wkb fragments" );
434  delete [] wkb;
435  }
436  }
437  }
438  else if ( elementName == GML_NAMESPACE + NS_SEPARATOR + "LineString" )
439  {
440  //add WKB point to the feature
441 
442  QList<QgsPoint> pointList;
443  if ( pointsFromString( pointList, mStringCash ) != 0 )
444  {
445  //error
446  }
447  if ( theParseMode == QgsGml::geometry )
448  {
449  if ( getLineWKB( &mCurrentWKB, &mCurrentWKBSize, pointList ) != 0 )
450  {
451  //error
452  }
453 
454  if ( *mWkbType != QGis::WKBMultiLineString )//keep multitype in case of geometry type mix
455  {
456  *mWkbType = QGis::WKBLineString;
457  }
458  }
459  else //multiline, add WKB as fragment
460  {
461  unsigned char* wkb = 0;
462  int wkbSize = 0;
463  QList<unsigned char*> wkbList;
464  QList<int> wkbSizeList;
465  if ( getLineWKB( &wkb, &wkbSize, pointList ) != 0 )
466  {
467  //error
468  }
469  if ( !mCurrentWKBFragments.isEmpty() )
470  {
471  mCurrentWKBFragments.last().push_back( wkb );
472  mCurrentWKBFragmentSizes.last().push_back( wkbSize );
473  }
474  else
475  {
476  QgsDebugMsg( "no wkb fragments" );
477  delete [] wkb;
478  }
479  }
480  }
481  else if (( theParseMode == geometry || theParseMode == multiPolygon ) && elementName == GML_NAMESPACE + NS_SEPARATOR + "LinearRing" )
482  {
483  QList<QgsPoint> pointList;
484  if ( pointsFromString( pointList, mStringCash ) != 0 )
485  {
486  //error
487  }
488  unsigned char* wkb = 0;
489  int wkbSize = 0;
490  if ( getRingWKB( &wkb, &wkbSize, pointList ) != 0 )
491  {
492  //error
493  }
494  if ( !mCurrentWKBFragments.isEmpty() )
495  {
496  mCurrentWKBFragments.last().push_back( wkb );
497  mCurrentWKBFragmentSizes.last().push_back( wkbSize );
498  }
499  else
500  {
501  delete[] wkb;
502  QgsDebugMsg( "no wkb fragments" );
503  }
504  }
505  else if (( theParseMode == geometry || theParseMode == multiPolygon ) && elementName == GML_NAMESPACE + NS_SEPARATOR + "Polygon" )
506  {
507  if ( *mWkbType != QGis::WKBMultiPolygon )//keep multitype in case of geometry type mix
508  {
509  *mWkbType = QGis::WKBPolygon;
510  }
511 
512  if ( theParseMode == geometry )
513  {
514  createPolygonFromFragments();
515  }
516  }
517  else if ( theParseMode == multiPoint && elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiPoint" )
518  {
519  *mWkbType = QGis::WKBMultiPoint;
520  mParseModeStack.pop();
521  createMultiPointFromFragments();
522  }
523  else if ( theParseMode == multiLine && elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiLineString" )
524  {
525  *mWkbType = QGis::WKBMultiLineString;
526  mParseModeStack.pop();
527  createMultiLineFromFragments();
528  }
529  else if ( theParseMode == multiPolygon && elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiPolygon" )
530  {
531  *mWkbType = QGis::WKBMultiPolygon;
532  mParseModeStack.pop();
533  createMultiPolygonFromFragments();
534  }
535 }
536 
537 void QgsGml::characters( const XML_Char* chars, int len )
538 {
539  //save chars in mStringCash attribute mode or coordinate mode
540  if ( mParseModeStack.size() == 0 )
541  {
542  return;
543  }
544 
545  QgsGml::ParseMode theParseMode = mParseModeStack.top();
546  if ( theParseMode == QgsGml::attribute || theParseMode == QgsGml::coordinate || theParseMode == QgsGml::posList )
547  {
548  mStringCash.append( QString::fromUtf8( chars, len ) );
549  }
550 }
551 
552 void QgsGml::setAttribute( const QString& name, const QString& value )
553 {
554  //find index with attribute name
555  QMap<QString, QPair<int, QgsField> >::const_iterator att_it = mThematicAttributes.find( name );
556  if ( att_it != mThematicAttributes.constEnd() )
557  {
558  QVariant var;
559  switch ( att_it.value().second.type() )
560  {
561  case QVariant::Double:
562  var = QVariant( value.toDouble() );
563  break;
564  case QVariant::Int:
565  var = QVariant( value.toInt() );
566  break;
567  case QVariant::LongLong:
568  var = QVariant( value.toLongLong() );
569  break;
570  default: //string type is default
571  var = QVariant( value );
572  break;
573  }
574  Q_ASSERT( mCurrentFeature );
575  mCurrentFeature->setAttribute( att_it.value().first, var );
576  }
577 }
578 
579 int QgsGml::readEpsgFromAttribute( int& epsgNr, const XML_Char** attr ) const
580 {
581  int i = 0;
582  while ( attr[i] != NULL )
583  {
584  if ( strcmp( attr[i], "srsName" ) == 0 )
585  {
586  QString epsgString( attr[i+1] );
587  QString epsgNrString;
588  if ( epsgString.startsWith( "http" ) ) //e.g. geoserver: "http://www.opengis.net/gml/srs/epsg.xml#4326"
589  {
590  epsgNrString = epsgString.section( "#", 1, 1 );
591  }
592  else //e.g. umn mapserver: "EPSG:4326">
593  {
594  epsgNrString = epsgString.section( ":", 1, 1 );
595  }
596  bool conversionOk;
597  int eNr = epsgNrString.toInt( &conversionOk );
598  if ( !conversionOk )
599  {
600  return 1;
601  }
602  epsgNr = eNr;
603  return 0;
604  }
605  ++i;
606  }
607  return 2;
608 }
609 
610 QString QgsGml::readAttribute( const QString& attributeName, const XML_Char** attr ) const
611 {
612  int i = 0;
613  while ( attr[i] != NULL )
614  {
615  if ( attributeName.compare( attr[i] ) == 0 )
616  {
617  return QString::fromUtf8( attr[i+1] );
618  }
619  i += 2;
620  }
621  return QString();
622 }
623 
624 int QgsGml::createBBoxFromCoordinateString( QgsRectangle &r, const QString& coordString ) const
625 {
626  QList<QgsPoint> points;
627  if ( pointsFromCoordinateString( points, coordString ) != 0 )
628  {
629  return 2;
630  }
631 
632  if ( points.size() < 2 )
633  {
634  return 3;
635  }
636 
637  r.set( points[0], points[1] );
638 
639  return 0;
640 }
641 
642 int QgsGml::pointsFromCoordinateString( QList<QgsPoint>& points, const QString& coordString ) const
643 {
644  //tuples are separated by space, x/y by ','
645  QStringList tuples = coordString.split( mTupleSeparator, QString::SkipEmptyParts );
646  QStringList tuples_coordinates;
647  double x, y;
648  bool conversionSuccess;
649 
650  QStringList::const_iterator tupleIterator;
651  for ( tupleIterator = tuples.constBegin(); tupleIterator != tuples.constEnd(); ++tupleIterator )
652  {
653  tuples_coordinates = tupleIterator->split( mCoordinateSeparator, QString::SkipEmptyParts );
654  if ( tuples_coordinates.size() < 2 )
655  {
656  continue;
657  }
658  x = tuples_coordinates.at( 0 ).toDouble( &conversionSuccess );
659  if ( !conversionSuccess )
660  {
661  continue;
662  }
663  y = tuples_coordinates.at( 1 ).toDouble( &conversionSuccess );
664  if ( !conversionSuccess )
665  {
666  continue;
667  }
668  points.push_back( QgsPoint( x, y ) );
669  }
670  return 0;
671 }
672 
673 int QgsGml::pointsFromPosListString( QList<QgsPoint>& points, const QString& coordString, int dimension ) const
674 {
675  // coordinates separated by spaces
676  QStringList coordinates = coordString.split( " ", QString::SkipEmptyParts );
677 
678  if ( coordinates.size() % dimension != 0 )
679  {
680  QgsDebugMsg( "Wrong number of coordinates" );
681  }
682 
683  int ncoor = coordinates.size() / dimension;
684  for ( int i = 0; i < ncoor; i++ )
685  {
686  bool conversionSuccess;
687  double x = coordinates.value( i * dimension ).toDouble( &conversionSuccess );
688  if ( !conversionSuccess )
689  {
690  continue;
691  }
692  double y = coordinates.value( i * dimension + 1 ).toDouble( &conversionSuccess );
693  if ( !conversionSuccess )
694  {
695  continue;
696  }
697  points.append( QgsPoint( x, y ) );
698  }
699  return 0;
700 }
701 
702 int QgsGml::pointsFromString( QList<QgsPoint>& points, const QString& coordString ) const
703 {
704  if ( mCoorMode == QgsGml::coordinate )
705  {
706  return pointsFromCoordinateString( points, coordString );
707  }
708  else if ( mCoorMode == QgsGml::posList )
709  {
710  return pointsFromPosListString( points, coordString, mDimension );
711  }
712  return 1;
713 }
714 
715 int QgsGml::getPointWKB( unsigned char** wkb, int* size, const QgsPoint& point ) const
716 {
717  int wkbSize = 1 + sizeof( int ) + 2 * sizeof( double );
718  *size = wkbSize;
719  *wkb = new unsigned char[wkbSize];
721  double x = point.x();
722  double y = point.y();
723  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
724 
725  memcpy( &( *wkb )[wkbPosition], &mEndian, 1 );
726  wkbPosition += 1;
727  memcpy( &( *wkb )[wkbPosition], &type, sizeof( int ) );
728  wkbPosition += sizeof( int );
729  memcpy( &( *wkb )[wkbPosition], &x, sizeof( double ) );
730  wkbPosition += sizeof( double );
731  memcpy( &( *wkb )[wkbPosition], &y, sizeof( double ) );
732  return 0;
733 }
734 
735 int QgsGml::getLineWKB( unsigned char** wkb, int* size, const QList<QgsPoint>& lineCoordinates ) const
736 {
737  int wkbSize = 1 + 2 * sizeof( int ) + lineCoordinates.size() * 2 * sizeof( double );
738  *size = wkbSize;
739  *wkb = new unsigned char[wkbSize];
741  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
742  double x, y;
743  int nPoints = lineCoordinates.size();
744 
745  //fill the contents into *wkb
746  memcpy( &( *wkb )[wkbPosition], &mEndian, 1 );
747  wkbPosition += 1;
748  memcpy( &( *wkb )[wkbPosition], &type, sizeof( int ) );
749  wkbPosition += sizeof( int );
750  memcpy( &( *wkb )[wkbPosition], &nPoints, sizeof( int ) );
751  wkbPosition += sizeof( int );
752 
753  QList<QgsPoint>::const_iterator iter;
754  for ( iter = lineCoordinates.begin(); iter != lineCoordinates.end(); ++iter )
755  {
756  x = iter->x();
757  y = iter->y();
758  memcpy( &( *wkb )[wkbPosition], &x, sizeof( double ) );
759  wkbPosition += sizeof( double );
760  memcpy( &( *wkb )[wkbPosition], &y, sizeof( double ) );
761  wkbPosition += sizeof( double );
762  }
763  return 0;
764 }
765 
766 int QgsGml::getRingWKB( unsigned char** wkb, int* size, const QList<QgsPoint>& ringCoordinates ) const
767 {
768  int wkbSize = sizeof( int ) + ringCoordinates.size() * 2 * sizeof( double );
769  *size = wkbSize;
770  *wkb = new unsigned char[wkbSize];
771  int wkbPosition = 0; //current offset from wkb beginning (in bytes)
772  double x, y;
773  int nPoints = ringCoordinates.size();
774  memcpy( &( *wkb )[wkbPosition], &nPoints, sizeof( int ) );
775  wkbPosition += sizeof( int );
776 
777  QList<QgsPoint>::const_iterator iter;
778  for ( iter = ringCoordinates.begin(); iter != ringCoordinates.end(); ++iter )
779  {
780  x = iter->x();
781  y = iter->y();
782  memcpy( &( *wkb )[wkbPosition], &x, sizeof( double ) );
783  wkbPosition += sizeof( double );
784  memcpy( &( *wkb )[wkbPosition], &y, sizeof( double ) );
785  wkbPosition += sizeof( double );
786  }
787  return 0;
788 }
789 
790 int QgsGml::createMultiLineFromFragments()
791 {
792  mCurrentWKBSize = 0;
793  mCurrentWKBSize += 1 + 2 * sizeof( int );
794  mCurrentWKBSize += totalWKBFragmentSize();
795 
796  mCurrentWKB = new unsigned char[mCurrentWKBSize];
797  int pos = 0;
799  int numLines = mCurrentWKBFragments.begin()->size();
800  //add endian
801  memcpy( &( mCurrentWKB[pos] ), &mEndian, 1 );
802  pos += 1;
803  memcpy( &( mCurrentWKB[pos] ), &type, sizeof( int ) );
804  pos += sizeof( int );
805  memcpy( &( mCurrentWKB[pos] ), &numLines, sizeof( int ) );
806  pos += sizeof( int );
807  QList<unsigned char*>::iterator wkbIt = mCurrentWKBFragments.begin()->begin();
808  QList<int>::iterator sizeIt = mCurrentWKBFragmentSizes.begin()->begin();
809 
810  //copy (and delete) all the wkb fragments
811  for ( ; wkbIt != mCurrentWKBFragments.begin()->end(); ++wkbIt, ++sizeIt )
812  {
813  memcpy( &( mCurrentWKB[pos] ), *wkbIt, *sizeIt );
814  pos += *sizeIt;
815  delete[] *wkbIt;
816  }
817 
818  mCurrentWKBFragments.clear();
819  mCurrentWKBFragmentSizes.clear();
820  *mWkbType = QGis::WKBMultiLineString;
821  return 0;
822 }
823 
824 int QgsGml::createMultiPointFromFragments()
825 {
826  mCurrentWKBSize = 0;
827  mCurrentWKBSize += 1 + 2 * sizeof( int );
828  mCurrentWKBSize += totalWKBFragmentSize();
829  mCurrentWKB = new unsigned char[mCurrentWKBSize];
830 
831  int pos = 0;
833  int numPoints = mCurrentWKBFragments.begin()->size();
834 
835  memcpy( &( mCurrentWKB[pos] ), &mEndian, 1 );
836  pos += 1;
837  memcpy( &( mCurrentWKB[pos] ), &type, sizeof( int ) );
838  pos += sizeof( int );
839  memcpy( &( mCurrentWKB[pos] ), &numPoints, sizeof( int ) );
840  pos += sizeof( int );
841 
842  QList<unsigned char*>::iterator wkbIt = mCurrentWKBFragments.begin()->begin();
843  QList<int>::iterator sizeIt = mCurrentWKBFragmentSizes.begin()->begin();
844 
845  for ( ; wkbIt != mCurrentWKBFragments.begin()->end(); ++wkbIt, ++sizeIt )
846  {
847  memcpy( &( mCurrentWKB[pos] ), *wkbIt, *sizeIt );
848  pos += *sizeIt;
849  delete[] *wkbIt;
850  }
851 
852  mCurrentWKBFragments.clear();
853  mCurrentWKBFragmentSizes.clear();
854  *mWkbType = QGis::WKBMultiPoint;
855  return 0;
856 }
857 
858 
859 int QgsGml::createPolygonFromFragments()
860 {
861  mCurrentWKBSize = 0;
862  mCurrentWKBSize += 1 + 2 * sizeof( int );
863  mCurrentWKBSize += totalWKBFragmentSize();
864 
865  mCurrentWKB = new unsigned char[mCurrentWKBSize];
866  int pos = 0;
868  int numRings = mCurrentWKBFragments.begin()->size();
869  memcpy( &( mCurrentWKB[pos] ), &mEndian, 1 );
870  pos += 1;
871  memcpy( &( mCurrentWKB[pos] ), &type, sizeof( int ) );
872  pos += sizeof( int );
873  memcpy( &( mCurrentWKB[pos] ), &numRings, sizeof( int ) );
874  pos += sizeof( int );
875 
876  QList<unsigned char*>::iterator wkbIt = mCurrentWKBFragments.begin()->begin();
877  QList<int>::iterator sizeIt = mCurrentWKBFragmentSizes.begin()->begin();
878  for ( ; wkbIt != mCurrentWKBFragments.begin()->end(); ++wkbIt, ++sizeIt )
879  {
880  memcpy( &( mCurrentWKB[pos] ), *wkbIt, *sizeIt );
881  pos += *sizeIt;
882  delete[] *wkbIt;
883  }
884 
885  mCurrentWKBFragments.clear();
886  mCurrentWKBFragmentSizes.clear();
887  *mWkbType = QGis::WKBPolygon;
888  return 0;
889 }
890 
891 int QgsGml::createMultiPolygonFromFragments()
892 {
893  mCurrentWKBSize = 0;
894  mCurrentWKBSize += 1 + 2 * sizeof( int );
895  mCurrentWKBSize += totalWKBFragmentSize();
896  mCurrentWKBSize += mCurrentWKBFragments.size() * ( 1 + 2 * sizeof( int ) ); //fragments are just the rings
897 
898  mCurrentWKB = new unsigned char[mCurrentWKBSize];
899  int pos = 0;
901  QGis::WkbType polygonType = QGis::WKBPolygon;
902  int numPolys = mCurrentWKBFragments.size();
903  int numRings;
904  memcpy( &( mCurrentWKB[pos] ), &mEndian, 1 );
905  pos += 1;
906  memcpy( &( mCurrentWKB[pos] ), &type, sizeof( int ) );
907  pos += sizeof( int );
908  memcpy( &( mCurrentWKB[pos] ), &numPolys, sizeof( int ) );
909  pos += sizeof( int );
910 
911  //have outer and inner iterators
912  QList< QList<unsigned char*> >::iterator outerWkbIt;
913  QList< QList<int> >::iterator outerSizeIt;
914  QList< unsigned char* >::iterator innerWkbIt;
915  QList< int >::iterator innerSizeIt;
916 
917  outerWkbIt = mCurrentWKBFragments.begin();
918  outerSizeIt = mCurrentWKBFragmentSizes.begin();
919 
920  for ( ; outerWkbIt != mCurrentWKBFragments.end(); ++outerWkbIt, ++outerSizeIt )
921  {
922  //new polygon
923  memcpy( &( mCurrentWKB[pos] ), &mEndian, 1 );
924  pos += 1;
925  memcpy( &( mCurrentWKB[pos] ), &polygonType, sizeof( int ) );
926  pos += sizeof( int );
927  numRings = outerWkbIt->size();
928  memcpy( &( mCurrentWKB[pos] ), &numRings, sizeof( int ) );
929  pos += sizeof( int );
930 
931  innerWkbIt = outerWkbIt->begin();
932  innerSizeIt = outerSizeIt->begin();
933  for ( ; innerWkbIt != outerWkbIt->end(); ++innerWkbIt, ++innerSizeIt )
934  {
935  memcpy( &( mCurrentWKB[pos] ), *innerWkbIt, *innerSizeIt );
936  pos += *innerSizeIt;
937  delete[] *innerWkbIt;
938  }
939  }
940 
941  mCurrentWKBFragments.clear();
942  mCurrentWKBFragmentSizes.clear();
943  *mWkbType = QGis::WKBMultiPolygon;
944  return 0;
945 }
946 
947 int QgsGml::totalWKBFragmentSize() const
948 {
949  int result = 0;
950  foreach ( const QList<int> &list, mCurrentWKBFragmentSizes )
951  {
952  foreach ( int i, list )
953  {
954  result += i;
955  }
956  }
957  return result;
958 }
959 
960 void QgsGml::calculateExtentFromFeatures()
961 {
962  if ( mFeatures.size() < 1 )
963  {
964  return;
965  }
966 
967  QgsFeature* currentFeature = 0;
968  QgsGeometry* currentGeometry = 0;
969  bool bboxInitialised = false; //gets true once bbox has been set to the first geometry
970 
971  for ( int i = 0; i < mFeatures.size(); ++i )
972  {
973  currentFeature = mFeatures[i];
974  if ( !currentFeature )
975  {
976  continue;
977  }
978  currentGeometry = currentFeature->geometry();
979  if ( currentGeometry )
980  {
981  if ( !bboxInitialised )
982  {
983  mExtent = currentGeometry->boundingBox();
984  bboxInitialised = true;
985  }
986  else
987  {
988  mExtent.unionRect( currentGeometry->boundingBox() );
989  }
990  }
991  }
992 }
993 
995 {
997  if ( mEpsg != 0 )
998  {
999  crs.createFromOgcWmsCrs( QString( "EPSG:%1" ).arg( mEpsg ) );
1000  }
1001  return crs;
1002 }