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