Branch data Line data Source code
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"
18 : : #include "qgscoordinatereferencesystem.h"
19 : : #include "qgsgeometry.h"
20 : : #include "qgslogger.h"
21 : : #include "qgsmessagelog.h"
22 : : #include "qgsnetworkaccessmanager.h"
23 : : #include "qgswkbptr.h"
24 : : #include "qgsogrutils.h"
25 : : #include "qgsapplication.h"
26 : : #include <QBuffer>
27 : : #include <QList>
28 : : #include <QNetworkRequest>
29 : : #include <QNetworkReply>
30 : : #include <QProgressDialog>
31 : : #include <QSet>
32 : : #include <QSettings>
33 : : #include <QUrl>
34 : :
35 : : #include "ogr_api.h"
36 : :
37 : : #include <limits>
38 : :
39 : : static const char NS_SEPARATOR = '?';
40 : : static const char *GML_NAMESPACE = "http://www.opengis.net/gml";
41 : : static const char *GML32_NAMESPACE = "http://www.opengis.net/gml/3.2";
42 : :
43 : 0 : QgsGml::QgsGml(
44 : : const QString &typeName,
45 : : const QString &geometryAttribute,
46 : : const QgsFields &fields )
47 : 0 : : mParser( typeName, geometryAttribute, fields )
48 : 0 : , mTypeName( typeName )
49 : 0 : , mFinished( false )
50 : 0 : {
51 : 0 : int index = mTypeName.indexOf( ':' );
52 : 0 : if ( index != -1 && index < mTypeName.length() )
53 : : {
54 : 0 : mTypeName = mTypeName.mid( index + 1 );
55 : 0 : }
56 : 0 : }
57 : :
58 : 0 : int QgsGml::getFeatures( const QString &uri, QgsWkbTypes::Type *wkbType, QgsRectangle *extent, const QString &userName, const QString &password, const QString &authcfg )
59 : : {
60 : : //start with empty extent
61 : 0 : mExtent.setMinimal();
62 : :
63 : 0 : QNetworkRequest request( uri );
64 : 0 : QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsGml" ) );
65 : :
66 : 0 : if ( !authcfg.isEmpty() )
67 : : {
68 : 0 : if ( !QgsApplication::authManager()->updateNetworkRequest( request, authcfg ) )
69 : : {
70 : 0 : QgsMessageLog::logMessage(
71 : 0 : tr( "GML Getfeature network request update failed for authcfg %1" ).arg( authcfg ),
72 : 0 : tr( "Network" ),
73 : : Qgis::Critical
74 : : );
75 : 0 : return 1;
76 : : }
77 : 0 : }
78 : 0 : else if ( !userName.isNull() || !password.isNull() )
79 : : {
80 : 0 : request.setRawHeader( "Authorization", "Basic " + QStringLiteral( "%1:%2" ).arg( userName, password ).toLatin1().toBase64() );
81 : 0 : }
82 : 0 : QNetworkReply *reply = QgsNetworkAccessManager::instance()->get( request );
83 : :
84 : 0 : if ( !authcfg.isEmpty() )
85 : : {
86 : 0 : if ( !QgsApplication::authManager()->updateNetworkReply( reply, authcfg ) )
87 : : {
88 : 0 : reply->deleteLater();
89 : 0 : QgsMessageLog::logMessage(
90 : 0 : tr( "GML Getfeature network reply update failed for authcfg %1" ).arg( authcfg ),
91 : 0 : tr( "Network" ),
92 : : Qgis::Critical
93 : : );
94 : 0 : return 1;
95 : : }
96 : 0 : }
97 : :
98 : 0 : connect( reply, &QNetworkReply::finished, this, &QgsGml::setFinished );
99 : 0 : connect( reply, &QNetworkReply::downloadProgress, this, &QgsGml::handleProgressEvent );
100 : :
101 : : //find out if there is a QGIS main window. If yes, display a progress dialog
102 : 0 : QProgressDialog *progressDialog = nullptr;
103 : 0 : QWidget *mainWindow = nullptr;
104 : 0 : QWidgetList topLevelWidgets = qApp->topLevelWidgets();
105 : 0 : for ( QWidgetList::const_iterator it = topLevelWidgets.constBegin(); it != topLevelWidgets.constEnd(); ++it )
106 : : {
107 : 0 : if ( ( *it )->objectName() == QLatin1String( "QgisApp" ) )
108 : : {
109 : 0 : mainWindow = *it;
110 : 0 : break;
111 : : }
112 : 0 : }
113 : 0 : if ( mainWindow )
114 : : {
115 : 0 : progressDialog = new QProgressDialog( tr( "Loading GML data\n%1" ).arg( mTypeName ), tr( "Abort" ), 0, 0, mainWindow );
116 : 0 : progressDialog->setWindowModality( Qt::ApplicationModal );
117 : 0 : connect( this, &QgsGml::dataReadProgress, progressDialog, &QProgressDialog::setValue );
118 : 0 : connect( this, &QgsGml::totalStepsUpdate, progressDialog, &QProgressDialog::setMaximum );
119 : 0 : connect( progressDialog, &QProgressDialog::canceled, this, &QgsGml::setFinished );
120 : 0 : progressDialog->show();
121 : 0 : }
122 : :
123 : 0 : int atEnd = 0;
124 : 0 : while ( !atEnd )
125 : : {
126 : 0 : if ( mFinished )
127 : : {
128 : 0 : atEnd = 1;
129 : 0 : }
130 : 0 : QByteArray readData = reply->readAll();
131 : 0 : if ( !readData.isEmpty() )
132 : : {
133 : 0 : QString errorMsg;
134 : 0 : if ( !mParser.processData( readData, atEnd, errorMsg ) )
135 : 0 : QgsMessageLog::logMessage( errorMsg, QObject::tr( "WFS" ) );
136 : :
137 : 0 : }
138 : 0 : QCoreApplication::processEvents();
139 : 0 : }
140 : :
141 : 0 : fillMapsFromParser();
142 : :
143 : 0 : QNetworkReply::NetworkError replyError = reply->error();
144 : 0 : QString replyErrorString = reply->errorString();
145 : :
146 : 0 : delete reply;
147 : 0 : delete progressDialog;
148 : :
149 : 0 : if ( replyError )
150 : : {
151 : 0 : QgsMessageLog::logMessage(
152 : 0 : tr( "GML Getfeature network request failed with error: %1" ).arg( replyErrorString ),
153 : 0 : tr( "Network" ),
154 : : Qgis::Critical
155 : : );
156 : 0 : return 1;
157 : : }
158 : :
159 : 0 : *wkbType = mParser.wkbType();
160 : :
161 : 0 : if ( *wkbType != QgsWkbTypes::Unknown )
162 : : {
163 : 0 : if ( mExtent.isEmpty() )
164 : : {
165 : : //reading of bbox from the server failed, so we calculate it less efficiently by evaluating the features
166 : 0 : calculateExtentFromFeatures();
167 : 0 : }
168 : 0 : }
169 : :
170 : 0 : if ( extent )
171 : 0 : *extent = mExtent;
172 : :
173 : 0 : return 0;
174 : 0 : }
175 : :
176 : 0 : int QgsGml::getFeatures( const QByteArray &data, QgsWkbTypes::Type *wkbType, QgsRectangle *extent )
177 : : {
178 : 0 : mExtent.setMinimal();
179 : :
180 : 0 : QString errorMsg;
181 : 0 : if ( !mParser.processData( data, true /* atEnd */, errorMsg ) )
182 : 0 : QgsMessageLog::logMessage( errorMsg, QObject::tr( "WFS" ) );
183 : :
184 : 0 : fillMapsFromParser();
185 : :
186 : 0 : *wkbType = mParser.wkbType();
187 : :
188 : 0 : if ( extent )
189 : 0 : *extent = mExtent;
190 : :
191 : : return 0;
192 : 0 : }
193 : :
194 : 0 : void QgsGml::fillMapsFromParser()
195 : : {
196 : 0 : QVector<QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair> features = mParser.getAndStealReadyFeatures();
197 : 0 : const auto constFeatures = features;
198 : 0 : for ( const QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair &featPair : constFeatures )
199 : : {
200 : 0 : QgsFeature *feat = featPair.first;
201 : 0 : const QString &gmlId = featPair.second;
202 : 0 : mFeatures.insert( feat->id(), feat );
203 : 0 : if ( !gmlId.isEmpty() )
204 : : {
205 : 0 : mIdMap.insert( feat->id(), gmlId );
206 : 0 : }
207 : : }
208 : 0 : }
209 : :
210 : 0 : void QgsGml::setFinished()
211 : : {
212 : 0 : mFinished = true;
213 : 0 : }
214 : :
215 : 0 : void QgsGml::handleProgressEvent( qint64 progress, qint64 totalSteps )
216 : : {
217 : 0 : if ( totalSteps < 0 )
218 : : {
219 : 0 : totalSteps = 0;
220 : 0 : progress = 0;
221 : 0 : }
222 : 0 : emit totalStepsUpdate( totalSteps );
223 : 0 : emit dataReadProgress( progress );
224 : 0 : emit dataProgressAndSteps( progress, totalSteps );
225 : 0 : }
226 : :
227 : 0 : void QgsGml::calculateExtentFromFeatures()
228 : : {
229 : 0 : if ( mFeatures.empty() )
230 : : {
231 : 0 : return;
232 : : }
233 : :
234 : 0 : QgsFeature *currentFeature = nullptr;
235 : 0 : QgsGeometry currentGeometry;
236 : 0 : bool bboxInitialized = false; //gets true once bbox has been set to the first geometry
237 : :
238 : 0 : for ( int i = 0; i < mFeatures.size(); ++i )
239 : : {
240 : 0 : currentFeature = mFeatures[i];
241 : 0 : if ( !currentFeature )
242 : : {
243 : 0 : continue;
244 : : }
245 : 0 : currentGeometry = currentFeature->geometry();
246 : 0 : if ( !currentGeometry.isNull() )
247 : : {
248 : 0 : if ( !bboxInitialized )
249 : : {
250 : 0 : mExtent = currentGeometry.boundingBox();
251 : 0 : bboxInitialized = true;
252 : 0 : }
253 : : else
254 : : {
255 : 0 : mExtent.combineExtentWith( currentGeometry.boundingBox() );
256 : : }
257 : 0 : }
258 : 0 : }
259 : 0 : }
260 : :
261 : 0 : QgsCoordinateReferenceSystem QgsGml::crs() const
262 : : {
263 : 0 : QgsCoordinateReferenceSystem crs;
264 : 0 : if ( mParser.getEPSGCode() != 0 )
265 : : {
266 : 0 : crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( mParser.getEPSGCode() ) );
267 : 0 : }
268 : 0 : return crs;
269 : 0 : }
270 : :
271 : :
272 : :
273 : :
274 : :
275 : 0 : QgsGmlStreamingParser::QgsGmlStreamingParser( const QString &typeName,
276 : : const QString &geometryAttribute,
277 : : const QgsFields &fields,
278 : : AxisOrientationLogic axisOrientationLogic,
279 : 0 : bool invertAxisOrientation )
280 : 0 : : mTypeName( typeName )
281 : 0 : , mTypeNameBA( mTypeName.toUtf8() )
282 : 0 : , mTypeNamePtr( mTypeNameBA.constData() )
283 : 0 : , mTypeNameUTF8Len( strlen( mTypeNamePtr ) )
284 : 0 : , mWkbType( QgsWkbTypes::Unknown )
285 : 0 : , mGeometryAttribute( geometryAttribute )
286 : 0 : , mGeometryAttributeBA( geometryAttribute.toUtf8() )
287 : 0 : , mGeometryAttributePtr( mGeometryAttributeBA.constData() )
288 : 0 : , mGeometryAttributeUTF8Len( strlen( mGeometryAttributePtr ) )
289 : 0 : , mFields( fields )
290 : 0 : , mIsException( false )
291 : 0 : , mTruncatedResponse( false )
292 : 0 : , mParseDepth( 0 )
293 : 0 : , mFeatureTupleDepth( 0 )
294 : 0 : , mFeatureCount( 0 )
295 : 0 : , mCurrentWKB( nullptr, 0 )
296 : 0 : , mBoundedByNullFound( false )
297 : 0 : , mDimension( 0 )
298 : 0 : , mCoorMode( Coordinate )
299 : 0 : , mEpsg( 0 )
300 : 0 : , mAxisOrientationLogic( axisOrientationLogic )
301 : 0 : , mInvertAxisOrientationRequest( invertAxisOrientation )
302 : 0 : , mInvertAxisOrientation( invertAxisOrientation )
303 : 0 : , mNumberReturned( -1 )
304 : 0 : , mNumberMatched( -1 )
305 : 0 : , mFoundUnhandledGeometryElement( false )
306 : : {
307 : 0 : mThematicAttributes.clear();
308 : 0 : for ( int i = 0; i < fields.size(); i++ )
309 : : {
310 : 0 : mThematicAttributes.insert( fields.at( i ).name(), qMakePair( i, fields.at( i ) ) );
311 : 0 : }
312 : :
313 : 0 : mEndian = QgsApplication::endian();
314 : :
315 : 0 : int index = mTypeName.indexOf( ':' );
316 : 0 : if ( index != -1 && index < mTypeName.length() )
317 : : {
318 : 0 : mTypeName = mTypeName.mid( index + 1 );
319 : 0 : mTypeNameBA = mTypeName.toUtf8();
320 : 0 : mTypeNamePtr = mTypeNameBA.constData();
321 : 0 : mTypeNameUTF8Len = strlen( mTypeNamePtr );
322 : 0 : }
323 : :
324 : 0 : mParser = XML_ParserCreateNS( nullptr, NS_SEPARATOR );
325 : 0 : XML_SetUserData( mParser, this );
326 : 0 : XML_SetElementHandler( mParser, QgsGmlStreamingParser::start, QgsGmlStreamingParser::end );
327 : 0 : XML_SetCharacterDataHandler( mParser, QgsGmlStreamingParser::chars );
328 : 0 : }
329 : :
330 : 0 : static QString stripNS( const QString &string )
331 : : {
332 : 0 : int index = string.indexOf( ':' );
333 : 0 : if ( index != -1 && index < string.length() )
334 : : {
335 : 0 : return string.mid( index + 1 );
336 : : }
337 : 0 : return string;
338 : 0 : }
339 : :
340 : 0 : QgsGmlStreamingParser::QgsGmlStreamingParser( const QList<LayerProperties> &layerProperties,
341 : : const QgsFields &fields,
342 : : const QMap< QString, QPair<QString, QString> > &mapFieldNameToSrcLayerNameFieldName,
343 : : AxisOrientationLogic axisOrientationLogic,
344 : : bool invertAxisOrientation )
345 : 0 : : mLayerProperties( layerProperties )
346 : 0 : , mTypeNameUTF8Len( 0 )
347 : 0 : , mWkbType( QgsWkbTypes::Unknown )
348 : 0 : , mGeometryAttributeUTF8Len( 0 )
349 : 0 : , mFields( fields )
350 : 0 : , mIsException( false )
351 : 0 : , mTruncatedResponse( false )
352 : 0 : , mParseDepth( 0 )
353 : 0 : , mFeatureTupleDepth( 0 )
354 : 0 : , mFeatureCount( 0 )
355 : 0 : , mCurrentWKB( nullptr, 0 )
356 : 0 : , mBoundedByNullFound( false )
357 : 0 : , mDimension( 0 )
358 : 0 : , mCoorMode( Coordinate )
359 : 0 : , mEpsg( 0 )
360 : 0 : , mAxisOrientationLogic( axisOrientationLogic )
361 : 0 : , mInvertAxisOrientationRequest( invertAxisOrientation )
362 : 0 : , mInvertAxisOrientation( invertAxisOrientation )
363 : 0 : , mNumberReturned( -1 )
364 : 0 : , mNumberMatched( -1 )
365 : 0 : , mFoundUnhandledGeometryElement( false )
366 : : {
367 : 0 : mThematicAttributes.clear();
368 : 0 : for ( int i = 0; i < fields.size(); i++ )
369 : : {
370 : 0 : QMap< QString, QPair<QString, QString> >::const_iterator att_it = mapFieldNameToSrcLayerNameFieldName.constFind( fields.at( i ).name() );
371 : 0 : if ( att_it != mapFieldNameToSrcLayerNameFieldName.constEnd() )
372 : : {
373 : 0 : if ( mLayerProperties.size() == 1 )
374 : 0 : mThematicAttributes.insert( att_it.value().second, qMakePair( i, fields.at( i ) ) );
375 : : else
376 : 0 : mThematicAttributes.insert( stripNS( att_it.value().first ) + "|" + att_it.value().second, qMakePair( i, fields.at( i ) ) );
377 : 0 : }
378 : 0 : }
379 : 0 : bool alreadyFoundGeometry = false;
380 : 0 : for ( int i = 0; i < mLayerProperties.size(); i++ )
381 : : {
382 : : // We only support one geometry field per feature
383 : 0 : if ( !mLayerProperties[i].mGeometryAttribute.isEmpty() )
384 : : {
385 : 0 : if ( alreadyFoundGeometry )
386 : : {
387 : 0 : QgsDebugMsg( QStringLiteral( "Will ignore geometry field %1 from typename %2" ).
388 : : arg( mLayerProperties[i].mGeometryAttribute, mLayerProperties[i].mName ) );
389 : 0 : mLayerProperties[i].mGeometryAttribute.clear();
390 : 0 : }
391 : 0 : alreadyFoundGeometry = true;
392 : 0 : }
393 : 0 : mMapTypeNameToProperties.insert( stripNS( mLayerProperties[i].mName ), mLayerProperties[i] );
394 : 0 : }
395 : :
396 : 0 : if ( mLayerProperties.size() == 1 )
397 : : {
398 : 0 : mTypeName = mLayerProperties[0].mName;
399 : 0 : mGeometryAttribute = mLayerProperties[0].mGeometryAttribute;
400 : 0 : mGeometryAttributeBA = mGeometryAttribute.toUtf8();
401 : 0 : mGeometryAttributePtr = mGeometryAttributeBA.constData();
402 : 0 : mGeometryAttributeUTF8Len = strlen( mGeometryAttributePtr );
403 : 0 : int index = mTypeName.indexOf( ':' );
404 : 0 : if ( index != -1 && index < mTypeName.length() )
405 : : {
406 : 0 : mTypeName = mTypeName.mid( index + 1 );
407 : 0 : }
408 : 0 : mTypeNameBA = mTypeName.toUtf8();
409 : 0 : mTypeNamePtr = mTypeNameBA.constData();
410 : 0 : mTypeNameUTF8Len = strlen( mTypeNamePtr );
411 : 0 : }
412 : :
413 : 0 : mEndian = QgsApplication::endian();
414 : :
415 : 0 : mParser = XML_ParserCreateNS( nullptr, NS_SEPARATOR );
416 : 0 : XML_SetUserData( mParser, this );
417 : 0 : XML_SetElementHandler( mParser, QgsGmlStreamingParser::start, QgsGmlStreamingParser::end );
418 : 0 : XML_SetCharacterDataHandler( mParser, QgsGmlStreamingParser::chars );
419 : 0 : }
420 : :
421 : :
422 : 0 : QgsGmlStreamingParser::~QgsGmlStreamingParser()
423 : : {
424 : 0 : XML_ParserFree( mParser );
425 : :
426 : : // Normally a sane user of this class should have consumed everything...
427 : 0 : const auto constMFeatureList = mFeatureList;
428 : 0 : for ( QgsGmlFeaturePtrGmlIdPair featPair : constMFeatureList )
429 : : {
430 : 0 : delete featPair.first;
431 : 0 : }
432 : :
433 : 0 : delete mCurrentFeature;
434 : 0 : }
435 : :
436 : 0 : bool QgsGmlStreamingParser::processData( const QByteArray &data, bool atEnd )
437 : : {
438 : 0 : QString errorMsg;
439 : 0 : if ( !processData( data, atEnd, errorMsg ) )
440 : : {
441 : 0 : QgsMessageLog::logMessage( errorMsg, QObject::tr( "WFS" ) );
442 : 0 : return false;
443 : : }
444 : 0 : return true;
445 : 0 : }
446 : :
447 : 0 : bool QgsGmlStreamingParser::processData( const QByteArray &data, bool atEnd, QString &errorMsg )
448 : : {
449 : 0 : if ( XML_Parse( mParser, data.data(), data.size(), atEnd ) == 0 )
450 : : {
451 : 0 : XML_Error errorCode = XML_GetErrorCode( mParser );
452 : 0 : errorMsg = QObject::tr( "Error: %1 on line %2, column %3" )
453 : 0 : .arg( XML_ErrorString( errorCode ) )
454 : 0 : .arg( XML_GetCurrentLineNumber( mParser ) )
455 : 0 : .arg( XML_GetCurrentColumnNumber( mParser ) );
456 : :
457 : 0 : return false;
458 : : }
459 : :
460 : 0 : return true;
461 : 0 : }
462 : :
463 : 0 : QVector<QgsGmlStreamingParser::QgsGmlFeaturePtrGmlIdPair> QgsGmlStreamingParser::getAndStealReadyFeatures()
464 : : {
465 : 0 : QVector<QgsGmlFeaturePtrGmlIdPair> ret = mFeatureList;
466 : 0 : mFeatureList.clear();
467 : 0 : return ret;
468 : 0 : }
469 : :
470 : : #define LOCALNAME_EQUALS(string_constant) \
471 : : ( localNameLen == static_cast<int>(strlen( string_constant )) && memcmp(pszLocalName, string_constant, localNameLen) == 0 )
472 : :
473 : 0 : void QgsGmlStreamingParser::startElement( const XML_Char *el, const XML_Char **attr )
474 : : {
475 : 0 : const int elLen = static_cast<int>( strlen( el ) );
476 : 0 : const char *pszSep = strchr( el, NS_SEPARATOR );
477 : 0 : const char *pszLocalName = ( pszSep ) ? pszSep + 1 : el;
478 : 0 : const int nsLen = ( pszSep ) ? ( int )( pszSep - el ) : 0;
479 : 0 : const int localNameLen = ( pszSep ) ? ( int )( elLen - nsLen ) - 1 : elLen;
480 : 0 : ParseMode parseMode( mParseModeStack.isEmpty() ? None : mParseModeStack.top() );
481 : 0 : int elDimension = 0;
482 : :
483 : : // Figure out if the GML namespace is GML_NAMESPACE or GML32_NAMESPACE
484 : 0 : if ( !mGMLNameSpaceURIPtr && pszSep )
485 : : {
486 : 0 : if ( nsLen == static_cast<int>( strlen( GML_NAMESPACE ) ) && memcmp( el, GML_NAMESPACE, nsLen ) == 0 )
487 : : {
488 : 0 : mGMLNameSpaceURI = GML_NAMESPACE;
489 : 0 : mGMLNameSpaceURIPtr = GML_NAMESPACE;
490 : 0 : }
491 : 0 : else if ( nsLen == static_cast<int>( strlen( GML32_NAMESPACE ) ) && memcmp( el, GML32_NAMESPACE, nsLen ) == 0 )
492 : : {
493 : 0 : mGMLNameSpaceURI = GML32_NAMESPACE;
494 : 0 : mGMLNameSpaceURIPtr = GML32_NAMESPACE;
495 : 0 : }
496 : 0 : }
497 : :
498 : 0 : const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
499 : 0 : bool isGeom = false;
500 : :
501 : 0 : if ( parseMode == Geometry || parseMode == Coordinate || parseMode == PosList ||
502 : 0 : parseMode == MultiPoint || parseMode == MultiLine || parseMode == MultiPolygon )
503 : : {
504 : 0 : mGeometryString.append( "<", 1 );
505 : 0 : mGeometryString.append( pszLocalName, localNameLen );
506 : 0 : mGeometryString.append( " ", 1 );
507 : 0 : for ( const XML_Char **attrIter = attr; attrIter && *attrIter; attrIter += 2 )
508 : : {
509 : 0 : const size_t nAttrLen = strlen( attrIter[0] );
510 : 0 : const size_t GML32_NAMESPACE_LEN = strlen( GML32_NAMESPACE );
511 : 0 : const size_t GML_NAMESPACE_LEN = strlen( GML_NAMESPACE );
512 : 0 : if ( nAttrLen > GML32_NAMESPACE_LEN &&
513 : 0 : attrIter[0][GML32_NAMESPACE_LEN] == '?' &&
514 : 0 : memcmp( attrIter[0], GML32_NAMESPACE, GML32_NAMESPACE_LEN ) == 0 )
515 : : {
516 : 0 : mGeometryString.append( "gml:" );
517 : 0 : mGeometryString.append( attrIter[0] + GML32_NAMESPACE_LEN + 1 );
518 : 0 : }
519 : 0 : else if ( nAttrLen > GML_NAMESPACE_LEN &&
520 : 0 : attrIter[0][GML_NAMESPACE_LEN] == '?' &&
521 : 0 : memcmp( attrIter[0], GML_NAMESPACE, GML_NAMESPACE_LEN ) == 0 )
522 : : {
523 : 0 : mGeometryString.append( "gml:" );
524 : 0 : mGeometryString.append( attrIter[0] + GML_NAMESPACE_LEN + 1 );
525 : 0 : }
526 : : else
527 : : {
528 : 0 : mGeometryString.append( attrIter[0] );
529 : : }
530 : 0 : mGeometryString.append( "=\"", 2 );
531 : 0 : mGeometryString.append( attrIter[1] );
532 : 0 : mGeometryString.append( "\" ", 2 );
533 : :
534 : 0 : }
535 : 0 : mGeometryString.append( ">", 1 );
536 : 0 : }
537 : :
538 : 0 : if ( isGMLNS && LOCALNAME_EQUALS( "coordinates" ) )
539 : : {
540 : 0 : mParseModeStack.push( Coordinate );
541 : 0 : mCoorMode = QgsGmlStreamingParser::Coordinate;
542 : 0 : mStringCash.clear();
543 : 0 : mCoordinateSeparator = readAttribute( QStringLiteral( "cs" ), attr );
544 : 0 : if ( mCoordinateSeparator.isEmpty() )
545 : : {
546 : 0 : mCoordinateSeparator = ',';
547 : 0 : }
548 : 0 : mTupleSeparator = readAttribute( QStringLiteral( "ts" ), attr );
549 : 0 : if ( mTupleSeparator.isEmpty() )
550 : : {
551 : 0 : mTupleSeparator = ' ';
552 : 0 : }
553 : 0 : }
554 : 0 : else if ( isGMLNS &&
555 : 0 : ( LOCALNAME_EQUALS( "pos" ) || LOCALNAME_EQUALS( "posList" ) ) )
556 : : {
557 : 0 : mParseModeStack.push( QgsGmlStreamingParser::PosList );
558 : 0 : mCoorMode = QgsGmlStreamingParser::PosList;
559 : 0 : mStringCash.clear();
560 : 0 : if ( elDimension == 0 )
561 : : {
562 : 0 : QString srsDimension = readAttribute( QStringLiteral( "srsDimension" ), attr );
563 : : bool ok;
564 : 0 : int dimension = srsDimension.toInt( &ok );
565 : 0 : if ( ok )
566 : : {
567 : 0 : elDimension = dimension;
568 : 0 : }
569 : 0 : }
570 : 0 : }
571 : 0 : else if ( ( parseMode == Feature || parseMode == FeatureTuple ) &&
572 : 0 : mCurrentFeature &&
573 : 0 : localNameLen == static_cast<int>( mGeometryAttributeUTF8Len ) &&
574 : 0 : memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
575 : : {
576 : 0 : mParseModeStack.push( QgsGmlStreamingParser::Geometry );
577 : 0 : mFoundUnhandledGeometryElement = false;
578 : 0 : mGeometryString.clear();
579 : 0 : }
580 : : //else if ( mParseModeStack.size() == 0 && elementName == mGMLNameSpaceURI + NS_SEPARATOR + "boundedBy" )
581 : 0 : else if ( isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
582 : : {
583 : 0 : mParseModeStack.push( QgsGmlStreamingParser::BoundingBox );
584 : 0 : mCurrentExtent = QgsRectangle();
585 : 0 : mBoundedByNullFound = false;
586 : 0 : }
587 : 0 : else if ( parseMode == BoundingBox &&
588 : 0 : isGMLNS && LOCALNAME_EQUALS( "null" ) )
589 : : {
590 : 0 : mParseModeStack.push( QgsGmlStreamingParser::Null );
591 : 0 : mBoundedByNullFound = true;
592 : 0 : }
593 : 0 : else if ( parseMode == BoundingBox &&
594 : 0 : isGMLNS && LOCALNAME_EQUALS( "Envelope" ) )
595 : : {
596 : 0 : isGeom = true;
597 : 0 : mParseModeStack.push( QgsGmlStreamingParser::Envelope );
598 : 0 : }
599 : 0 : else if ( parseMode == Envelope &&
600 : 0 : isGMLNS && LOCALNAME_EQUALS( "lowerCorner" ) )
601 : : {
602 : 0 : mParseModeStack.push( QgsGmlStreamingParser::LowerCorner );
603 : 0 : mStringCash.clear();
604 : 0 : }
605 : 0 : else if ( parseMode == Envelope &&
606 : 0 : isGMLNS && LOCALNAME_EQUALS( "upperCorner" ) )
607 : : {
608 : 0 : mParseModeStack.push( QgsGmlStreamingParser::UpperCorner );
609 : 0 : mStringCash.clear();
610 : 0 : }
611 : 0 : else if ( parseMode == None && !mTypeNamePtr &&
612 : 0 : LOCALNAME_EQUALS( "Tuple" ) )
613 : : {
614 : : Q_ASSERT( !mCurrentFeature );
615 : 0 : mCurrentFeature = new QgsFeature( mFeatureCount );
616 : 0 : mCurrentFeature->setFields( mFields ); // allow name-based attribute lookups
617 : 0 : QgsAttributes attributes( mThematicAttributes.size() ); //add empty attributes
618 : 0 : mCurrentFeature->setAttributes( attributes );
619 : 0 : mParseModeStack.push( QgsGmlStreamingParser::Tuple );
620 : 0 : mCurrentFeatureId.clear();
621 : 0 : }
622 : 0 : else if ( parseMode == Tuple )
623 : : {
624 : 0 : QString currentTypename( QString::fromUtf8( pszLocalName, localNameLen ) );
625 : 0 : QMap< QString, LayerProperties >::const_iterator iter = mMapTypeNameToProperties.constFind( currentTypename );
626 : 0 : if ( iter != mMapTypeNameToProperties.constEnd() )
627 : : {
628 : 0 : mFeatureTupleDepth = mParseDepth;
629 : 0 : mCurrentTypename = currentTypename;
630 : 0 : mGeometryAttribute.clear();
631 : 0 : if ( mCurrentWKB.size() == 0 )
632 : : {
633 : 0 : mGeometryAttribute = iter.value().mGeometryAttribute;
634 : 0 : }
635 : 0 : mGeometryAttributeBA = mGeometryAttribute.toUtf8();
636 : 0 : mGeometryAttributePtr = mGeometryAttributeBA.constData();
637 : 0 : mGeometryAttributeUTF8Len = strlen( mGeometryAttributePtr );
638 : 0 : mParseModeStack.push( QgsGmlStreamingParser::FeatureTuple );
639 : 0 : QString id;
640 : 0 : if ( mGMLNameSpaceURI.isEmpty() )
641 : : {
642 : 0 : id = readAttribute( QString( GML_NAMESPACE ) + NS_SEPARATOR + "id", attr );
643 : 0 : if ( !id.isEmpty() )
644 : : {
645 : 0 : mGMLNameSpaceURI = GML_NAMESPACE;
646 : 0 : mGMLNameSpaceURIPtr = GML_NAMESPACE;
647 : 0 : }
648 : : else
649 : : {
650 : 0 : id = readAttribute( QString( GML32_NAMESPACE ) + NS_SEPARATOR + "id", attr );
651 : 0 : if ( !id.isEmpty() )
652 : : {
653 : 0 : mGMLNameSpaceURI = GML32_NAMESPACE;
654 : 0 : mGMLNameSpaceURIPtr = GML32_NAMESPACE;
655 : 0 : }
656 : : }
657 : 0 : }
658 : : else
659 : 0 : id = readAttribute( mGMLNameSpaceURI + NS_SEPARATOR + "id", attr );
660 : 0 : if ( !mCurrentFeatureId.isEmpty() )
661 : 0 : mCurrentFeatureId += '|';
662 : 0 : mCurrentFeatureId += id;
663 : 0 : }
664 : 0 : }
665 : 0 : else if ( parseMode == None &&
666 : 0 : localNameLen == static_cast<int>( mTypeNameUTF8Len ) &&
667 : 0 : memcmp( pszLocalName, mTypeNamePtr, mTypeNameUTF8Len ) == 0 )
668 : : {
669 : : Q_ASSERT( !mCurrentFeature );
670 : 0 : mCurrentFeature = new QgsFeature( mFeatureCount );
671 : 0 : mCurrentFeature->setFields( mFields ); // allow name-based attribute lookups
672 : 0 : QgsAttributes attributes( mThematicAttributes.size() ); //add empty attributes
673 : 0 : mCurrentFeature->setAttributes( attributes );
674 : 0 : mParseModeStack.push( QgsGmlStreamingParser::Feature );
675 : 0 : mCurrentFeatureId = readAttribute( QStringLiteral( "fid" ), attr );
676 : 0 : if ( mCurrentFeatureId.isEmpty() )
677 : : {
678 : : // Figure out if the GML namespace is GML_NAMESPACE or GML32_NAMESPACE
679 : : // (should happen only for the first features if there's no gml: element
680 : : // encountered before
681 : 0 : if ( mGMLNameSpaceURI.isEmpty() )
682 : : {
683 : 0 : mCurrentFeatureId = readAttribute( QString( GML_NAMESPACE ) + NS_SEPARATOR + "id", attr );
684 : 0 : if ( !mCurrentFeatureId.isEmpty() )
685 : : {
686 : 0 : mGMLNameSpaceURI = GML_NAMESPACE;
687 : 0 : mGMLNameSpaceURIPtr = GML_NAMESPACE;
688 : 0 : }
689 : : else
690 : : {
691 : 0 : mCurrentFeatureId = readAttribute( QString( GML32_NAMESPACE ) + NS_SEPARATOR + "id", attr );
692 : 0 : if ( !mCurrentFeatureId.isEmpty() )
693 : : {
694 : 0 : mGMLNameSpaceURI = GML32_NAMESPACE;
695 : 0 : mGMLNameSpaceURIPtr = GML32_NAMESPACE;
696 : 0 : }
697 : : }
698 : 0 : }
699 : : else
700 : 0 : mCurrentFeatureId = readAttribute( mGMLNameSpaceURI + NS_SEPARATOR + "id", attr );
701 : 0 : }
702 : 0 : }
703 : :
704 : 0 : else if ( parseMode == BoundingBox && isGMLNS && LOCALNAME_EQUALS( "Box" ) )
705 : : {
706 : 0 : isGeom = true;
707 : 0 : }
708 : 0 : else if ( isGMLNS && LOCALNAME_EQUALS( "Point" ) )
709 : : {
710 : 0 : isGeom = true;
711 : 0 : }
712 : 0 : else if ( isGMLNS && LOCALNAME_EQUALS( "LineString" ) )
713 : : {
714 : 0 : isGeom = true;
715 : 0 : }
716 : 0 : else if ( isGMLNS &&
717 : 0 : localNameLen == static_cast<int>( strlen( "Polygon" ) ) && memcmp( pszLocalName, "Polygon", localNameLen ) == 0 )
718 : : {
719 : 0 : isGeom = true;
720 : 0 : mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
721 : 0 : }
722 : 0 : else if ( isGMLNS && LOCALNAME_EQUALS( "MultiPoint" ) )
723 : : {
724 : 0 : isGeom = true;
725 : 0 : mParseModeStack.push( QgsGmlStreamingParser::MultiPoint );
726 : : //we need one nested list for intermediate WKB
727 : 0 : mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
728 : 0 : }
729 : 0 : else if ( isGMLNS && ( LOCALNAME_EQUALS( "MultiLineString" ) || LOCALNAME_EQUALS( "MultiCurve" ) ) )
730 : : {
731 : 0 : isGeom = true;
732 : 0 : mParseModeStack.push( QgsGmlStreamingParser::MultiLine );
733 : : //we need one nested list for intermediate WKB
734 : 0 : mCurrentWKBFragments.push_back( QList<QgsWkbPtr>() );
735 : 0 : }
736 : 0 : else if ( isGMLNS && ( LOCALNAME_EQUALS( "MultiPolygon" ) || LOCALNAME_EQUALS( "MultiSurface" ) ) )
737 : : {
738 : 0 : isGeom = true;
739 : 0 : mParseModeStack.push( QgsGmlStreamingParser::MultiPolygon );
740 : 0 : }
741 : 0 : else if ( parseMode == FeatureTuple )
742 : : {
743 : 0 : QString localName( QString::fromUtf8( pszLocalName, localNameLen ) );
744 : 0 : if ( mThematicAttributes.contains( mCurrentTypename + '|' + localName ) )
745 : : {
746 : 0 : mParseModeStack.push( QgsGmlStreamingParser::AttributeTuple );
747 : 0 : mAttributeName = mCurrentTypename + '|' + localName;
748 : 0 : mStringCash.clear();
749 : 0 : }
750 : 0 : }
751 : 0 : else if ( parseMode == Feature )
752 : : {
753 : 0 : QString localName( QString::fromUtf8( pszLocalName, localNameLen ) );
754 : 0 : if ( mThematicAttributes.contains( localName ) )
755 : : {
756 : 0 : mParseModeStack.push( QgsGmlStreamingParser::Attribute );
757 : 0 : mAttributeName = localName;
758 : 0 : mStringCash.clear();
759 : 0 : }
760 : : else
761 : : {
762 : : // QGIS server (2.2) is using:
763 : : // <Attribute value="My description" name="desc"/>
764 : 0 : if ( localName.compare( QLatin1String( "attribute" ), Qt::CaseInsensitive ) == 0 )
765 : : {
766 : 0 : QString name = readAttribute( QStringLiteral( "name" ), attr );
767 : 0 : if ( mThematicAttributes.contains( name ) )
768 : : {
769 : 0 : QString value = readAttribute( QStringLiteral( "value" ), attr );
770 : 0 : setAttribute( name, value );
771 : 0 : }
772 : 0 : }
773 : : }
774 : 0 : }
775 : 0 : else if ( mParseDepth == 0 && LOCALNAME_EQUALS( "FeatureCollection" ) )
776 : : {
777 : 0 : QString numberReturned = readAttribute( QStringLiteral( "numberReturned" ), attr ); // WFS 2.0
778 : 0 : if ( numberReturned.isEmpty() )
779 : 0 : numberReturned = readAttribute( QStringLiteral( "numberOfFeatures" ), attr ); // WFS 1.1
780 : : bool conversionOk;
781 : 0 : mNumberReturned = numberReturned.toInt( &conversionOk );
782 : 0 : if ( !conversionOk )
783 : 0 : mNumberReturned = -1;
784 : :
785 : 0 : QString numberMatched = readAttribute( QStringLiteral( "numberMatched" ), attr ); // WFS 2.0
786 : 0 : mNumberMatched = numberMatched.toInt( &conversionOk );
787 : 0 : if ( !conversionOk ) // likely since numberMatched="unknown" is legal
788 : 0 : mNumberMatched = -1;
789 : 0 : }
790 : 0 : else if ( mParseDepth == 0 && LOCALNAME_EQUALS( "ExceptionReport" ) )
791 : : {
792 : 0 : mIsException = true;
793 : 0 : mParseModeStack.push( QgsGmlStreamingParser::ExceptionReport );
794 : 0 : }
795 : 0 : else if ( mIsException && LOCALNAME_EQUALS( "ExceptionText" ) )
796 : : {
797 : 0 : mStringCash.clear();
798 : 0 : mParseModeStack.push( QgsGmlStreamingParser::ExceptionText );
799 : 0 : }
800 : 0 : else if ( mParseDepth == 1 && LOCALNAME_EQUALS( "truncatedResponse" ) )
801 : : {
802 : : // e.g: http://services.cuzk.cz/wfs/inspire-cp-wfs.asp?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=cp:CadastralParcel
803 : 0 : mTruncatedResponse = true;
804 : 0 : }
805 : 0 : else if ( !mGeometryString.empty() &&
806 : 0 : !LOCALNAME_EQUALS( "exterior" ) &&
807 : 0 : !LOCALNAME_EQUALS( "interior" ) &&
808 : 0 : !LOCALNAME_EQUALS( "innerBoundaryIs" ) &&
809 : 0 : !LOCALNAME_EQUALS( "outerBoundaryIs" ) &&
810 : 0 : !LOCALNAME_EQUALS( "LinearRing" ) &&
811 : 0 : !LOCALNAME_EQUALS( "pointMember" ) &&
812 : 0 : !LOCALNAME_EQUALS( "curveMember" ) &&
813 : 0 : !LOCALNAME_EQUALS( "lineStringMember" ) &&
814 : 0 : !LOCALNAME_EQUALS( "polygonMember" ) &&
815 : 0 : !LOCALNAME_EQUALS( "surfaceMember" ) &&
816 : 0 : !LOCALNAME_EQUALS( "Curve" ) &&
817 : 0 : !LOCALNAME_EQUALS( "segments" ) &&
818 : 0 : !LOCALNAME_EQUALS( "LineStringSegment" ) )
819 : : {
820 : : //QgsDebugMsg( "Found unhandled geometry element " + QString::fromUtf8( pszLocalName, localNameLen ) );
821 : 0 : mFoundUnhandledGeometryElement = true;
822 : 0 : }
823 : :
824 : 0 : if ( !mGeometryString.empty() )
825 : 0 : isGeom = true;
826 : :
827 : 0 : if ( elDimension == 0 && isGeom )
828 : : {
829 : : // srsDimension can also be set on the top geometry element
830 : : // e.g. https://data.linz.govt.nz/services;key=XXXXXXXX/wfs?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=data.linz.govt.nz:layer-524
831 : 0 : QString srsDimension = readAttribute( QStringLiteral( "srsDimension" ), attr );
832 : : bool ok;
833 : 0 : int dimension = srsDimension.toInt( &ok );
834 : 0 : if ( ok )
835 : : {
836 : 0 : elDimension = dimension;
837 : 0 : }
838 : 0 : }
839 : :
840 : 0 : if ( elDimension != 0 || mDimensionStack.isEmpty() )
841 : : {
842 : 0 : mDimensionStack.push( elDimension );
843 : 0 : }
844 : : else
845 : : {
846 : 0 : mDimensionStack.push( mDimensionStack.back() );
847 : : }
848 : :
849 : 0 : if ( mEpsg == 0 && isGeom )
850 : : {
851 : 0 : if ( readEpsgFromAttribute( mEpsg, attr ) != 0 )
852 : : {
853 : 0 : QgsDebugMsg( QStringLiteral( "error, could not get epsg id" ) );
854 : 0 : }
855 : : else
856 : : {
857 : 0 : QgsDebugMsg( QStringLiteral( "mEpsg = %1" ).arg( mEpsg ) );
858 : : }
859 : 0 : }
860 : :
861 : 0 : mParseDepth ++;
862 : 0 : }
863 : :
864 : 0 : void QgsGmlStreamingParser::endElement( const XML_Char *el )
865 : : {
866 : 0 : mParseDepth --;
867 : :
868 : 0 : const int elLen = static_cast<int>( strlen( el ) );
869 : 0 : const char *pszSep = strchr( el, NS_SEPARATOR );
870 : 0 : const char *pszLocalName = ( pszSep ) ? pszSep + 1 : el;
871 : 0 : const int nsLen = ( pszSep ) ? ( int )( pszSep - el ) : 0;
872 : 0 : const int localNameLen = ( pszSep ) ? ( int )( elLen - nsLen ) - 1 : elLen;
873 : 0 : ParseMode parseMode( mParseModeStack.isEmpty() ? None : mParseModeStack.top() );
874 : :
875 : 0 : int lastDimension = mDimensionStack.isEmpty() ? 0 : mDimensionStack.pop();
876 : :
877 : 0 : const bool isGMLNS = ( nsLen == mGMLNameSpaceURI.size() && mGMLNameSpaceURIPtr && memcmp( el, mGMLNameSpaceURIPtr, nsLen ) == 0 );
878 : :
879 : 0 : if ( parseMode == Coordinate && isGMLNS && LOCALNAME_EQUALS( "coordinates" ) )
880 : : {
881 : 0 : mParseModeStack.pop();
882 : 0 : }
883 : 0 : else if ( parseMode == PosList && isGMLNS &&
884 : 0 : ( LOCALNAME_EQUALS( "pos" ) || LOCALNAME_EQUALS( "posList" ) ) )
885 : : {
886 : 0 : mDimension = lastDimension;
887 : 0 : mParseModeStack.pop();
888 : 0 : }
889 : 0 : else if ( parseMode == AttributeTuple &&
890 : 0 : mCurrentTypename + '|' + QString::fromUtf8( pszLocalName, localNameLen ) == mAttributeName ) //add a thematic attribute to the feature
891 : : {
892 : 0 : mParseModeStack.pop();
893 : :
894 : 0 : setAttribute( mAttributeName, mStringCash );
895 : 0 : }
896 : 0 : else if ( parseMode == Attribute && QString::fromUtf8( pszLocalName, localNameLen ) == mAttributeName ) //add a thematic attribute to the feature
897 : : {
898 : 0 : mParseModeStack.pop();
899 : :
900 : 0 : setAttribute( mAttributeName, mStringCash );
901 : 0 : }
902 : 0 : else if ( parseMode == Geometry &&
903 : 0 : localNameLen == static_cast<int>( mGeometryAttributeUTF8Len ) &&
904 : 0 : memcmp( pszLocalName, mGeometryAttributePtr, localNameLen ) == 0 )
905 : : {
906 : 0 : mParseModeStack.pop();
907 : 0 : if ( mFoundUnhandledGeometryElement )
908 : : {
909 : 0 : gdal::ogr_geometry_unique_ptr hGeom( OGR_G_CreateFromGML( mGeometryString.c_str() ) );
910 : : //QgsDebugMsg( QStringLiteral("for OGR: %1 -> %2").arg(mGeometryString.c_str()).arg(hGeom != nullptr));
911 : 0 : if ( hGeom )
912 : : {
913 : 0 : const int wkbSize = OGR_G_WkbSize( hGeom.get() );
914 : 0 : unsigned char *pabyBuffer = new unsigned char[ wkbSize ];
915 : 0 : OGR_G_ExportToIsoWkb( hGeom.get(), wkbNDR, pabyBuffer );
916 : 0 : QgsGeometry g;
917 : 0 : g.fromWkb( pabyBuffer, wkbSize );
918 : 0 : if ( mInvertAxisOrientation )
919 : : {
920 : 0 : g.transform( QTransform( 0, 1, 1, 0, 0, 0 ) );
921 : 0 : }
922 : : Q_ASSERT( mCurrentFeature );
923 : 0 : mCurrentFeature->setGeometry( g );
924 : 0 : }
925 : 0 : }
926 : 0 : mGeometryString.clear();
927 : 0 : }
928 : 0 : else if ( parseMode == BoundingBox && isGMLNS && LOCALNAME_EQUALS( "boundedBy" ) )
929 : : {
930 : : //create bounding box from mStringCash
931 : 0 : if ( mCurrentExtent.isNull() &&
932 : 0 : !mBoundedByNullFound &&
933 : 0 : !createBBoxFromCoordinateString( mCurrentExtent, mStringCash ) )
934 : : {
935 : 0 : QgsDebugMsg( QStringLiteral( "creation of bounding box failed" ) );
936 : 0 : }
937 : 0 : if ( !mCurrentExtent.isNull() && mLayerExtent.isNull() &&
938 : 0 : !mCurrentFeature && mFeatureCount == 0 )
939 : : {
940 : 0 : mLayerExtent = mCurrentExtent;
941 : 0 : mCurrentExtent = QgsRectangle();
942 : 0 : }
943 : :
944 : 0 : mParseModeStack.pop();
945 : 0 : }
946 : 0 : else if ( parseMode == Null && isGMLNS && LOCALNAME_EQUALS( "null" ) )
947 : : {
948 : 0 : mParseModeStack.pop();
949 : 0 : }
950 : 0 : else if ( parseMode == Envelope && isGMLNS && LOCALNAME_EQUALS( "Envelope" ) )
951 : : {
952 : 0 : mParseModeStack.pop();
953 : 0 : }
954 : 0 : else if ( parseMode == LowerCorner && isGMLNS && LOCALNAME_EQUALS( "lowerCorner" ) )
955 : : {
956 : 0 : QList<QgsPointXY> points;
957 : 0 : pointsFromPosListString( points, mStringCash, 2 );
958 : 0 : if ( points.size() == 1 )
959 : : {
960 : 0 : mCurrentExtent.setXMinimum( points[0].x() );
961 : 0 : mCurrentExtent.setYMinimum( points[0].y() );
962 : 0 : }
963 : 0 : mParseModeStack.pop();
964 : 0 : }
965 : 0 : else if ( parseMode == UpperCorner && isGMLNS && LOCALNAME_EQUALS( "upperCorner" ) )
966 : : {
967 : 0 : QList<QgsPointXY> points;
968 : 0 : pointsFromPosListString( points, mStringCash, 2 );
969 : 0 : if ( points.size() == 1 )
970 : : {
971 : 0 : mCurrentExtent.setXMaximum( points[0].x() );
972 : 0 : mCurrentExtent.setYMaximum( points[0].y() );
973 : 0 : }
974 : 0 : mParseModeStack.pop();
975 : 0 : }
976 : 0 : else if ( parseMode == FeatureTuple && mParseDepth == mFeatureTupleDepth )
977 : : {
978 : 0 : mParseModeStack.pop();
979 : 0 : mFeatureTupleDepth = 0;
980 : 0 : }
981 : 0 : else if ( ( parseMode == Tuple && !mTypeNamePtr &&
982 : 0 : LOCALNAME_EQUALS( "Tuple" ) ) ||
983 : 0 : ( parseMode == Feature &&
984 : 0 : localNameLen == static_cast<int>( mTypeNameUTF8Len ) &&
985 : 0 : memcmp( pszLocalName, mTypeNamePtr, mTypeNameUTF8Len ) == 0 ) )
986 : : {
987 : : Q_ASSERT( mCurrentFeature );
988 : 0 : if ( !mCurrentFeature->hasGeometry() )
989 : : {
990 : 0 : if ( mCurrentWKB.size() > 0 )
991 : : {
992 : 0 : QgsGeometry g;
993 : 0 : g.fromWkb( mCurrentWKB, mCurrentWKB.size() );
994 : 0 : mCurrentFeature->setGeometry( g );
995 : 0 : mCurrentWKB = QgsWkbPtr( nullptr, 0 );
996 : 0 : }
997 : 0 : else if ( !mCurrentExtent.isEmpty() )
998 : : {
999 : 0 : mCurrentFeature->setGeometry( QgsGeometry::fromRect( mCurrentExtent ) );
1000 : 0 : }
1001 : 0 : }
1002 : 0 : mCurrentFeature->setValid( true );
1003 : :
1004 : 0 : mFeatureList.push_back( QgsGmlFeaturePtrGmlIdPair( mCurrentFeature, mCurrentFeatureId ) );
1005 : :
1006 : 0 : mCurrentFeature = nullptr;
1007 : 0 : ++mFeatureCount;
1008 : 0 : mParseModeStack.pop();
1009 : 0 : }
1010 : 0 : else if ( isGMLNS && LOCALNAME_EQUALS( "Point" ) )
1011 : : {
1012 : 0 : QList<QgsPointXY> pointList;
1013 : 0 : if ( pointsFromString( pointList, mStringCash ) != 0 )
1014 : : {
1015 : : //error
1016 : 0 : }
1017 : :
1018 : 0 : if ( pointList.isEmpty() )
1019 : 0 : return; // error
1020 : :
1021 : 0 : if ( parseMode == QgsGmlStreamingParser::Geometry )
1022 : : {
1023 : : //directly add WKB point to the feature
1024 : 0 : if ( getPointWKB( mCurrentWKB, *( pointList.constBegin() ) ) != 0 )
1025 : : {
1026 : : //error
1027 : 0 : }
1028 : :
1029 : 0 : if ( mWkbType != QgsWkbTypes::MultiPoint ) //keep multitype in case of geometry type mix
1030 : : {
1031 : 0 : mWkbType = QgsWkbTypes::Point;
1032 : 0 : }
1033 : 0 : }
1034 : : else //multipoint, add WKB as fragment
1035 : : {
1036 : 0 : QgsWkbPtr wkbPtr( nullptr, 0 );
1037 : 0 : if ( getPointWKB( wkbPtr, *( pointList.constBegin() ) ) != 0 )
1038 : : {
1039 : : //error
1040 : 0 : }
1041 : 0 : if ( !mCurrentWKBFragments.isEmpty() )
1042 : : {
1043 : 0 : mCurrentWKBFragments.last().push_back( wkbPtr );
1044 : 0 : }
1045 : : else
1046 : : {
1047 : 0 : QgsDebugMsg( QStringLiteral( "No wkb fragments" ) );
1048 : 0 : delete [] wkbPtr;
1049 : : }
1050 : : }
1051 : 0 : }
1052 : 0 : else if ( isGMLNS && ( LOCALNAME_EQUALS( "LineString" ) || LOCALNAME_EQUALS( "LineStringSegment" ) ) )
1053 : : {
1054 : : //add WKB point to the feature
1055 : :
1056 : 0 : QList<QgsPointXY> pointList;
1057 : 0 : if ( pointsFromString( pointList, mStringCash ) != 0 )
1058 : : {
1059 : : //error
1060 : 0 : }
1061 : 0 : if ( parseMode == QgsGmlStreamingParser::Geometry )
1062 : : {
1063 : 0 : if ( getLineWKB( mCurrentWKB, pointList ) != 0 )
1064 : : {
1065 : : //error
1066 : 0 : }
1067 : :
1068 : 0 : if ( mWkbType != QgsWkbTypes::MultiLineString )//keep multitype in case of geometry type mix
1069 : : {
1070 : 0 : mWkbType = QgsWkbTypes::LineString;
1071 : 0 : }
1072 : 0 : }
1073 : : else //multiline, add WKB as fragment
1074 : : {
1075 : 0 : QgsWkbPtr wkbPtr( nullptr, 0 );
1076 : 0 : if ( getLineWKB( wkbPtr, pointList ) != 0 )
1077 : : {
1078 : : //error
1079 : 0 : }
1080 : 0 : if ( !mCurrentWKBFragments.isEmpty() )
1081 : : {
1082 : 0 : mCurrentWKBFragments.last().push_back( wkbPtr );
1083 : 0 : }
1084 : : else
1085 : : {
1086 : 0 : QgsDebugMsg( QStringLiteral( "no wkb fragments" ) );
1087 : 0 : delete [] wkbPtr;
1088 : : }
1089 : : }
1090 : 0 : }
1091 : 0 : else if ( ( parseMode == Geometry || parseMode == MultiPolygon ) &&
1092 : 0 : isGMLNS && LOCALNAME_EQUALS( "LinearRing" ) )
1093 : : {
1094 : 0 : QList<QgsPointXY> pointList;
1095 : 0 : if ( pointsFromString( pointList, mStringCash ) != 0 )
1096 : : {
1097 : : //error
1098 : 0 : }
1099 : :
1100 : 0 : QgsWkbPtr wkbPtr( nullptr, 0 );
1101 : 0 : if ( getRingWKB( wkbPtr, pointList ) != 0 )
1102 : : {
1103 : : //error
1104 : 0 : }
1105 : :
1106 : 0 : if ( !mCurrentWKBFragments.isEmpty() )
1107 : : {
1108 : 0 : mCurrentWKBFragments.last().push_back( wkbPtr );
1109 : 0 : }
1110 : : else
1111 : : {
1112 : 0 : delete[] wkbPtr;
1113 : 0 : QgsDebugMsg( QStringLiteral( "no wkb fragments" ) );
1114 : : }
1115 : 0 : }
1116 : 0 : else if ( ( parseMode == Geometry || parseMode == MultiPolygon ) && isGMLNS &&
1117 : 0 : LOCALNAME_EQUALS( "Polygon" ) )
1118 : : {
1119 : 0 : if ( mWkbType != QgsWkbTypes::MultiPolygon )//keep multitype in case of geometry type mix
1120 : : {
1121 : 0 : mWkbType = QgsWkbTypes::Polygon;
1122 : 0 : }
1123 : :
1124 : 0 : if ( parseMode == Geometry )
1125 : : {
1126 : 0 : createPolygonFromFragments();
1127 : 0 : }
1128 : 0 : }
1129 : 0 : else if ( parseMode == MultiPoint && isGMLNS &&
1130 : 0 : LOCALNAME_EQUALS( "MultiPoint" ) )
1131 : : {
1132 : 0 : mWkbType = QgsWkbTypes::MultiPoint;
1133 : 0 : mParseModeStack.pop();
1134 : 0 : createMultiPointFromFragments();
1135 : 0 : }
1136 : 0 : else if ( parseMode == MultiLine && isGMLNS &&
1137 : 0 : ( LOCALNAME_EQUALS( "MultiLineString" ) || LOCALNAME_EQUALS( "MultiCurve" ) ) )
1138 : : {
1139 : 0 : mWkbType = QgsWkbTypes::MultiLineString;
1140 : 0 : mParseModeStack.pop();
1141 : 0 : createMultiLineFromFragments();
1142 : 0 : }
1143 : 0 : else if ( parseMode == MultiPolygon && isGMLNS &&
1144 : 0 : ( LOCALNAME_EQUALS( "MultiPolygon" ) || LOCALNAME_EQUALS( "MultiSurface" ) ) )
1145 : : {
1146 : 0 : mWkbType = QgsWkbTypes::MultiPolygon;
1147 : 0 : mParseModeStack.pop();
1148 : 0 : createMultiPolygonFromFragments();
1149 : 0 : }
1150 : 0 : else if ( mParseDepth == 0 && LOCALNAME_EQUALS( "ExceptionReport" ) )
1151 : : {
1152 : 0 : mParseModeStack.pop();
1153 : 0 : }
1154 : 0 : else if ( parseMode == ExceptionText && LOCALNAME_EQUALS( "ExceptionText" ) )
1155 : : {
1156 : 0 : mExceptionText = mStringCash;
1157 : 0 : mParseModeStack.pop();
1158 : 0 : }
1159 : :
1160 : 0 : if ( !mGeometryString.empty() )
1161 : : {
1162 : 0 : mGeometryString.append( "</", 2 );
1163 : 0 : mGeometryString.append( pszLocalName, localNameLen );
1164 : 0 : mGeometryString.append( ">", 1 );
1165 : 0 : }
1166 : :
1167 : 0 : }
1168 : :
1169 : 0 : void QgsGmlStreamingParser::characters( const XML_Char *chars, int len )
1170 : : {
1171 : : //save chars in mStringCash attribute mode or coordinate mode
1172 : 0 : if ( mParseModeStack.isEmpty() )
1173 : : {
1174 : 0 : return;
1175 : : }
1176 : :
1177 : 0 : if ( !mGeometryString.empty() )
1178 : : {
1179 : 0 : mGeometryString.append( chars, len );
1180 : 0 : }
1181 : :
1182 : 0 : QgsGmlStreamingParser::ParseMode parseMode = mParseModeStack.top();
1183 : 0 : if ( parseMode == QgsGmlStreamingParser::Attribute ||
1184 : 0 : parseMode == QgsGmlStreamingParser::AttributeTuple ||
1185 : 0 : parseMode == QgsGmlStreamingParser::Coordinate ||
1186 : 0 : parseMode == QgsGmlStreamingParser::PosList ||
1187 : 0 : parseMode == QgsGmlStreamingParser::LowerCorner ||
1188 : 0 : parseMode == QgsGmlStreamingParser::UpperCorner ||
1189 : 0 : parseMode == QgsGmlStreamingParser::ExceptionText )
1190 : : {
1191 : 0 : mStringCash.append( QString::fromUtf8( chars, len ) );
1192 : 0 : }
1193 : 0 : }
1194 : :
1195 : 0 : void QgsGmlStreamingParser::setAttribute( const QString &name, const QString &value )
1196 : : {
1197 : : //find index with attribute name
1198 : 0 : QMap<QString, QPair<int, QgsField> >::const_iterator att_it = mThematicAttributes.constFind( name );
1199 : 0 : bool conversionOk = true;
1200 : 0 : if ( att_it != mThematicAttributes.constEnd() )
1201 : : {
1202 : 0 : QVariant var;
1203 : 0 : switch ( att_it.value().second.type() )
1204 : : {
1205 : : case QVariant::Double:
1206 : 0 : var = QVariant( value.toDouble( &conversionOk ) );
1207 : 0 : break;
1208 : : case QVariant::Int:
1209 : 0 : var = QVariant( value.toInt( &conversionOk ) );
1210 : 0 : break;
1211 : : case QVariant::LongLong:
1212 : 0 : var = QVariant( value.toLongLong( &conversionOk ) );
1213 : 0 : break;
1214 : : case QVariant::DateTime:
1215 : 0 : var = QVariant( QDateTime::fromString( value, Qt::ISODate ) );
1216 : 0 : break;
1217 : : default: //string type is default
1218 : 0 : var = QVariant( value );
1219 : 0 : break;
1220 : : }
1221 : 0 : if ( ! conversionOk ) // Assume is NULL
1222 : : {
1223 : 0 : var = QVariant();
1224 : 0 : }
1225 : : Q_ASSERT( mCurrentFeature );
1226 : 0 : mCurrentFeature->setAttribute( att_it.value().first, var );
1227 : 0 : }
1228 : 0 : }
1229 : :
1230 : 0 : int QgsGmlStreamingParser::readEpsgFromAttribute( int &epsgNr, const XML_Char **attr )
1231 : : {
1232 : 0 : int i = 0;
1233 : 0 : while ( attr[i] )
1234 : : {
1235 : 0 : if ( strcmp( attr[i], "srsName" ) == 0 )
1236 : : {
1237 : 0 : QString epsgString( attr[i + 1] );
1238 : 0 : QString epsgNrString;
1239 : 0 : bool bIsUrn = false;
1240 : 0 : if ( epsgString.startsWith( QLatin1String( "http://www.opengis.net/gml/srs/" ) ) ) //e.g. geoserver: "http://www.opengis.net/gml/srs/epsg.xml#4326"
1241 : : {
1242 : 0 : epsgNrString = epsgString.section( '#', 1, 1 );
1243 : 0 : }
1244 : : // WFS >= 1.1
1245 : 0 : else if ( epsgString.startsWith( QLatin1String( "urn:ogc:def:crs:EPSG:" ) ) ||
1246 : 0 : epsgString.startsWith( QLatin1String( "urn:x-ogc:def:crs:EPSG:" ) ) )
1247 : : {
1248 : 0 : bIsUrn = true;
1249 : 0 : epsgNrString = epsgString.split( ':' ).last();
1250 : 0 : }
1251 : 0 : else if ( epsgString.startsWith( QLatin1String( "http://www.opengis.net/def/crs/EPSG/" ) ) ) //e.g. geoserver: "http://www.opengis.net/def/crs/EPSG/4326"
1252 : : {
1253 : 0 : bIsUrn = true;
1254 : 0 : epsgNrString = epsgString.split( '/' ).last();
1255 : 0 : }
1256 : : else //e.g. umn mapserver: "EPSG:4326">
1257 : : {
1258 : 0 : epsgNrString = epsgString.section( ':', 1, 1 );
1259 : : }
1260 : : bool conversionOk;
1261 : 0 : int eNr = epsgNrString.toInt( &conversionOk );
1262 : 0 : if ( !conversionOk )
1263 : : {
1264 : 0 : return 1;
1265 : : }
1266 : 0 : epsgNr = eNr;
1267 : 0 : mSrsName = epsgString;
1268 : :
1269 : 0 : QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( epsgNr ) );
1270 : 0 : if ( crs.isValid() )
1271 : : {
1272 : 0 : if ( ( ( mAxisOrientationLogic == Honour_EPSG_if_urn && bIsUrn ) ||
1273 : 0 : mAxisOrientationLogic == Honour_EPSG ) && crs.hasAxisInverted() )
1274 : : {
1275 : 0 : mInvertAxisOrientation = !mInvertAxisOrientationRequest;
1276 : 0 : }
1277 : 0 : }
1278 : :
1279 : 0 : return 0;
1280 : 0 : }
1281 : 0 : ++i;
1282 : : }
1283 : 0 : return 2;
1284 : 0 : }
1285 : :
1286 : 0 : QString QgsGmlStreamingParser::readAttribute( const QString &attributeName, const XML_Char **attr ) const
1287 : : {
1288 : 0 : int i = 0;
1289 : 0 : while ( attr[i] )
1290 : : {
1291 : 0 : if ( attributeName.compare( attr[i] ) == 0 )
1292 : : {
1293 : 0 : return QString::fromUtf8( attr[i + 1] );
1294 : : }
1295 : 0 : i += 2;
1296 : : }
1297 : 0 : return QString();
1298 : 0 : }
1299 : :
1300 : 0 : bool QgsGmlStreamingParser::createBBoxFromCoordinateString( QgsRectangle &r, const QString &coordString ) const
1301 : : {
1302 : 0 : QList<QgsPointXY> points;
1303 : 0 : if ( pointsFromCoordinateString( points, coordString ) != 0 )
1304 : : {
1305 : 0 : return false;
1306 : : }
1307 : :
1308 : 0 : if ( points.size() < 2 )
1309 : : {
1310 : 0 : return false;
1311 : : }
1312 : :
1313 : 0 : r.set( points[0], points[1] );
1314 : :
1315 : 0 : return true;
1316 : 0 : }
1317 : :
1318 : 0 : int QgsGmlStreamingParser::pointsFromCoordinateString( QList<QgsPointXY> &points, const QString &coordString ) const
1319 : : {
1320 : : //tuples are separated by space, x/y by ','
1321 : : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1322 : : QStringList tuples = coordString.split( mTupleSeparator, QString::SkipEmptyParts );
1323 : : #else
1324 : 0 : QStringList tuples = coordString.split( mTupleSeparator, Qt::SkipEmptyParts );
1325 : : #endif
1326 : 0 : QStringList tuples_coordinates;
1327 : : double x, y;
1328 : : bool conversionSuccess;
1329 : :
1330 : 0 : QStringList::const_iterator tupleIterator;
1331 : 0 : for ( tupleIterator = tuples.constBegin(); tupleIterator != tuples.constEnd(); ++tupleIterator )
1332 : : {
1333 : : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1334 : : tuples_coordinates = tupleIterator->split( mCoordinateSeparator, QString::SkipEmptyParts );
1335 : : #else
1336 : 0 : tuples_coordinates = tupleIterator->split( mCoordinateSeparator, Qt::SkipEmptyParts );
1337 : : #endif
1338 : 0 : if ( tuples_coordinates.size() < 2 )
1339 : : {
1340 : 0 : continue;
1341 : : }
1342 : 0 : x = tuples_coordinates.at( 0 ).toDouble( &conversionSuccess );
1343 : 0 : if ( !conversionSuccess )
1344 : : {
1345 : 0 : continue;
1346 : : }
1347 : 0 : y = tuples_coordinates.at( 1 ).toDouble( &conversionSuccess );
1348 : 0 : if ( !conversionSuccess )
1349 : : {
1350 : 0 : continue;
1351 : : }
1352 : 0 : points.push_back( ( mInvertAxisOrientation ) ? QgsPointXY( y, x ) : QgsPointXY( x, y ) );
1353 : 0 : }
1354 : : return 0;
1355 : 0 : }
1356 : :
1357 : 0 : int QgsGmlStreamingParser::pointsFromPosListString( QList<QgsPointXY> &points, const QString &coordString, int dimension ) const
1358 : : {
1359 : : // coordinates separated by spaces
1360 : : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1361 : : QStringList coordinates = coordString.split( ' ', QString::SkipEmptyParts );
1362 : : #else
1363 : 0 : QStringList coordinates = coordString.split( ' ', Qt::SkipEmptyParts );
1364 : : #endif
1365 : :
1366 : 0 : if ( coordinates.size() % dimension != 0 )
1367 : : {
1368 : 0 : QgsDebugMsg( QStringLiteral( "Wrong number of coordinates" ) );
1369 : 0 : }
1370 : :
1371 : 0 : int ncoor = coordinates.size() / dimension;
1372 : 0 : for ( int i = 0; i < ncoor; i++ )
1373 : : {
1374 : : bool conversionSuccess;
1375 : 0 : double x = coordinates.value( i * dimension ).toDouble( &conversionSuccess );
1376 : 0 : if ( !conversionSuccess )
1377 : : {
1378 : 0 : continue;
1379 : : }
1380 : 0 : double y = coordinates.value( i * dimension + 1 ).toDouble( &conversionSuccess );
1381 : 0 : if ( !conversionSuccess )
1382 : : {
1383 : 0 : continue;
1384 : : }
1385 : 0 : points.append( ( mInvertAxisOrientation ) ? QgsPointXY( y, x ) : QgsPointXY( x, y ) );
1386 : 0 : }
1387 : : return 0;
1388 : 0 : }
1389 : :
1390 : 0 : int QgsGmlStreamingParser::pointsFromString( QList<QgsPointXY> &points, const QString &coordString ) const
1391 : : {
1392 : 0 : if ( mCoorMode == QgsGmlStreamingParser::Coordinate )
1393 : : {
1394 : 0 : return pointsFromCoordinateString( points, coordString );
1395 : : }
1396 : 0 : else if ( mCoorMode == QgsGmlStreamingParser::PosList )
1397 : : {
1398 : 0 : return pointsFromPosListString( points, coordString, mDimension ? mDimension : 2 );
1399 : : }
1400 : 0 : return 1;
1401 : 0 : }
1402 : :
1403 : 0 : int QgsGmlStreamingParser::getPointWKB( QgsWkbPtr &wkbPtr, const QgsPointXY &point ) const
1404 : : {
1405 : 0 : int wkbSize = 1 + sizeof( int ) + 2 * sizeof( double );
1406 : 0 : wkbPtr = QgsWkbPtr( new unsigned char[wkbSize], wkbSize );
1407 : :
1408 : 0 : QgsWkbPtr fillPtr( wkbPtr );
1409 : 0 : fillPtr << mEndian << QgsWkbTypes::Point << point.x() << point.y();
1410 : :
1411 : 0 : return 0;
1412 : : }
1413 : :
1414 : 0 : int QgsGmlStreamingParser::getLineWKB( QgsWkbPtr &wkbPtr, const QList<QgsPointXY> &lineCoordinates ) const
1415 : : {
1416 : 0 : int wkbSize = 1 + 2 * sizeof( int ) + lineCoordinates.size() * 2 * sizeof( double );
1417 : 0 : wkbPtr = QgsWkbPtr( new unsigned char[wkbSize], wkbSize );
1418 : :
1419 : 0 : QgsWkbPtr fillPtr( wkbPtr );
1420 : :
1421 : 0 : fillPtr << mEndian << QgsWkbTypes::LineString << lineCoordinates.size();
1422 : :
1423 : 0 : QList<QgsPointXY>::const_iterator iter;
1424 : 0 : for ( iter = lineCoordinates.constBegin(); iter != lineCoordinates.constEnd(); ++iter )
1425 : : {
1426 : 0 : fillPtr << iter->x() << iter->y();
1427 : 0 : }
1428 : :
1429 : 0 : return 0;
1430 : : }
1431 : :
1432 : 0 : int QgsGmlStreamingParser::getRingWKB( QgsWkbPtr &wkbPtr, const QList<QgsPointXY> &ringCoordinates ) const
1433 : : {
1434 : 0 : int wkbSize = sizeof( int ) + ringCoordinates.size() * 2 * sizeof( double );
1435 : 0 : wkbPtr = QgsWkbPtr( new unsigned char[wkbSize], wkbSize );
1436 : :
1437 : 0 : QgsWkbPtr fillPtr( wkbPtr );
1438 : :
1439 : 0 : fillPtr << ringCoordinates.size();
1440 : :
1441 : 0 : QList<QgsPointXY>::const_iterator iter;
1442 : 0 : for ( iter = ringCoordinates.constBegin(); iter != ringCoordinates.constEnd(); ++iter )
1443 : : {
1444 : 0 : fillPtr << iter->x() << iter->y();
1445 : 0 : }
1446 : :
1447 : 0 : return 0;
1448 : : }
1449 : :
1450 : 0 : int QgsGmlStreamingParser::createMultiLineFromFragments()
1451 : : {
1452 : 0 : int size = 1 + 2 * sizeof( int ) + totalWKBFragmentSize();
1453 : 0 : mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1454 : :
1455 : 0 : QgsWkbPtr wkbPtr( mCurrentWKB );
1456 : :
1457 : 0 : wkbPtr << mEndian << QgsWkbTypes::MultiLineString << mCurrentWKBFragments.constBegin()->size();
1458 : :
1459 : : //copy (and delete) all the wkb fragments
1460 : 0 : QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1461 : 0 : for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1462 : : {
1463 : 0 : memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1464 : 0 : wkbPtr += wkbIt->size();
1465 : 0 : delete[] *wkbIt;
1466 : 0 : }
1467 : :
1468 : 0 : mCurrentWKBFragments.clear();
1469 : 0 : mWkbType = QgsWkbTypes::MultiLineString;
1470 : 0 : return 0;
1471 : : }
1472 : :
1473 : 0 : int QgsGmlStreamingParser::createMultiPointFromFragments()
1474 : : {
1475 : 0 : int size = 1 + 2 * sizeof( int ) + totalWKBFragmentSize();
1476 : 0 : mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1477 : :
1478 : 0 : QgsWkbPtr wkbPtr( mCurrentWKB );
1479 : 0 : wkbPtr << mEndian << QgsWkbTypes::MultiPoint << mCurrentWKBFragments.constBegin()->size();
1480 : :
1481 : 0 : QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1482 : 0 : for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1483 : : {
1484 : 0 : memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1485 : 0 : wkbPtr += wkbIt->size();
1486 : 0 : delete[] *wkbIt;
1487 : 0 : }
1488 : :
1489 : 0 : mCurrentWKBFragments.clear();
1490 : 0 : mWkbType = QgsWkbTypes::MultiPoint;
1491 : 0 : return 0;
1492 : : }
1493 : :
1494 : :
1495 : 0 : int QgsGmlStreamingParser::createPolygonFromFragments()
1496 : : {
1497 : 0 : int size = 1 + 2 * sizeof( int ) + totalWKBFragmentSize();
1498 : 0 : mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1499 : :
1500 : 0 : QgsWkbPtr wkbPtr( mCurrentWKB );
1501 : 0 : wkbPtr << mEndian << QgsWkbTypes::Polygon << mCurrentWKBFragments.constBegin()->size();
1502 : :
1503 : 0 : QList<QgsWkbPtr>::const_iterator wkbIt = mCurrentWKBFragments.constBegin()->constBegin();
1504 : 0 : for ( ; wkbIt != mCurrentWKBFragments.constBegin()->constEnd(); ++wkbIt )
1505 : : {
1506 : 0 : memcpy( wkbPtr, *wkbIt, wkbIt->size() );
1507 : 0 : wkbPtr += wkbIt->size();
1508 : 0 : delete[] *wkbIt;
1509 : 0 : }
1510 : :
1511 : 0 : mCurrentWKBFragments.clear();
1512 : 0 : mWkbType = QgsWkbTypes::Polygon;
1513 : 0 : return 0;
1514 : : }
1515 : :
1516 : 0 : int QgsGmlStreamingParser::createMultiPolygonFromFragments()
1517 : : {
1518 : 0 : int size = 0;
1519 : 0 : size += 1 + 2 * sizeof( int );
1520 : 0 : size += totalWKBFragmentSize();
1521 : 0 : size += mCurrentWKBFragments.size() * ( 1 + 2 * sizeof( int ) ); //fragments are just the rings
1522 : :
1523 : 0 : mCurrentWKB = QgsWkbPtr( new unsigned char[size], size );
1524 : :
1525 : 0 : QgsWkbPtr wkbPtr( mCurrentWKB );
1526 : 0 : wkbPtr << ( char ) mEndian << QgsWkbTypes::MultiPolygon << mCurrentWKBFragments.size();
1527 : :
1528 : : //have outer and inner iterators
1529 : 0 : QList< QList<QgsWkbPtr> >::const_iterator outerWkbIt = mCurrentWKBFragments.constBegin();
1530 : :
1531 : 0 : for ( ; outerWkbIt != mCurrentWKBFragments.constEnd(); ++outerWkbIt )
1532 : : {
1533 : : //new polygon
1534 : 0 : wkbPtr << ( char ) mEndian << QgsWkbTypes::Polygon << outerWkbIt->size();
1535 : :
1536 : 0 : QList<QgsWkbPtr>::const_iterator innerWkbIt = outerWkbIt->constBegin();
1537 : 0 : for ( ; innerWkbIt != outerWkbIt->constEnd(); ++innerWkbIt )
1538 : : {
1539 : 0 : memcpy( wkbPtr, *innerWkbIt, innerWkbIt->size() );
1540 : 0 : wkbPtr += innerWkbIt->size();
1541 : 0 : delete[] *innerWkbIt;
1542 : 0 : }
1543 : 0 : }
1544 : :
1545 : 0 : mCurrentWKBFragments.clear();
1546 : 0 : mWkbType = QgsWkbTypes::MultiPolygon;
1547 : 0 : return 0;
1548 : : }
1549 : :
1550 : 0 : int QgsGmlStreamingParser::totalWKBFragmentSize() const
1551 : : {
1552 : 0 : int result = 0;
1553 : 0 : const auto constMCurrentWKBFragments = mCurrentWKBFragments;
1554 : 0 : for ( const QList<QgsWkbPtr> &list : constMCurrentWKBFragments )
1555 : : {
1556 : 0 : const auto constList = list;
1557 : 0 : for ( const QgsWkbPtr &i : constList )
1558 : : {
1559 : 0 : result += i.size();
1560 : : }
1561 : 0 : }
1562 : 0 : return result;
1563 : 0 : }
|