Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsgml.h
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 : : #ifndef QGSGML_H
16 : : #define QGSGML_H
17 : :
18 : : #include "qgis_core.h"
19 : : #include <expat.h>
20 : : #include "qgis_sip.h"
21 : : #include "qgsfields.h"
22 : : #include "qgsrectangle.h"
23 : : #include "qgswkbptr.h"
24 : : #include "qgsfeature.h"
25 : :
26 : : #include <QPair>
27 : : #include <QByteArray>
28 : : #include <QDomElement>
29 : : #include <QStringList>
30 : : #include <QStack>
31 : : #include <QVector>
32 : :
33 : : #include <string>
34 : :
35 : : class QgsCoordinateReferenceSystem;
36 : :
37 : : #ifndef SIP_RUN
38 : :
39 : : /**
40 : : * \ingroup core
41 : : * \brief This class builds features from GML data in a streaming way.
42 : : *
43 : : * The caller must call processData()
44 : : * as soon it has new content from the source. At any point, it can call
45 : : * getAndStealReadyFeatures() to collect the features that have been completely
46 : : * parsed.
47 : : * \note not available in Python bindings
48 : : * \since QGIS 2.16
49 : : */
50 : : class CORE_EXPORT QgsGmlStreamingParser
51 : : {
52 : : public:
53 : :
54 : : typedef QPair<QgsFeature *, QString> QgsGmlFeaturePtrGmlIdPair;
55 : :
56 : : /**
57 : : * \ingroup core
58 : : * \brief Layer properties
59 : : */
60 : 0 : class LayerProperties
61 : : {
62 : : public:
63 : : //! Constructor
64 : : LayerProperties() = default;
65 : :
66 : : //! Layer name
67 : : QString mName;
68 : : //! Geometry attribute name
69 : : QString mGeometryAttribute;
70 : : };
71 : :
72 : : //! Axis orientation logic.
73 : : typedef enum
74 : : {
75 : : //! Honour EPSG axis order only if srsName is of the form urn:ogc:def:crs:EPSG:
76 : : Honour_EPSG_if_urn,
77 : : //! Honour EPSG axis order
78 : : Honour_EPSG,
79 : : //! Ignore EPSG axis order
80 : : Ignore_EPSG,
81 : : } AxisOrientationLogic;
82 : :
83 : : //! Constructor
84 : : QgsGmlStreamingParser( const QString &typeName,
85 : : const QString &geometryAttribute,
86 : : const QgsFields &fields,
87 : : AxisOrientationLogic axisOrientationLogic = Honour_EPSG_if_urn,
88 : : bool invertAxisOrientation = false );
89 : :
90 : : //! Constructor for a join layer, or dealing with renamed fields
91 : : QgsGmlStreamingParser( const QList<LayerProperties> &layerProperties,
92 : : const QgsFields &fields,
93 : : const QMap< QString, QPair<QString, QString> > &mapFieldNameToSrcLayerNameFieldName,
94 : : AxisOrientationLogic axisOrientationLogic = Honour_EPSG_if_urn,
95 : : bool invertAxisOrientation = false );
96 : : ~QgsGmlStreamingParser();
97 : :
98 : : //! QgsGmlStreamingParser cannot be copied.
99 : : QgsGmlStreamingParser( const QgsGmlStreamingParser &other ) = delete;
100 : : //! QgsGmlStreamingParser cannot be copied.
101 : : QgsGmlStreamingParser &operator=( const QgsGmlStreamingParser &other ) = delete;
102 : :
103 : : /**
104 : : * Process a new chunk of data. atEnd must be set to TRUE when this is
105 : : * the last chunk of data.
106 : : */
107 : : bool processData( const QByteArray &data, bool atEnd, QString &errorMsg );
108 : :
109 : : /**
110 : : * Process a new chunk of data. atEnd must be set to TRUE when this is
111 : : * the last chunk of data.
112 : : */
113 : : bool processData( const QByteArray &data, bool atEnd );
114 : :
115 : : /**
116 : : * Returns the list of features that have been completely parsed. This
117 : : * can be called at any point. This will empty the list maintained internally
118 : : * by the parser, so that features already returned will no longer be returned
119 : : * by later calls.
120 : : */
121 : : QVector<QgsGmlFeaturePtrGmlIdPair> getAndStealReadyFeatures();
122 : :
123 : : //! Returns the EPSG code, or 0 if unknown
124 : 0 : int getEPSGCode() const { return mEpsg; }
125 : :
126 : : //! Returns the value of the srsName attribute
127 : : QString srsName() const { return mSrsName; }
128 : :
129 : : //! Returns layer bounding box
130 : : const QgsRectangle &layerExtent() const { return mLayerExtent; }
131 : :
132 : : //! Returns the geometry type
133 : 0 : QgsWkbTypes::Type wkbType() const { return mWkbType; }
134 : :
135 : : //! Returns WFS 2.0 "numberMatched" attribute, or -1 if invalid/not found
136 : : int numberMatched() const { return mNumberMatched; }
137 : :
138 : : //! Returns WFS 2.0 "numberReturned" or WFS 1.1 "numberOfFeatures" attribute, or -1 if invalid/not found
139 : : int numberReturned() const { return mNumberReturned; }
140 : :
141 : : //! Returns whether the document parser is a OGC exception
142 : : bool isException() const { return mIsException; }
143 : :
144 : : //! Returns the exception text.
145 : : QString exceptionText() const { return mExceptionText; }
146 : :
147 : : //! Returns whether a "truncatedResponse" element is found
148 : : bool isTruncatedResponse() const { return mTruncatedResponse; }
149 : :
150 : : private:
151 : :
152 : : enum ParseMode
153 : : {
154 : : None,
155 : : BoundingBox,
156 : : Null,
157 : : Envelope,
158 : : LowerCorner,
159 : : UpperCorner,
160 : : Feature, // feature element containing attrs and geo (inside gml:featureMember)
161 : : Attribute,
162 : : Tuple, // wfs:Tuple of a join layer
163 : : FeatureTuple,
164 : : AttributeTuple,
165 : : Geometry,
166 : : Coordinate,
167 : : PosList,
168 : : MultiPoint,
169 : : MultiLine,
170 : : MultiPolygon,
171 : : ExceptionReport,
172 : : ExceptionText
173 : : };
174 : :
175 : : //! XML handler methods
176 : : void startElement( const XML_Char *el, const XML_Char **attr );
177 : : void endElement( const XML_Char *el );
178 : : void characters( const XML_Char *chars, int len );
179 : 0 : static void start( void *data, const XML_Char *el, const XML_Char **attr )
180 : : {
181 : 0 : static_cast<QgsGmlStreamingParser *>( data )->startElement( el, attr );
182 : 0 : }
183 : 0 : static void end( void *data, const XML_Char *el )
184 : : {
185 : 0 : static_cast<QgsGmlStreamingParser *>( data )->endElement( el );
186 : 0 : }
187 : 0 : static void chars( void *data, const XML_Char *chars, int len )
188 : : {
189 : 0 : static_cast<QgsGmlStreamingParser *>( data )->characters( chars, len );
190 : 0 : }
191 : :
192 : : // Set current feature attribute
193 : : void setAttribute( const QString &name, const QString &value );
194 : :
195 : : //helper routines
196 : :
197 : : /**
198 : : * Reads attribute srsName="EpsgCrsId:..."
199 : : * \param epsgNr result
200 : : * \param attr attribute strings
201 : : * \returns 0 in case of success
202 : : */
203 : : int readEpsgFromAttribute( int &epsgNr, const XML_Char **attr );
204 : :
205 : : /**
206 : : * Reads attribute as string
207 : : * \param attributeName
208 : : * \param attr
209 : : * \returns attribute value or an empty string if no such attribute
210 : : */
211 : : QString readAttribute( const QString &attributeName, const XML_Char **attr ) const;
212 : : //! Creates a rectangle from a coordinate string.
213 : : bool createBBoxFromCoordinateString( QgsRectangle &bb, const QString &coordString ) const;
214 : :
215 : : /**
216 : : * Creates a set of points from a coordinate string.
217 : : * \param points list that will contain the created points
218 : : * \param coordString the text containing the coordinates
219 : : * \returns 0 in case of success
220 : : */
221 : : int pointsFromCoordinateString( QList<QgsPointXY> &points, const QString &coordString ) const;
222 : :
223 : : /**
224 : : * Creates a set of points from a gml:posList or gml:pos coordinate string.
225 : : * \param points list that will contain the created points
226 : : * \param coordString the text containing the coordinates
227 : : * \param dimension number of dimensions
228 : : * \returns 0 in case of success
229 : : */
230 : : int pointsFromPosListString( QList<QgsPointXY> &points, const QString &coordString, int dimension ) const;
231 : :
232 : : int pointsFromString( QList<QgsPointXY> &points, const QString &coordString ) const;
233 : : int getPointWKB( QgsWkbPtr &wkbPtr, const QgsPointXY & ) const;
234 : : int getLineWKB( QgsWkbPtr &wkbPtr, const QList<QgsPointXY> &lineCoordinates ) const;
235 : : int getRingWKB( QgsWkbPtr &wkbPtr, const QList<QgsPointXY> &ringCoordinates ) const;
236 : :
237 : : /**
238 : : * Creates a multiline from the information in mCurrentWKBFragments and
239 : : * mCurrentWKBFragmentSizes. Assign the result. The multiline is in
240 : : * mCurrentWKB. The function deletes the memory in
241 : : * mCurrentWKBFragments. Returns 0 in case of success.
242 : : */
243 : : int createMultiLineFromFragments();
244 : : int createMultiPointFromFragments();
245 : : int createPolygonFromFragments();
246 : : int createMultiPolygonFromFragments();
247 : : //! Adds all the integers contained in mCurrentWKBFragmentSizes
248 : : int totalWKBFragmentSize() const;
249 : :
250 : : //! Gets safely (if empty) top from mode stack
251 : : ParseMode modeStackTop() { return mParseModeStack.isEmpty() ? None : mParseModeStack.top(); }
252 : :
253 : : //! Safely (if empty) pop from mode stack
254 : : ParseMode modeStackPop() { return mParseModeStack.isEmpty() ? None : mParseModeStack.pop(); }
255 : :
256 : : //! Expat parser
257 : : XML_Parser mParser;
258 : :
259 : : //! List of (feature, gml_id) pairs
260 : : QVector<QgsGmlFeaturePtrGmlIdPair> mFeatureList;
261 : :
262 : : //! Describe the various feature types of a join layer
263 : : QList<LayerProperties> mLayerProperties;
264 : : QMap< QString, LayerProperties > mMapTypeNameToProperties;
265 : :
266 : : //! Typename without namespace prefix
267 : : QString mTypeName;
268 : : QByteArray mTypeNameBA;
269 : : const char *mTypeNamePtr = nullptr;
270 : : size_t mTypeNameUTF8Len;
271 : :
272 : : QgsWkbTypes::Type mWkbType;
273 : :
274 : : //results are members such that handler routines are able to manipulate them
275 : :
276 : : //! Name of geometry attribute
277 : : QString mGeometryAttribute;
278 : : QByteArray mGeometryAttributeBA;
279 : : const char *mGeometryAttributePtr = nullptr;
280 : : size_t mGeometryAttributeUTF8Len;
281 : :
282 : : QgsFields mFields;
283 : : QMap<QString, QPair<int, QgsField> > mThematicAttributes;
284 : :
285 : : bool mIsException;
286 : : QString mExceptionText;
287 : : bool mTruncatedResponse;
288 : : //! Parsing depth
289 : : int mParseDepth;
290 : : int mFeatureTupleDepth;
291 : : QString mCurrentTypename; //!< Used to track the current (unprefixed) typename for wfs:Member in join layer
292 : : //! Keep track about the most important nested elements
293 : : QStack<ParseMode> mParseModeStack;
294 : : //! This contains the character data if an important element has been encountered
295 : : QString mStringCash;
296 : : QgsFeature *mCurrentFeature = nullptr;
297 : : QVector<QVariant> mCurrentAttributes; //attributes of current feature
298 : : QString mCurrentFeatureId;
299 : : int mFeatureCount;
300 : : //! The total WKB for a feature
301 : : QgsWkbPtr mCurrentWKB;
302 : : QgsRectangle mCurrentExtent;
303 : : bool mBoundedByNullFound;
304 : :
305 : : /**
306 : : * WKB intermediate storage during parsing. For points and lines, no
307 : : * intermediate WKB is stored at all. For multipoints and multilines and
308 : : * polygons, only one nested list is used. For multipolygons, both nested lists
309 : : * are used
310 : : */
311 : : QList< QList<QgsWkbPtr> > mCurrentWKBFragments;
312 : : QString mAttributeName;
313 : : char mEndian;
314 : : //! Coordinate separator for coordinate strings. Usually ","
315 : : QString mCoordinateSeparator;
316 : : //! Tuple separator for coordinate strings. Usually " "
317 : : QString mTupleSeparator;
318 : : //! Keep track about number of dimensions in pos or posList
319 : : QStack<int> mDimensionStack;
320 : : //! Number of dimensions in pos or posList for the current geometry
321 : : int mDimension;
322 : : //! Coordinates mode, coordinate or posList
323 : : ParseMode mCoorMode;
324 : : //! EPSG of parsed features geometries
325 : : int mEpsg;
326 : : //! Literal srsName attribute
327 : : QString mSrsName;
328 : : //! Layer bounding box
329 : : QgsRectangle mLayerExtent;
330 : : //! GML namespace URI
331 : : QString mGMLNameSpaceURI;
332 : : const char *mGMLNameSpaceURIPtr = nullptr;
333 : : //! Axis orientation logic
334 : : AxisOrientationLogic mAxisOrientationLogic;
335 : : //! Whether to invert axis orientation. This value is immutable, but combined with what is inferred from data and mAxisOrientationLogic, is used to compute mInvertAxisOrientation
336 : : bool mInvertAxisOrientationRequest;
337 : : //! Whether to invert axis orientation: result of mAxisOrientationLogic, mInvertAxisOrientationRequest and what is inferred from data and mAxisOrientationLogic
338 : : bool mInvertAxisOrientation;
339 : : //! WFS 2.0 "numberReturned" or WFS 1.1 "numberOfFeatures" attribute, or -1 if invalid/not found
340 : : int mNumberReturned;
341 : : //! WFS 2.0 "numberMatched" attribute, or -1 if invalid/not found
342 : : int mNumberMatched;
343 : : //! XML blob containing geometry
344 : : std::string mGeometryString;
345 : : //! Whether we found a unhandled geometry element
346 : : bool mFoundUnhandledGeometryElement;
347 : : };
348 : :
349 : : #endif
350 : :
351 : : /**
352 : : * \ingroup core
353 : : * \brief This class reads data from a WFS server or alternatively from a GML file.
354 : : *
355 : : * It
356 : : * uses the expat XML parser and an event based model to keep performance high.
357 : : * The parsing starts when the first data arrives, it does not wait until the
358 : : * request is finished
359 : : */
360 : : class CORE_EXPORT QgsGml : public QObject
361 : : {
362 : 0 : Q_OBJECT
363 : : public:
364 : : QgsGml(
365 : : const QString &typeName,
366 : : const QString &geometryAttribute,
367 : : const QgsFields &fields );
368 : :
369 : : /**
370 : : * Does the Http GET request to the wfs server
371 : : * Supports only UTF-8, UTF-16, ISO-8859-1, ISO-8859-1 XML encodings.
372 : : * \param uri GML URL
373 : : * \param wkbType wkbType to retrieve
374 : : * \param extent retrieved extents
375 : : * \param userName username for authentication
376 : : * \param password password for authentication
377 : : * \param authcfg authentication configuration id
378 : : * \returns 0 in case of success
379 : : * \note available in Python as getFeaturesUri
380 : : */
381 : : int getFeatures( const QString &uri,
382 : : QgsWkbTypes::Type *wkbType,
383 : : QgsRectangle *extent = nullptr,
384 : : const QString &userName = QString(),
385 : : const QString &password = QString(),
386 : : const QString &authcfg = QString() ) SIP_PYNAME( getFeaturesUri );
387 : :
388 : : /**
389 : : * Read from GML data. Constructor uri param is ignored
390 : : * Supports only UTF-8, UTF-16, ISO-8859-1, ISO-8859-1 XML encodings.
391 : : */
392 : : int getFeatures( const QByteArray &data, QgsWkbTypes::Type *wkbType, QgsRectangle *extent = nullptr );
393 : :
394 : : //! Gets parsed features for given type name
395 : : QMap<QgsFeatureId, QgsFeature * > featuresMap() const { return mFeatures; }
396 : :
397 : : //! Gets feature ids map
398 : : QMap<QgsFeatureId, QString > idsMap() const { return mIdMap; }
399 : :
400 : : /**
401 : : * Returns features spatial reference system
402 : : * \since QGIS 2.1
403 : : */
404 : : QgsCoordinateReferenceSystem crs() const;
405 : :
406 : : signals:
407 : : void dataReadProgress( int progress );
408 : : void totalStepsUpdate( int totalSteps );
409 : : //! Also emit signal with progress and totalSteps together (this is better for the status message)
410 : : void dataProgressAndSteps( int progress, int totalSteps );
411 : :
412 : : private slots:
413 : :
414 : : void setFinished();
415 : :
416 : : //! Takes progress value and total steps and emit signals 'dataReadProgress' and 'totalStepUpdate'
417 : : void handleProgressEvent( qint64 progress, qint64 totalSteps );
418 : :
419 : : private:
420 : :
421 : : /**
422 : : * This function evaluates the layer bounding box from the features and
423 : : * sets it to mExtent. Less efficient compared to reading the bbox from
424 : : * the provider, so it is only done if the wfs server does not provider
425 : : * extent information.
426 : : */
427 : : void calculateExtentFromFeatures();
428 : :
429 : : void fillMapsFromParser();
430 : :
431 : : QgsGmlStreamingParser mParser;
432 : :
433 : : //! Typename without namespace prefix
434 : : QString mTypeName;
435 : :
436 : : //! True if the request is finished
437 : : bool mFinished;
438 : :
439 : : //! The features of the layer, map of feature maps for each feature type
440 : : //QMap<QgsFeatureId, QgsFeature* > &mFeatures;
441 : : QMap<QgsFeatureId, QgsFeature * > mFeatures;
442 : : //QMap<QString, QMap<QgsFeatureId, QgsFeature* > > mFeatures;
443 : :
444 : : //! Stores the relation between provider ids and WFS server ids
445 : : //QMap<QgsFeatureId, QString > &mIdMap;
446 : : QMap<QgsFeatureId, QString > mIdMap;
447 : : //QMap<QString, QMap<QgsFeatureId, QString > > mIdMap;
448 : :
449 : : //! Bounding box of the layer
450 : : QgsRectangle mExtent;
451 : : };
452 : :
453 : : #endif
|