LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsonwritelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 193 229 84.3 %
Date: 2026-04-20 19:56:30 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implementation of OGRGeoJSONWriteLayer class (OGR GeoJSON Driver).
       5             :  * Author:   Mateusz Loskot, mateusz@loskot.net
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
       9             :  * Copyright (c) 2007, Mateusz Loskot
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "ogr_geojson.h"
      15             : #include "ogrgeojsonwriter.h"
      16             : 
      17             : #include "cpl_vsi_virtual.h"
      18             : 
      19             : #include <algorithm>
      20             : 
      21             : /************************************************************************/
      22             : /*                        OGRGeoJSONWriteLayer()                        */
      23             : /************************************************************************/
      24             : 
      25         168 : OGRGeoJSONWriteLayer::OGRGeoJSONWriteLayer(const char *pszName,
      26             :                                            OGRwkbGeometryType eGType,
      27             :                                            CSLConstList papszOptions,
      28             :                                            bool bWriteFC_BBOXIn,
      29             :                                            OGRCoordinateTransformation *poCT,
      30         168 :                                            OGRGeoJSONDataSource *poDS)
      31         168 :     : poDS_(poDS), poFeatureDefn_(new OGRFeatureDefn(pszName)), nOutCounter_(0),
      32         168 :       bWriteBBOX(CPLTestBool(
      33             :           CSLFetchNameValueDef(papszOptions, "WRITE_BBOX", "FALSE"))),
      34             :       bBBOX3D(false), bWriteFC_BBOX(bWriteFC_BBOXIn),
      35         168 :       nSignificantFigures_(atoi(
      36             :           CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1"))),
      37             :       bRFC7946_(
      38         168 :           CPLTestBool(CSLFetchNameValueDef(papszOptions, "RFC7946", "FALSE"))),
      39         168 :       bWrapDateLine_(CPLTestBool(
      40             :           CSLFetchNameValueDef(papszOptions, "WRAPDATELINE", "YES"))),
      41             :       osForeignMembers_(
      42             :           CSLFetchNameValueDef(papszOptions, "FOREIGN_MEMBERS_FEATURE", "")),
      43        1008 :       poCT_(poCT)
      44             : {
      45         168 :     if (!osForeignMembers_.empty())
      46             :     {
      47             :         // Already checked in OGRGeoJSONDataSource::ICreateLayer()
      48           1 :         CPLAssert(osForeignMembers_.front() == '{');
      49           1 :         CPLAssert(osForeignMembers_.back() == '}');
      50             :         osForeignMembers_ =
      51           1 :             osForeignMembers_.substr(1, osForeignMembers_.size() - 2);
      52             :     }
      53         168 :     poFeatureDefn_->Reference();
      54         168 :     poFeatureDefn_->SetGeomType(eGType);
      55         168 :     SetDescription(poFeatureDefn_->GetName());
      56             :     const char *pszCoordPrecision =
      57         168 :         CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION");
      58         168 :     if (pszCoordPrecision)
      59             :     {
      60           3 :         oWriteOptions_.nXYCoordPrecision = atoi(pszCoordPrecision);
      61           3 :         oWriteOptions_.nZCoordPrecision = atoi(pszCoordPrecision);
      62             :     }
      63             :     else
      64             :     {
      65         165 :         oWriteOptions_.nXYCoordPrecision = atoi(CSLFetchNameValueDef(
      66         165 :             papszOptions, "XY_COORD_PRECISION", bRFC7946_ ? "7" : "-1"));
      67         165 :         oWriteOptions_.nZCoordPrecision = atoi(CSLFetchNameValueDef(
      68         165 :             papszOptions, "Z_COORD_PRECISION", bRFC7946_ ? "3" : "-1"));
      69             :     }
      70         168 :     oWriteOptions_.bWriteBBOX = bWriteBBOX;
      71         168 :     oWriteOptions_.nSignificantFigures = nSignificantFigures_;
      72         168 :     if (bRFC7946_)
      73             :     {
      74          23 :         oWriteOptions_.SetRFC7946Settings();
      75             :     }
      76         168 :     oWriteOptions_.SetIDOptions(papszOptions);
      77         168 :     oWriteOptions_.bAllowNonFiniteValues = CPLTestBool(
      78             :         CSLFetchNameValueDef(papszOptions, "WRITE_NON_FINITE_VALUES", "FALSE"));
      79         168 :     oWriteOptions_.bAutodetectJsonStrings = CPLTestBool(
      80             :         CSLFetchNameValueDef(papszOptions, "AUTODETECT_JSON_STRINGS", "TRUE"));
      81         168 : }
      82             : 
      83             : /************************************************************************/
      84             : /*                       ~OGRGeoJSONWriteLayer()                        */
      85             : /************************************************************************/
      86             : 
      87         336 : OGRGeoJSONWriteLayer::~OGRGeoJSONWriteLayer()
      88             : {
      89         168 :     FinishWriting();
      90             : 
      91         168 :     if (nullptr != poFeatureDefn_)
      92             :     {
      93         168 :         poFeatureDefn_->Release();
      94             :     }
      95             : 
      96         168 :     delete poCT_;
      97         336 : }
      98             : 
      99             : /************************************************************************/
     100             : /*                           FinishWriting()                            */
     101             : /************************************************************************/
     102             : 
     103         328 : void OGRGeoJSONWriteLayer::FinishWriting()
     104             : {
     105         328 :     if (m_nPositionBeforeFCClosed == 0)
     106             :     {
     107         169 :         VSILFILE *fp = poDS_->GetOutputFile();
     108             : 
     109         169 :         m_nPositionBeforeFCClosed = fp->Tell();
     110             : 
     111         169 :         VSIFPrintfL(fp, "\n]");
     112             : 
     113         169 :         if (bWriteFC_BBOX && sEnvelopeLayer.IsInit())
     114             :         {
     115          46 :             CPLString osBBOX = "[ ";
     116             :             char szFormat[32];
     117          23 :             if (oWriteOptions_.nXYCoordPrecision >= 0)
     118          20 :                 snprintf(szFormat, sizeof(szFormat), "%%.%df",
     119             :                          oWriteOptions_.nXYCoordPrecision);
     120             :             else
     121           3 :                 snprintf(szFormat, sizeof(szFormat), "%s", "%.15g");
     122             : 
     123          23 :             osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MinX);
     124          23 :             osBBOX += ", ";
     125          23 :             osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MinY);
     126          23 :             osBBOX += ", ";
     127          23 :             if (bBBOX3D)
     128             :             {
     129           1 :                 osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MinZ);
     130           1 :                 osBBOX += ", ";
     131             :             }
     132          23 :             osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MaxX);
     133          23 :             osBBOX += ", ";
     134          23 :             osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MaxY);
     135          23 :             if (bBBOX3D)
     136             :             {
     137           1 :                 osBBOX += ", ";
     138           1 :                 osBBOX += CPLSPrintf(szFormat, sEnvelopeLayer.MaxZ);
     139             :             }
     140          23 :             osBBOX += " ]";
     141             : 
     142          46 :             if (poDS_->GetFpOutputIsSeekable() &&
     143          23 :                 osBBOX.size() + 9 < OGRGeoJSONDataSource::SPACE_FOR_BBOX)
     144             :             {
     145          23 :                 VSIFSeekL(fp, poDS_->GetBBOXInsertLocation(), SEEK_SET);
     146          23 :                 VSIFPrintfL(fp, "\"bbox\": %s,", osBBOX.c_str());
     147          23 :                 VSIFSeekL(fp, 0, SEEK_END);
     148             :             }
     149             :             else
     150             :             {
     151           0 :                 VSIFPrintfL(fp, ",\n\"bbox\": %s", osBBOX.c_str());
     152             :             }
     153             :         }
     154             : 
     155         169 :         VSIFPrintfL(fp, "\n}\n");
     156         169 :         fp->Flush();
     157             :     }
     158         328 : }
     159             : 
     160             : /************************************************************************/
     161             : /*                             SyncToDisk()                             */
     162             : /************************************************************************/
     163             : 
     164         257 : OGRErr OGRGeoJSONWriteLayer::SyncToDisk()
     165             : {
     166         257 :     if (m_nPositionBeforeFCClosed == 0 && poDS_->GetFpOutputIsSeekable())
     167             :     {
     168         160 :         FinishWriting();
     169             :     }
     170             : 
     171         257 :     return OGRERR_NONE;
     172             : }
     173             : 
     174             : /************************************************************************/
     175             : /*                           ICreateFeature()                           */
     176             : /************************************************************************/
     177             : 
     178        1116 : OGRErr OGRGeoJSONWriteLayer::ICreateFeature(OGRFeature *poFeature)
     179             : {
     180        1116 :     VSILFILE *fp = poDS_->GetOutputFile();
     181             : 
     182             :     OGRFeature *poFeatureToWrite;
     183        1116 :     if (poCT_ != nullptr || bRFC7946_)
     184             :     {
     185          42 :         poFeatureToWrite = new OGRFeature(poFeatureDefn_);
     186          42 :         poFeatureToWrite->SetFrom(poFeature);
     187          42 :         poFeatureToWrite->SetFID(poFeature->GetFID());
     188          42 :         OGRGeometry *poGeometry = poFeatureToWrite->GetGeometryRef();
     189          42 :         if (poGeometry)
     190             :         {
     191          42 :             const char *const apszOptions[] = {
     192          42 :                 bWrapDateLine_ ? "WRAPDATELINE=YES" : nullptr, nullptr};
     193          84 :             OGRGeometry *poNewGeom = OGRGeometryFactory::transformWithOptions(
     194             :                 poGeometry, poCT_, const_cast<char **>(apszOptions),
     195          42 :                 oTransformCache_);
     196          42 :             if (poNewGeom == nullptr)
     197             :             {
     198           0 :                 delete poFeatureToWrite;
     199           0 :                 return OGRERR_FAILURE;
     200             :             }
     201             : 
     202          42 :             OGREnvelope sEnvelope;
     203          42 :             poNewGeom->getEnvelope(&sEnvelope);
     204          42 :             if (sEnvelope.MinX < -180.0 || sEnvelope.MaxX > 180.0 ||
     205          42 :                 sEnvelope.MinY < -90.0 || sEnvelope.MaxY > 90.0)
     206             :             {
     207           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     208             :                          "Geometry extent outside of "
     209             :                          "[-180.0,180.0]x[-90.0,90.0] bounds");
     210           0 :                 delete poFeatureToWrite;
     211           0 :                 return OGRERR_FAILURE;
     212             :             }
     213             : 
     214          42 :             poFeatureToWrite->SetGeometryDirectly(poNewGeom);
     215          42 :         }
     216             :     }
     217             :     else
     218             :     {
     219        1074 :         poFeatureToWrite = poFeature;
     220             :     }
     221             : 
     222             :     // Special processing to detect and repair invalid geometries due to
     223             :     // coordinate precision.
     224             :     // Normally drivers shouldn't do that as similar code is triggered by
     225             :     // setting the OGR_APPLY_GEOM_SET_PRECISION=YES configuration option by
     226             :     // the generic OGRLayer::CreateFeature() code path. But this code predates
     227             :     // its introduction and RFC99, and can be useful in RFC7946 mode due to
     228             :     // coordinate reprojection.
     229        1116 :     std::string osReason;
     230        1116 :     OGRGeometry *poOrigGeom = poFeature->GetGeometryRef();
     231        1116 :     if (OGRGeometryFactory::haveGEOS() &&
     232        1116 :         oWriteOptions_.nXYCoordPrecision >= 0 && poOrigGeom &&
     233        2269 :         wkbFlatten(poOrigGeom->getGeometryType()) != wkbPoint &&
     234          37 :         poOrigGeom->IsValid(&osReason))
     235             :     {
     236             :         const double dfXYResolution =
     237          34 :             std::pow(10.0, double(-oWriteOptions_.nXYCoordPrecision));
     238             :         auto poNewGeom = std::unique_ptr<OGRGeometry>(
     239          68 :             poFeatureToWrite->GetGeometryRef()->clone());
     240          68 :         OGRGeomCoordinatePrecision sPrecision;
     241          34 :         sPrecision.dfXYResolution = dfXYResolution;
     242          34 :         poNewGeom->roundCoordinates(sPrecision);
     243          34 :         if (!poNewGeom->IsValid(&osReason))
     244             :         {
     245           2 :             std::unique_ptr<OGRGeometry> poValidGeom;
     246           2 :             if (poFeature == poFeatureToWrite)
     247             :             {
     248           1 :                 CPLDebug("GeoJSON",
     249             :                          "Running SetPrecision() to correct an invalid "
     250             :                          "geometry due to reduced precision output");
     251           1 :                 poValidGeom.reset(
     252             :                     poOrigGeom->SetPrecision(dfXYResolution, /* nFlags = */ 0));
     253             :             }
     254             :             else
     255             :             {
     256           1 :                 CPLDebug("GeoJSON", "Running MakeValid() to correct an invalid "
     257             :                                     "geometry due to reduced precision output");
     258           1 :                 poValidGeom.reset(poNewGeom->MakeValid());
     259           1 :                 if (poValidGeom)
     260             :                 {
     261             :                     auto poValidGeomRoundCoordinates =
     262           2 :                         std::unique_ptr<OGRGeometry>(poValidGeom->clone());
     263           1 :                     poValidGeomRoundCoordinates->roundCoordinates(sPrecision);
     264           1 :                     if (!poValidGeomRoundCoordinates->IsValid(&osReason))
     265             :                     {
     266           1 :                         CPLDebug("GeoJSON",
     267             :                                  "Running SetPrecision() to correct an invalid "
     268             :                                  "geometry due to reduced precision output");
     269             :                         auto poValidGeom2 = std::unique_ptr<OGRGeometry>(
     270             :                             poValidGeom->SetPrecision(dfXYResolution,
     271           2 :                                                       /* nFlags = */ 0));
     272           1 :                         if (poValidGeom2)
     273           1 :                             poValidGeom = std::move(poValidGeom2);
     274             :                     }
     275             :                 }
     276             :                 else
     277             :                 {
     278           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     279             :                              "Geometry %s is not valid: %s",
     280           0 :                              poNewGeom->exportToWkt().c_str(),
     281             :                              osReason.c_str());
     282             :                 }
     283             :             }
     284           2 :             if (poValidGeom)
     285             :             {
     286           2 :                 if (poFeature == poFeatureToWrite)
     287             :                 {
     288           1 :                     poFeatureToWrite = new OGRFeature(poFeatureDefn_);
     289           1 :                     poFeatureToWrite->SetFrom(poFeature);
     290           1 :                     poFeatureToWrite->SetFID(poFeature->GetFID());
     291             :                 }
     292           2 :                 poFeatureToWrite->SetGeometryDirectly(poValidGeom.release());
     293             :             }
     294             :         }
     295             :     }
     296             : 
     297        1116 :     if (oWriteOptions_.bGenerateID && poFeatureToWrite->GetFID() == OGRNullFID)
     298             :     {
     299           3 :         poFeatureToWrite->SetFID(nOutCounter_);
     300             :     }
     301             :     json_object *poObj =
     302        1116 :         OGRGeoJSONWriteFeature(poFeatureToWrite, oWriteOptions_);
     303        1116 :     CPLAssert(nullptr != poObj);
     304             : 
     305        1116 :     if (m_nPositionBeforeFCClosed)
     306             :     {
     307             :         // If we had called SyncToDisk() previously, undo its effects
     308           1 :         fp->Seek(m_nPositionBeforeFCClosed, SEEK_SET);
     309           1 :         m_nPositionBeforeFCClosed = 0;
     310             :     }
     311             : 
     312        1116 :     if (nOutCounter_ > 0)
     313             :     {
     314             :         /* Separate "Feature" entries in "FeatureCollection" object. */
     315         969 :         VSIFPrintfL(fp, ",\n");
     316             :     }
     317        1116 :     const char *pszJson = json_object_to_json_string_ext(
     318             :         poObj, JSON_C_TO_STRING_PLAIN
     319             : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
     320             :                    | JSON_C_TO_STRING_NOSLASHESCAPE
     321             : #endif
     322             :     );
     323             : 
     324        1116 :     OGRErr eErr = OGRERR_NONE;
     325        1116 :     size_t nLen = strlen(pszJson);
     326        1116 :     if (!osForeignMembers_.empty())
     327             :     {
     328           1 :         if (nLen > 1 && pszJson[nLen - 1] == '}')
     329             :         {
     330           1 :             nLen -= 1;
     331             :         }
     332             :         else
     333             :         {
     334             :             // should not happen
     335           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     336             :                      "Unexpected JSON output for feature. Cannot write foreign "
     337             :                      "member");
     338           0 :             osForeignMembers_.clear();
     339             :         }
     340             :     }
     341        1116 :     if (VSIFWriteL(pszJson, nLen, 1, fp) != 1)
     342             :     {
     343           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
     344           0 :         eErr = OGRERR_FAILURE;
     345             :     }
     346        1117 :     else if (!osForeignMembers_.empty() &&
     347           1 :              (VSIFWriteL(",", 1, 1, fp) != 1 ||
     348           1 :               VSIFWriteL(osForeignMembers_.c_str(), osForeignMembers_.size(), 1,
     349           1 :                          fp) != 1 ||
     350           1 :               VSIFWriteL("}", 1, 1, fp) != 1))
     351             :     {
     352           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
     353           0 :         eErr = OGRERR_FAILURE;
     354             :     }
     355             : 
     356        1116 :     json_object_put(poObj);
     357             : 
     358        1116 :     ++nOutCounter_;
     359             : 
     360        1116 :     OGRGeometry *poGeometry = poFeatureToWrite->GetGeometryRef();
     361        1116 :     if (poGeometry != nullptr && !poGeometry->IsEmpty())
     362             :     {
     363        1017 :         OGREnvelope3D sEnvelope = OGRGeoJSONGetBBox(poGeometry, oWriteOptions_);
     364        1017 :         if (poGeometry->getCoordinateDimension() == 3)
     365          21 :             bBBOX3D = true;
     366             : 
     367        1017 :         if (!sEnvelopeLayer.IsInit())
     368             :         {
     369         110 :             sEnvelopeLayer = sEnvelope;
     370             :         }
     371         907 :         else if (oWriteOptions_.bBBOXRFC7946)
     372             :         {
     373          20 :             const bool bEnvelopeCrossAM = (sEnvelope.MinX > sEnvelope.MaxX);
     374          20 :             const bool bEnvelopeLayerCrossAM =
     375          20 :                 (sEnvelopeLayer.MinX > sEnvelopeLayer.MaxX);
     376          20 :             if (bEnvelopeCrossAM)
     377             :             {
     378           8 :                 if (bEnvelopeLayerCrossAM)
     379             :                 {
     380           8 :                     sEnvelopeLayer.MinX =
     381           8 :                         std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
     382           8 :                     sEnvelopeLayer.MaxX =
     383           8 :                         std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
     384             :                 }
     385             :                 else
     386             :                 {
     387           0 :                     if (sEnvelopeLayer.MinX > 0)
     388             :                     {
     389           0 :                         sEnvelopeLayer.MinX =
     390           0 :                             std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
     391           0 :                         sEnvelopeLayer.MaxX = sEnvelope.MaxX;
     392             :                     }
     393           0 :                     else if (sEnvelopeLayer.MaxX < 0)
     394             :                     {
     395           0 :                         sEnvelopeLayer.MaxX =
     396           0 :                             std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
     397           0 :                         sEnvelopeLayer.MinX = sEnvelope.MinX;
     398             :                     }
     399             :                     else
     400             :                     {
     401           0 :                         sEnvelopeLayer.MinX = -180.0;
     402           0 :                         sEnvelopeLayer.MaxX = 180.0;
     403             :                     }
     404             :                 }
     405             :             }
     406          12 :             else if (bEnvelopeLayerCrossAM)
     407             :             {
     408           0 :                 if (sEnvelope.MinX > 0)
     409             :                 {
     410           0 :                     sEnvelopeLayer.MinX =
     411           0 :                         std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
     412             :                 }
     413           0 :                 else if (sEnvelope.MaxX < 0)
     414             :                 {
     415           0 :                     sEnvelopeLayer.MaxX =
     416           0 :                         std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
     417             :                 }
     418             :                 else
     419             :                 {
     420           0 :                     sEnvelopeLayer.MinX = -180.0;
     421           0 :                     sEnvelopeLayer.MaxX = 180.0;
     422             :                 }
     423             :             }
     424             :             else
     425             :             {
     426          12 :                 sEnvelopeLayer.MinX =
     427          12 :                     std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
     428          12 :                 sEnvelopeLayer.MaxX =
     429          12 :                     std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
     430             :             }
     431             : 
     432          20 :             sEnvelopeLayer.MinY = std::min(sEnvelopeLayer.MinY, sEnvelope.MinY);
     433          20 :             sEnvelopeLayer.MaxY = std::max(sEnvelopeLayer.MaxY, sEnvelope.MaxY);
     434             :         }
     435             :         else
     436             :         {
     437         887 :             sEnvelopeLayer.Merge(sEnvelope);
     438             :         }
     439             :     }
     440             : 
     441        1116 :     if (poFeatureToWrite != poFeature)
     442          43 :         delete poFeatureToWrite;
     443             : 
     444        1116 :     return eErr;
     445             : }
     446             : 
     447             : /************************************************************************/
     448             : /*                            CreateField()                             */
     449             : /************************************************************************/
     450             : 
     451         231 : OGRErr OGRGeoJSONWriteLayer::CreateField(const OGRFieldDefn *poField,
     452             :                                          int /* bApproxOK */)
     453             : {
     454         231 :     if (poFeatureDefn_->GetFieldIndexCaseSensitive(poField->GetNameRef()) >= 0)
     455             :     {
     456           0 :         CPLDebug("GeoJSON", "Field '%s' already present in schema",
     457             :                  poField->GetNameRef());
     458             : 
     459             :         // TODO - mloskot: Is this return code correct?
     460           0 :         return OGRERR_NONE;
     461             :     }
     462             : 
     463         231 :     poFeatureDefn_->AddFieldDefn(poField);
     464             : 
     465         231 :     return OGRERR_NONE;
     466             : }
     467             : 
     468             : /************************************************************************/
     469             : /*                           TestCapability()                           */
     470             : /************************************************************************/
     471             : 
     472         558 : int OGRGeoJSONWriteLayer::TestCapability(const char *pszCap) const
     473             : {
     474         558 :     if (EQUAL(pszCap, OLCCreateField))
     475          16 :         return TRUE;
     476         542 :     else if (EQUAL(pszCap, OLCSequentialWrite))
     477          20 :         return TRUE;
     478         522 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
     479           0 :         return TRUE;
     480         522 :     return FALSE;
     481             : }
     482             : 
     483             : /************************************************************************/
     484             : /*                             IGetExtent()                             */
     485             : /************************************************************************/
     486             : 
     487           2 : OGRErr OGRGeoJSONWriteLayer::IGetExtent(int /*iGeomField*/,
     488             :                                         OGREnvelope *psExtent, bool)
     489             : {
     490           2 :     if (sEnvelopeLayer.IsInit())
     491             :     {
     492           2 :         *psExtent = sEnvelopeLayer;
     493           2 :         return OGRERR_NONE;
     494             :     }
     495           0 :     return OGRERR_FAILURE;
     496             : }
     497             : 
     498             : /************************************************************************/
     499             : /*                             GetDataset()                             */
     500             : /************************************************************************/
     501             : 
     502          39 : GDALDataset *OGRGeoJSONWriteLayer::GetDataset()
     503             : {
     504          39 :     return poDS_;
     505             : }

Generated by: LCOV version 1.14