Skip to content

Commit

Permalink
QgsVectorFileWriter map NaN to -DBL_MAX exporting to Shape
Browse files Browse the repository at this point in the history
  • Loading branch information
domi4484 authored and github-actions[bot] committed Nov 15, 2022
1 parent dd47d4a commit 19da39f
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 10 deletions.
Expand Up @@ -210,6 +210,7 @@ Sets the geometry from a WKT string.
enum WkbFlag
{
FlagExportTrianglesAsPolygons,
FlagExportNanAsDoubleMin,
};
typedef QFlags<QgsAbstractGeometry::WkbFlag> WkbFlags;

Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgsabstractgeometry.h
Expand Up @@ -273,6 +273,7 @@ class CORE_EXPORT QgsAbstractGeometry
enum WkbFlag
{
FlagExportTrianglesAsPolygons = 1 << 0, //!< Triangles should be exported as polygon geometries
FlagExportNanAsDoubleMin = 1 << 1, //!< Use -DOUBLE_MAX to represent NaN (since QGIS 3.30)
};
Q_DECLARE_FLAGS( WkbFlags, WkbFlag )

Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgscircularstring.cpp
Expand Up @@ -517,7 +517,7 @@ QByteArray QgsCircularString::asWkb( WkbFlags flags ) const
wkb << static_cast<quint32>( wkbType() );
QgsPointSequence pts;
points( pts );
QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure(), flags );
return wkbArray;
}

Expand Down
16 changes: 13 additions & 3 deletions src/core/geometry/qgsgeometryutils.cpp
Expand Up @@ -1216,19 +1216,29 @@ QgsPointSequence QgsGeometryUtils::pointsFromWKT( const QString &wktCoordinateLi
return points;
}

void QgsGeometryUtils::pointsToWKB( QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure )
void QgsGeometryUtils::pointsToWKB( QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags )
{
wkb << static_cast<quint32>( points.size() );
for ( const QgsPoint &point : points )
{
wkb << point.x() << point.y();
if ( is3D )
{
wkb << point.z();
double z = point.z();
if ( flags & QgsAbstractGeometry::FlagExportNanAsDoubleMin
&& std::isnan( z ) )
z = -DBL_MAX;

wkb << z;
}
if ( isMeasure )
{
wkb << point.m();
double m = point.m();
if ( flags & QgsAbstractGeometry::FlagExportNanAsDoubleMin
&& std::isnan( m ) )
m = -DBL_MAX;

wkb << m;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgsgeometryutils.h
Expand Up @@ -455,7 +455,7 @@ class CORE_EXPORT QgsGeometryUtils
* Returns a LinearRing { uint32 numPoints; Point points[numPoints]; }
* \note not available in Python bindings
*/
static void pointsToWKB( QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure ) SIP_SKIP;
static void pointsToWKB( QgsWkbPtr &wkb, const QgsPointSequence &points, bool is3D, bool isMeasure, QgsAbstractGeometry::WkbFlags flags ) SIP_SKIP;

/**
* Returns a WKT coordinate list
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgslinestring.cpp
Expand Up @@ -703,7 +703,7 @@ QByteArray QgsLineString::asWkb( WkbFlags flags ) const
wkb << static_cast<quint32>( wkbType() );
QgsPointSequence pts;
points( pts );
QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure() );
QgsGeometryUtils::pointsToWKB( wkb, pts, is3D(), isMeasure(), flags );
return wkbArray;
}

Expand Down
4 changes: 2 additions & 2 deletions src/core/geometry/qgspolygon.cpp
Expand Up @@ -173,13 +173,13 @@ QByteArray QgsPolygon::asWkb( QgsAbstractGeometry::WkbFlags flags ) const
{
QgsPointSequence pts;
mExteriorRing->points( pts );
QgsGeometryUtils::pointsToWKB( wkb, pts, mExteriorRing->is3D(), mExteriorRing->isMeasure() );
QgsGeometryUtils::pointsToWKB( wkb, pts, mExteriorRing->is3D(), mExteriorRing->isMeasure(), flags );
}
for ( const QgsCurve *curve : mInteriorRings )
{
QgsPointSequence pts;
curve->points( pts );
QgsGeometryUtils::pointsToWKB( wkb, pts, curve->is3D(), curve->isMeasure() );
QgsGeometryUtils::pointsToWKB( wkb, pts, curve->is3D(), curve->isMeasure(), flags );
}

return wkbArray;
Expand Down
6 changes: 5 additions & 1 deletion src/core/qgsvectorfilewriter.cpp
Expand Up @@ -2840,7 +2840,11 @@ gdal::ogr_feature_unique_ptr QgsVectorFileWriter::createFeature( const QgsFeatur
}
else // wkb type matches
{
QByteArray wkb( geom.asWkb( QgsAbstractGeometry::FlagExportTrianglesAsPolygons ) );
QgsAbstractGeometry::WkbFlags wkbFlags = QgsAbstractGeometry::FlagExportTrianglesAsPolygons;
if ( mOgrDriverName == QLatin1String( "ESRI Shapefile" ) )
wkbFlags |= QgsAbstractGeometry::FlagExportNanAsDoubleMin;

QByteArray wkb( geom.asWkb( wkbFlags ) );
OGRGeometryH ogrGeom = createEmptyGeometry( mWkbType );
OGRErr err = OGR_G_ImportFromWkb( ogrGeom, reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), wkb.length() );
if ( err != OGRERR_NONE )
Expand Down
49 changes: 48 additions & 1 deletion tests/src/core/testqgsvectorfilewriter.cpp
Expand Up @@ -110,7 +110,8 @@ class TestQgsVectorFileWriter: public QObject
void testExportToGpxMultiLineStringForceRoute();
//! Test export using custom field names
void testExportCustomFieldNames();

//! Test export to shape with NaN values for Z
void testExportToShapeNanValuesForZ();
private:
// a little util fn used by all tests
bool cleanupFile( QString fileBase );
Expand Down Expand Up @@ -704,5 +705,51 @@ void TestQgsVectorFileWriter::testExportCustomFieldNames()
QCOMPARE( details.outputFields.at( 1 ).name(), "customfieldname" );
}

void TestQgsVectorFileWriter::testExportToShapeNanValuesForZ()
{
//
// Remove old copies that may be lying around
//
QString myFileName = QStringLiteral( "/testln.shp" );
myFileName = QDir::tempPath() + myFileName;
QVERIFY( QgsVectorFileWriter::deleteShapeFile( myFileName ) );

QgsVectorFileWriter::SaveVectorOptions saveOptions;
saveOptions.fileEncoding = mEncoding;
std::unique_ptr< QgsVectorFileWriter > writer( QgsVectorFileWriter::create( myFileName, mFields, QgsWkbTypes::LineStringZ, mCRS, QgsCoordinateTransformContext(), saveOptions ) );
//
// Create a feature
//
QgsLineString *ls = new QgsLineString();
ls->setPoints( QgsPointSequence() << QgsPoint( mPoint1 )
<< QgsPoint( mPoint2 )
<< QgsPoint( mPoint3 ) );
ls->setZAt( 1, std::numeric_limits<double>::quiet_NaN() );
const QgsGeometry mypLineGeometry( ls );
QgsFeature myFeature;
myFeature.setGeometry( mypLineGeometry );
myFeature.initAttributes( 1 );
myFeature.setAttribute( 0, "HelloWorld" );
//
// Write the feature to the filewriter
// and check for errors
//
QVERIFY( writer->addFeature( myFeature ) );
mError = writer->hasError();
if ( mError == QgsVectorFileWriter::ErrDriverNotFound )
{
std::cout << "Driver not found error" << std::endl;
}
else if ( mError == QgsVectorFileWriter::ErrCreateDataSource )
{
std::cout << "Create data source error" << std::endl;
}
else if ( mError == QgsVectorFileWriter::ErrCreateLayer )
{
std::cout << "Create layer error" << std::endl;
}
QVERIFY( mError == QgsVectorFileWriter::NoError );
}

QGSTEST_MAIN( TestQgsVectorFileWriter )
#include "testqgsvectorfilewriter.moc"

0 comments on commit 19da39f

Please sign in to comment.