QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgsjsonutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsjsonutils.h
3  -------------
4  Date : May 206
5  Copyright : (C) 2016 Nyall Dawson
6  Email : nyall dot dawson 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 
16 #include "qgsjsonutils.h"
17 #include "qgsfeatureiterator.h"
18 #include "qgsogrutils.h"
19 #include "qgsgeometry.h"
20 #include "qgsvectorlayer.h"
21 #include "qgsrelation.h"
22 #include "qgsrelationmanager.h"
23 #include "qgsproject.h"
24 #include "qgsexception.h"
25 #include "qgslogger.h"
27 #include "qgsfieldformatter.h"
28 
29 #include <QJsonDocument>
30 #include <QJsonArray>
31 
33  : mPrecision( precision )
34  , mLayer( vectorLayer )
35 {
36  if ( vectorLayer )
37  {
38  mCrs = vectorLayer->crs();
39  mTransform.setSourceCrs( mCrs );
40  }
42 }
43 
45 {
46  mLayer = vectorLayer;
47  if ( vectorLayer )
48  {
49  mCrs = vectorLayer->crs();
50  mTransform.setSourceCrs( mCrs );
51  }
52 }
53 
55 {
56  return mLayer.data();
57 }
58 
60 {
61  mCrs = crs;
62  mTransform.setSourceCrs( mCrs );
63 }
64 
66 {
67  return mCrs;
68 }
69 
70 QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties,
71  const QVariant &id ) const
72 {
73 
74  QString s = QStringLiteral( "{\n \"type\":\"Feature\",\n" );
75 
76  // ID
77  s += QStringLiteral( " \"id\":%1,\n" ).arg( !id.isValid() ? QString::number( feature.id() ) : QgsJsonUtils::encodeValue( id ) );
78 
79  QgsGeometry geom = feature.geometry();
80  if ( !geom.isNull() && mIncludeGeometry )
81  {
82  if ( mCrs.isValid() )
83  {
84  try
85  {
86  QgsGeometry transformed = geom;
87  if ( transformed.transform( mTransform ) == 0 )
88  geom = transformed;
89  }
90  catch ( QgsCsException &cse )
91  {
92  Q_UNUSED( cse );
93  }
94  }
95  QgsRectangle box = geom.boundingBox();
96 
98  {
99  s += QStringLiteral( " \"bbox\":[%1, %2, %3, %4],\n" ).arg( qgsDoubleToString( box.xMinimum(), mPrecision ),
100  qgsDoubleToString( box.yMinimum(), mPrecision ),
101  qgsDoubleToString( box.xMaximum(), mPrecision ),
102  qgsDoubleToString( box.yMaximum(), mPrecision ) );
103  }
104  s += QLatin1String( " \"geometry\":\n " );
105  s += geom.asJson( mPrecision );
106  s += QLatin1String( ",\n" );
107  }
108  else
109  {
110  s += QLatin1String( " \"geometry\":null,\n" );
111  }
112 
113  // build up properties element
114  QString properties;
115  int attributeCounter = 0;
116  if ( mIncludeAttributes || !extraProperties.isEmpty() )
117  {
118  //read all attribute values from the feature
119 
120  if ( mIncludeAttributes )
121  {
122  QgsFields fields = mLayer ? mLayer->fields() : feature.fields();
123  // List of formatters through we want to pass the values
124  QStringList formattersWhiteList;
125  formattersWhiteList << QStringLiteral( "KeyValue" )
126  << QStringLiteral( "List" )
127  << QStringLiteral( "ValueRelation" )
128  << QStringLiteral( "ValueMap" );
129 
130  for ( int i = 0; i < fields.count(); ++i )
131  {
132  if ( ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) ) || mExcludedAttributeIndexes.contains( i ) )
133  continue;
134 
135  if ( attributeCounter > 0 )
136  properties += QLatin1String( ",\n" );
137  QVariant val = feature.attributes().at( i );
138 
139  if ( mLayer )
140  {
141  QgsEditorWidgetSetup setup = fields.at( i ).editorWidgetSetup();
143  if ( formattersWhiteList.contains( fieldFormatter->id() ) )
144  val = fieldFormatter->representValue( mLayer.data(), i, setup.config(), QVariant(), val );
145  }
146 
147  properties += QStringLiteral( " \"%1\":%2" ).arg( fields.at( i ).name(), QgsJsonUtils::encodeValue( val ) );
148 
149  ++attributeCounter;
150  }
151  }
152 
153  if ( !extraProperties.isEmpty() )
154  {
155  QVariantMap::const_iterator it = extraProperties.constBegin();
156  for ( ; it != extraProperties.constEnd(); ++it )
157  {
158  if ( attributeCounter > 0 )
159  properties += QLatin1String( ",\n" );
160 
161  properties += QStringLiteral( " \"%1\":%2" ).arg( it.key(), QgsJsonUtils::encodeValue( it.value() ) );
162 
163  ++attributeCounter;
164  }
165  }
166 
167  // related attributes
168  if ( mLayer && mIncludeRelatedAttributes )
169  {
170  QList< QgsRelation > relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer.data() );
171  Q_FOREACH ( const QgsRelation &relation, relations )
172  {
173  if ( attributeCounter > 0 )
174  properties += QLatin1String( ",\n" );
175 
176  QgsFeatureRequest req = relation.getRelatedFeaturesRequest( feature );
178  QgsVectorLayer *childLayer = relation.referencingLayer();
179  QString relatedFeatureAttributes;
180  if ( childLayer )
181  {
182  QgsFeatureIterator it = childLayer->getFeatures( req );
183  QVector<QVariant> attributeWidgetCaches;
184  int fieldIndex = 0;
185  const QgsFields fields = childLayer->fields();
186  for ( const QgsField &field : fields )
187  {
188  QgsEditorWidgetSetup setup = field.editorWidgetSetup();
190  attributeWidgetCaches.append( fieldFormatter->createCache( childLayer, fieldIndex, setup.config() ) );
191  fieldIndex++;
192  }
193 
194  QgsFeature relatedFet;
195  int relationFeatures = 0;
196  while ( it.nextFeature( relatedFet ) )
197  {
198  if ( relationFeatures > 0 )
199  relatedFeatureAttributes += QLatin1String( ",\n" );
200 
201  relatedFeatureAttributes += QgsJsonUtils::exportAttributes( relatedFet, childLayer, attributeWidgetCaches );
202  relationFeatures++;
203  }
204  }
205  relatedFeatureAttributes.prepend( '[' ).append( ']' );
206 
207  properties += QStringLiteral( " \"%1\":%2" ).arg( relation.name(), relatedFeatureAttributes );
208  attributeCounter++;
209  }
210  }
211  }
212 
213  bool hasProperties = attributeCounter > 0;
214 
215  s += QLatin1String( " \"properties\":" );
216  if ( hasProperties )
217  {
218  //read all attribute values from the feature
219  s += "{\n" + properties + "\n }\n";
220  }
221  else
222  {
223  s += QLatin1String( "null\n" );
224  }
225 
226  s += '}';
227 
228  return s;
229 }
230 
231 QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features ) const
232 {
233  QStringList featureJSON;
234  Q_FOREACH ( const QgsFeature &feature, features )
235  {
236  featureJSON << exportFeature( feature );
237  }
238 
239  return QStringLiteral( "{ \"type\": \"FeatureCollection\",\n \"features\":[\n%1\n]}" ).arg( featureJSON.join( QStringLiteral( ",\n" ) ) );
240 }
241 
242 
243 
244 //
245 // QgsJsonUtils
246 //
247 
248 QgsFeatureList QgsJsonUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
249 {
250  return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
251 }
252 
253 QgsFields QgsJsonUtils::stringToFields( const QString &string, QTextCodec *encoding )
254 {
255  return QgsOgrUtils::stringToFields( string, encoding );
256 }
257 
258 QString QgsJsonUtils::encodeValue( const QVariant &value )
259 {
260  if ( value.isNull() )
261  return QStringLiteral( "null" );
262 
263  switch ( value.type() )
264  {
265  case QVariant::Int:
266  case QVariant::UInt:
267  case QVariant::LongLong:
268  case QVariant::ULongLong:
269  case QVariant::Double:
270  return value.toString();
271 
272  case QVariant::Bool:
273  return value.toBool() ? "true" : "false";
274 
275  case QVariant::StringList:
276  case QVariant::List:
277  case QVariant::Map:
278  return QString::fromUtf8( QJsonDocument::fromVariant( value ).toJson( QJsonDocument::Compact ) );
279 
280  default:
281  case QVariant::String:
282  QString v = value.toString()
283  .replace( '\\', QLatin1String( "\\\\" ) )
284  .replace( '"', QLatin1String( "\\\"" ) )
285  .replace( '\r', QLatin1String( "\\r" ) )
286  .replace( '\b', QLatin1String( "\\b" ) )
287  .replace( '\t', QLatin1String( "\\t" ) )
288  .replace( '/', QLatin1String( "\\/" ) )
289  .replace( '\n', QLatin1String( "\\n" ) );
290 
291  return v.prepend( '"' ).append( '"' );
292  }
293 }
294 
295 QString QgsJsonUtils::exportAttributes( const QgsFeature &feature, QgsVectorLayer *layer, const QVector<QVariant> &attributeWidgetCaches )
296 {
297  QgsFields fields = feature.fields();
298  QString attrs;
299  for ( int i = 0; i < fields.count(); ++i )
300  {
301  if ( i > 0 )
302  attrs += QLatin1String( ",\n" );
303 
304  QVariant val = feature.attributes().at( i );
305 
306  if ( layer )
307  {
308  QgsEditorWidgetSetup setup = layer->fields().at( i ).editorWidgetSetup();
311  val = fieldFormatter->representValue( layer, i, setup.config(), attributeWidgetCaches.count() >= i ? attributeWidgetCaches.at( i ) : QVariant(), val );
312  }
313 
314  attrs += encodeValue( fields.at( i ).name() ) + ':' + encodeValue( val );
315  }
316  return attrs.prepend( '{' ).append( '}' );
317 }
318 
319 QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type )
320 {
321  QJsonParseError error;
322  const QJsonDocument jsonDoc = QJsonDocument::fromJson( json.toUtf8(), &error );
323  QVariantList result;
324  if ( error.error != QJsonParseError::NoError )
325  {
326  QgsLogger::warning( QStringLiteral( "Cannot parse json (%1): %2" ).arg( error.errorString(), json ) );
327  return result;
328  }
329  if ( !jsonDoc.isArray() )
330  {
331  QgsLogger::warning( QStringLiteral( "Cannot parse json (%1) as array: %2" ).arg( error.errorString(), json ) );
332  return result;
333  }
334  Q_FOREACH ( const QJsonValue &cur, jsonDoc.array() )
335  {
336  QVariant curVariant = cur.toVariant();
337  if ( curVariant.convert( type ) )
338  result.append( curVariant );
339  else
340  QgsLogger::warning( QStringLiteral( "Cannot convert json array element: %1" ).arg( cur.toString() ) );
341  }
342  return result;
343 }
QString name
Definition: qgsrelation.h:48
QgsFeatureId id
Definition: qgsfeature.h:64
Wrapper for iterator of features from vector data provider or vector layer.
int precision
A rectangle specified with double values.
Definition: qgsrectangle.h:40
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
QString name
Definition: qgsfield.h:57
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:171
void setVectorLayer(QgsVectorLayer *vectorLayer)
Sets the associated vector layer (required for related attribute export).
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:571
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source CRS for feature geometries.
QgsVectorLayer referencingLayer
Definition: qgsrelation.h:46
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:106
QgsFieldFormatter * fallbackFieldFormatter() const
Returns a basic fallback field formatter which can be used to represent any field in an unspectacular...
virtual QString id() const =0
Returns a unique id for this field formatter.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
const QgsCoordinateReferenceSystem & crs
QgsFields fields
Definition: qgsfeature.h:66
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QVariantMap config() const
QString exportFeatures(const QgsFeatureList &features) const
Returns a GeoJSON string representation of a list of features (feature collection).
static QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields, QTextCodec *encoding)
Attempts to parse a string representing a collection of features using OGR.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:176
static QString encodeValue(const QVariant &value)
Encodes a value to a JSON string representation, adding appropriate quotations and escaping where req...
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:161
static QgsFields stringToFields(const QString &string, QTextCodec *encoding)
Attempts to retrieve the fields from a GeoJSON string representing a collection of features...
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:238
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QString asJson(int precision=17) const
Exports the geometry to a GeoJSON string.
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
static QgsFields stringToFields(const QString &string, QTextCodec *encoding)
Attempts to retrieve the fields from a string representing a collection of features using OGR...
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source CRS for feature geometries.
QgsRelationManager relationManager
Definition: qgsproject.h:100
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
static QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields, QTextCodec *encoding)
Attempts to parse a GeoJSON string to a collection of features.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
static QString exportAttributes(const QgsFeature &feature, QgsVectorLayer *layer=nullptr, const QVector< QVariant > &attributeWidgetCaches=QVector< QVariant >())
Exports all attributes from a QgsFeature as a JSON map type.
A field formatter helps to handle and display values for a field.
QgsJsonExporter(QgsVectorLayer *vectorLayer=nullptr, int precision=6)
Constructor for QgsJsonExporter.
QString exportFeature(const QgsFeature &feature, const QVariantMap &extraProperties=QVariantMap(), const QVariant &id=QVariant()) const
Returns a GeoJSON string representation of a feature.
QList< QgsRelation > referencedRelations(QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Holder for the widget type and its configuration for a field.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:411
This class represents a coordinate reference system (CRS).
static QVariantList parseArray(const QString &json, QVariant::Type type)
Parse a simple array (depth=1).
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
QgsGeometry geometry
Definition: qgsfeature.h:67
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsVectorLayer * vectorLayer() const
Returns the associated vector layer, if set.
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Represents a vector layer which manages a vector based data sets.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:429
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:565
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:166
void setSourceCrs(const QgsCoordinateReferenceSystem &crs)
Sets the source coordinate reference system.
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
QgsAttributes attributes
Definition: qgsfeature.h:65
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:70
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.