mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Always export GeoJSON features in WGS84 (match specifications)
This commit is contained in:
parent
55793a4534
commit
ca2c6290b1
@ -1,6 +1,9 @@
|
||||
/** \ingroup core
|
||||
* \class QgsJSONExporter
|
||||
* \brief Handles exporting QgsFeature features to GeoJSON features.
|
||||
*
|
||||
* Note that geometries will be automatically reprojected to WGS84 to match GeoJSON spec
|
||||
* if either the source vector layer or source CRS is set.
|
||||
* \note Added in version 2.16
|
||||
*/
|
||||
|
||||
@ -63,7 +66,8 @@ class QgsJSONExporter
|
||||
*/
|
||||
bool includeRelated() const;
|
||||
|
||||
/** Sets the associated vector layer (required for related attribute export).
|
||||
/** Sets the associated vector layer (required for related attribute export). This will automatically
|
||||
* update the sourceCrs() to match.
|
||||
* @param vectorLayer vector layer
|
||||
* @see vectorLayer()
|
||||
*/
|
||||
@ -74,6 +78,20 @@ class QgsJSONExporter
|
||||
*/
|
||||
QgsVectorLayer* vectorLayer() const;
|
||||
|
||||
/** Sets the source CRS for feature geometries. The source CRS must be set if geometries are to be
|
||||
* correctly automatically reprojected to WGS 84, to match GeoJSON specifications.
|
||||
* @param crs source CRS for input feature geometries
|
||||
* @note the source CRS will be overwritten when a vector layer is specified via setVectorLayer()
|
||||
* @see sourceCrs()
|
||||
*/
|
||||
void setSourceCrs( const QgsCoordinateReferenceSystem& crs );
|
||||
|
||||
/** Returns the source CRS for feature geometries. The source CRS must be set if geometries are to be
|
||||
* correctly automatically reprojected to WGS 84, to match GeoJSON specifications.
|
||||
* @see setSourceCrs()
|
||||
*/
|
||||
const QgsCoordinateReferenceSystem& sourceCrs() 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
|
||||
@ -128,6 +146,10 @@ class QgsJSONExporter
|
||||
*/
|
||||
QString exportFeatures( const QgsFeatureList& features ) const;
|
||||
|
||||
private:
|
||||
|
||||
QgsJSONExporter( const QgsJSONExporter& );
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -29,12 +29,22 @@ QgsJSONExporter::QgsJSONExporter( const QgsVectorLayer* vectorLayer, int precisi
|
||||
, mIncludeRelatedAttributes( false )
|
||||
, mLayerId( vectorLayer ? vectorLayer->id() : QString() )
|
||||
{
|
||||
|
||||
if ( vectorLayer )
|
||||
{
|
||||
mCrs = vectorLayer->crs();
|
||||
mTransform.setSourceCrs( mCrs );
|
||||
}
|
||||
mTransform.setDestCRS( QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ) );
|
||||
}
|
||||
|
||||
void QgsJSONExporter::setVectorLayer( const QgsVectorLayer* vectorLayer )
|
||||
{
|
||||
mLayerId = vectorLayer ? vectorLayer->id() : QString();
|
||||
if ( vectorLayer )
|
||||
{
|
||||
mCrs = vectorLayer->crs();
|
||||
mTransform.setSourceCrs( mCrs );
|
||||
}
|
||||
}
|
||||
|
||||
QgsVectorLayer *QgsJSONExporter::vectorLayer() const
|
||||
@ -42,6 +52,17 @@ QgsVectorLayer *QgsJSONExporter::vectorLayer() const
|
||||
return qobject_cast< QgsVectorLayer* >( QgsMapLayerRegistry::instance()->mapLayer( mLayerId ) );
|
||||
}
|
||||
|
||||
void QgsJSONExporter::setSourceCrs( const QgsCoordinateReferenceSystem& crs )
|
||||
{
|
||||
mCrs = crs;
|
||||
mTransform.setSourceCrs( mCrs );
|
||||
}
|
||||
|
||||
const QgsCoordinateReferenceSystem& QgsJSONExporter::sourceCrs() const
|
||||
{
|
||||
return mCrs;
|
||||
}
|
||||
|
||||
QString QgsJSONExporter::exportFeature( const QgsFeature& feature, const QVariantMap& extraProperties,
|
||||
const QVariant& id ) const
|
||||
{
|
||||
@ -53,9 +74,18 @@ QString QgsJSONExporter::exportFeature( const QgsFeature& feature, const QVarian
|
||||
const QgsGeometry* geom = feature.constGeometry();
|
||||
if ( geom && !geom->isEmpty() && mIncludeGeometry )
|
||||
{
|
||||
QgsRectangle box = geom->boundingBox();
|
||||
const QgsGeometry* exportGeom = geom;
|
||||
if ( mCrs.isValid() )
|
||||
{
|
||||
QgsGeometry* clone = new QgsGeometry( *geom );
|
||||
if ( clone->transform( mTransform ) == 0 )
|
||||
exportGeom = clone;
|
||||
else
|
||||
delete clone;
|
||||
}
|
||||
QgsRectangle box = exportGeom->boundingBox();
|
||||
|
||||
if ( QgsWKBTypes::flatType( geom->geometry()->wkbType() ) != QgsWKBTypes::Point )
|
||||
if ( QgsWKBTypes::flatType( exportGeom->geometry()->wkbType() ) != QgsWKBTypes::Point )
|
||||
{
|
||||
s += QString( " \"bbox\":[%1, %2, %3, %4],\n" ).arg( qgsDoubleToString( box.xMinimum(), mPrecision ),
|
||||
qgsDoubleToString( box.yMinimum(), mPrecision ),
|
||||
@ -63,8 +93,11 @@ QString QgsJSONExporter::exportFeature( const QgsFeature& feature, const QVarian
|
||||
qgsDoubleToString( box.yMaximum(), mPrecision ) );
|
||||
}
|
||||
s += " \"geometry\":\n ";
|
||||
s += geom->exportToGeoJSON( mPrecision );
|
||||
s += exportGeom->exportToGeoJSON( mPrecision );
|
||||
s += ",\n";
|
||||
|
||||
if ( exportGeom != geom )
|
||||
delete exportGeom;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -17,6 +17,8 @@
|
||||
#define QGSJSONUTILS_H
|
||||
|
||||
#include "qgsfeature.h"
|
||||
#include "qgscoordinatereferencesystem.h"
|
||||
#include "qgscoordinatetransform.h"
|
||||
|
||||
class QTextCodec;
|
||||
class QgsVectorLayer;
|
||||
@ -24,6 +26,9 @@ class QgsVectorLayer;
|
||||
/** \ingroup core
|
||||
* \class QgsJSONExporter
|
||||
* \brief Handles exporting QgsFeature features to GeoJSON features.
|
||||
*
|
||||
* Note that geometries will be automatically reprojected to WGS84 to match GeoJSON spec
|
||||
* if either the source vector layer or source CRS is set.
|
||||
* \note Added in version 2.16
|
||||
*/
|
||||
|
||||
@ -83,7 +88,8 @@ class CORE_EXPORT QgsJSONExporter
|
||||
*/
|
||||
bool includeRelated() const { return mIncludeRelatedAttributes; }
|
||||
|
||||
/** Sets the associated vector layer (required for related attribute export).
|
||||
/** Sets the associated vector layer (required for related attribute export). This will automatically
|
||||
* update the sourceCrs() to match.
|
||||
* @param vectorLayer vector layer
|
||||
* @see vectorLayer()
|
||||
*/
|
||||
@ -94,6 +100,20 @@ class CORE_EXPORT QgsJSONExporter
|
||||
*/
|
||||
QgsVectorLayer* vectorLayer() const;
|
||||
|
||||
/** Sets the source CRS for feature geometries. The source CRS must be set if geometries are to be
|
||||
* correctly automatically reprojected to WGS 84, to match GeoJSON specifications.
|
||||
* @param crs source CRS for input feature geometries
|
||||
* @note the source CRS will be overwritten when a vector layer is specified via setVectorLayer()
|
||||
* @see sourceCrs()
|
||||
*/
|
||||
void setSourceCrs( const QgsCoordinateReferenceSystem& crs );
|
||||
|
||||
/** Returns the source CRS for feature geometries. The source CRS must be set if geometries are to be
|
||||
* correctly automatically reprojected to WGS 84, to match GeoJSON specifications.
|
||||
* @see setSourceCrs()
|
||||
*/
|
||||
const QgsCoordinateReferenceSystem& sourceCrs() 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
|
||||
@ -148,7 +168,6 @@ class CORE_EXPORT QgsJSONExporter
|
||||
*/
|
||||
QString exportFeatures( const QgsFeatureList& features ) const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
//! Maximum number of decimal places for geometry coordinates
|
||||
@ -173,6 +192,10 @@ class CORE_EXPORT QgsJSONExporter
|
||||
//! Layer ID of associated vector layer. Required for related attribute export.
|
||||
QString mLayerId;
|
||||
|
||||
QgsCoordinateReferenceSystem mCrs;
|
||||
|
||||
QgsCoordinateTransform mTransform;
|
||||
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
|
@ -15,7 +15,22 @@ __revision__ = '$Format:%H$'
|
||||
import qgis # NOQA
|
||||
|
||||
from qgis.testing import unittest, start_app
|
||||
from qgis.core import QgsJSONUtils, QgsJSONExporter, QgsProject, QgsMapLayerRegistry, QgsFeature, QgsField, QgsFields, QgsWKBTypes, QgsGeometry, QgsPointV2, QgsLineStringV2, NULL, QgsVectorLayer, QgsRelation
|
||||
from qgis.core import (QgsJSONUtils,
|
||||
QgsJSONExporter,
|
||||
QgsCoordinateReferenceSystem,
|
||||
QgsProject,
|
||||
QgsMapLayerRegistry,
|
||||
QgsFeature,
|
||||
QgsField,
|
||||
QgsFields,
|
||||
QgsWKBTypes,
|
||||
QgsGeometry,
|
||||
QgsPointV2,
|
||||
QgsLineStringV2,
|
||||
NULL,
|
||||
QgsVectorLayer,
|
||||
QgsRelation
|
||||
)
|
||||
from qgis.PyQt.QtCore import QVariant, QTextCodec
|
||||
|
||||
start_app()
|
||||
@ -364,6 +379,45 @@ class TestQgsJSONUtils(unittest.TestCase):
|
||||
self.assertEqual(exporter.exportFeature(feature, extraProperties={"extra": "val1", "extra2": {"nested_map": 5, "nested_map2": "val"}, "extra3": [1, 2, 3]}), expected)
|
||||
exporter.setIncludeGeometry(True)
|
||||
|
||||
def testExportFeatureCrs(self):
|
||||
""" Test CRS transform when exporting features """
|
||||
|
||||
exporter = QgsJSONExporter()
|
||||
self.assertFalse(exporter.sourceCrs().isValid())
|
||||
|
||||
#test layer
|
||||
layer = QgsVectorLayer("Point?crs=epsg:3111&field=fldtxt:string",
|
||||
"parent", "memory")
|
||||
exporter = QgsJSONExporter(layer)
|
||||
self.assertTrue(exporter.sourceCrs().isValid())
|
||||
self.assertEqual(exporter.sourceCrs().authid(), 'EPSG:3111')
|
||||
|
||||
exporter.setSourceCrs(QgsCoordinateReferenceSystem(3857, QgsCoordinateReferenceSystem.EpsgCrsId))
|
||||
self.assertTrue(exporter.sourceCrs().isValid())
|
||||
self.assertEqual(exporter.sourceCrs().authid(), 'EPSG:3857')
|
||||
|
||||
# vector layer CRS should override
|
||||
exporter.setVectorLayer(layer)
|
||||
self.assertEqual(exporter.sourceCrs().authid(), 'EPSG:3111')
|
||||
|
||||
# test that exported feature is reprojected
|
||||
feature = QgsFeature(layer.fields(), 5)
|
||||
feature.setGeometry(QgsGeometry(QgsPointV2(2502577, 2403869)))
|
||||
feature.setAttributes(['test point'])
|
||||
|
||||
# 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"
|
||||
}
|
||||
}"""
|
||||
self.assertEqual(exporter.exportFeature(feature), expected)
|
||||
|
||||
def testExportFeatureRelations(self):
|
||||
""" Test exporting a feature with relations """
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user