From 2975e065bddab5fcf677c344f82c221cb2462101 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 6 Mar 2024 17:49:52 +0100 Subject: [PATCH] OGRGeoJSONWriteLayer::ICreateFeature(): simplify using OGRGeometry::roundCoordinates() and SetPrecision() --- .../geojson/ogrgeojsonwritelayer.cpp | 55 +++++-------------- 1 file changed, 13 insertions(+), 42 deletions(-) diff --git a/ogr/ogrsf_frmts/geojson/ogrgeojsonwritelayer.cpp b/ogr/ogrsf_frmts/geojson/ogrgeojsonwritelayer.cpp index 34ce6ef631ec..f866bcd40167 100644 --- a/ogr/ogrsf_frmts/geojson/ogrgeojsonwritelayer.cpp +++ b/ogr/ogrsf_frmts/geojson/ogrgeojsonwritelayer.cpp @@ -243,42 +243,32 @@ OGRErr OGRGeoJSONWriteLayer::ICreateFeature(OGRFeature *poFeature) // Special processing to detect and repair invalid geometries due to // coordinate precision. + // Normally drivers shouldn't do that as similar code is triggered by + // setting the OGR_APPLY_GEOM_SET_PRECISION=YES configuration option by + // the generic OGRLayer::CreateFeature() code path. But this code predates + // its introduction and RFC99, and can be useful in RFC7946 mode due to + // coordinate reprojection. OGRGeometry *poOrigGeom = poFeature->GetGeometryRef(); if (OGRGeometryFactory::haveGEOS() && oWriteOptions_.nXYCoordPrecision >= 0 && poOrigGeom && wkbFlatten(poOrigGeom->getGeometryType()) != wkbPoint && IsValid(poOrigGeom)) { - struct CoordinateRoundingVisitor : public OGRDefaultGeometryVisitor - { - const double dfFactor_; - const double dfInvFactor_; - - explicit CoordinateRoundingVisitor(int nCoordPrecision) - : dfFactor_(std::pow(10.0, double(nCoordPrecision))), - dfInvFactor_(std::pow(10.0, double(-nCoordPrecision))) - { - } - - using OGRDefaultGeometryVisitor::visit; - void visit(OGRPoint *p) override - { - p->setX(std::round(p->getX() * dfFactor_) * dfInvFactor_); - p->setY(std::round(p->getY() * dfFactor_) * dfInvFactor_); - } - }; - - CoordinateRoundingVisitor oVisitor(oWriteOptions_.nXYCoordPrecision); + const double dfXYResolution = + std::pow(10.0, double(-oWriteOptions_.nXYCoordPrecision)); auto poNewGeom = poFeature == poFeatureToWrite ? poOrigGeom->clone() : poFeatureToWrite->GetGeometryRef(); bool bDeleteNewGeom = (poFeature == poFeatureToWrite); - poNewGeom->accept(&oVisitor); + OGRGeomCoordinatePrecision sPrecision; + sPrecision.dfXYResolution = dfXYResolution; + poNewGeom->roundCoordinates(sPrecision); if (!IsValid(poNewGeom)) { - CPLDebug("GeoJSON", "Running MakeValid() to correct an invalid " + CPLDebug("GeoJSON", "Running SetPrecision() to correct an invalid " "geometry due to reduced precision output"); - auto poValidGeom = poNewGeom->MakeValid(); + auto poValidGeom = + poOrigGeom->SetPrecision(dfXYResolution, /* nFlags = */ 0); if (poValidGeom) { if (poFeature == poFeatureToWrite) @@ -288,25 +278,6 @@ OGRErr OGRGeoJSONWriteLayer::ICreateFeature(OGRFeature *poFeature) poFeatureToWrite->SetFID(poFeature->GetFID()); } - // It may happen that after MakeValid(), and rounding again, - // we end up with an invalid result. Run MakeValid() again... - poValidGeom->accept(&oVisitor); - if (!IsValid(poValidGeom)) - { - auto poValidGeom2 = poValidGeom->MakeValid(); - if (poValidGeom2) - { - delete poValidGeom; - poValidGeom = poValidGeom2; - if (!IsValid(poValidGeom)) - { - // hopefully should not happen - CPLDebug("GeoJSON", - "... still not valid! Giving up"); - } - } - } - poFeatureToWrite->SetGeometryDirectly(poValidGeom); } }