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