From 66d1e37f1f285de46c78f376331a51ed3e327df9 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 5 Mar 2024 00:11:41 +0100 Subject: [PATCH] ogr2ogr: run OGRGeometry::SetPrecision() when specifying -xyRes --- apps/ogr2ogr_lib.cpp | 19 +++++++++++++++++-- autotest/utilities/test_ogr2ogr_lib.py | 20 ++++++++++++++++++++ doc/source/programs/ogr2ogr.rst | 5 +++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/apps/ogr2ogr_lib.cpp b/apps/ogr2ogr_lib.cpp index b61a47f50b38..01546c6319cb 100644 --- a/apps/ogr2ogr_lib.cpp +++ b/apps/ogr2ogr_lib.cpp @@ -3893,6 +3893,7 @@ bool SetupTargetLayer::CanUseWriteArrowBatch( !m_bUnsetFieldWidth && !m_bExplodeCollections && !m_pszZField && m_bExactFieldNameMatch && !m_bForceNullable && !m_bResolveDomains && !m_bUnsetDefault && psOptions->nFIDToFetch == OGRNullFID && + psOptions->dfXYRes == OGRGeomCoordinatePrecision::UNKNOWN && !psOptions->bMakeValid) { struct ArrowArrayStream streamSrc; @@ -4328,11 +4329,13 @@ SetupTargetLayer::Setup(OGRLayer *poSrcLayer, const char *pszNewLayerName, if (psOptions->dfXYRes != OGRGeomCoordinatePrecision::UNKNOWN) { if (m_poDstDS->GetDriver()->GetMetadataItem( - GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION) == nullptr) + GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION) == nullptr && + !OGRGeometryFactory::haveGEOS()) { CPLError(CE_Warning, CPLE_AppDefined, "-xyRes specified, but driver does not expose the " - "DCAP_HONOR_GEOM_COORDINATE_PRECISION capability"); + "DCAP_HONOR_GEOM_COORDINATE_PRECISION capability, " + "and this build has no GEOS support"); } oCoordPrec.dfXYResolution = psOptions->dfXYRes; @@ -6378,6 +6381,18 @@ bool LayerTranslator::Translate( poDstGeometry = poClipped.release(); } + if (psOptions->dfXYRes != + OGRGeomCoordinatePrecision::UNKNOWN && + OGRGeometryFactory::haveGEOS()) + { + OGRGeometry *poRoundedGeom = + poDstGeometry->SetPrecision(psOptions->dfXYRes, 0); + delete poDstGeometry; + poDstGeometry = poRoundedGeom; + if (poDstGeometry == nullptr) + goto end_loop; + } + if (m_bMakeValid) { const bool bIsGeomCollection = diff --git a/autotest/utilities/test_ogr2ogr_lib.py b/autotest/utilities/test_ogr2ogr_lib.py index e932577bdcb8..d071cf1e55dc 100755 --- a/autotest/utilities/test_ogr2ogr_lib.py +++ b/autotest/utilities/test_ogr2ogr_lib.py @@ -2619,3 +2619,23 @@ def test_ogr2ogr_lib_coordinate_precision(tmp_vsimem): assert prec.GetZResolution() == 0 assert prec.GetMResolution() == 0 ds.Close() + + +############################################################################### + + +def test_ogr2ogr_lib_coordinate_precision_with_geom(): + + src_ds = gdal.GetDriverByName("Memory").Create("", 0, 0, 0, gdal.GDT_Unknown) + src_lyr = src_ds.CreateLayer("test") + f = ogr.Feature(src_lyr.GetLayerDefn()) + f.SetGeometry(ogr.CreateGeometryFromWkt("LINESTRING (1 1,9 9)")) + src_lyr.CreateFeature(f) + + out_ds = gdal.VectorTranslate("", src_ds, format="Memory", xyRes=10) + out_lyr = out_ds.GetLayer(0) + f = out_lyr.GetNextFeature() + if ogr.GetGEOSVersionMajor() > 0: + assert f.GetGeometryRef().ExportToWkt() == "LINESTRING (0 0,10 10)" + else: + assert f.GetGeometryRef().ExportToWkt() == "LINESTRING (1 1,9 9)" diff --git a/doc/source/programs/ogr2ogr.rst b/doc/source/programs/ogr2ogr.rst index a5655cbab169..cc9311cd8af9 100644 --- a/doc/source/programs/ogr2ogr.rst +++ b/doc/source/programs/ogr2ogr.rst @@ -265,6 +265,11 @@ output coordinate system or even reprojecting the features during translation. is specified, it is assumed to be expressed in the units of the target SRS. The m, mm or deg suffixes can be specified to indicate that the value must be interpreted as being in metre, millimeter or degree. + + When specifying this option, the :cpp:func:``OGRGeometry::SetPrecision`` + method is run on geometries before passing them to the output driver, to + avoid generating invalid geometries due to the potentially reduced precision. + If neither this option nor :option:`-unsetCoordPrecision` are specified, the coordinate resolution of the source layer, if available, is used.