From bd3d75fce898c9263d871b32e7cd463f8be17723 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Fri, 3 May 2019 19:02:58 +0200 Subject: [PATCH] Indent on JSON export --- .../core/auto_generated/qgsjsonutils.sip.in | 7 +- src/core/qgsjsonutils.cpp | 73 +- src/core/qgsjsonutils.h | 7 +- tests/src/app/testqgisappclipboard.cpp | 25 +- tests/src/python/test_qgsjsonutils.py | 684 ++++++++++-------- ...getfeatureinfo_group_name_areas_cities.txt | 90 +-- .../wms_getfeatureinfo_group_name_top.txt | 165 +++-- .../wms_getfeatureinfo_group_query_child.txt | 63 +- 8 files changed, 638 insertions(+), 476 deletions(-) diff --git a/python/core/auto_generated/qgsjsonutils.sip.in b/python/core/auto_generated/qgsjsonutils.sip.in index 63e847db6c2..08dd1dc2e6d 100644 --- a/python/core/auto_generated/qgsjsonutils.sip.in +++ b/python/core/auto_generated/qgsjsonutils.sip.in @@ -219,7 +219,8 @@ take precedence over attributes included via attributes(). QString exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties = QVariantMap(), - const QVariant &id = QVariant() ) const; + const QVariant &id = QVariant(), + int indent = -1 ) const; %Docstring Returns a GeoJSON string representation of a feature. @@ -227,6 +228,7 @@ Returns a GeoJSON string representation of a feature. :param extraProperties: map of extra attributes to include in feature's properties :param id: optional ID to use as GeoJSON feature's ID instead of input feature's ID. If omitted, feature's ID is used. +:param indent: number of indentation spaces for generated JSON (defaults to none) :return: GeoJSON string @@ -237,11 +239,12 @@ Returns a GeoJSON string representation of a feature. - QString exportFeatures( const QgsFeatureList &features ) const; + QString exportFeatures( const QgsFeatureList &features, int indent = -1 ) const; %Docstring Returns a GeoJSON string representation of a list of features (feature collection). :param features: features to convert +:param indent: number of indentation spaces for generated JSON (defaults to none) :return: GeoJSON string diff --git a/src/core/qgsjsonutils.cpp b/src/core/qgsjsonutils.cpp index f6dae6c6f2d..55cade6fcb3 100644 --- a/src/core/qgsjsonutils.cpp +++ b/src/core/qgsjsonutils.cpp @@ -69,9 +69,9 @@ QgsCoordinateReferenceSystem QgsJsonExporter::sourceCrs() const } QString QgsJsonExporter::exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties, - const QVariant &id ) const + const QVariant &id, int indent ) const { - return QString::fromStdString( exportFeatureToJsonObject( feature, extraProperties, id ).dump() ); + return QString::fromStdString( exportFeatureToJsonObject( feature, extraProperties, id ).dump( indent ) ); } json QgsJsonExporter::exportFeatureToJsonObject( const QgsFeature &feature, const QVariantMap &extraProperties, const QVariant &id ) const @@ -82,7 +82,16 @@ json QgsJsonExporter::exportFeatureToJsonObject( const QgsFeature &feature, cons }; if ( id.isValid() ) { - featureJson["id"] = id.toString().toStdString(); + bool ok = false; + auto intId = id.toLongLong( &ok ); + if ( ok ) + { + featureJson["id"] = intId; + } + else + { + featureJson["id"] = id.toString().toStdString(); + } } else { @@ -213,15 +222,19 @@ json QgsJsonExporter::exportFeatureToJsonObject( const QgsFeature &feature, cons return featureJson; } -QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features ) const +QString QgsJsonExporter::exportFeatures( const QgsFeatureList &features, int indent ) const { - QStringList featureJSON; + json data + { + { "type", "FeatureCollection" }, + { "features", json::array() } + }; const auto constFeatures = features; for ( const QgsFeature &feature : constFeatures ) { - featureJSON << exportFeature( feature ); + data["features"].push_back( exportFeatureToJsonObject( feature ) ); } - return QStringLiteral( "{ \"type\": \"FeatureCollection\",\n \"features\":[\n%1\n]}" ).arg( featureJSON.join( QStringLiteral( ",\n" ) ) ); + return QString::fromStdString( data.dump( indent ) ); } // @@ -328,19 +341,41 @@ QVariantList QgsJsonUtils::parseArray( const QString &json, QVariant::Type type json QgsJsonUtils::jsonFromVariant( const QVariant &val ) { - - switch ( val.userType() ) + if ( val.type() == QVariant::Type::Map ) { - case QMetaType::Int: - case QMetaType::UInt: - case QMetaType::LongLong: - case QMetaType::ULongLong: - return val.toLongLong(); - case QMetaType::Double: - case QMetaType::Float: - return val.toDouble(); - default: - return val.toString().toStdString(); + const auto vMap { val.toMap() }; + auto jMap { json::object() }; + for ( auto it = vMap.constBegin(); it != vMap.constEnd(); it++ ) + { + jMap[ it.key().toStdString() ] = jsonFromVariant( it.value() ); + } + return jMap; + } + else if ( val.type() == QVariant::Type::List ) + { + const auto vList{ val.toList() }; + auto jList { json::array() }; + for ( const auto &v : vList ) + { + jList.push_back( jsonFromVariant( v ) ); + } + return jList; + } + else + { + switch ( val.userType() ) + { + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::LongLong: + case QMetaType::ULongLong: + return val.toLongLong(); + case QMetaType::Double: + case QMetaType::Float: + return val.toDouble(); + default: + return val.toString().toStdString(); + } } } diff --git a/src/core/qgsjsonutils.h b/src/core/qgsjsonutils.h index 631091a9240..e2d25f26aac 100644 --- a/src/core/qgsjsonutils.h +++ b/src/core/qgsjsonutils.h @@ -198,13 +198,15 @@ class CORE_EXPORT QgsJsonExporter * \param extraProperties map of extra attributes to include in feature's properties * \param id optional ID to use as GeoJSON feature's ID instead of input feature's ID. If omitted, feature's * ID is used. + * \param indent number of indentation spaces for generated JSON (defaults to none) * \returns GeoJSON string * \see exportFeatures() * \see exportFeatureToJsonObject() */ QString exportFeature( const QgsFeature &feature, const QVariantMap &extraProperties = QVariantMap(), - const QVariant &id = QVariant() ) const; + const QVariant &id = QVariant(), + int indent = -1 ) const; /** * Returns a QJsonObject representation of a feature. @@ -223,10 +225,11 @@ class CORE_EXPORT QgsJsonExporter /** * Returns a GeoJSON string representation of a list of features (feature collection). * \param features features to convert + * \param indent number of indentation spaces for generated JSON (defaults to none) * \returns GeoJSON string * \see exportFeature() */ - QString exportFeatures( const QgsFeatureList &features ) const; + QString exportFeatures( const QgsFeatureList &features, int indent = -1 ) const; private: diff --git a/tests/src/app/testqgisappclipboard.cpp b/tests/src/app/testqgisappclipboard.cpp index d98382d10de..daf572b4ccf 100644 --- a/tests/src/app/testqgisappclipboard.cpp +++ b/tests/src/app/testqgisappclipboard.cpp @@ -157,24 +157,11 @@ void TestQgisAppClipboard::copyToText() // GeoJSON settings.setEnumValue( QStringLiteral( "/qgis/copyFeatureFormat" ), QgsClipboard::GeoJSON ); result = mQgisApp->clipboard()->generateClipboardText(); - QString expected = "{ \"type\": \"FeatureCollection\",\n \"features\":[\n" - "{\n \"type\":\"Feature\",\n" - " \"id\":5,\n" - " \"geometry\":\n" - " {\"type\": \"Point\", \"coordinates\": [5, 6]},\n" - " \"properties\":{\n" - " \"int_field\":9,\n" - " \"string_field\":\"val\"\n" - " }\n" - "},\n" - "{\n \"type\":\"Feature\",\n" - " \"id\":6,\n" - " \"geometry\":\n" - " {\"type\": \"Point\", \"coordinates\": [7, 8]},\n" - " \"properties\":{\n" - " \"int_field\":19,\n" - " \"string_field\":\"val2\"\n" - " }\n}\n]}"; + QString expected = "{\"features\":[{\"geometry\":{\"coordinates\":[5.0,6.0],\"type\":\"Point\"},\"id\":5," + "\"properties\":{\"int_field\":9,\"string_field\":\"val\"},\"type\":\"Feature\"}," + "{\"geometry\":{\"coordinates\":[7.0,8.0],\"type\":\"Point\"},\"id\":6," + "\"properties\":{\"int_field\":19,\"string_field\":\"val2\"},\"type\":\"Feature\"}]," + "\"type\":\"FeatureCollection\"}"; QCOMPARE( result, expected ); // test CRS is transformed correctly for GeoJSON @@ -191,7 +178,7 @@ void TestQgisAppClipboard::copyToText() // just test coordinates as integers - that's enough to verify that reprojection has occurred // and helps avoid rounding issues - QRegExp regex( "\\[([-\\d.]+), ([-\\d.]+)\\]" ); + QRegExp regex( "\\[([-\\d.]+),([-\\d.]+)\\]" ); ( void )regex.indexIn( result ); QStringList list = regex.capturedTexts(); QCOMPARE( list.count(), 3 ); diff --git a/tests/src/python/test_qgsjsonutils.py b/tests/src/python/test_qgsjsonutils.py index 212be4a7749..2ef245d403d 100644 --- a/tests/src/python/test_qgsjsonutils.py +++ b/tests/src/python/test_qgsjsonutils.py @@ -174,17 +174,22 @@ class TestQgsJsonUtils(unittest.TestCase): exporter = QgsJsonExporter() expected = """{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":{ - "name":"Valsier Peninsula", - "cost":6.8, - "population":198 - } + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "cost": 6.8, + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) # test with linestring for bbox inclusion l = QgsLineString() @@ -192,35 +197,58 @@ class TestQgsJsonUtils(unittest.TestCase): feature.setGeometry(QgsGeometry(QgsLineString(l))) expected = """{ - "type":"Feature", - "id":5, - "bbox":[5, 6, 15, 16], - "geometry": - {"type": "LineString", "coordinates": [ [5, 6], [15, 16]]}, - "properties":{ - "name":"Valsier Peninsula", - "cost":6.8, - "population":198 - } + "bbox": [ + [ + 5.0, + 6.0, + 15.0, + 16.0 + ] + ], + "geometry": { + "coordinates": [ + [ + 5.0, + 6.0 + ], + [ + 15.0, + 16.0 + ] + ], + "type": "LineString" + }, + "id": 5, + "properties": { + "cost": 6.8, + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) # test that precision is respected feature.setGeometry(QgsGeometry(QgsPoint(5.444444444, 6.333333333))) exporter.setPrecision(3) self.assertEqual(exporter.precision(), 3) expected = """{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5.444, 6.333]}, - "properties":{ - "name":"Valsier Peninsula", - "cost":6.8, - "population":198 - } + "geometry": { + "coordinates": [ + 5.444, + 6.333 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "cost": 6.8, + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) feature.setGeometry(QgsGeometry(QgsPoint(5, 6))) exporter.setPrecision(17) @@ -228,29 +256,39 @@ class TestQgsJsonUtils(unittest.TestCase): exporter.setAttributes([0, 2]) self.assertEqual(exporter.attributes(), [0, 2]) expected = """{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":{ - "name":"Valsier Peninsula", - "population":198 - } + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) exporter.setAttributes([1]) self.assertEqual(exporter.attributes(), [1]) expected = """{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":{ - "cost":6.8 - } + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "cost": 6.8 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) exporter.setAttributes([]) # text excluding attributes @@ -258,55 +296,75 @@ class TestQgsJsonUtils(unittest.TestCase): exporter.setExcludedAttributes([1]) self.assertEqual(exporter.excludedAttributes(), [1]) expected = """{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":{ - "name":"Valsier Peninsula", - "population":198 - } + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) exporter.setExcludedAttributes([1, 2]) self.assertEqual(exporter.excludedAttributes(), [1, 2]) expected = """{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":{ - "name":"Valsier Peninsula" - } + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "name": "Valsier Peninsula" + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) exporter.setExcludedAttributes([0, 1, 2]) self.assertEqual(exporter.excludedAttributes(), [0, 1, 2]) expected = """{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":null + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": null, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) # test that excluded attributes take precedence over included exporter.setAttributes([1, 2]) exporter.setExcludedAttributes([0, 1]) expected = """{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":{ - "population":198 - } + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "population": 198 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) exporter.setAttributes([]) exporter.setExcludedAttributes([]) @@ -317,16 +375,16 @@ class TestQgsJsonUtils(unittest.TestCase): feature.setGeometry(QgsGeometry(QgsLineString(l))) expected = """{ - "type":"Feature", - "id":5, - "geometry":null, - "properties":{ - "name":"Valsier Peninsula", - "cost":6.8, - "population":198 - } + "geometry": null, + "id": 5, + "properties": { + "cost": 6.8, + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) exporter.setIncludeGeometry(True) feature.setGeometry(QgsGeometry(QgsPoint(5, 6))) @@ -335,76 +393,90 @@ class TestQgsJsonUtils(unittest.TestCase): exporter.setIncludeAttributes(False) self.assertEqual(exporter.includeAttributes(), False) expected = """{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":null + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": null, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) exporter.setIncludeGeometry(False) expected = """{ - "type":"Feature", - "id":5, - "geometry":null, - "properties":null + "geometry": null, + "id": 5, + "properties": null, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) exporter.setIncludeAttributes(True) # test overriding ID expected = """{ - "type":"Feature", - "id":29, - "geometry":null, - "properties":{ - "name":"Valsier Peninsula", - "cost":6.8, - "population":198 - } + "geometry": null, + "id": 29, + "properties": { + "cost": 6.8, + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature, id=29), expected) + self.assertEqual(exporter.exportFeature(feature, id=29, indent=2), expected) + + expected = """{ + "geometry": null, + "id": "mylayer.29", + "properties": { + "cost": 6.8, + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" +}""" + self.assertEqual(exporter.exportFeature(feature, id="mylayer.29", indent=2), expected) # test injecting extra attributes expected = """{ - "type":"Feature", - "id":5, - "geometry":null, - "properties":{ - "name":"Valsier Peninsula", - "cost":6.8, - "population":198, - "extra":"val1", - "extra2":2 - } + "geometry": null, + "id": 5, + "properties": { + "cost": 6.8, + "extra": "val1", + "extra2": 2, + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature, extraProperties={"extra": "val1", "extra2": 2}), expected) + self.assertEqual(exporter.exportFeature(feature, extraProperties={"extra": "val1", "extra2": 2}, indent=2), expected) exporter.setIncludeAttributes(False) expected = """{ - "type":"Feature", - "id":5, - "geometry":null, - "properties":{ - "extra":"val1", - "extra2":{"nested_map":5, -"nested_map2":"val"}, - "extra3":[1,2,3] - } + "geometry": null, + "id": 5, + "properties": { + "extra": "val1", + "extra2": { + "nested_map": 5, + "nested_map2": "val" + }, + "extra3": [ + 1, + 2, + 3 + ] + }, + "type": "Feature" }""" - expected2 = """{ - "type":"Feature", - "id":5, - "geometry":null, - "properties":{ - "extra":"val1", - "extra2":{"nested_map":5,"nested_map2":"val"}, - "extra3":[1,2,3] - } -}""" - exp_f = exporter.exportFeature(feature, extraProperties={"extra": "val1", "extra2": {"nested_map": 5, "nested_map2": "val"}, "extra3": [1, 2, 3]}) - self.assertTrue(exp_f == expected or exp_f == expected2) + + exp_f = exporter.exportFeature(feature, extraProperties={"extra": "val1", "extra2": {"nested_map": 5, "nested_map2": "val"}, "extra3": [1, 2, 3]}, indent=2) + self.assertEqual(exp_f, expected) exporter.setIncludeGeometry(True) def testExportFeatureFieldFormatter(self): @@ -429,15 +501,15 @@ class TestQgsJsonUtils(unittest.TestCase): exporter.setVectorLayer(source) expected = """{ - "type":"Feature", - "id":0, - "geometry":null, - "properties":{ - "fldtxt":"test1", - "fldint":"one" - } + "geometry": null, + "id": 0, + "properties": { + "fldint": "one", + "fldtxt": "test1" + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(pf1), expected) + self.assertEqual(exporter.exportFeature(pf1, indent=2), expected) def testExportFeatureCrs(self): """ Test CRS transform when exporting features """ @@ -468,15 +540,20 @@ class TestQgsJsonUtils(unittest.TestCase): # low precision, only need rough coordinate to check and don't want to deal with rounding errors exporter.setPrecision(1) expected = """{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [145, -37.9]}, - "properties":{ - "fldtxt":"test point" - } + "geometry": { + "coordinates": [ + 145.0, + -37.9 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "fldtxt": "test point" + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(feature), expected) + self.assertEqual(exporter.exportFeature(feature, indent=2), expected) def testExportFeatureRelations(self): """ Test exporting a feature with relations """ @@ -528,90 +605,107 @@ class TestQgsJsonUtils(unittest.TestCase): self.assertEqual(exporter.includeRelated(), True) expected = """{ - "type":"Feature", - "id":0, - "geometry":null, - "properties":{ - "fldtxt":"test1", - "fldint":67, - "foreignkey":123, - "relation one":[{"x":"foo", -"y":123, -"z":321}, -{"x":"bar", -"y":123, -"z":654}] - } + "geometry": null, + "id": 0, + "properties": { + "fldint": 67, + "fldtxt": "test1", + "foreignkey": 123, + "relation one": [ + { + "x": "foo", + "y": 123, + "z": 321 + }, + { + "x": "bar", + "y": 123, + "z": 654 + } + ] + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(pf1), expected) + self.assertEqual(exporter.exportFeature(pf1, indent=2), expected) expected = """{ - "type":"Feature", - "id":0, - "geometry":null, - "properties":{ - "fldtxt":"test2", - "fldint":68, - "foreignkey":124, - "relation one":[{"x":"foobar", -"y":124, -"z":554}] - } + "geometry": null, + "id": 0, + "properties": { + "fldint": 68, + "fldtxt": "test2", + "foreignkey": 124, + "relation one": [ + { + "x": "foobar", + "y": 124, + "z": 554 + } + ] + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(pf2), expected) + + self.assertEqual(exporter.exportFeature(pf2, indent=2), expected) # with field formatter setup = QgsEditorWidgetSetup('ValueMap', {"map": {"apples": 123, "bananas": 124}}) child.setEditorWidgetSetup(1, setup) expected = """{ - "type":"Feature", - "id":0, - "geometry":null, - "properties":{ - "fldtxt":"test1", - "fldint":67, - "foreignkey":123, - "relation one":[{"x":"foo", -"y":"apples", -"z":321}, -{"x":"bar", -"y":"apples", -"z":654}] - } + "geometry": null, + "id": 0, + "properties": { + "fldint": 67, + "fldtxt": "test1", + "foreignkey": 123, + "relation one": [ + { + "x": "foo", + "y": "apples", + "z": 321 + }, + { + "x": "bar", + "y": "apples", + "z": 654 + } + ] + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(pf1), expected) + self.assertEqual(exporter.exportFeature(pf1, indent=2), expected) # test excluding related attributes exporter.setIncludeRelated(False) self.assertEqual(exporter.includeRelated(), False) expected = """{ - "type":"Feature", - "id":0, - "geometry":null, - "properties":{ - "fldtxt":"test2", - "fldint":68, - "foreignkey":124 - } + "geometry": null, + "id": 0, + "properties": { + "fldint": 68, + "fldtxt": "test2", + "foreignkey": 124 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(pf2), expected) + self.assertEqual(exporter.exportFeature(pf2, indent=2), expected) # test without vector layer set exporter.setIncludeRelated(True) exporter.setVectorLayer(None) expected = """{ - "type":"Feature", - "id":0, - "geometry":null, - "properties":{ - "fldtxt":"test2", - "fldint":68, - "foreignkey":124 - } + "geometry": null, + "id": 0, + "properties": { + "fldint": 68, + "fldtxt": "test2", + "foreignkey": 124 + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(pf2), expected) + self.assertEqual(exporter.exportFeature(pf2, indent=2), expected) def testExportFeatures(self): """ Test exporting feature collections """ @@ -628,53 +722,72 @@ class TestQgsJsonUtils(unittest.TestCase): exporter = QgsJsonExporter() # single feature - expected = """{ "type": "FeatureCollection", - "features":[ -{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":{ - "name":"Valsier Peninsula", - "cost":6.8, - "population":198 - } -} -]}""" - self.assertEqual(exporter.exportFeatures([feature]), expected) + expected = """{ + "features": [ + { + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "cost": 6.8, + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" + } + ], + "type": "FeatureCollection" +}""" + self.assertEqual(exporter.exportFeatures([feature], 2), expected) # multiple features feature2 = QgsFeature(fields, 6) feature2.setGeometry(QgsGeometry(QgsPoint(7, 8))) feature2.setAttributes(['Henry Gale Island', 9.7, 38]) - expected = """{ "type": "FeatureCollection", - "features":[ -{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":{ - "name":"Valsier Peninsula", - "cost":6.8, - "population":198 - } -}, -{ - "type":"Feature", - "id":6, - "geometry": - {"type": "Point", "coordinates": [7, 8]}, - "properties":{ - "name":"Henry Gale Island", - "cost":9.7, - "population":38 - } -} -]}""" - self.assertEqual(exporter.exportFeatures([feature, feature2]), expected) + expected = """{ + "features": [ + { + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "cost": 6.8, + "name": "Valsier Peninsula", + "population": 198 + }, + "type": "Feature" + }, + { + "geometry": { + "coordinates": [ + 7.0, + 8.0 + ], + "type": "Point" + }, + "id": 6, + "properties": { + "cost": 9.7, + "name": "Henry Gale Island", + "population": 38 + }, + "type": "Feature" + } + ], + "type": "FeatureCollection" +}""" + self.assertEqual(exporter.exportFeatures([feature, feature2], 2), expected) def testExportFeaturesWithLocale_regression20053(self): """ Test exporting feature export with range widgets and locale different than C @@ -693,22 +806,29 @@ class TestQgsJsonUtils(unittest.TestCase): exporter = QgsJsonExporter() # single feature - expected = """{ "type": "FeatureCollection", - "features":[ -{ - "type":"Feature", - "id":5, - "geometry": - {"type": "Point", "coordinates": [5, 6]}, - "properties":{ - "name":"Valsier Peninsula", - "cost":6.8, - "population":198000, - "date":"2018-09-10" - } -} -]}""" - self.assertEqual(exporter.exportFeatures([feature]), expected) + expected = """{ + "features": [ + { + "geometry": { + "coordinates": [ + 5.0, + 6.0 + ], + "type": "Point" + }, + "id": 5, + "properties": { + "cost": 6.8, + "date": "2018-09-10", + "name": "Valsier Peninsula", + "population": 198000 + }, + "type": "Feature" + } + ], + "type": "FeatureCollection" +}""" + self.assertEqual(exporter.exportFeatures([feature], 2), expected) setup = QgsEditorWidgetSetup('Range', { 'AllowNull': True, @@ -724,7 +844,7 @@ class TestQgsJsonUtils(unittest.TestCase): QLocale.setDefault(QLocale('it')) exporter.setVectorLayer(source) - self.assertEqual(exporter.exportFeatures([feature]), expected) + self.assertEqual(exporter.exportFeatures([feature], 2), expected) def testExportFieldAlias(self): """ Test exporting a feature with fields' alias """ @@ -749,15 +869,15 @@ class TestQgsJsonUtils(unittest.TestCase): exporter.setVectorLayer(source) expected = """{ - "type":"Feature", - "id":0, - "geometry":null, - "properties":{ - "alias_fldtxt":"test1", - "alias_fldint":1 - } + "geometry": null, + "id": 0, + "properties": { + "alias_fldint": 1, + "alias_fldtxt": "test1" + }, + "type": "Feature" }""" - self.assertEqual(exporter.exportFeature(pf1), expected) + self.assertEqual(exporter.exportFeature(pf1, indent=2), expected) if __name__ == "__main__": diff --git a/tests/testdata/qgis_server/wms_getfeatureinfo_group_name_areas_cities.txt b/tests/testdata/qgis_server/wms_getfeatureinfo_group_name_areas_cities.txt index 711d37613d3..a8ca1d8152f 100644 --- a/tests/testdata/qgis_server/wms_getfeatureinfo_group_name_areas_cities.txt +++ b/tests/testdata/qgis_server/wms_getfeatureinfo_group_name_areas_cities.txt @@ -1,46 +1,50 @@ -Content-Length: 992 +***** Content-Type: application/json; charset=utf-8 -{"type": "FeatureCollection", - "features":[ { - "type":"Feature", - "id":"as_areas.18", - "geometry":null, - "properties":{ - "fid":18, - "gid":34, - "datum":"2013-06-11", - "bearbeiter":"scholle-b", - "veranstaltung":"", - "beschriftung":"", - "name":"", - "flaechentyp":"Schraffur", - "farbe":"255 0 0", - "schraff_width":"2", - "schraff_width_prt":"6.00", - "schraff_size":"10", - "schraff_size_prt":"30.00", - "schraff_winkel":"45", - "umrissfarbe":"0 0 0", - "umrisstyp":"durchgezogen", - "umrissstaerke":"1", - "umrissstaerke_prt":"3.00", - "umfang":758.4703, - "flaeche":12879, - "bemerkung":"", - "last_change":"2013-06-11 10:03:45.52301+02" - } -},{ - "type":"Feature", - "id":"cdb_lines.13", - "geometry":null, - "properties":{ - "fid":13, - "id":12, - "typ":"Ortsteil", - "name":"Fallersleben", - "ortsrat":"Fallersleben\/Sülfeld", - "id_long":null - } -}]} \ No newline at end of file + "features": [ + { + "geometry": null, + "id": "as_areas.18", + "properties": { + "bearbeiter": "scholle-b", + "bemerkung": "", + "beschriftung": "", + "datum": "2013-06-11", + "farbe": "255 0 0", + "fid": 18, + "flaeche": 12879.0, + "flaechentyp": "Schraffur", + "gid": 34, + "last_change": "2013-06-11 10:03:45.52301+02", + "name": "", + "schraff_size": "10", + "schraff_size_prt": "30.00", + "schraff_width": "2", + "schraff_width_prt": "6.00", + "schraff_winkel": "45", + "umfang": 758.4703, + "umrissfarbe": "0 0 0", + "umrissstaerke": "1", + "umrissstaerke_prt": "3.00", + "umrisstyp": "durchgezogen", + "veranstaltung": "" + }, + "type": "Feature" + }, + { + "geometry": null, + "id": "cdb_lines.13", + "properties": { + "fid": 13, + "id": 12, + "id_long": "", + "name": "Fallersleben", + "ortsrat": "Fallersleben/Sülfeld", + "typ": "Ortsteil" + }, + "type": "Feature" + } + ], + "type": "FeatureCollection" +} \ No newline at end of file diff --git a/tests/testdata/qgis_server/wms_getfeatureinfo_group_name_top.txt b/tests/testdata/qgis_server/wms_getfeatureinfo_group_name_top.txt index dff6e2389ba..86016771dc5 100644 --- a/tests/testdata/qgis_server/wms_getfeatureinfo_group_name_top.txt +++ b/tests/testdata/qgis_server/wms_getfeatureinfo_group_name_top.txt @@ -1,82 +1,89 @@ -Content-Length: 1124 +***** Content-Type: application/json; charset=utf-8 -{"type": "FeatureCollection", - "features":[ { - "type":"Feature", - "id":"cdb_lines.13", - "geometry":null, - "properties":{ - "fid":13, - "id":12, - "typ":"Ortsteil", - "name":"Fallersleben", - "ortsrat":"Fallersleben\/Sülfeld", - "id_long":null - } -},{ - "type":"Feature", - "id":"as_areas.18", - "geometry":null, - "properties":{ - "fid":18, - "gid":34, - "datum":"2013-06-11", - "bearbeiter":"scholle-b", - "veranstaltung":"", - "beschriftung":"", - "name":"", - "flaechentyp":"Schraffur", - "farbe":"255 0 0", - "schraff_width":"2", - "schraff_width_prt":"6.00", - "schraff_size":"10", - "schraff_size_prt":"30.00", - "schraff_winkel":"45", - "umrissfarbe":"0 0 0", - "umrisstyp":"durchgezogen", - "umrissstaerke":"1", - "umrissstaerke_prt":"3.00", - "umfang":758.4703, - "flaeche":12879, - "bemerkung":"", - "last_change":"2013-06-11 10:03:45.52301+02" - } -},{ - "type":"Feature", - "id":"as_areas_query_copy.18", - "geometry":null, - "properties":{ - "fid":18, - "gid":34, - "datum":"2013-06-11", - "bearbeiter":"scholle-b", - "veranstaltung":"", - "beschriftung":"", - "name":"", - "flaechentyp":"Schraffur", - "farbe":"255 0 0", - "schraff_width":"2", - "schraff_width_prt":"6.00", - "schraff_size":"10", - "schraff_size_prt":"30.00", - "schraff_winkel":"45", - "umrissfarbe":"0 0 0", - "umrisstyp":"durchgezogen", - "umrissstaerke":"1", - "umrissstaerke_prt":"3.00", - "umfang":758.4703, - "flaeche":12879, - "bemerkung":"", - "last_change":"2013-06-11 10:03:45.52301+02" - } -},{"type":"Feature", -"id":"osm", -"properties":{ - "Band 1": "255", - "Band 2": "255", - "Band 3": "255", - "Band 4": "255" -} -}]} \ No newline at end of file + "features": [ + { + "geometry": null, + "id": "cdb_lines.13", + "properties": { + "fid": 13, + "id": 12, + "id_long": "", + "name": "Fallersleben", + "ortsrat": "Fallersleben/Sülfeld", + "typ": "Ortsteil" + }, + "type": "Feature" + }, + { + "geometry": null, + "id": "as_areas.18", + "properties": { + "bearbeiter": "scholle-b", + "bemerkung": "", + "beschriftung": "", + "datum": "2013-06-11", + "farbe": "255 0 0", + "fid": 18, + "flaeche": 12879.0, + "flaechentyp": "Schraffur", + "gid": 34, + "last_change": "2013-06-11 10:03:45.52301+02", + "name": "", + "schraff_size": "10", + "schraff_size_prt": "30.00", + "schraff_width": "2", + "schraff_width_prt": "6.00", + "schraff_winkel": "45", + "umfang": 758.4703, + "umrissfarbe": "0 0 0", + "umrissstaerke": "1", + "umrissstaerke_prt": "3.00", + "umrisstyp": "durchgezogen", + "veranstaltung": "" + }, + "type": "Feature" + }, + { + "geometry": null, + "id": "as_areas_query_copy.18", + "properties": { + "bearbeiter": "scholle-b", + "bemerkung": "", + "beschriftung": "", + "datum": "2013-06-11", + "farbe": "255 0 0", + "fid": 18, + "flaeche": 12879.0, + "flaechentyp": "Schraffur", + "gid": 34, + "last_change": "2013-06-11 10:03:45.52301+02", + "name": "", + "schraff_size": "10", + "schraff_size_prt": "30.00", + "schraff_width": "2", + "schraff_width_prt": "6.00", + "schraff_winkel": "45", + "umfang": 758.4703, + "umrissfarbe": "0 0 0", + "umrissstaerke": "1", + "umrissstaerke_prt": "3.00", + "umrisstyp": "durchgezogen", + "veranstaltung": "" + }, + "type": "Feature" + }, + { + "id": "osm", + "properties": { + "Band 1": "255", + "Band 2": "255", + "Band 3": "255", + "Band 4": "255" + }, + "type": "Feature" + } + ], + "type": "FeatureCollection" +} \ No newline at end of file diff --git a/tests/testdata/qgis_server/wms_getfeatureinfo_group_query_child.txt b/tests/testdata/qgis_server/wms_getfeatureinfo_group_query_child.txt index 02bdf60f735..62bb8089672 100644 --- a/tests/testdata/qgis_server/wms_getfeatureinfo_group_query_child.txt +++ b/tests/testdata/qgis_server/wms_getfeatureinfo_group_query_child.txt @@ -1,34 +1,37 @@ ***** Content-Type: application/json; charset=utf-8 -{"type": "FeatureCollection", - "features":[ { - "type":"Feature", - "id":"as_areas_query_copy.18", - "geometry":null, - "properties":{ - "fid":18, - "gid":34, - "datum":"2013-06-11", - "bearbeiter":"scholle-b", - "veranstaltung":"", - "beschriftung":"", - "name":"", - "flaechentyp":"Schraffur", - "farbe":"255 0 0", - "schraff_width":"2", - "schraff_width_prt":"6.00", - "schraff_size":"10", - "schraff_size_prt":"30.00", - "schraff_winkel":"45", - "umrissfarbe":"0 0 0", - "umrisstyp":"durchgezogen", - "umrissstaerke":"1", - "umrissstaerke_prt":"3.00", - "umfang":758.4703, - "flaeche":12879, - "bemerkung":"", - "last_change":"2013-06-11 10:03:45.52301+02" - } -}]} + "features": [ + { + "geometry": null, + "id": "as_areas_query_copy.18", + "properties": { + "bearbeiter": "scholle-b", + "bemerkung": "", + "beschriftung": "", + "datum": "2013-06-11", + "farbe": "255 0 0", + "fid": 18, + "flaeche": 12879.0, + "flaechentyp": "Schraffur", + "gid": 34, + "last_change": "2013-06-11 10:03:45.52301+02", + "name": "", + "schraff_size": "10", + "schraff_size_prt": "30.00", + "schraff_width": "2", + "schraff_width_prt": "6.00", + "schraff_winkel": "45", + "umfang": 758.4703, + "umrissfarbe": "0 0 0", + "umrissstaerke": "1", + "umrissstaerke_prt": "3.00", + "umrisstyp": "durchgezogen", + "veranstaltung": "" + }, + "type": "Feature" + } + ], + "type": "FeatureCollection" +} \ No newline at end of file