LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrgeojsonwritelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 195 229 85.2 %
Date: 2024-11-21 22:18:42 Functions: 11 11 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         156 : OGRGeoJSONWriteLayer::OGRGeoJSONWriteLayer(const char *pszName,
      26             :                                            OGRwkbGeometryType eGType,
      27             :                                            CSLConstList papszOptions,
      28             :                                            bool bWriteFC_BBOXIn,
      29             :                                            OGRCoordinateTransformation *poCT,
      30         156 :                                            OGRGeoJSONDataSource *poDS)
      31         156 :     : poDS_(poDS), poFeatureDefn_(new OGRFeatureDefn(pszName)), nOutCounter_(0),
      32         156 :       bWriteBBOX(CPLTestBool(
      33             :           CSLFetchNameValueDef(papszOptions, "WRITE_BBOX", "FALSE"))),
      34             :       bBBOX3D(false), bWriteFC_BBOX(bWriteFC_BBOXIn),
      35         156 :       nSignificantFigures_(atoi(
      36             :           CSLFetchNameValueDef(papszOptions, "SIGNIFICANT_FIGURES", "-1"))),
      37             :       bRFC7946_(
      38         156 :           CPLTestBool(CSLFetchNameValueDef(papszOptions, "RFC7946", "FALSE"))),
      39         156 :       bWrapDateLine_(CPLTestBool(
      40             :           CSLFetchNameValueDef(papszOptions, "WRAPDATELINE", "YES"))),
      41             :       osForeignMembers_(
      42             :           CSLFetchNameValueDef(papszOptions, "FOREIGN_MEMBERS_FEATURE", "")),
      43         936 :       poCT_(poCT)
      44             : {
      45         156 :     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         156 :     poFeatureDefn_->Reference();
      54         156 :     poFeatureDefn_->SetGeomType(eGType);
      55         156 :     SetDescription(poFeatureDefn_->GetName());
      56             :     const char *pszCoordPrecision =
      57         156 :         CSLFetchNameValue(papszOptions, "COORDINATE_PRECISION");
      58         156 :     if (pszCoordPrecision)
      59             :     {
      60           3 :         oWriteOptions_.nXYCoordPrecision = atoi(pszCoordPrecision);
      61           3 :         oWriteOptions_.nZCoordPrecision = atoi(pszCoordPrecision);
      62             :     }
      63             :     else
      64             :     {
      65         153 :         oWriteOptions_.nXYCoordPrecision = atoi(CSLFetchNameValueDef(
      66         153 :             papszOptions, "XY_COORD_PRECISION", bRFC7946_ ? "7" : "-1"));
      67         153 :         oWriteOptions_.nZCoordPrecision = atoi(CSLFetchNameValueDef(
      68         153 :             papszOptions, "Z_COORD_PRECISION", bRFC7946_ ? "3" : "-1"));
      69             :     }
      70         156 :     oWriteOptions_.bWriteBBOX = bWriteBBOX;
      71         156 :     oWriteOptions_.nSignificantFigures = nSignificantFigures_;
      72         156 :     if (bRFC7946_)
      73             :     {
      74          23 :         oWriteOptions_.SetRFC7946Settings();
      75             :     }
      76         156 :     oWriteOptions_.SetIDOptions(papszOptions);
      77         156 :     oWriteOptions_.bAllowNonFiniteValues = CPLTestBool(
      78             :         CSLFetchNameValueDef(papszOptions, "WRITE_NON_FINITE_VALUES", "FALSE"));
      79         156 :     oWriteOptions_.bAutodetectJsonStrings = CPLTestBool(
      80             :         CSLFetchNameValueDef(papszOptions, "AUTODETECT_JSON_STRINGS", "TRUE"));
      81         156 : }
      82             : 
      83             : /************************************************************************/
      84             : /*                        ~OGRGeoJSONWriteLayer()                       */
      85             : /************************************************************************/
      86             : 
      87         312 : OGRGeoJSONWriteLayer::~OGRGeoJSONWriteLayer()
      88             : {
      89         156 :     FinishWriting();
      90             : 
      91         156 :     if (nullptr != poFeatureDefn_)
      92             :     {
      93         156 :         poFeatureDefn_->Release();
      94             :     }
      95             : 
      96         156 :     delete poCT_;
      97         312 : }
      98             : 
      99             : /************************************************************************/
     100             : /*                           FinishWriting()                            */
     101             : /************************************************************************/
     102             : 
     103         306 : void OGRGeoJSONWriteLayer::FinishWriting()
     104             : {
     105         306 :     if (m_nPositionBeforeFCClosed == 0)
     106             :     {
     107         157 :         VSILFILE *fp = poDS_->GetOutputFile();
     108             : 
     109         157 :         m_nPositionBeforeFCClosed = fp->Tell();
     110             : 
     111         157 :         VSIFPrintfL(fp, "\n]");
     112             : 
     113         157 :         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         157 :         VSIFPrintfL(fp, "\n}\n");
     156         157 :         fp->Flush();
     157             :     }
     158         306 : }
     159             : 
     160             : /************************************************************************/
     161             : /*                           SyncToDisk()                               */
     162             : /************************************************************************/
     163             : 
     164         234 : OGRErr OGRGeoJSONWriteLayer::SyncToDisk()
     165             : {
     166         234 :     if (m_nPositionBeforeFCClosed == 0 && poDS_->GetFpOutputIsSeekable())
     167             :     {
     168         150 :         FinishWriting();
     169             :     }
     170             : 
     171         234 :     return OGRERR_NONE;
     172             : }
     173             : 
     174             : /************************************************************************/
     175             : /*                           ICreateFeature()                            */
     176             : /************************************************************************/
     177             : 
     178        1083 : OGRErr OGRGeoJSONWriteLayer::ICreateFeature(OGRFeature *poFeature)
     179             : {
     180        1083 :     VSILFILE *fp = poDS_->GetOutputFile();
     181             : 
     182             :     OGRFeature *poFeatureToWrite;
     183        1083 :     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        1041 :         poFeatureToWrite = poFeature;
     220             :     }
     221             : 
     222          72 :     const auto IsValid = [](const OGRGeometry *poGeom)
     223             :     {
     224         144 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
     225         144 :         return poGeom->IsValid();
     226             :     };
     227             : 
     228             :     // Special processing to detect and repair invalid geometries due to
     229             :     // coordinate precision.
     230             :     // Normally drivers shouldn't do that as similar code is triggered by
     231             :     // setting the OGR_APPLY_GEOM_SET_PRECISION=YES configuration option by
     232             :     // the generic OGRLayer::CreateFeature() code path. But this code predates
     233             :     // its introduction and RFC99, and can be useful in RFC7946 mode due to
     234             :     // coordinate reprojection.
     235        1083 :     OGRGeometry *poOrigGeom = poFeature->GetGeometryRef();
     236        1083 :     if (OGRGeometryFactory::haveGEOS() &&
     237        1083 :         oWriteOptions_.nXYCoordPrecision >= 0 && poOrigGeom &&
     238        2203 :         wkbFlatten(poOrigGeom->getGeometryType()) != wkbPoint &&
     239          37 :         IsValid(poOrigGeom))
     240             :     {
     241             :         const double dfXYResolution =
     242          34 :             std::pow(10.0, double(-oWriteOptions_.nXYCoordPrecision));
     243             :         auto poNewGeom = std::unique_ptr<OGRGeometry>(
     244          68 :             poFeatureToWrite->GetGeometryRef()->clone());
     245          68 :         OGRGeomCoordinatePrecision sPrecision;
     246          34 :         sPrecision.dfXYResolution = dfXYResolution;
     247          34 :         poNewGeom->roundCoordinates(sPrecision);
     248          34 :         if (!IsValid(poNewGeom.get()))
     249             :         {
     250           2 :             std::unique_ptr<OGRGeometry> poValidGeom;
     251           2 :             if (poFeature == poFeatureToWrite)
     252             :             {
     253           1 :                 CPLDebug("GeoJSON",
     254             :                          "Running SetPrecision() to correct an invalid "
     255             :                          "geometry due to reduced precision output");
     256           1 :                 poValidGeom.reset(
     257             :                     poOrigGeom->SetPrecision(dfXYResolution, /* nFlags = */ 0));
     258             :             }
     259             :             else
     260             :             {
     261           1 :                 CPLDebug("GeoJSON", "Running MakeValid() to correct an invalid "
     262             :                                     "geometry due to reduced precision output");
     263           1 :                 poValidGeom.reset(poNewGeom->MakeValid());
     264           1 :                 if (poValidGeom)
     265             :                 {
     266             :                     auto poValidGeomRoundCoordinates =
     267           2 :                         std::unique_ptr<OGRGeometry>(poValidGeom->clone());
     268           1 :                     poValidGeomRoundCoordinates->roundCoordinates(sPrecision);
     269           1 :                     if (!IsValid(poValidGeomRoundCoordinates.get()))
     270             :                     {
     271           1 :                         CPLDebug("GeoJSON",
     272             :                                  "Running SetPrecision() to correct an invalid "
     273             :                                  "geometry due to reduced precision output");
     274             :                         auto poValidGeom2 = std::unique_ptr<OGRGeometry>(
     275             :                             poValidGeom->SetPrecision(dfXYResolution,
     276           2 :                                                       /* nFlags = */ 0));
     277           1 :                         if (poValidGeom2)
     278           1 :                             poValidGeom = std::move(poValidGeom2);
     279             :                     }
     280             :                 }
     281             :             }
     282           2 :             if (poValidGeom)
     283             :             {
     284           2 :                 if (poFeature == poFeatureToWrite)
     285             :                 {
     286           1 :                     poFeatureToWrite = new OGRFeature(poFeatureDefn_);
     287           1 :                     poFeatureToWrite->SetFrom(poFeature);
     288           1 :                     poFeatureToWrite->SetFID(poFeature->GetFID());
     289             :                 }
     290           2 :                 poFeatureToWrite->SetGeometryDirectly(poValidGeom.release());
     291             :             }
     292             :         }
     293             :     }
     294             : 
     295        1083 :     if (oWriteOptions_.bGenerateID && poFeatureToWrite->GetFID() == OGRNullFID)
     296             :     {
     297           3 :         poFeatureToWrite->SetFID(nOutCounter_);
     298             :     }
     299             :     json_object *poObj =
     300        1083 :         OGRGeoJSONWriteFeature(poFeatureToWrite, oWriteOptions_);
     301        1083 :     CPLAssert(nullptr != poObj);
     302             : 
     303        1083 :     if (m_nPositionBeforeFCClosed)
     304             :     {
     305             :         // If we had called SyncToDisk() previously, undo its effects
     306           1 :         fp->Seek(m_nPositionBeforeFCClosed, SEEK_SET);
     307           1 :         m_nPositionBeforeFCClosed = 0;
     308             :     }
     309             : 
     310        1083 :     if (nOutCounter_ > 0)
     311             :     {
     312             :         /* Separate "Feature" entries in "FeatureCollection" object. */
     313         948 :         VSIFPrintfL(fp, ",\n");
     314             :     }
     315        1083 :     const char *pszJson = json_object_to_json_string_ext(
     316             :         poObj, JSON_C_TO_STRING_SPACED
     317             : #ifdef JSON_C_TO_STRING_NOSLASHESCAPE
     318             :                    | JSON_C_TO_STRING_NOSLASHESCAPE
     319             : #endif
     320             :     );
     321             : 
     322        1083 :     OGRErr eErr = OGRERR_NONE;
     323        1083 :     size_t nLen = strlen(pszJson);
     324        1083 :     if (!osForeignMembers_.empty())
     325             :     {
     326           1 :         if (nLen > 2 && pszJson[nLen - 2] == ' ' && pszJson[nLen - 1] == '}')
     327             :         {
     328           1 :             nLen -= 2;
     329             :         }
     330             :         else
     331             :         {
     332             :             // should not happen
     333           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     334             :                      "Unexpected JSON output for feature. Cannot write foreign "
     335             :                      "member");
     336           0 :             osForeignMembers_.clear();
     337             :         }
     338             :     }
     339        1083 :     if (VSIFWriteL(pszJson, nLen, 1, fp) != 1)
     340             :     {
     341           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
     342           0 :         eErr = OGRERR_FAILURE;
     343             :     }
     344        1084 :     else if (!osForeignMembers_.empty() &&
     345           1 :              (VSIFWriteL(", ", 2, 1, fp) != 1 ||
     346           1 :               VSIFWriteL(osForeignMembers_.c_str(), osForeignMembers_.size(), 1,
     347           1 :                          fp) != 1 ||
     348           1 :               VSIFWriteL("}", 1, 1, fp) != 1))
     349             :     {
     350           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot write feature");
     351           0 :         eErr = OGRERR_FAILURE;
     352             :     }
     353             : 
     354        1083 :     json_object_put(poObj);
     355             : 
     356        1083 :     ++nOutCounter_;
     357             : 
     358        1083 :     OGRGeometry *poGeometry = poFeatureToWrite->GetGeometryRef();
     359        1083 :     if (poGeometry != nullptr && !poGeometry->IsEmpty())
     360             :     {
     361         983 :         OGREnvelope3D sEnvelope = OGRGeoJSONGetBBox(poGeometry, oWriteOptions_);
     362         983 :         if (poGeometry->getCoordinateDimension() == 3)
     363          21 :             bBBOX3D = true;
     364             : 
     365         983 :         if (!sEnvelopeLayer.IsInit())
     366             :         {
     367          98 :             sEnvelopeLayer = sEnvelope;
     368             :         }
     369         885 :         else if (oWriteOptions_.bBBOXRFC7946)
     370             :         {
     371          20 :             const bool bEnvelopeCrossAM = (sEnvelope.MinX > sEnvelope.MaxX);
     372          20 :             const bool bEnvelopeLayerCrossAM =
     373          20 :                 (sEnvelopeLayer.MinX > sEnvelopeLayer.MaxX);
     374          20 :             if (bEnvelopeCrossAM)
     375             :             {
     376           8 :                 if (bEnvelopeLayerCrossAM)
     377             :                 {
     378           8 :                     sEnvelopeLayer.MinX =
     379           8 :                         std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
     380           8 :                     sEnvelopeLayer.MaxX =
     381           8 :                         std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
     382             :                 }
     383             :                 else
     384             :                 {
     385           0 :                     if (sEnvelopeLayer.MinX > 0)
     386             :                     {
     387           0 :                         sEnvelopeLayer.MinX =
     388           0 :                             std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
     389           0 :                         sEnvelopeLayer.MaxX = sEnvelope.MaxX;
     390             :                     }
     391           0 :                     else if (sEnvelopeLayer.MaxX < 0)
     392             :                     {
     393           0 :                         sEnvelopeLayer.MaxX =
     394           0 :                             std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
     395           0 :                         sEnvelopeLayer.MinX = sEnvelope.MinX;
     396             :                     }
     397             :                     else
     398             :                     {
     399           0 :                         sEnvelopeLayer.MinX = -180.0;
     400           0 :                         sEnvelopeLayer.MaxX = 180.0;
     401             :                     }
     402             :                 }
     403             :             }
     404          12 :             else if (bEnvelopeLayerCrossAM)
     405             :             {
     406           0 :                 if (sEnvelope.MinX > 0)
     407             :                 {
     408           0 :                     sEnvelopeLayer.MinX =
     409           0 :                         std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
     410             :                 }
     411           0 :                 else if (sEnvelope.MaxX < 0)
     412             :                 {
     413           0 :                     sEnvelopeLayer.MaxX =
     414           0 :                         std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
     415             :                 }
     416             :                 else
     417             :                 {
     418           0 :                     sEnvelopeLayer.MinX = -180.0;
     419           0 :                     sEnvelopeLayer.MaxX = 180.0;
     420             :                 }
     421             :             }
     422             :             else
     423             :             {
     424          12 :                 sEnvelopeLayer.MinX =
     425          12 :                     std::min(sEnvelopeLayer.MinX, sEnvelope.MinX);
     426          12 :                 sEnvelopeLayer.MaxX =
     427          12 :                     std::max(sEnvelopeLayer.MaxX, sEnvelope.MaxX);
     428             :             }
     429             : 
     430          20 :             sEnvelopeLayer.MinY = std::min(sEnvelopeLayer.MinY, sEnvelope.MinY);
     431          20 :             sEnvelopeLayer.MaxY = std::max(sEnvelopeLayer.MaxY, sEnvelope.MaxY);
     432             :         }
     433             :         else
     434             :         {
     435         865 :             sEnvelopeLayer.Merge(sEnvelope);
     436             :         }
     437             :     }
     438             : 
     439        1083 :     if (poFeatureToWrite != poFeature)
     440          43 :         delete poFeatureToWrite;
     441             : 
     442        1083 :     return eErr;
     443             : }
     444             : 
     445             : /************************************************************************/
     446             : /*                           CreateField()                              */
     447             : /************************************************************************/
     448             : 
     449         223 : OGRErr OGRGeoJSONWriteLayer::CreateField(const OGRFieldDefn *poField,
     450             :                                          int /* bApproxOK */)
     451             : {
     452         223 :     if (poFeatureDefn_->GetFieldIndexCaseSensitive(poField->GetNameRef()) >= 0)
     453             :     {
     454           0 :         CPLDebug("GeoJSON", "Field '%s' already present in schema",
     455             :                  poField->GetNameRef());
     456             : 
     457             :         // TODO - mloskot: Is this return code correct?
     458           0 :         return OGRERR_NONE;
     459             :     }
     460             : 
     461         223 :     poFeatureDefn_->AddFieldDefn(poField);
     462             : 
     463         223 :     return OGRERR_NONE;
     464             : }
     465             : 
     466             : /************************************************************************/
     467             : /*                           TestCapability()                           */
     468             : /************************************************************************/
     469             : 
     470         371 : int OGRGeoJSONWriteLayer::TestCapability(const char *pszCap)
     471             : {
     472         371 :     if (EQUAL(pszCap, OLCCreateField))
     473          16 :         return TRUE;
     474         355 :     else if (EQUAL(pszCap, OLCSequentialWrite))
     475          20 :         return TRUE;
     476         335 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
     477           0 :         return TRUE;
     478         335 :     return FALSE;
     479             : }
     480             : 
     481             : /************************************************************************/
     482             : /*                            GetExtent()                               */
     483             : /************************************************************************/
     484             : 
     485           2 : OGRErr OGRGeoJSONWriteLayer::GetExtent(OGREnvelope *psExtent, int)
     486             : {
     487           2 :     if (sEnvelopeLayer.IsInit())
     488             :     {
     489           2 :         *psExtent = sEnvelopeLayer;
     490           2 :         return OGRERR_NONE;
     491             :     }
     492           0 :     return OGRERR_FAILURE;
     493             : }
     494             : 
     495             : /************************************************************************/
     496             : /*                             GetDataset()                             */
     497             : /************************************************************************/
     498             : 
     499          34 : GDALDataset *OGRGeoJSONWriteLayer::GetDataset()
     500             : {
     501          34 :     return poDS_;
     502             : }

Generated by: LCOV version 1.14