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