mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Refactor to move JSON exporter to its own class
This commit is contained in:
parent
935f4ad21c
commit
c3f6c39784
@ -1,3 +1,81 @@
|
||||
/** \ingroup core
|
||||
* \class QgsJSONExporter
|
||||
* \brief Handles exporting QgsFeature features to GeoJSON features.
|
||||
* \note Added in version 2.16
|
||||
*/
|
||||
|
||||
class QgsJSONExporter
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgsjsonutils.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
/** Constructor for QgsJSONExporter.
|
||||
* @param precision maximum number of decimal places to use for geometry coordinates
|
||||
* @param includeGeometry set to false to avoid including the geometry representation in the JSON output
|
||||
* @param includeAttributes set to false to avoid including any attribute values in the JSON output
|
||||
*/
|
||||
QgsJSONExporter( int precision = 17, bool includeGeometry = true, bool includeAttributes = true );
|
||||
|
||||
/** Sets the maximum number of decimal places to use in geometry coordinates.
|
||||
* @param precision number of decimal places
|
||||
* @see precision()
|
||||
*/
|
||||
void setPrecision( int precision );
|
||||
|
||||
/** Returns the maximum number of decimal places to use in geometry coordinates.
|
||||
* @see setPrecision()
|
||||
*/
|
||||
int precision() const;
|
||||
|
||||
/** Sets whether to include geometry in the JSON exports.
|
||||
* @param includeGeometry set to false to prevent geometry inclusion
|
||||
* @see includeGeometry()
|
||||
*/
|
||||
void setIncludeGeometry( bool includeGeometry );
|
||||
|
||||
/** Returns whether geometry will be included in the JSON exports.
|
||||
* @see setIncludeGeometry()
|
||||
*/
|
||||
bool includeGeometry() const;
|
||||
|
||||
/** Sets whether to include attributes in the JSON exports.
|
||||
* @param includeAttributes set to false to prevent attribute inclusion
|
||||
* @see includeAttributes()
|
||||
*/
|
||||
void setIncludeAttributes( bool includeAttributes );
|
||||
|
||||
/** Returns whether attributes will be included in the JSON exports.
|
||||
* @see setIncludeAttributes()
|
||||
*/
|
||||
bool includeAttributes() const;
|
||||
|
||||
/** Sets the list of attributes to include in the JSON exports.
|
||||
* @param attributes list of attribute indexes, or an empty list to include all
|
||||
* attributes
|
||||
* @see attributes()
|
||||
*/
|
||||
void setAttributes( const QgsAttributeList& attributes );
|
||||
|
||||
/** Returns the list of attributes which will be included in the JSON exports, or
|
||||
* an empty list if all attributes will be included.
|
||||
* @see setAttributes()
|
||||
*/
|
||||
QgsAttributeList attributes() const;
|
||||
|
||||
/** Returns a GeoJSON string representation of a feature.
|
||||
* @param feature feature to convert
|
||||
* @param id optional ID to use as GeoJSON feature's ID instead of input feature's ID. If omitted, feature's
|
||||
* ID is used.
|
||||
* @returns GeoJSON string
|
||||
*/
|
||||
QString exportFeature( const QgsFeature& feature,
|
||||
const QVariant& id = QVariant() ) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsJSONUtils
|
||||
* \brief Helper utilities for working with JSON and GeoJSON conversions.
|
||||
@ -31,24 +109,6 @@ class QgsJSONUtils
|
||||
*/
|
||||
static QgsFields stringToFields( const QString& string, QTextCodec* encoding );
|
||||
|
||||
/** Returns a GeoJSON string representation of a feature.
|
||||
* @param feature feature to convert
|
||||
* @param precision maximum number of decimal places to use for geometry coordinates
|
||||
* @param attrIndexes list of attribute indexes to include in GeoJSON, or an empty list to include
|
||||
* all attributes
|
||||
* @param includeGeom set to false to avoid including the geometry representation in the JSON output
|
||||
* @param includeAttributes set to false to avoid including any attribute values in the JSON output
|
||||
* @param id optional ID to use as GeoJSON feature's ID instead of input feature's ID. If omitted, feature's
|
||||
* ID is used.
|
||||
* @returns GeoJSON string
|
||||
*/
|
||||
static QString featureToGeoJSON( const QgsFeature& feature,
|
||||
int precision = 17,
|
||||
const QgsAttributeList& attrIndexes = QgsAttributeList(),
|
||||
bool includeGeom = true,
|
||||
bool includeAttributes = true,
|
||||
const QVariant& id = QVariant() );
|
||||
|
||||
/** Encodes a value to a JSON string representation, adding appropriate quotations and escaping
|
||||
* where required.
|
||||
* @param value value to encode
|
||||
|
@ -17,54 +17,48 @@
|
||||
#include "qgsogrutils.h"
|
||||
#include "qgsgeometry.h"
|
||||
|
||||
QgsFeatureList QgsJSONUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
|
||||
|
||||
QgsJSONExporter::QgsJSONExporter( int precision, bool includeGeometry, bool includeAttributes )
|
||||
: mPrecision( precision )
|
||||
, mIncludeGeometry( includeGeometry )
|
||||
, mIncludeAttributes( includeAttributes )
|
||||
{
|
||||
return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
|
||||
|
||||
}
|
||||
|
||||
QgsFields QgsJSONUtils::stringToFields( const QString &string, QTextCodec *encoding )
|
||||
{
|
||||
return QgsOgrUtils::stringToFields( string, encoding );
|
||||
}
|
||||
|
||||
QString QgsJSONUtils::featureToGeoJSON( const QgsFeature& feature,
|
||||
int precision,
|
||||
const QgsAttributeList& attrIndexes,
|
||||
bool includeGeom,
|
||||
bool includeAttributes,
|
||||
const QVariant& id )
|
||||
QString QgsJSONExporter::exportFeature( const QgsFeature& feature, const QVariant& id ) const
|
||||
{
|
||||
QString s = "{\n \"type\":\"Feature\",\n";
|
||||
|
||||
// ID
|
||||
s += QString( " \"id\":%1" ).arg( !id.isValid() ? QString::number( feature.id() ) : encodeValue( id ) );
|
||||
s += QString( " \"id\":%1" ).arg( !id.isValid() ? QString::number( feature.id() ) : QgsJSONUtils::encodeValue( id ) );
|
||||
|
||||
if ( includeAttributes || includeGeom )
|
||||
if ( mIncludeAttributes || mIncludeGeometry )
|
||||
s += ",\n";
|
||||
else
|
||||
s += '\n';
|
||||
|
||||
const QgsGeometry* geom = feature.constGeometry();
|
||||
if ( geom && !geom->isEmpty() && includeGeom )
|
||||
if ( geom && !geom->isEmpty() && mIncludeGeometry )
|
||||
{
|
||||
QgsRectangle box = geom->boundingBox();
|
||||
|
||||
if ( QgsWKBTypes::flatType( geom->geometry()->wkbType() ) != QgsWKBTypes::Point )
|
||||
{
|
||||
s += QString( " \"bbox\":[%1, %2, %3, %4],\n" ).arg( qgsDoubleToString( box.xMinimum(), precision ),
|
||||
qgsDoubleToString( box.yMinimum(), precision ),
|
||||
qgsDoubleToString( box.xMaximum(), precision ),
|
||||
qgsDoubleToString( box.yMaximum(), precision ) );
|
||||
s += QString( " \"bbox\":[%1, %2, %3, %4],\n" ).arg( qgsDoubleToString( box.xMinimum(), mPrecision ),
|
||||
qgsDoubleToString( box.yMinimum(), mPrecision ),
|
||||
qgsDoubleToString( box.xMaximum(), mPrecision ),
|
||||
qgsDoubleToString( box.yMaximum(), mPrecision ) );
|
||||
}
|
||||
s += " \"geometry\":\n ";
|
||||
s += geom->exportToGeoJSON( precision );
|
||||
if ( includeAttributes )
|
||||
s += geom->exportToGeoJSON( mPrecision );
|
||||
if ( mIncludeAttributes )
|
||||
s += ",\n";
|
||||
else
|
||||
s += '\n';
|
||||
}
|
||||
|
||||
if ( includeAttributes )
|
||||
if ( mIncludeAttributes )
|
||||
{
|
||||
//read all attribute values from the feature
|
||||
s += " \"properties\":{\n";
|
||||
@ -74,14 +68,14 @@ QString QgsJSONUtils::featureToGeoJSON( const QgsFeature& feature,
|
||||
|
||||
for ( int i = 0; i < fields->count(); ++i )
|
||||
{
|
||||
if ( !attrIndexes.isEmpty() && !attrIndexes.contains( i ) )
|
||||
if ( !mAttributeIndexes.isEmpty() && !mAttributeIndexes.contains( i ) )
|
||||
continue;
|
||||
|
||||
if ( attributeCounter > 0 )
|
||||
s += ",\n";
|
||||
QVariant val = feature.attributes().at( i );
|
||||
|
||||
s += QString( " \"%1\":%2" ).arg( fields->at( i ).name(), encodeValue( val ) );
|
||||
s += QString( " \"%1\":%2" ).arg( fields->at( i ).name(), QgsJSONUtils::encodeValue( val ) );
|
||||
|
||||
++attributeCounter;
|
||||
}
|
||||
@ -94,6 +88,21 @@ QString QgsJSONUtils::featureToGeoJSON( const QgsFeature& feature,
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsJSONUtils
|
||||
//
|
||||
|
||||
QgsFeatureList QgsJSONUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
|
||||
{
|
||||
return QgsOgrUtils::stringToFeatureList( string, fields, encoding );
|
||||
}
|
||||
|
||||
QgsFields QgsJSONUtils::stringToFields( const QString &string, QTextCodec *encoding )
|
||||
{
|
||||
return QgsOgrUtils::stringToFields( string, encoding );
|
||||
}
|
||||
|
||||
QString QgsJSONUtils::encodeValue( const QVariant &value )
|
||||
{
|
||||
if ( value.isNull() )
|
||||
|
@ -20,6 +20,94 @@
|
||||
|
||||
class QTextCodec;
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsJSONExporter
|
||||
* \brief Handles exporting QgsFeature features to GeoJSON features.
|
||||
* \note Added in version 2.16
|
||||
*/
|
||||
|
||||
class CORE_EXPORT QgsJSONExporter
|
||||
{
|
||||
public:
|
||||
|
||||
/** Constructor for QgsJSONExporter.
|
||||
* @param precision maximum number of decimal places to use for geometry coordinates
|
||||
* @param includeGeometry set to false to avoid including the geometry representation in the JSON output
|
||||
* @param includeAttributes set to false to avoid including any attribute values in the JSON output
|
||||
*/
|
||||
QgsJSONExporter( int precision = 17, bool includeGeometry = true, bool includeAttributes = true );
|
||||
|
||||
/** Sets the maximum number of decimal places to use in geometry coordinates.
|
||||
* @param precision number of decimal places
|
||||
* @see precision()
|
||||
*/
|
||||
void setPrecision( int precision ) { mPrecision = precision; }
|
||||
|
||||
/** Returns the maximum number of decimal places to use in geometry coordinates.
|
||||
* @see setPrecision()
|
||||
*/
|
||||
int precision() const { return mPrecision; }
|
||||
|
||||
/** Sets whether to include geometry in the JSON exports.
|
||||
* @param includeGeometry set to false to prevent geometry inclusion
|
||||
* @see includeGeometry()
|
||||
*/
|
||||
void setIncludeGeometry( bool includeGeometry ) { mIncludeGeometry = includeGeometry; }
|
||||
|
||||
/** Returns whether geometry will be included in the JSON exports.
|
||||
* @see setIncludeGeometry()
|
||||
*/
|
||||
bool includeGeometry() const { return mIncludeGeometry; }
|
||||
|
||||
/** Sets whether to include attributes in the JSON exports.
|
||||
* @param includeAttributes set to false to prevent attribute inclusion
|
||||
* @see includeAttributes()
|
||||
*/
|
||||
void setIncludeAttributes( bool includeAttributes ) { mIncludeAttributes = includeAttributes; }
|
||||
|
||||
/** Returns whether attributes will be included in the JSON exports.
|
||||
* @see setIncludeAttributes()
|
||||
*/
|
||||
bool includeAttributes() const { return mIncludeAttributes; }
|
||||
|
||||
/** Sets the list of attributes to include in the JSON exports.
|
||||
* @param attributes list of attribute indexes, or an empty list to include all
|
||||
* attributes
|
||||
* @see attributes()
|
||||
*/
|
||||
void setAttributes( const QgsAttributeList& attributes ) { mAttributeIndexes = attributes; }
|
||||
|
||||
/** Returns the list of attributes which will be included in the JSON exports, or
|
||||
* an empty list if all attributes will be included.
|
||||
* @see setAttributes()
|
||||
*/
|
||||
QgsAttributeList attributes() const { return mAttributeIndexes; }
|
||||
|
||||
/** Returns a GeoJSON string representation of a feature.
|
||||
* @param feature feature to convert
|
||||
* @param id optional ID to use as GeoJSON feature's ID instead of input feature's ID. If omitted, feature's
|
||||
* ID is used.
|
||||
* @returns GeoJSON string
|
||||
*/
|
||||
QString exportFeature( const QgsFeature& feature,
|
||||
const QVariant& id = QVariant() ) const;
|
||||
|
||||
private:
|
||||
|
||||
//! Maximum number of decimal places for geometry coordinates
|
||||
int mPrecision;
|
||||
|
||||
//! List of attribute indexes to include in export, or empty list to include all attributes
|
||||
QgsAttributeList mAttributeIndexes;
|
||||
|
||||
//! Whether to include geometry in JSON export
|
||||
bool mIncludeGeometry;
|
||||
|
||||
//! Whether to include attributes in JSON export
|
||||
bool mIncludeAttributes;
|
||||
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsJSONUtils
|
||||
* \brief Helper utilities for working with JSON and GeoJSON conversions.
|
||||
|
@ -1868,13 +1868,16 @@ QString QgsWFSServer::createFeatureGeoJSON( QgsFeature* feat, int prec, QgsCoord
|
||||
{
|
||||
QString id = QString( "%1.%2" ).arg( mTypeName, FID_TO_STRING( feat->id() ) );
|
||||
|
||||
QgsJSONExporter exporter;
|
||||
exporter.setPrecision( prec );
|
||||
|
||||
//copy feature so we can modify its geometry as required
|
||||
QgsFeature f( *feat );
|
||||
const QgsGeometry* geom = feat->constGeometry();
|
||||
bool withGeom = false;
|
||||
exporter.setIncludeGeometry( false );
|
||||
if ( geom && mWithGeom && mGeometryName != "NONE" )
|
||||
{
|
||||
withGeom = true;
|
||||
exporter.setIncludeGeometry( true );
|
||||
if ( mGeometryName == "EXTENT" )
|
||||
{
|
||||
QgsRectangle box = geom->boundingBox();
|
||||
@ -1907,9 +1910,10 @@ QString QgsWFSServer::createFeatureGeoJSON( QgsFeature* feat, int prec, QgsCoord
|
||||
attrsToExport << idx;
|
||||
}
|
||||
|
||||
bool withAttributes = !attrsToExport.isEmpty();
|
||||
exporter.setIncludeAttributes( !attrsToExport.isEmpty() );
|
||||
exporter.setAttributes( attrsToExport );
|
||||
|
||||
return QgsJSONUtils::featureToGeoJSON( f, prec, attrsToExport, withGeom, withAttributes, id );
|
||||
return exporter.exportFeature( f, id );
|
||||
}
|
||||
|
||||
QDomElement QgsWFSServer::createFeatureGML2( QgsFeature* feat, QDomDocument& doc, int prec, QgsCoordinateReferenceSystem& crs, const QgsAttributeList& attrIndexes, const QSet<QString>& excludedAttributes ) /*const*/
|
||||
|
@ -15,7 +15,7 @@ __revision__ = '$Format:%H$'
|
||||
import qgis # NOQA
|
||||
|
||||
from qgis.testing import unittest, start_app
|
||||
from qgis.core import QgsJSONUtils, QgsFeature, QgsField, QgsFields, QgsWKBTypes, QgsGeometry, QgsPointV2, QgsLineStringV2, NULL
|
||||
from qgis.core import QgsJSONUtils, QgsJSONExporter, QgsFeature, QgsField, QgsFields, QgsWKBTypes, QgsGeometry, QgsPointV2, QgsLineStringV2, NULL
|
||||
from qgis.PyQt.QtCore import QVariant, QTextCodec
|
||||
|
||||
start_app()
|
||||
@ -105,7 +105,7 @@ class TestQgsJSONUtils(unittest.TestCase):
|
||||
self.assertEqual(QgsJSONUtils.encodeValue({'key': 'value', 'key2': 5}), '{"key":"value",\n"key2":5}')
|
||||
self.assertEqual(QgsJSONUtils.encodeValue({'key': [1, 2, 3], 'key2': {'nested': 'nested\\result'}}), '{"key":[1,2,3],\n"key2":{"nested":"nested\\\\result"}}')
|
||||
|
||||
def testFeatureToGeoJSON(self):
|
||||
def testJSONExporter(self):
|
||||
""" test converting features to GeoJSON """
|
||||
fields = QgsFields()
|
||||
fields.append(QgsField("name", QVariant.String))
|
||||
@ -116,6 +116,8 @@ class TestQgsJSONUtils(unittest.TestCase):
|
||||
feature.setGeometry(QgsGeometry(QgsPointV2(5, 6)))
|
||||
feature.setAttributes(['Valsier Peninsula', 6.8, 198])
|
||||
|
||||
exporter = QgsJSONExporter()
|
||||
|
||||
expected = """{
|
||||
"type":"Feature",
|
||||
"id":5,
|
||||
@ -127,7 +129,7 @@ class TestQgsJSONUtils(unittest.TestCase):
|
||||
"population":198
|
||||
}
|
||||
}"""
|
||||
self.assertEqual(QgsJSONUtils.featureToGeoJSON(feature), expected)
|
||||
self.assertEqual(exporter.exportFeature(feature), expected)
|
||||
|
||||
# test with linestring for bbox inclusion
|
||||
l = QgsLineStringV2()
|
||||
@ -146,10 +148,12 @@ class TestQgsJSONUtils(unittest.TestCase):
|
||||
"population":198
|
||||
}
|
||||
}"""
|
||||
self.assertEqual(QgsJSONUtils.featureToGeoJSON(feature), expected)
|
||||
self.assertEqual(exporter.exportFeature(feature), expected)
|
||||
|
||||
# test that precision is respected
|
||||
feature.setGeometry(QgsGeometry(QgsPointV2(5.444444444, 6.333333333)))
|
||||
exporter.setPrecision(3)
|
||||
self.assertEqual(exporter.precision(), 3)
|
||||
expected = """{
|
||||
"type":"Feature",
|
||||
"id":5,
|
||||
@ -161,10 +165,13 @@ class TestQgsJSONUtils(unittest.TestCase):
|
||||
"population":198
|
||||
}
|
||||
}"""
|
||||
self.assertEqual(QgsJSONUtils.featureToGeoJSON(feature, 3), expected)
|
||||
self.assertEqual(exporter.exportFeature(feature), expected)
|
||||
feature.setGeometry(QgsGeometry(QgsPointV2(5, 6)))
|
||||
exporter.setPrecision(17)
|
||||
|
||||
# test that attribute subset is respected
|
||||
exporter.setAttributes([0, 2])
|
||||
self.assertEqual(exporter.attributes(), [0, 2])
|
||||
expected = """{
|
||||
"type":"Feature",
|
||||
"id":5,
|
||||
@ -175,8 +182,10 @@ class TestQgsJSONUtils(unittest.TestCase):
|
||||
"population":198
|
||||
}
|
||||
}"""
|
||||
self.assertEqual(QgsJSONUtils.featureToGeoJSON(feature, attrIndexes=[0, 2]), expected)
|
||||
self.assertEqual(exporter.exportFeature(feature), expected)
|
||||
|
||||
exporter.setAttributes([1])
|
||||
self.assertEqual(exporter.attributes(), [1])
|
||||
expected = """{
|
||||
"type":"Feature",
|
||||
"id":5,
|
||||
@ -186,9 +195,12 @@ class TestQgsJSONUtils(unittest.TestCase):
|
||||
"cost":6.8
|
||||
}
|
||||
}"""
|
||||
self.assertEqual(QgsJSONUtils.featureToGeoJSON(feature, attrIndexes=[1]), expected)
|
||||
self.assertEqual(exporter.exportFeature(feature), expected)
|
||||
exporter.setAttributes([])
|
||||
|
||||
# test excluding geometry
|
||||
exporter.setIncludeGeometry(False)
|
||||
self.assertEqual(exporter.includeGeometry(), False)
|
||||
feature.setGeometry(QgsGeometry(QgsLineStringV2(l)))
|
||||
|
||||
expected = """{
|
||||
@ -200,23 +212,29 @@ class TestQgsJSONUtils(unittest.TestCase):
|
||||
"population":198
|
||||
}
|
||||
}"""
|
||||
self.assertEqual(QgsJSONUtils.featureToGeoJSON(feature, includeGeom=False), expected)
|
||||
self.assertEqual(exporter.exportFeature(feature), expected)
|
||||
exporter.setIncludeGeometry(True)
|
||||
|
||||
feature.setGeometry(QgsGeometry(QgsPointV2(5, 6)))
|
||||
|
||||
# test excluding attributes
|
||||
exporter.setIncludeAttributes(False)
|
||||
self.assertEqual(exporter.includeAttributes(), False)
|
||||
expected = """{
|
||||
"type":"Feature",
|
||||
"id":5,
|
||||
"geometry":
|
||||
{"type": "Point", "coordinates": [5, 6]}
|
||||
}"""
|
||||
self.assertEqual(QgsJSONUtils.featureToGeoJSON(feature, includeAttributes=False), expected)
|
||||
self.assertEqual(exporter.exportFeature(feature), expected)
|
||||
|
||||
exporter.setIncludeGeometry(False)
|
||||
expected = """{
|
||||
"type":"Feature",
|
||||
"id":5
|
||||
}"""
|
||||
self.assertEqual(QgsJSONUtils.featureToGeoJSON(feature, includeGeom=False, includeAttributes=False), expected)
|
||||
self.assertEqual(exporter.exportFeature(feature), expected)
|
||||
exporter.setIncludeAttributes(True)
|
||||
|
||||
# test overriding ID
|
||||
expected = """{
|
||||
@ -228,7 +246,7 @@ class TestQgsJSONUtils(unittest.TestCase):
|
||||
"population":198
|
||||
}
|
||||
}"""
|
||||
self.assertEqual(QgsJSONUtils.featureToGeoJSON(feature, includeGeom=False, id=29), expected)
|
||||
self.assertEqual(exporter.exportFeature(feature, id=29), expected)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user