LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/ngw - ogrngwlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 934 0.0 %
Date: 2025-09-10 17:48:50 Functions: 0 52 0.0 %

          Line data    Source code
       1             : /*******************************************************************************
       2             :  *  Project: NextGIS Web Driver
       3             :  *  Purpose: Implements NextGIS Web Driver
       4             :  *  Author: Dmitry Baryshnikov, dmitry.baryshnikov@nextgis.com
       5             :  *  Language: C++
       6             :  *******************************************************************************
       7             :  *  The MIT License (MIT)
       8             :  *
       9             :  *  Copyright (c) 2018-2025, NextGIS <info@nextgis.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  *******************************************************************************/
      13             : 
      14             : #include "ogr_ngw.h"
      15             : 
      16             : /*
      17             :  * OGRGeometryToWKT()
      18             :  */
      19           0 : static std::string OGRGeometryToWKT(OGRGeometry *poGeom)
      20             : {
      21           0 :     std::string osOut;
      22           0 :     if (nullptr == poGeom)
      23             :     {
      24           0 :         return osOut;
      25             :     }
      26             : 
      27           0 :     char *pszWkt = nullptr;
      28           0 :     if (poGeom->exportToWkt(&pszWkt) == OGRERR_NONE)
      29             :     {
      30           0 :         osOut = pszWkt;
      31             :     }
      32           0 :     CPLFree(pszWkt);
      33             : 
      34           0 :     return osOut;
      35             : }
      36             : 
      37             : /*
      38             :  * JSONToFeature()
      39             :  */
      40           0 : static OGRFeature *JSONToFeature(const CPLJSONObject &featureJson,
      41             :                                  OGRFeatureDefn *poFeatureDefn,
      42             :                                  bool bCheckIgnoredFields = false,
      43             :                                  bool bStoreExtensionData = false)
      44             : {
      45           0 :     OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
      46           0 :     poFeature->SetFID(featureJson.GetLong("id"));
      47           0 :     CPLJSONObject oFields = featureJson.GetObj("fields");
      48           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
      49             :     {
      50           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
      51           0 :         if (bCheckIgnoredFields && poFieldDefn->IsIgnored())
      52             :         {
      53           0 :             continue;
      54             :         }
      55           0 :         CPLJSONObject oJSONField = oFields[poFieldDefn->GetNameRef()];
      56           0 :         if (oJSONField.IsValid() &&
      57           0 :             oJSONField.GetType() != CPLJSONObject::Type::Null)
      58             :         {
      59           0 :             switch (poFieldDefn->GetType())
      60             :             {
      61           0 :                 case OFTInteger:
      62           0 :                     poFeature->SetField(iField, oJSONField.ToInteger());
      63           0 :                     break;
      64           0 :                 case OFTInteger64:
      65           0 :                     poFeature->SetField(iField, oJSONField.ToLong());
      66           0 :                     break;
      67           0 :                 case OFTReal:
      68           0 :                     poFeature->SetField(iField, oJSONField.ToDouble());
      69           0 :                     break;
      70           0 :                 case OFTBinary:
      71             :                     // Not supported.
      72           0 :                     break;
      73           0 :                 case OFTString:
      74             :                 case OFTIntegerList:
      75             :                 case OFTInteger64List:
      76             :                 case OFTRealList:
      77             :                 case OFTStringList:
      78           0 :                     poFeature->SetField(iField, oJSONField.ToString().c_str());
      79           0 :                     break;
      80           0 :                 case OFTDate:
      81             :                 case OFTTime:
      82             :                 case OFTDateTime:
      83             :                 {
      84           0 :                     int nYear = oJSONField.GetInteger("year");
      85           0 :                     int nMonth = oJSONField.GetInteger("month");
      86           0 :                     int nDay = oJSONField.GetInteger("day");
      87           0 :                     int nHour = oJSONField.GetInteger("hour");
      88           0 :                     int nMinute = oJSONField.GetInteger("minute");
      89           0 :                     int nSecond = oJSONField.GetInteger("second");
      90           0 :                     poFeature->SetField(iField, nYear, nMonth, nDay, nHour,
      91             :                                         nMinute, float(nSecond));
      92           0 :                     break;
      93             :                 }
      94           0 :                 default:
      95           0 :                     break;
      96             :             }
      97             :         }
      98             :     }
      99             : 
     100             :     bool bFillGeometry =
     101           0 :         !(bCheckIgnoredFields && poFeatureDefn->IsGeometryIgnored());
     102             : 
     103           0 :     if (bFillGeometry)
     104             :     {
     105           0 :         OGRGeometry *poGeometry = nullptr;
     106           0 :         OGRGeometryFactory::createFromWkt(featureJson.GetString("geom").c_str(),
     107             :                                           nullptr, &poGeometry);
     108           0 :         if (poGeometry != nullptr)
     109             :         {
     110             :             const OGRSpatialReference *poSpatialRef =
     111           0 :                 poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef();
     112           0 :             if (poSpatialRef != nullptr)
     113             :             {
     114           0 :                 poGeometry->assignSpatialReference(poSpatialRef);
     115             :             }
     116           0 :             poFeature->SetGeomFieldDirectly(0, poGeometry);
     117             :         }
     118             :     }
     119             : 
     120             :     // Get extensions key and store it in native data.
     121           0 :     if (bStoreExtensionData)
     122             :     {
     123           0 :         CPLJSONObject oExtensions = featureJson.GetObj("extensions");
     124           0 :         if (oExtensions.IsValid() &&
     125           0 :             oExtensions.GetType() != CPLJSONObject::Type::Null)
     126             :         {
     127           0 :             poFeature->SetNativeData(
     128           0 :                 oExtensions.Format(CPLJSONObject::PrettyFormat::Plain).c_str());
     129           0 :             poFeature->SetNativeMediaType("application/json");
     130             :         }
     131             :     }
     132             : 
     133           0 :     return poFeature;
     134             : }
     135             : 
     136             : /*
     137             :  * FeatureToJson()
     138             :  */
     139           0 : static CPLJSONObject FeatureToJson(OGRFeature *poFeature)
     140             : {
     141           0 :     CPLJSONObject oFeatureJson;
     142           0 :     if (poFeature == nullptr)
     143             :     {
     144             :         // Should not happen.
     145           0 :         return oFeatureJson;
     146             :     }
     147             : 
     148           0 :     if (poFeature->GetFID() >= 0)
     149             :     {
     150           0 :         oFeatureJson.Add("id", poFeature->GetFID());
     151             :     }
     152             : 
     153           0 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
     154           0 :     std::string osGeomWKT = OGRGeometryToWKT(poGeom);
     155           0 :     if (!osGeomWKT.empty())
     156             :     {
     157           0 :         oFeatureJson.Add("geom", osGeomWKT);
     158             :     }
     159             : 
     160           0 :     const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
     161           0 :     CPLJSONObject oFieldsJson("fields", oFeatureJson);
     162           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
     163             :     {
     164           0 :         const OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
     165           0 :         if (poFeature->IsFieldNull(iField) == TRUE)
     166             :         {
     167           0 :             oFieldsJson.AddNull(poFieldDefn->GetNameRef());
     168           0 :             continue;
     169             :         }
     170             : 
     171           0 :         if (poFeature->IsFieldSet(iField) == TRUE)
     172             :         {
     173           0 :             switch (poFieldDefn->GetType())
     174             :             {
     175           0 :                 case OFTInteger:
     176           0 :                     oFieldsJson.Add(poFieldDefn->GetNameRef(),
     177             :                                     poFeature->GetFieldAsInteger(iField));
     178           0 :                     break;
     179           0 :                 case OFTInteger64:
     180           0 :                     oFieldsJson.Add(
     181             :                         poFieldDefn->GetNameRef(),
     182             :                         static_cast<GInt64>(
     183           0 :                             poFeature->GetFieldAsInteger64(iField)));
     184           0 :                     break;
     185           0 :                 case OFTReal:
     186           0 :                     oFieldsJson.Add(poFieldDefn->GetNameRef(),
     187             :                                     poFeature->GetFieldAsDouble(iField));
     188           0 :                     break;
     189           0 :                 case OFTBinary:
     190             :                     // Not supported.
     191           0 :                     break;
     192           0 :                 case OFTString:
     193             :                 case OFTIntegerList:
     194             :                 case OFTInteger64List:
     195             :                 case OFTRealList:
     196             :                 case OFTStringList:
     197           0 :                     oFieldsJson.Add(poFieldDefn->GetNameRef(),
     198             :                                     poFeature->GetFieldAsString(iField));
     199           0 :                     break;
     200           0 :                 case OFTDate:
     201             :                 case OFTTime:
     202             :                 case OFTDateTime:
     203             :                 {
     204             :                     int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag;
     205           0 :                     if (poFeature->GetFieldAsDateTime(
     206             :                             iField, &nYear, &nMonth, &nDay, &nHour, &nMinute,
     207           0 :                             &nSecond, &nTZFlag) == TRUE)
     208             :                     {
     209             :                         // TODO: Convert timestamp to UTC.
     210           0 :                         if (nTZFlag == 0 || nTZFlag == 100)
     211             :                         {
     212             :                             CPLJSONObject oDateJson(poFieldDefn->GetNameRef(),
     213           0 :                                                     oFieldsJson);
     214             : 
     215           0 :                             oDateJson.Add("year", nYear);
     216           0 :                             oDateJson.Add("month", nMonth);
     217           0 :                             oDateJson.Add("day", nDay);
     218           0 :                             oDateJson.Add("hour", nHour);
     219           0 :                             oDateJson.Add("minute", nMinute);
     220           0 :                             oDateJson.Add("second", nSecond);
     221             :                         }
     222             :                     }
     223           0 :                     break;
     224             :                 }
     225           0 :                 default:
     226           0 :                     break;
     227             :             }
     228             :         }
     229             :     }
     230             : 
     231           0 :     if (poFeature->GetNativeData())
     232             :     {
     233           0 :         CPLJSONDocument oExtensions;
     234           0 :         if (oExtensions.LoadMemory(poFeature->GetNativeData()))
     235             :         {
     236           0 :             oFeatureJson.Add("extensions", oExtensions.GetRoot());
     237             :         }
     238             :     }
     239             : 
     240           0 :     return oFeatureJson;
     241             : }
     242             : 
     243             : /*
     244             :  * FeatureToJsonString()
     245             :  */
     246           0 : static std::string FeatureToJsonString(OGRFeature *poFeature)
     247             : {
     248           0 :     return FeatureToJson(poFeature).Format(CPLJSONObject::PrettyFormat::Plain);
     249             : }
     250             : 
     251             : /*
     252             :  * FeaturesIDToJsonString()
     253             :  */
     254             : static std::string
     255           0 : FeaturesIDToJsonString(const std::vector<GIntBig> &vFeaturesID)
     256             : {
     257           0 :     CPLJSONArray oFeaturesIDJsonArray;
     258             : 
     259           0 :     for (GIntBig nFeatureID : vFeaturesID)
     260             :     {
     261           0 :         CPLJSONObject oFeatureIDJson;
     262           0 :         oFeatureIDJson.Add("id", nFeatureID);
     263             : 
     264           0 :         oFeaturesIDJsonArray.Add(oFeatureIDJson);
     265             :     }
     266             : 
     267           0 :     return oFeaturesIDJsonArray.Format(CPLJSONObject::PrettyFormat::Plain);
     268             : }
     269             : 
     270             : /*
     271             :  * FreeMap()
     272             :  */
     273           0 : static void FreeMap(std::map<GIntBig, OGRFeature *> &moFeatures)
     274             : {
     275             :     // cppcheck-suppress constVariableReference
     276           0 :     for (auto &oPair : moFeatures)
     277             :     {
     278           0 :         OGRFeature::DestroyFeature(oPair.second);
     279             :     }
     280             : 
     281           0 :     moFeatures.clear();
     282           0 : }
     283             : 
     284           0 : static bool CheckFieldNameUnique(OGRFeatureDefn *poFeatureDefn, int iField,
     285             :                                  const char *pszFieldName)
     286             : {
     287           0 :     for (int i = 0; i < poFeatureDefn->GetFieldCount(); ++i)
     288             :     {
     289           0 :         if (i == iField)
     290             :         {
     291           0 :             continue;
     292             :         }
     293             : 
     294           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(i);
     295           0 :         if (poFieldDefn && EQUAL(poFieldDefn->GetNameRef(), pszFieldName))
     296             :         {
     297           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     298             :                      "Field name %s already present in field %d.", pszFieldName,
     299             :                      i);
     300           0 :             return false;
     301             :         }
     302             :     }
     303           0 :     return true;
     304             : }
     305             : 
     306             : /*
     307             :  * TranslateSQLToFilter()
     308             :  */
     309           0 : std::string OGRNGWLayer::TranslateSQLToFilter(swq_expr_node *poNode)
     310             : {
     311           0 :     if (nullptr == poNode)
     312             :     {
     313           0 :         return "";
     314             :     }
     315             : 
     316           0 :     if (poNode->eNodeType == SNT_OPERATION)
     317             :     {
     318           0 :         if (poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2)
     319             :         {
     320             :             std::string osFilter1 =
     321           0 :                 TranslateSQLToFilter(poNode->papoSubExpr[0]);
     322             :             std::string osFilter2 =
     323           0 :                 TranslateSQLToFilter(poNode->papoSubExpr[1]);
     324             : 
     325           0 :             if (osFilter1.empty() || osFilter2.empty())
     326             :             {
     327           0 :                 return "";
     328             :             }
     329           0 :             return osFilter1 + "&" + osFilter2;
     330             :         }
     331           0 :         else if ((poNode->nOperation == SWQ_EQ ||
     332           0 :                   poNode->nOperation == SWQ_NE ||
     333           0 :                   poNode->nOperation == SWQ_GE ||
     334           0 :                   poNode->nOperation == SWQ_LE ||
     335           0 :                   poNode->nOperation == SWQ_LT ||
     336           0 :                   poNode->nOperation == SWQ_GT ||
     337           0 :                   poNode->nOperation == SWQ_LIKE ||
     338           0 :                   poNode->nOperation == SWQ_ILIKE) &&
     339           0 :                  poNode->nSubExprCount == 2 &&
     340           0 :                  poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     341           0 :                  poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
     342             :         {
     343           0 :             if (poNode->papoSubExpr[0]->string_value == nullptr)
     344             :             {
     345           0 :                 return "";
     346             :             }
     347           0 :             char *pszNameEncoded = CPLEscapeString(
     348           0 :                 poNode->papoSubExpr[0]->string_value, -1, CPLES_URL);
     349           0 :             std::string osFieldName = "fld_" + std::string(pszNameEncoded);
     350           0 :             CPLFree(pszNameEncoded);
     351             : 
     352           0 :             switch (poNode->nOperation)
     353             :             {
     354           0 :                 case SWQ_EQ:
     355           0 :                     osFieldName += "__eq";
     356           0 :                     break;
     357           0 :                 case SWQ_NE:
     358           0 :                     osFieldName += "__ne";
     359           0 :                     break;
     360           0 :                 case SWQ_GE:
     361           0 :                     osFieldName += "__ge";
     362           0 :                     break;
     363           0 :                 case SWQ_LE:
     364           0 :                     osFieldName += "__le";
     365           0 :                     break;
     366           0 :                 case SWQ_LT:
     367           0 :                     osFieldName += "__lt";
     368           0 :                     break;
     369           0 :                 case SWQ_GT:
     370           0 :                     osFieldName += "__gt";
     371           0 :                     break;
     372           0 :                 case SWQ_LIKE:
     373           0 :                     osFieldName += "__like";
     374           0 :                     break;
     375           0 :                 case SWQ_ILIKE:
     376           0 :                     osFieldName += "__ilike";
     377           0 :                     break;
     378           0 :                 default:
     379           0 :                     CPLAssert(false);
     380             :                     break;
     381             :             }
     382             : 
     383           0 :             std::string osVal;
     384           0 :             switch (poNode->papoSubExpr[1]->field_type)
     385             :             {
     386           0 :                 case SWQ_INTEGER64:
     387             :                 case SWQ_INTEGER:
     388           0 :                     osVal = std::to_string(poNode->papoSubExpr[1]->int_value);
     389           0 :                     break;
     390           0 :                 case SWQ_FLOAT:
     391           0 :                     osVal = std::to_string(poNode->papoSubExpr[1]->float_value);
     392           0 :                     break;
     393           0 :                 case SWQ_STRING:
     394           0 :                     if (poNode->papoSubExpr[1]->string_value)
     395             :                     {
     396           0 :                         char *pszValueEncoded = CPLEscapeString(
     397           0 :                             poNode->papoSubExpr[1]->string_value, -1,
     398             :                             CPLES_URL);
     399           0 :                         osVal = pszValueEncoded;
     400           0 :                         CPLFree(pszValueEncoded);
     401             :                     }
     402           0 :                     break;
     403           0 :                 case SWQ_DATE:
     404             :                 case SWQ_TIME:
     405             :                 case SWQ_TIMESTAMP:
     406           0 :                     if (poNode->papoSubExpr[1]->string_value)
     407             :                     {
     408           0 :                         char *pszValueEncoded = CPLEscapeString(
     409           0 :                             poNode->papoSubExpr[1]->string_value, -1,
     410             :                             CPLES_URL);
     411           0 :                         osVal = pszValueEncoded;
     412           0 :                         CPLFree(pszValueEncoded);
     413             :                     }
     414           0 :                     break;
     415           0 :                 default:
     416           0 :                     break;
     417             :             }
     418           0 :             if (osFieldName.empty() || osVal.empty())
     419             :             {
     420           0 :                 CPLDebug("NGW", "Unsupported filter operation for server side");
     421           0 :                 return "";
     422             :             }
     423             : 
     424           0 :             return osFieldName + "=" + osVal;
     425             :         }
     426             :         else
     427             :         {
     428           0 :             CPLDebug("NGW", "Unsupported filter operation for server side");
     429           0 :             return "";
     430             :         }
     431             :     }
     432           0 :     return "";
     433             : }
     434             : 
     435             : /*
     436             :  * OGRNGWLayer()
     437             :  */
     438           0 : OGRNGWLayer::OGRNGWLayer(OGRNGWDataset *poDSIn,
     439           0 :                          const CPLJSONObject &oResourceJsonObject)
     440             :     : osResourceId(oResourceJsonObject.GetString("resource/id", "-1")),
     441             :       poDS(poDSIn), bFetchedPermissions(false), poFeatureDefn(nullptr),
     442           0 :       nFeatureCount(-1), oNextPos(moFeatures.begin()), nPageStart(0),
     443             :       bNeedSyncData(false), bNeedSyncStructure(false),
     444           0 :       bClientSideAttributeFilter(false)
     445             : {
     446           0 :     Fill(oResourceJsonObject);
     447           0 : }
     448             : 
     449             : /*
     450             :  * OGRNGWLayer()
     451             :  */
     452           0 : OGRNGWLayer::OGRNGWLayer(const std::string &osResourceIdIn,
     453             :                          OGRNGWDataset *poDSIn,
     454             :                          const NGWAPI::Permissions &stPermissionsIn,
     455             :                          OGRFeatureDefn *poFeatureDefnIn,
     456           0 :                          GIntBig nFeatureCountIn, const OGREnvelope &stExtentIn)
     457             :     : osResourceId(osResourceIdIn), poDS(poDSIn),
     458             :       stPermissions(stPermissionsIn), bFetchedPermissions(true),
     459             :       poFeatureDefn(poFeatureDefnIn), nFeatureCount(nFeatureCountIn),
     460           0 :       stExtent(stExtentIn), oNextPos(moFeatures.begin()), nPageStart(0),
     461             :       bNeedSyncData(false), bNeedSyncStructure(false),
     462           0 :       bClientSideAttributeFilter(false)
     463             : {
     464           0 :     poFeatureDefn->Reference();
     465           0 :     SetDescription(poFeatureDefn->GetName());
     466           0 : }
     467             : 
     468             : /*
     469             :  * OGRNGWLayer()
     470             :  */
     471           0 : OGRNGWLayer::OGRNGWLayer(OGRNGWDataset *poDSIn, const std::string &osNameIn,
     472             :                          OGRSpatialReference *poSpatialRef,
     473             :                          OGRwkbGeometryType eGType, const std::string &osKeyIn,
     474           0 :                          const std::string &osDescIn)
     475             :     : osResourceId("-1"), poDS(poDSIn), bFetchedPermissions(false),
     476           0 :       nFeatureCount(0), oNextPos(moFeatures.begin()), nPageStart(0),
     477             :       bNeedSyncData(false), bNeedSyncStructure(false),
     478           0 :       bClientSideAttributeFilter(false)
     479             : {
     480           0 :     poFeatureDefn = new OGRFeatureDefn(osNameIn.c_str());
     481           0 :     poFeatureDefn->Reference();
     482             : 
     483           0 :     poFeatureDefn->SetGeomType(eGType);
     484             : 
     485           0 :     if (poSpatialRef)
     486             :     {
     487           0 :         if (poFeatureDefn->GetGeomFieldCount() != 0)
     488             :         {
     489           0 :             poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSpatialRef);
     490             :         }
     491             :     }
     492             : 
     493           0 :     if (!osDescIn.empty())
     494             :     {
     495           0 :         OGRLayer::SetMetadataItem("description", osDescIn.c_str());
     496             :     }
     497           0 :     if (!osKeyIn.empty())
     498             :     {
     499           0 :         OGRLayer::SetMetadataItem("keyname", osKeyIn.c_str());
     500             :     }
     501             : 
     502           0 :     SetDescription(poFeatureDefn->GetName());
     503             : 
     504           0 :     stPermissions.bDatastructCanRead = true;
     505           0 :     stPermissions.bDatastructCanWrite = true;
     506           0 :     stPermissions.bDataCanRead = true;
     507           0 :     stPermissions.bDataCanWrite = true;
     508           0 :     stPermissions.bMetadataCanRead = true;
     509           0 :     stPermissions.bMetadataCanWrite = true;
     510           0 : }
     511             : 
     512             : /*
     513             :  * ~OGRNGWLayer()
     514             :  */
     515           0 : OGRNGWLayer::~OGRNGWLayer()
     516             : {
     517           0 :     FreeFeaturesCache(true);
     518           0 :     if (poFeatureDefn != nullptr)
     519             :     {
     520           0 :         poFeatureDefn->Release();
     521             :     }
     522           0 : }
     523             : 
     524             : /*
     525             :  * FreeFeaturesCache()
     526             :  */
     527           0 : void OGRNGWLayer::FreeFeaturesCache(bool bForce)
     528             : {
     529           0 :     if (!soChangedIds.empty())
     530             :     {
     531           0 :         bNeedSyncData = true;
     532             :     }
     533             : 
     534           0 :     if (SyncFeatures() == OGRERR_NONE || bForce)  // Try sync first
     535             :     {
     536             :         // Free only if synced with server successfully or executed from
     537             :         // destructor.
     538           0 :         FreeMap(moFeatures);
     539             :     }
     540           0 : }
     541             : 
     542             : /*
     543             :  * GetResourceId()
     544             :  */
     545           0 : std::string OGRNGWLayer::GetResourceId() const
     546             : {
     547           0 :     return osResourceId;
     548             : }
     549             : 
     550             : /*
     551             :  * Delete()
     552             :  */
     553           0 : bool OGRNGWLayer::Delete()
     554             : {
     555           0 :     if (osResourceId == "-1")
     556             :     {
     557           0 :         return true;
     558             :     }
     559             : 
     560             :     // Headers free in DeleteResource method.
     561           0 :     return NGWAPI::DeleteResource(poDS->GetUrl(), osResourceId,
     562           0 :                                   poDS->GetHeaders(false));
     563             : }
     564             : 
     565             : /*
     566             :  * Rename()
     567             :  */
     568           0 : OGRErr OGRNGWLayer::Rename(const char *pszNewName)
     569             : {
     570           0 :     bool bResult = true;
     571           0 :     if (osResourceId != "-1")
     572             :     {
     573           0 :         bResult = NGWAPI::RenameResource(poDS->GetUrl(), osResourceId,
     574           0 :                                          pszNewName, poDS->GetHeaders(false));
     575             :     }
     576           0 :     if (bResult)
     577             :     {
     578           0 :         poFeatureDefn->SetName(pszNewName);
     579           0 :         SetDescription(poFeatureDefn->GetName());
     580             :     }
     581             :     else
     582             :     {
     583           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Rename layer to %s failed",
     584             :                  pszNewName);
     585             :     }
     586           0 :     return bResult ? OGRERR_NONE : OGRERR_FAILURE;
     587             : }
     588             : 
     589             : /*
     590             :  * ResetReading()
     591             :  */
     592           0 : void OGRNGWLayer::ResetReading()
     593             : {
     594           0 :     m_bEOF = false;
     595           0 :     SyncToDisk();
     596           0 :     FreeFeaturesCache();
     597           0 :     if (poDS->GetPageSize() > 0)
     598             :     {
     599           0 :         nPageStart = 0;
     600             :     }
     601           0 :     oNextPos = moFeatures.begin();
     602           0 : }
     603             : 
     604           0 : CPLJSONObject OGRNGWLayer::LoadUrl(const std::string &osUrl) const
     605             : {
     606           0 :     CPLErrorReset();
     607           0 :     auto aosHTTPOptions = poDS->GetHeaders(false);
     608           0 :     CPLJSONDocument oFeatureReq;
     609             : 
     610           0 :     bool bResult = oFeatureReq.LoadUrl(osUrl, aosHTTPOptions);
     611           0 :     CPLJSONObject oRoot = oFeatureReq.GetRoot();
     612           0 :     if (NGWAPI::CheckRequestResult(bResult, oRoot,
     613             :                                    "GetFeatures request failed"))
     614             :     {
     615           0 :         CPLErrorReset();  // If we are here no error occurred
     616           0 :         return oRoot;
     617             :     }
     618             : 
     619           0 :     return CPLJSONObject();
     620             : }
     621             : 
     622             : /*
     623             :  * FillFeatures()
     624             :  */
     625           0 : bool OGRNGWLayer::FillFeatures(const std::string &osUrl)
     626             : {
     627           0 :     CPLDebug("NGW", "GetNextFeature: Url: %s", osUrl.c_str());
     628             : 
     629           0 :     CPLJSONObject oRoot = LoadUrl(osUrl);
     630           0 :     if (!oRoot.IsValid())
     631             :     {
     632           0 :         return false;
     633             :     }
     634             : 
     635           0 :     CPLJSONArray aoJSONFeatures = oRoot.ToArray();
     636           0 :     for (int i = 0; i < aoJSONFeatures.Size(); ++i)
     637             :     {
     638           0 :         OGRFeature *poFeature = JSONToFeature(aoJSONFeatures[i], poFeatureDefn,
     639           0 :                                               true, poDS->IsExtInNativeData());
     640           0 :         moFeatures[poFeature->GetFID()] = poFeature;
     641             :     }
     642             : 
     643           0 :     return true;
     644             : }
     645             : 
     646             : /*
     647             :  * SetNextByIndex()
     648             :  */
     649           0 : OGRErr OGRNGWLayer::SetNextByIndex(GIntBig nIndex)
     650             : {
     651           0 :     SyncToDisk();
     652           0 :     if (nIndex < 0)
     653             :     {
     654           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     655             :                  "Feature index must be greater or equal 0. Got " CPL_FRMT_GIB,
     656             :                  nIndex);
     657           0 :         return OGRERR_NON_EXISTING_FEATURE;
     658             :     }
     659           0 :     if (poDS->GetPageSize() > 0)
     660             :     {
     661             :         // Check if index is in current cache
     662           0 :         if (nPageStart > nIndex && nIndex <= nPageStart - poDS->GetPageSize())
     663             :         {
     664           0 :             if (moFeatures.empty() ||
     665           0 :                 static_cast<GIntBig>(moFeatures.size()) <= nIndex)
     666             :             {
     667           0 :                 oNextPos = moFeatures.end();
     668             :             }
     669             :             else
     670             :             {
     671           0 :                 oNextPos = moFeatures.begin();
     672           0 :                 std::advance(oNextPos, static_cast<size_t>(nIndex));
     673             :             }
     674             :         }
     675             :         else
     676             :         {
     677           0 :             ResetReading();
     678           0 :             nPageStart = nIndex;
     679             :         }
     680             :     }
     681             :     else
     682             :     {
     683           0 :         if (moFeatures.empty() && GetMaxFeatureCount(false) > 0)
     684             :         {
     685           0 :             std::string osUrl;
     686           0 :             if (poDS->HasFeaturePaging())
     687             :             {
     688           0 :                 osUrl = NGWAPI::GetFeaturePageURL(
     689           0 :                     poDS->GetUrl(), osResourceId, 0, 0, osFields, osWhere,
     690           0 :                     osSpatialFilter, poDS->Extensions(),
     691           0 :                     poFeatureDefn->IsGeometryIgnored() == TRUE);
     692             :             }
     693             :             else
     694             :             {
     695           0 :                 osUrl = NGWAPI::GetFeatureURL(poDS->GetUrl(), osResourceId);
     696             :             }
     697             : 
     698           0 :             if (!FillFeatures(osUrl))
     699             :             {
     700           0 :                 return OGRERR_FAILURE;
     701             :             }
     702             :         }
     703             : 
     704           0 :         if (moFeatures.empty() ||
     705           0 :             static_cast<GIntBig>(moFeatures.size()) <= nIndex)
     706             :         {
     707           0 :             oNextPos = moFeatures.end();
     708             :         }
     709             :         else
     710             :         {
     711           0 :             oNextPos = moFeatures.begin();
     712           0 :             std::advance(oNextPos, static_cast<size_t>(nIndex));
     713             :         }
     714             :     }
     715           0 :     return OGRERR_NONE;
     716             : }
     717             : 
     718             : /*
     719             :  * GetNextFeature()
     720             :  */
     721           0 : OGRFeature *OGRNGWLayer::GetNextFeature()
     722             : {
     723           0 :     if (m_bEOF)
     724           0 :         return nullptr;
     725           0 :     std::string osUrl;
     726             : 
     727           0 :     if (poDS->GetPageSize() > 0)
     728             :     {
     729           0 :         if (oNextPos == moFeatures.end() &&
     730           0 :             nPageStart < GetMaxFeatureCount(false))
     731             :         {
     732           0 :             FreeFeaturesCache();
     733             : 
     734           0 :             osUrl = NGWAPI::GetFeaturePageURL(
     735           0 :                 poDS->GetUrl(), osResourceId, nPageStart, poDS->GetPageSize(),
     736           0 :                 osFields, osWhere, osSpatialFilter, poDS->Extensions(),
     737           0 :                 poFeatureDefn->IsGeometryIgnored() == TRUE);
     738           0 :             nPageStart += poDS->GetPageSize();
     739             :         }
     740             :     }
     741           0 :     else if (moFeatures.empty() && GetMaxFeatureCount(false) > 0)
     742             :     {
     743           0 :         if (poDS->HasFeaturePaging())
     744             :         {
     745           0 :             osUrl = NGWAPI::GetFeaturePageURL(
     746           0 :                 poDS->GetUrl(), osResourceId, 0, 0, osFields, osWhere,
     747           0 :                 osSpatialFilter, poDS->Extensions(),
     748           0 :                 poFeatureDefn->IsGeometryIgnored() == TRUE);
     749             :         }
     750             :         else
     751             :         {
     752           0 :             osUrl = NGWAPI::GetFeatureURL(poDS->GetUrl(), osResourceId);
     753             :         }
     754             :     }
     755             : 
     756           0 :     bool bFinalRead = true;
     757           0 :     if (!osUrl.empty())
     758             :     {
     759           0 :         if (!FillFeatures(osUrl))
     760             :         {
     761           0 :             return nullptr;
     762             :         }
     763             : 
     764           0 :         oNextPos = moFeatures.begin();
     765             : 
     766           0 :         if (poDS->GetPageSize() < 1)
     767             :         {
     768             :             // Without paging we read all features at once.
     769           0 :             m_nFeaturesRead = moFeatures.size();
     770             :         }
     771             :         else
     772             :         {
     773           0 :             if (poDS->GetPageSize() - moFeatures.size() == 0)
     774             :             {
     775           0 :                 m_nFeaturesRead = nPageStart;
     776           0 :                 bFinalRead = false;
     777             :             }
     778             :             else
     779             :             {
     780           0 :                 m_nFeaturesRead =
     781           0 :                     nPageStart - poDS->GetPageSize() + moFeatures.size();
     782             :             }
     783             :         }
     784             :     }
     785             : 
     786           0 :     while (oNextPos != moFeatures.end())
     787             :     {
     788           0 :         OGRFeature *poFeature = oNextPos->second;
     789           0 :         ++oNextPos;
     790             : 
     791           0 :         if (poFeature == nullptr)  // Feature may be deleted.
     792             :         {
     793           0 :             continue;
     794             :         }
     795             : 
     796             :         // Check local filters only for new features which not send to server
     797             :         // yet or if attribute filter process on client side.
     798           0 :         if (poFeature->GetFID() < 0 || bClientSideAttributeFilter)
     799             :         {
     800           0 :             if ((m_poFilterGeom == nullptr ||
     801           0 :                  FilterGeometry(poFeature->GetGeometryRef())) &&
     802           0 :                 (m_poAttrQuery == nullptr ||
     803           0 :                  m_poAttrQuery->Evaluate(poFeature)))
     804             :             {
     805           0 :                 return poFeature->Clone();
     806             :             }
     807             :         }
     808             :         else
     809             :         {
     810           0 :             return poFeature->Clone();
     811             :         }
     812             :     }
     813             : 
     814           0 :     if (poDS->GetPageSize() > 0 && !bFinalRead)
     815             :     {
     816           0 :         return GetNextFeature();
     817             :     }
     818           0 :     return nullptr;
     819             : }
     820             : 
     821             : /*
     822             :  * GetFeature()
     823             :  */
     824           0 : OGRFeature *OGRNGWLayer::GetFeature(GIntBig nFID)
     825             : {
     826             :     // Check feature in cache.
     827           0 :     if (moFeatures[nFID] != nullptr)
     828             :     {
     829           0 :         return moFeatures[nFID]->Clone();
     830             :     }
     831           0 :     auto osUrl = NGWAPI::GetFeatureURL(poDS->GetUrl(), osResourceId) +
     832           0 :                  std::to_string(nFID);
     833           0 :     CPLJSONObject oRoot = LoadUrl(osUrl);
     834           0 :     if (!oRoot.IsValid())
     835             :     {
     836           0 :         return nullptr;
     837             :     }
     838             : 
     839             :     // Don't store feature in cache. This can broke sequence read.
     840           0 :     return JSONToFeature(oRoot, poFeatureDefn, true, poDS->IsExtInNativeData());
     841             : }
     842             : 
     843             : /*
     844             :  * GetLayerDefn()
     845             :  */
     846           0 : const OGRFeatureDefn *OGRNGWLayer::GetLayerDefn() const
     847             : {
     848           0 :     return poFeatureDefn;
     849             : }
     850             : 
     851             : /*
     852             :  * TestCapability()
     853             :  */
     854           0 : int OGRNGWLayer::TestCapability(const char *pszCap) const
     855             : {
     856           0 :     const_cast<OGRNGWLayer *>(this)->FetchPermissions();
     857           0 :     if (EQUAL(pszCap, OLCRandomRead))
     858           0 :         return TRUE;
     859           0 :     else if (EQUAL(pszCap, OLCSequentialWrite))
     860           0 :         return stPermissions.bDataCanWrite && poDS->IsUpdateMode();
     861           0 :     else if (EQUAL(pszCap, OLCRandomWrite))
     862           0 :         return stPermissions.bDataCanWrite && poDS->IsUpdateMode();
     863           0 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
     864           0 :         return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
     865           0 :     else if (EQUAL(pszCap, OLCFastGetExtent))
     866           0 :         return TRUE;
     867           0 :     else if (EQUAL(pszCap, OLCAlterFieldDefn))
     868           0 :         return stPermissions.bDatastructCanWrite && poDS->IsUpdateMode();
     869           0 :     else if (EQUAL(pszCap, OLCDeleteFeature))
     870           0 :         return stPermissions.bDataCanWrite && poDS->IsUpdateMode();
     871           0 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
     872           0 :         return TRUE;
     873           0 :     else if (EQUAL(pszCap, OLCFastSetNextByIndex))
     874           0 :         return TRUE;
     875           0 :     else if (EQUAL(pszCap, OLCCreateField))
     876           0 :         return stPermissions.bDatastructCanWrite && poDS->IsUpdateMode();
     877           0 :     else if (EQUAL(pszCap, OLCIgnoreFields))
     878           0 :         return poDS->HasFeaturePaging();  // Ignore fields, paging support and
     879             :                                           // attribute/spatial filters were
     880             :                                           // introduced in NGW v3.1
     881           0 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
     882           0 :         return poDS->HasFeaturePaging();
     883           0 :     else if (EQUAL(pszCap, OLCRename))
     884           0 :         return poDS->IsUpdateMode();
     885           0 :     else if (EQUAL(pszCap, OLCZGeometries))
     886           0 :         return TRUE;
     887           0 :     return FALSE;
     888             : }
     889             : 
     890             : /*
     891             :  * Fill()
     892             :  */
     893           0 : void OGRNGWLayer::Fill(const CPLJSONObject &oRootObject)
     894             : {
     895           0 :     auto osName = oRootObject.GetString("resource/display_name");
     896           0 :     CPLStringList soIgnoredFieldsNames;
     897           0 :     if (nullptr != poFeatureDefn)
     898             :     {
     899           0 :         while (poFeatureDefn->GetFieldCount() > 0)
     900             :         {
     901           0 :             OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(0);
     902           0 :             if (poFieldDefn->IsIgnored())
     903             :             {
     904           0 :                 soIgnoredFieldsNames.AddString(poFieldDefn->GetNameRef());
     905             :             }
     906           0 :             poFeatureDefn->DeleteFieldDefn(0);
     907             :         }
     908           0 :         poFeatureDefn->SetName(osName.c_str());
     909             : 
     910           0 :         poFeatureDefn->SetGeomType(NGWAPI::NGWGeomTypeToOGRGeomType(
     911           0 :             oRootObject.GetString("vector_layer/geometry_type")));
     912             :     }
     913             :     else
     914             :     {
     915           0 :         poFeatureDefn = new OGRFeatureDefn(osName.c_str());
     916           0 :         poFeatureDefn->Reference();
     917             : 
     918           0 :         poFeatureDefn->SetGeomType(NGWAPI::NGWGeomTypeToOGRGeomType(
     919           0 :             oRootObject.GetString("vector_layer/geometry_type")));
     920             : 
     921           0 :         OGRSpatialReference *poSRS = new OGRSpatialReference;
     922           0 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     923           0 :         int nEPSG = oRootObject.GetInteger(
     924             :             "vector_layer/srs/id",
     925             :             3857);  // Default NGW SRS is Web mercator EPSG:3857.
     926           0 :         if (poSRS->importFromEPSG(nEPSG) == OGRERR_NONE)
     927             :         {
     928           0 :             if (poFeatureDefn->GetGeomFieldCount() != 0)
     929             :             {
     930           0 :                 poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
     931             :             }
     932             :         }
     933           0 :         poSRS->Release();
     934             :     }
     935             : 
     936           0 :     CPLJSONArray oFields = oRootObject.GetArray("feature_layer/fields");
     937           0 :     FillFields(oFields, soIgnoredFieldsNames);
     938           0 :     FillMetadata(oRootObject);
     939             : 
     940           0 :     auto osDescription = oRootObject.GetString("resource/description");
     941           0 :     SetDescription(osDescription.c_str());
     942             :     // SetDescription(poFeatureDefn->GetName());
     943           0 : }
     944             : 
     945             : /*
     946             :  * FillMetadata()
     947             :  */
     948           0 : void OGRNGWLayer::FillMetadata(const CPLJSONObject &oRootObject)
     949             : {
     950           0 :     std::string osCreateDate = oRootObject.GetString("resource/creation_date");
     951           0 :     if (!osCreateDate.empty())
     952             :     {
     953           0 :         OGRLayer::SetMetadataItem("creation_date", osCreateDate.c_str());
     954             :     }
     955           0 :     std::string osDescription = oRootObject.GetString("resource/description");
     956           0 :     if (!osDescription.empty())
     957             :     {
     958           0 :         OGRLayer::SetMetadataItem("description", osDescription.c_str());
     959             :     }
     960           0 :     std::string osKeyName = oRootObject.GetString("resource/keyname");
     961           0 :     if (!osKeyName.empty())
     962             :     {
     963           0 :         OGRLayer::SetMetadataItem("keyname", osKeyName.c_str());
     964             :     }
     965           0 :     std::string osResourceType = oRootObject.GetString("resource/cls");
     966           0 :     if (!osResourceType.empty())
     967             :     {
     968           0 :         OGRLayer::SetMetadataItem("resource_type", osResourceType.c_str());
     969             :     }
     970             :     std::string osResourceParentId =
     971           0 :         oRootObject.GetString("resource/parent/id");
     972           0 :     if (!osResourceParentId.empty())
     973             :     {
     974           0 :         OGRLayer::SetMetadataItem("parent_id", osResourceParentId.c_str());
     975             :     }
     976           0 :     OGRLayer::SetMetadataItem("id", osResourceId.c_str());
     977             : 
     978             :     std::vector<CPLJSONObject> items =
     979           0 :         oRootObject.GetObj("resmeta/items").GetChildren();
     980             : 
     981           0 :     for (const CPLJSONObject &item : items)
     982             :     {
     983           0 :         std::string osSuffix = NGWAPI::GetResmetaSuffix(item.GetType());
     984           0 :         OGRLayer::SetMetadataItem((item.GetName() + osSuffix).c_str(),
     985           0 :                                   item.ToString().c_str(), "NGW");
     986             :     }
     987           0 : }
     988             : 
     989             : /*
     990             :  * FillFields()
     991             :  */
     992           0 : void OGRNGWLayer::FillFields(const CPLJSONArray &oFields,
     993             :                              const CPLStringList &soIgnoredFieldsNames)
     994             : {
     995           0 :     for (const auto &oField : oFields)
     996             :     {
     997           0 :         std::string osFieldName = oField.GetString("keyname");
     998             :         OGRFieldType eFieldtype =
     999           0 :             NGWAPI::NGWFieldTypeToOGRFieldType(oField.GetString("datatype"));
    1000             : 
    1001           0 :         OGRFieldDefn oFieldDefn(osFieldName.c_str(), eFieldtype);
    1002           0 :         std::string osFieldAlias = oField.GetString("display_name");
    1003           0 :         oFieldDefn.SetAlternativeName(osFieldAlias.c_str());
    1004             : 
    1005             :         // Add additional parameters to comment as JSON string
    1006           0 :         CPLJSONObject oFieldComment;
    1007           0 :         oFieldComment.Add("id", oField.GetLong("id"));
    1008           0 :         oFieldComment.Add("label_field", oField.GetBool("label_field"));
    1009           0 :         oFieldComment.Add("grid_visibility", oField.GetBool("grid_visibility"));
    1010           0 :         oFieldComment.Add("text_search", oField.GetBool("text_search"));
    1011           0 :         oFieldDefn.SetComment(
    1012           0 :             oFieldComment.Format(CPLJSONObject::PrettyFormat::Plain));
    1013             : 
    1014             :         // Domain
    1015           0 :         auto nDomainID = oField.GetInteger("lookup_table/id", -1);
    1016           0 :         if (nDomainID != -1)
    1017             :         {
    1018           0 :             auto oDom = poDS->GetDomainByID(nDomainID);
    1019           0 :             auto pOgrDom = oDom.ToFieldDomain(eFieldtype);
    1020           0 :             if (pOgrDom != nullptr)
    1021             :             {
    1022           0 :                 oFieldDefn.SetDomainName(pOgrDom->GetName());
    1023             :             }
    1024             :         }
    1025             : 
    1026           0 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1027             :     }
    1028             : 
    1029           0 :     OGRLayer::SetIgnoredFields(soIgnoredFieldsNames);
    1030           0 : }
    1031             : 
    1032             : /*
    1033             :  * GetMaxFeatureCount()
    1034             :  */
    1035           0 : GIntBig OGRNGWLayer::GetMaxFeatureCount(bool bForce)
    1036             : {
    1037           0 :     if (nFeatureCount < 0 || bForce)
    1038             :     {
    1039             :         CPLJSONObject oRoot =
    1040           0 :             LoadUrl(NGWAPI::GetFeatureCount(poDS->GetUrl(), osResourceId));
    1041           0 :         if (!oRoot.IsValid())
    1042             :         {
    1043           0 :             return nFeatureCount;
    1044             :         }
    1045             : 
    1046           0 :         nFeatureCount = oRoot.GetLong("total_count");
    1047           0 :         nFeatureCount += GetNewFeaturesCount();
    1048             :     }
    1049           0 :     return nFeatureCount;
    1050             : }
    1051             : 
    1052             : /*
    1053             :  * GetFeatureCount()
    1054             :  */
    1055           0 : GIntBig OGRNGWLayer::GetFeatureCount(int bForce)
    1056             : {
    1057           0 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
    1058             :     {
    1059           0 :         return GetMaxFeatureCount(CPL_TO_BOOL(bForce));
    1060             :     }
    1061             :     else
    1062             :     {
    1063           0 :         return OGRLayer::GetFeatureCount(bForce);
    1064             :     }
    1065             : }
    1066             : 
    1067             : /*
    1068             :  * IGetExtent()
    1069             :  */
    1070           0 : OGRErr OGRNGWLayer::IGetExtent(int /* iGeomField */, OGREnvelope *psExtent,
    1071             :                                bool bForce)
    1072             : {
    1073           0 :     if (!stExtent.IsInit() || CPL_TO_BOOL(bForce))
    1074             :     {
    1075           0 :         auto aosHTTPOptions = poDS->GetHeaders(false);
    1076           0 :         bool bResult = NGWAPI::GetExtent(poDS->GetUrl(), osResourceId,
    1077           0 :                                          aosHTTPOptions, 3857, stExtent);
    1078           0 :         if (!bResult)
    1079             :         {
    1080           0 :             return OGRERR_FAILURE;
    1081             :         }
    1082             :     }
    1083           0 :     *psExtent = stExtent;
    1084           0 :     return OGRERR_NONE;
    1085             : }
    1086             : 
    1087             : /*
    1088             :  * FetchPermissions()
    1089             :  */
    1090           0 : void OGRNGWLayer::FetchPermissions()
    1091             : {
    1092           0 :     if (bFetchedPermissions || osResourceId == "-1")
    1093             :     {
    1094           0 :         return;
    1095             :     }
    1096             : 
    1097           0 :     if (poDS->IsUpdateMode())
    1098             :     {
    1099           0 :         auto aosHTTPOptions = poDS->GetHeaders(false);
    1100             :         stPermissions = NGWAPI::CheckPermissions(
    1101           0 :             poDS->GetUrl(), osResourceId, aosHTTPOptions, poDS->IsUpdateMode());
    1102             :     }
    1103             :     else
    1104             :     {
    1105           0 :         stPermissions.bDataCanRead = true;
    1106           0 :         stPermissions.bResourceCanRead = true;
    1107           0 :         stPermissions.bDatastructCanRead = true;
    1108           0 :         stPermissions.bMetadataCanRead = true;
    1109             :     }
    1110           0 :     bFetchedPermissions = true;
    1111             : }
    1112             : 
    1113             : /*
    1114             :  * CreateField()
    1115             :  */
    1116           0 : OGRErr OGRNGWLayer::CreateField(const OGRFieldDefn *poField,
    1117             :                                 CPL_UNUSED int bApproxOK)
    1118             : {
    1119           0 :     CPLAssert(nullptr != poField);
    1120             : 
    1121           0 :     if (!CheckFieldNameUnique(poFeatureDefn, -1, poField->GetNameRef()))
    1122             :     {
    1123           0 :         return OGRERR_FAILURE;
    1124             :     }
    1125             : 
    1126           0 :     OGRFieldDefn oModFieldDefn(poField);
    1127           0 :     poFeatureDefn->AddFieldDefn(&oModFieldDefn);
    1128           0 :     bNeedSyncStructure = true;
    1129           0 :     return OGRERR_NONE;
    1130             : }
    1131             : 
    1132             : /*
    1133             :  * DeleteField()
    1134             :  */
    1135           0 : OGRErr OGRNGWLayer::DeleteField(int iField)
    1136             : {
    1137           0 :     if (osResourceId != "-1")
    1138             :     {
    1139           0 :         auto poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1140           0 :         if (poFieldDefn == nullptr)
    1141             :         {
    1142           0 :             return OGRERR_FAILURE;
    1143             :         }
    1144             :         // Get field NGW ID
    1145           0 :         CPLJSONDocument oComment;
    1146           0 :         if (oComment.LoadMemory(poFieldDefn->GetComment()))
    1147             :         {
    1148           0 :             auto oRoot = oComment.GetRoot();
    1149           0 :             auto nNGWID = oRoot.GetLong("id", -1);
    1150           0 :             if (nNGWID != -1)
    1151             :             {
    1152           0 :                 soDeletedFieldsIds.insert(nNGWID);
    1153             :             }
    1154             :         }
    1155             :     }
    1156           0 :     return poFeatureDefn->DeleteFieldDefn(iField);
    1157             : }
    1158             : 
    1159             : /*
    1160             :  * ReorderFields()
    1161             :  */
    1162           0 : OGRErr OGRNGWLayer::ReorderFields(int *panMap)
    1163             : {
    1164           0 :     return poFeatureDefn->ReorderFieldDefns(panMap);
    1165             : }
    1166             : 
    1167             : /*
    1168             :  * AlterFieldDefn()
    1169             :  */
    1170           0 : OGRErr OGRNGWLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
    1171             :                                    int nFlagsIn)
    1172             : {
    1173           0 :     CPLDebug("NGW", "AlterFieldDefn fld #%d", iField);
    1174           0 :     CPLAssert(nullptr != poNewFieldDefn);
    1175             : 
    1176           0 :     if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
    1177             :     {
    1178           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    1179           0 :         return OGRERR_FAILURE;
    1180             :     }
    1181             : 
    1182           0 :     FetchPermissions();
    1183           0 :     if (!stPermissions.bDatastructCanWrite)
    1184             :     {
    1185           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Insufficient permissions");
    1186           0 :         return OGRERR_FAILURE;
    1187             :     }
    1188             : 
    1189           0 :     if (!poDS->IsUpdateMode())
    1190             :     {
    1191           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Layer is read only");
    1192           0 :         return OGRERR_FAILURE;
    1193             :     }
    1194             : 
    1195           0 :     OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1196             :     // Check new field name is not equal for another fields.
    1197           0 :     if (!CheckFieldNameUnique(poFeatureDefn, iField,
    1198             :                               poNewFieldDefn->GetNameRef()))
    1199             :     {
    1200           0 :         return OGRERR_FAILURE;
    1201             :     }
    1202             : 
    1203           0 :     if (osResourceId == "-1")  // Can full alter field only on new layers
    1204             :                                // (not synced with server).
    1205             :     {
    1206             :         // Field name 'id' forbidden.
    1207           0 :         OGRFieldDefn oModFieldDefn(poNewFieldDefn);
    1208             : 
    1209           0 :         poFieldDefn->SetName(oModFieldDefn.GetNameRef());
    1210           0 :         poFieldDefn->SetAlternativeName(oModFieldDefn.GetAlternativeNameRef());
    1211           0 :         poFieldDefn->SetComment(oModFieldDefn.GetComment());
    1212           0 :         poFieldDefn->SetType(oModFieldDefn.GetType());
    1213           0 :         poFieldDefn->SetSubType(oModFieldDefn.GetSubType());
    1214           0 :         poFieldDefn->SetWidth(oModFieldDefn.GetWidth());
    1215           0 :         poFieldDefn->SetPrecision(oModFieldDefn.GetPrecision());
    1216             :     }
    1217             :     else
    1218             :     {
    1219           0 :         if (nFlagsIn & ALTER_NAME_FLAG)
    1220             :         {
    1221           0 :             bNeedSyncStructure = true;
    1222           0 :             poFieldDefn->SetName(poNewFieldDefn->GetNameRef());
    1223             :         }
    1224           0 :         if (nFlagsIn & ALTER_DOMAIN_FLAG)
    1225             :         {
    1226           0 :             bNeedSyncStructure = true;
    1227           0 :             poFieldDefn->SetDomainName(poNewFieldDefn->GetDomainName());
    1228             :         }
    1229           0 :         if (nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG)
    1230             :         {
    1231           0 :             bNeedSyncStructure = true;
    1232           0 :             poFieldDefn->SetAlternativeName(
    1233             :                 poNewFieldDefn->GetAlternativeNameRef());
    1234             :         }
    1235           0 :         if (nFlagsIn & ALTER_COMMENT_FLAG)
    1236             :         {
    1237           0 :             bNeedSyncStructure = true;
    1238           0 :             poFieldDefn->SetComment(poNewFieldDefn->GetComment());
    1239             :         }
    1240             :     }
    1241           0 :     ResetReading();
    1242           0 :     return OGRERR_NONE;
    1243             : }
    1244             : 
    1245             : /*
    1246             :  * SetMetadata()
    1247             :  */
    1248           0 : CPLErr OGRNGWLayer::SetMetadata(char **papszMetadata, const char *pszDomain)
    1249             : {
    1250           0 :     bNeedSyncStructure = true;
    1251           0 :     return OGRLayer::SetMetadata(papszMetadata, pszDomain);
    1252             : }
    1253             : 
    1254             : /*
    1255             :  * SetMetadataItem()
    1256             :  */
    1257           0 : CPLErr OGRNGWLayer::SetMetadataItem(const char *pszName, const char *pszValue,
    1258             :                                     const char *pszDomain)
    1259             : {
    1260           0 :     bNeedSyncStructure = true;
    1261           0 :     return OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
    1262             : }
    1263             : 
    1264             : /*
    1265             :  * CreateNGWResourceJson()
    1266             :  */
    1267           0 : std::string OGRNGWLayer::CreateNGWResourceJson()
    1268             : {
    1269           0 :     CPLJSONObject oResourceJson;
    1270             : 
    1271             :     // Add resource json item.
    1272           0 :     CPLJSONObject oResource("resource", oResourceJson);
    1273           0 :     oResource.Add("cls", "vector_layer");
    1274           0 :     CPLJSONObject oResourceParent("parent", oResource);
    1275           0 :     oResourceParent.Add("id",
    1276           0 :                         static_cast<GIntBig>(std::stol(poDS->GetResourceId())));
    1277           0 :     oResource.Add("display_name", GetName());
    1278           0 :     const char *pszKeyName = GetMetadataItem("keyname");
    1279           0 :     if (pszKeyName)
    1280             :     {
    1281           0 :         oResource.Add("keyname", pszKeyName);
    1282             :     }
    1283           0 :     const char *pszDescription = GetMetadataItem("description");
    1284           0 :     if (pszDescription)
    1285             :     {
    1286           0 :         oResource.Add("description", pszDescription);
    1287             :     }
    1288             : 
    1289             :     // Add vector_layer json item.
    1290           0 :     CPLJSONObject oVectorLayer("vector_layer", oResourceJson);
    1291           0 :     CPLJSONObject oVectorLayerSrs("srs", oVectorLayer);
    1292             : 
    1293           0 :     int nEPSG = 3857;
    1294           0 :     if (const auto poSRSConst = GetSpatialRef())
    1295             :     {
    1296           0 :         OGRSpatialReference oSRS(*poSRSConst);
    1297           0 :         oSRS.AutoIdentifyEPSG();
    1298           0 :         const char *pszEPSG = oSRS.GetAuthorityCode(nullptr);
    1299           0 :         if (pszEPSG != nullptr)
    1300             :         {
    1301           0 :             nEPSG = atoi(pszEPSG);
    1302             :         }
    1303             :     }
    1304           0 :     oVectorLayerSrs.Add("id", nEPSG);
    1305             :     // In OGRNGWDataset::ICreateLayer we limit supported geometry types.
    1306           0 :     oVectorLayer.Add("geometry_type",
    1307           0 :                      NGWAPI::OGRGeomTypeToNGWGeomType(GetGeomType()));
    1308             : 
    1309             :     // Fill fields
    1310           0 :     CPLJSONArray oVectorLayerFields;
    1311           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
    1312             :     {
    1313           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1314           0 :         CPLJSONObject oField;
    1315           0 :         auto const &sComment = poFieldDefn->GetComment();
    1316           0 :         if (!sComment.empty())
    1317             :         {
    1318           0 :             CPLJSONDocument oComment;
    1319           0 :             if (oComment.LoadMemory(sComment))
    1320             :             {
    1321           0 :                 oField = oComment.GetRoot();
    1322             :             }
    1323             :         }
    1324           0 :         oField.Add("keyname", poFieldDefn->GetNameRef());
    1325           0 :         oField.Add("datatype",
    1326           0 :                    NGWAPI::OGRFieldTypeToNGWFieldType(poFieldDefn->GetType()));
    1327           0 :         std::string osFieldAliasName = poFieldDefn->GetAlternativeNameRef();
    1328             :         // Get alias from metadata.
    1329           0 :         if (osFieldAliasName.empty())
    1330             :         {
    1331           0 :             osFieldAliasName = poFieldDefn->GetNameRef();
    1332             :         }
    1333           0 :         oField.Add("display_name", osFieldAliasName);
    1334             : 
    1335           0 :         if (poFieldDefn->GetDomainName().empty())
    1336             :         {
    1337           0 :             oField.AddNull("lookup_table");
    1338             :         }
    1339             :         else
    1340             :         {
    1341           0 :             CPLJSONObject oFieldDom("lookup_table", oField);
    1342           0 :             auto nDomId = poDS->GetDomainIdByName(poFieldDefn->GetDomainName());
    1343           0 :             oFieldDom.Add("id", nDomId);
    1344             :         }
    1345             : 
    1346           0 :         oVectorLayerFields.Add(oField);
    1347             :     }
    1348           0 :     for (auto nFieldID : soDeletedFieldsIds)
    1349             :     {
    1350           0 :         CPLJSONObject oField;
    1351           0 :         oField.Add("id", nFieldID);
    1352           0 :         oField.Add("delete", true);
    1353           0 :         oVectorLayerFields.Add(oField);
    1354             :     }
    1355             : 
    1356           0 :     CPLJSONObject oFeatureLayer("feature_layer", oResourceJson);
    1357           0 :     oFeatureLayer.Add("fields", oVectorLayerFields);
    1358             : 
    1359             :     // Add resmeta json item.
    1360           0 :     NGWAPI::FillResmeta(oResourceJson, GetMetadata("NGW"));
    1361             : 
    1362           0 :     return oResourceJson.Format(CPLJSONObject::PrettyFormat::Plain);
    1363             : }
    1364             : 
    1365             : /*
    1366             :  * SyncFeatures()
    1367             :  */
    1368           0 : OGRErr OGRNGWLayer::SyncFeatures()
    1369             : {
    1370           0 :     if (!bNeedSyncData)
    1371             :     {
    1372           0 :         return OGRERR_NONE;
    1373             :     }
    1374             : 
    1375           0 :     CPLJSONArray oFeatureJsonArray;
    1376           0 :     std::vector<GIntBig> aoPatchedFIDs;
    1377           0 :     for (GIntBig nFID : soChangedIds)
    1378             :     {
    1379           0 :         if (moFeatures[nFID] != nullptr)
    1380             :         {
    1381           0 :             oFeatureJsonArray.Add(FeatureToJson(moFeatures[nFID]));
    1382           0 :             aoPatchedFIDs.push_back(nFID);
    1383             :         }
    1384             :     }
    1385             : 
    1386           0 :     if (!aoPatchedFIDs.empty())
    1387             :     {
    1388             :         auto osIDs = NGWAPI::PatchFeatures(
    1389           0 :             poDS->GetUrl(), osResourceId,
    1390           0 :             oFeatureJsonArray.Format(CPLJSONObject::PrettyFormat::Plain),
    1391           0 :             poDS->GetHeaders());
    1392           0 :         if (!osIDs.empty())
    1393             :         {
    1394           0 :             bNeedSyncData = false;
    1395           0 :             nFeatureCount += GetNewFeaturesCount();
    1396           0 :             soChangedIds.clear();
    1397           0 :             if (osIDs.size() !=
    1398           0 :                 aoPatchedFIDs.size())  // Expected equal identifier count.
    1399             :             {
    1400           0 :                 CPLDebug("NGW", "Patched feature count is not equal. Reload "
    1401             :                                 "features from server.");
    1402           0 :                 FreeMap(moFeatures);
    1403             :             }
    1404             :             else  // Just update identifiers.
    1405             :             {
    1406           0 :                 int nCounter = 0;
    1407           0 :                 for (GIntBig nFID : aoPatchedFIDs)
    1408             :                 {
    1409           0 :                     GIntBig nNewFID = osIDs[nCounter++];
    1410           0 :                     OGRFeature *poFeature = moFeatures[nFID];
    1411           0 :                     poFeature->SetFID(nNewFID);
    1412           0 :                     moFeatures.erase(nFID);
    1413           0 :                     moFeatures[nNewFID] = poFeature;
    1414             :                 }
    1415             :             }
    1416             :         }
    1417             :         else
    1418             :         {
    1419             :             // Error message should set in NGWAPI::PatchFeatures function.
    1420           0 :             if (CPLGetLastErrorNo() != 0)
    1421             :             {
    1422           0 :                 return OGRERR_FAILURE;
    1423             :             }
    1424             :         }
    1425             :     }
    1426           0 :     return OGRERR_NONE;
    1427             : }
    1428             : 
    1429             : /*
    1430             :  * SyncToDisk()
    1431             :  */
    1432           0 : OGRErr OGRNGWLayer::SyncToDisk()
    1433             : {
    1434           0 :     if (osResourceId == "-1")  // Create vector layer at NextGIS Web.
    1435             :     {
    1436           0 :         bNeedSyncData = !moFeatures.empty();
    1437             :         std::string osResourceIdInt = NGWAPI::CreateResource(
    1438           0 :             poDS->GetUrl(), CreateNGWResourceJson(), poDS->GetHeaders());
    1439           0 :         if (osResourceIdInt == "-1")
    1440             :         {
    1441             :             // Error message should set in CreateResource.
    1442           0 :             return OGRERR_FAILURE;
    1443             :         }
    1444           0 :         osResourceId = std::move(osResourceIdInt);
    1445           0 :         OGRLayer::SetMetadataItem("id", osResourceId.c_str());
    1446           0 :         FetchPermissions();
    1447           0 :         bNeedSyncStructure = false;
    1448             :     }
    1449           0 :     else if (bNeedSyncStructure)  // Update vector layer at NextGIS Web.
    1450             :     {
    1451           0 :         if (!NGWAPI::UpdateResource(poDS->GetUrl(), GetResourceId(),
    1452           0 :                                     CreateNGWResourceJson(),
    1453           0 :                                     poDS->GetHeaders()))
    1454             :         {
    1455             :             // Error message should set in UpdateResource.
    1456           0 :             return OGRERR_FAILURE;
    1457             :         }
    1458           0 :         bNeedSyncStructure = false;
    1459           0 :         soDeletedFieldsIds.clear();
    1460             :     }
    1461             : 
    1462           0 :     auto oRoot = LoadUrl(NGWAPI::GetResourceURL(poDS->GetUrl(), osResourceId));
    1463           0 :     if (!oRoot.IsValid())
    1464             :     {
    1465           0 :         return OGRERR_FAILURE;
    1466             :     }
    1467           0 :     Fill(oRoot);
    1468             : 
    1469             :     // Update NGW layer state
    1470           0 :     FetchPermissions();
    1471             : 
    1472             :     // Sync features.
    1473           0 :     return SyncFeatures();
    1474             : }
    1475             : 
    1476             : /*
    1477             :  * DeleteFeature()
    1478             :  */
    1479           0 : OGRErr OGRNGWLayer::DeleteFeature(GIntBig nFID)
    1480             : {
    1481           0 :     CPLErrorReset();
    1482           0 :     if (nFID < 0)
    1483             :     {
    1484           0 :         if (moFeatures[nFID] != nullptr)
    1485             :         {
    1486           0 :             OGRFeature::DestroyFeature(moFeatures[nFID]);
    1487           0 :             moFeatures[nFID] = nullptr;
    1488           0 :             nFeatureCount--;
    1489           0 :             soChangedIds.erase(nFID);
    1490           0 :             return OGRERR_NONE;
    1491             :         }
    1492           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1493             :                  "Feature with id " CPL_FRMT_GIB " not found.", nFID);
    1494           0 :         return OGRERR_FAILURE;
    1495             :     }
    1496             :     else
    1497             :     {
    1498           0 :         FetchPermissions();
    1499           0 :         if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
    1500             :         {
    1501           0 :             bool bResult = NGWAPI::DeleteFeature(poDS->GetUrl(), osResourceId,
    1502           0 :                                                  std::to_string(nFID),
    1503           0 :                                                  poDS->GetHeaders(false));
    1504           0 :             if (bResult)
    1505             :             {
    1506           0 :                 if (moFeatures[nFID] != nullptr)
    1507             :                 {
    1508           0 :                     OGRFeature::DestroyFeature(moFeatures[nFID]);
    1509           0 :                     moFeatures[nFID] = nullptr;
    1510             :                 }
    1511           0 :                 nFeatureCount--;
    1512           0 :                 soChangedIds.erase(nFID);
    1513           0 :                 return OGRERR_NONE;
    1514             :             }
    1515           0 :             return OGRERR_FAILURE;
    1516             :         }
    1517           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1518             :                  "Delete feature " CPL_FRMT_GIB " operation is not permitted.",
    1519             :                  nFID);
    1520           0 :         return OGRERR_FAILURE;
    1521             :     }
    1522             : }
    1523             : 
    1524             : /*
    1525             :  * DeleteFeatures()
    1526             :  */
    1527           0 : OGRErr OGRNGWLayer::DeleteFeatures(const std::vector<GIntBig> &vFeaturesID)
    1528             : {
    1529           0 :     CPLErrorReset();
    1530             : 
    1531             :     // Try to delete local features (not synchronized with NGW)
    1532           0 :     std::vector<GIntBig> vFeaturesIDInt(vFeaturesID);
    1533           0 :     for (size_t i = 0; i < vFeaturesIDInt.size(); i++)
    1534             :     {
    1535           0 :         auto nFID = vFeaturesIDInt[i];
    1536           0 :         if (nFID < 0)
    1537             :         {
    1538           0 :             if (moFeatures[nFID] != nullptr)
    1539             :             {
    1540           0 :                 OGRFeature::DestroyFeature(moFeatures[nFID]);
    1541           0 :                 moFeatures[nFID] = nullptr;
    1542           0 :                 nFeatureCount--;
    1543           0 :                 soChangedIds.erase(nFID);
    1544           0 :                 vFeaturesIDInt.erase(vFeaturesIDInt.begin() + i);
    1545             :             }
    1546             :         }
    1547             :     }
    1548             : 
    1549           0 :     FetchPermissions();
    1550           0 :     if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
    1551             :     {
    1552           0 :         bool bResult = NGWAPI::DeleteFeatures(
    1553           0 :             poDS->GetUrl(), osResourceId,
    1554           0 :             FeaturesIDToJsonString(vFeaturesIDInt), poDS->GetHeaders(false));
    1555           0 :         if (bResult)
    1556             :         {
    1557           0 :             for (GIntBig nFID : vFeaturesIDInt)
    1558             :             {
    1559           0 :                 if (moFeatures[nFID] != nullptr)
    1560             :                 {
    1561           0 :                     OGRFeature::DestroyFeature(moFeatures[nFID]);
    1562           0 :                     moFeatures[nFID] = nullptr;
    1563           0 :                     nFeatureCount--;
    1564           0 :                     soChangedIds.erase(nFID);
    1565             :                 }
    1566           0 :                 nFeatureCount--;
    1567           0 :                 soChangedIds.erase(nFID);
    1568             :             }
    1569           0 :             return OGRERR_NONE;
    1570             :         }
    1571             :     }
    1572           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Delete features failed");
    1573           0 :     return OGRERR_FAILURE;
    1574             : }
    1575             : 
    1576             : /*
    1577             :  * DeleteAllFeatures()
    1578             :  */
    1579           0 : bool OGRNGWLayer::DeleteAllFeatures()
    1580             : {
    1581           0 :     if (osResourceId == "-1")
    1582             :     {
    1583           0 :         soChangedIds.clear();
    1584           0 :         bNeedSyncData = false;
    1585           0 :         FreeFeaturesCache();
    1586           0 :         nFeatureCount = 0;
    1587           0 :         return true;
    1588             :     }
    1589             :     else
    1590             :     {
    1591           0 :         FetchPermissions();
    1592           0 :         if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
    1593             :         {
    1594           0 :             bool bResult = NGWAPI::DeleteFeature(poDS->GetUrl(), osResourceId,
    1595           0 :                                                  "", poDS->GetHeaders(false));
    1596           0 :             if (bResult)
    1597             :             {
    1598           0 :                 soChangedIds.clear();
    1599           0 :                 bNeedSyncData = false;
    1600           0 :                 FreeFeaturesCache();
    1601           0 :                 nFeatureCount = 0;
    1602             :             }
    1603           0 :             return bResult;
    1604             :         }
    1605             :     }
    1606           0 :     CPLErrorReset();
    1607           0 :     CPLError(CE_Failure, CPLE_AppDefined,
    1608             :              "Delete all features operation is not permitted.");
    1609           0 :     return false;
    1610             : }
    1611             : 
    1612             : /*
    1613             :  * ISetFeature()
    1614             :  */
    1615           0 : OGRErr OGRNGWLayer::ISetFeature(OGRFeature *poFeature)
    1616             : {
    1617           0 :     if (poDS->IsBatchMode())
    1618             :     {
    1619           0 :         if (moFeatures[poFeature->GetFID()] == nullptr)
    1620             :         {
    1621           0 :             if (poFeature->GetFID() < 0)
    1622             :             {
    1623           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1624             :                          "Cannot update not existing feature " CPL_FRMT_GIB,
    1625             :                          poFeature->GetFID());
    1626           0 :                 return OGRERR_FAILURE;
    1627             :             }
    1628             :         }
    1629             :         else
    1630             :         {
    1631           0 :             OGRFeature::DestroyFeature(moFeatures[poFeature->GetFID()]);
    1632             :         }
    1633           0 :         moFeatures[poFeature->GetFID()] = poFeature->Clone();
    1634           0 :         soChangedIds.insert(poFeature->GetFID());
    1635             : 
    1636           0 :         if (soChangedIds.size() > static_cast<size_t>(poDS->GetBatchSize()))
    1637             :         {
    1638           0 :             bNeedSyncData = true;
    1639             :         }
    1640             : 
    1641           0 :         return SyncToDisk();
    1642             :     }
    1643             :     else
    1644             :     {
    1645             :         OGRErr eResult =
    1646           0 :             SyncToDisk();  // For create new layer if not yet created.
    1647           0 :         if (eResult == OGRERR_NONE)
    1648             :         {
    1649           0 :             if (poFeature->GetFID() < 0)
    1650             :             {
    1651           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1652             :                          "Cannot update not existing feature " CPL_FRMT_GIB,
    1653             :                          poFeature->GetFID());
    1654           0 :                 return OGRERR_FAILURE;
    1655             :             }
    1656             : 
    1657           0 :             bool bResult = NGWAPI::UpdateFeature(
    1658           0 :                 poDS->GetUrl(), osResourceId,
    1659           0 :                 std::to_string(poFeature->GetFID()),
    1660           0 :                 FeatureToJsonString(poFeature), poDS->GetHeaders());
    1661           0 :             if (bResult)
    1662             :             {
    1663           0 :                 CPLDebug("NGW", "ISetFeature with FID " CPL_FRMT_GIB,
    1664             :                          poFeature->GetFID());
    1665             : 
    1666           0 :                 OGRFeature::DestroyFeature(moFeatures[poFeature->GetFID()]);
    1667           0 :                 moFeatures[poFeature->GetFID()] = poFeature->Clone();
    1668           0 :                 return OGRERR_NONE;
    1669             :             }
    1670             :             else
    1671             :             {
    1672             :                 // CPLError should be set in NGWAPI::UpdateFeature.
    1673           0 :                 return OGRERR_FAILURE;
    1674             :             }
    1675             :         }
    1676             :         else
    1677             :         {
    1678           0 :             return eResult;
    1679             :         }
    1680             :     }
    1681             : }
    1682             : 
    1683             : /*
    1684             :  * ICreateFeature()
    1685             :  */
    1686           0 : OGRErr OGRNGWLayer::ICreateFeature(OGRFeature *poFeature)
    1687             : {
    1688           0 :     if (poDS->IsBatchMode())
    1689             :     {
    1690           0 :         GIntBig nNewFID = -1;
    1691           0 :         if (!soChangedIds.empty())
    1692             :         {
    1693           0 :             nNewFID = *(soChangedIds.begin()) - 1;
    1694             :         }
    1695           0 :         poFeature->SetFID(nNewFID);
    1696           0 :         moFeatures[nNewFID] = poFeature->Clone();
    1697           0 :         soChangedIds.insert(nNewFID);
    1698           0 :         nFeatureCount++;
    1699             : 
    1700           0 :         if (soChangedIds.size() > static_cast<size_t>(poDS->GetBatchSize()))
    1701             :         {
    1702           0 :             bNeedSyncData = true;
    1703             :         }
    1704             : 
    1705           0 :         return SyncToDisk();
    1706             :     }
    1707             :     else
    1708             :     {
    1709             :         OGRErr eResult =
    1710           0 :             SyncToDisk();  // For create new layer if not yet created.
    1711           0 :         if (eResult == OGRERR_NONE)
    1712             :         {
    1713           0 :             GIntBig nNewFID = NGWAPI::CreateFeature(
    1714           0 :                 poDS->GetUrl(), osResourceId, FeatureToJsonString(poFeature),
    1715           0 :                 poDS->GetHeaders());
    1716           0 :             if (nNewFID >= 0)
    1717             :             {
    1718           0 :                 poFeature->SetFID(nNewFID);
    1719           0 :                 moFeatures[nNewFID] = poFeature->Clone();
    1720           0 :                 nFeatureCount++;
    1721           0 :                 return OGRERR_NONE;
    1722             :             }
    1723             :             else
    1724             :             {
    1725             :                 // CPLError should be set in NGWAPI::CreateFeature.
    1726           0 :                 return OGRERR_FAILURE;
    1727             :             }
    1728             :         }
    1729             :         else
    1730             :         {
    1731           0 :             return eResult;
    1732             :         }
    1733             :     }
    1734             : }
    1735             : 
    1736             : /*
    1737             :  * SetIgnoredFields()
    1738             :  */
    1739           0 : OGRErr OGRNGWLayer::SetIgnoredFields(CSLConstList papszFields)
    1740             : {
    1741           0 :     OGRErr eResult = OGRLayer::SetIgnoredFields(papszFields);
    1742           0 :     if (eResult != OGRERR_NONE)
    1743             :     {
    1744           0 :         return eResult;
    1745             :     }
    1746             : 
    1747           0 :     osFields.clear();
    1748             : 
    1749           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
    1750             :     {
    1751           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1752           0 :         if (poFieldDefn->IsIgnored())
    1753             :         {
    1754           0 :             CPLDebug("NGW", "SetIgnoredFields: Field '%s' set to ignored",
    1755             :                      poFieldDefn->GetNameRef());
    1756           0 :             continue;
    1757             :         }
    1758             : 
    1759           0 :         if (osFields.empty())
    1760             :         {
    1761           0 :             osFields = poFieldDefn->GetNameRef();
    1762             :         }
    1763             :         else
    1764             :         {
    1765           0 :             osFields += "," + std::string(poFieldDefn->GetNameRef());
    1766             :         }
    1767             :     }
    1768             : 
    1769             :     // Encode osFields string for URL
    1770           0 :     if (!osFields.empty())
    1771             :     {
    1772           0 :         char *pszValuesEncoded = CPLEscapeString(
    1773           0 :             osFields.c_str(), static_cast<int>(osFields.size()), CPLES_URL);
    1774           0 :         osFields = pszValuesEncoded;
    1775           0 :         CPLFree(pszValuesEncoded);
    1776             :     }
    1777             : 
    1778           0 :     CPLDebug("NGW", "SetIgnoredFields: NGW fields filter set to '%s'",
    1779             :              osFields.c_str());
    1780             : 
    1781           0 :     ResetReading();
    1782           0 :     return OGRERR_NONE;
    1783             : }
    1784             : 
    1785             : /*
    1786             :  * ISetSpatialFilter()
    1787             :  */
    1788           0 : OGRErr OGRNGWLayer::ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom)
    1789             : {
    1790           0 :     OGRLayer::ISetSpatialFilter(iGeomField, poGeom);
    1791             : 
    1792           0 :     if (nullptr == m_poFilterGeom)
    1793             :     {
    1794           0 :         CPLDebug("NGW", "Spatial filter unset");
    1795           0 :         osSpatialFilter.clear();
    1796             :     }
    1797             :     else
    1798             :     {
    1799           0 :         OGREnvelope sEnvelope;
    1800           0 :         m_poFilterGeom->getEnvelope(&sEnvelope);
    1801             : 
    1802           0 :         OGREnvelope sBigEnvelope;
    1803           0 :         sBigEnvelope.MinX = -40000000.0;
    1804           0 :         sBigEnvelope.MinY = -40000000.0;
    1805           0 :         sBigEnvelope.MaxX = 40000000.0;
    1806           0 :         sBigEnvelope.MaxY = 40000000.0;
    1807             : 
    1808             :         // Case for infinity filter
    1809           0 :         if (sEnvelope.Contains(sBigEnvelope) == TRUE)
    1810             :         {
    1811           0 :             CPLDebug("NGW", "Spatial filter unset as filter envelope covers "
    1812             :                             "whole features.");
    1813           0 :             osSpatialFilter.clear();
    1814             :         }
    1815             :         else
    1816             :         {
    1817           0 :             if (sEnvelope.MinX == sEnvelope.MaxX &&
    1818           0 :                 sEnvelope.MinY == sEnvelope.MaxY)
    1819             :             {
    1820           0 :                 OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
    1821           0 :                 InstallFilter(&p);
    1822             :             }
    1823             : 
    1824           0 :             osSpatialFilter = OGRGeometryToWKT(m_poFilterGeom);
    1825           0 :             CPLDebug("NGW", "Spatial filter: %s", osSpatialFilter.c_str());
    1826           0 :             char *pszSpatFilterEncoded = CPLEscapeString(
    1827             :                 osSpatialFilter.c_str(),
    1828           0 :                 static_cast<int>(osSpatialFilter.size()), CPLES_URL);
    1829           0 :             osSpatialFilter = pszSpatFilterEncoded;
    1830           0 :             CPLFree(pszSpatFilterEncoded);
    1831             :         }
    1832             :     }
    1833             : 
    1834           0 :     ResetReading();
    1835             : 
    1836           0 :     return OGRERR_NONE;
    1837             : }
    1838             : 
    1839             : /*
    1840             :  * SetAttributeFilter()
    1841             :  */
    1842           0 : OGRErr OGRNGWLayer::SetAttributeFilter(const char *pszQuery)
    1843             : {
    1844           0 :     OGRErr eResult = OGRERR_NONE;
    1845           0 :     if (nullptr == pszQuery)
    1846             :     {
    1847           0 :         eResult = OGRLayer::SetAttributeFilter(pszQuery);
    1848           0 :         osWhere.clear();
    1849           0 :         bClientSideAttributeFilter = false;
    1850             :     }
    1851           0 :     else if (STARTS_WITH_CI(pszQuery,
    1852             :                             "NGW:"))  // Already formatted for NGW REST API
    1853             :     {
    1854           0 :         osWhere = pszQuery + strlen("NGW:");
    1855           0 :         bClientSideAttributeFilter = false;
    1856             :     }
    1857             :     else
    1858             :     {
    1859           0 :         eResult = OGRLayer::SetAttributeFilter(pszQuery);
    1860           0 :         if (eResult == OGRERR_NONE && m_poAttrQuery != nullptr)
    1861             :         {
    1862             :             swq_expr_node *poNode =
    1863           0 :                 reinterpret_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
    1864           0 :             std::string osWhereIn = TranslateSQLToFilter(poNode);
    1865           0 :             if (osWhereIn.empty())
    1866             :             {
    1867           0 :                 osWhere.clear();
    1868           0 :                 bClientSideAttributeFilter = true;
    1869           0 :                 CPLDebug(
    1870             :                     "NGW",
    1871             :                     "Attribute filter '%s' will be evaluated on client side.",
    1872             :                     pszQuery);
    1873             :             }
    1874             :             else
    1875             :             {
    1876           0 :                 bClientSideAttributeFilter = false;
    1877           0 :                 CPLDebug("NGW", "Attribute filter: %s", osWhereIn.c_str());
    1878           0 :                 osWhere = std::move(osWhereIn);
    1879             :             }
    1880             :         }
    1881             :     }
    1882             : 
    1883           0 :     ResetReading();
    1884           0 :     return eResult;
    1885             : }
    1886             : 
    1887             : /*
    1888             :  * SetSelectedFields()
    1889             :  */
    1890           0 : OGRErr OGRNGWLayer::SetSelectedFields(const std::set<std::string> &aosFields)
    1891             : {
    1892           0 :     CPLStringList aosIgnoreFields;
    1893           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
    1894             :     {
    1895           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1896           0 :         if (aosFields.find(poFieldDefn->GetNameRef()) != aosFields.end())
    1897             :         {
    1898           0 :             continue;
    1899             :         }
    1900           0 :         aosIgnoreFields.AddString(poFieldDefn->GetNameRef());
    1901             :     }
    1902           0 :     return SetIgnoredFields(aosIgnoreFields.List());
    1903             : }
    1904             : 
    1905             : /*
    1906             :  * Clone()
    1907             :  */
    1908           0 : OGRNGWLayer *OGRNGWLayer::Clone() const
    1909             : {
    1910           0 :     return new OGRNGWLayer(osResourceId, poDS, stPermissions,
    1911           0 :                            poFeatureDefn->Clone(), nFeatureCount, stExtent);
    1912             : }
    1913             : 
    1914             : /*
    1915             :  * GetNewFeaturesCount()
    1916             :  */
    1917           0 : GIntBig OGRNGWLayer::GetNewFeaturesCount() const
    1918             : {
    1919           0 :     if (soChangedIds.empty())
    1920             :     {
    1921           0 :         return 0;
    1922             :     }
    1923             : 
    1924           0 :     if (*soChangedIds.begin() >= 0)
    1925             :     {
    1926           0 :         return 0;
    1927             :     }
    1928             : 
    1929             :     // The lowest negative identifier equal new feature count
    1930           0 :     return *soChangedIds.begin() * -1;
    1931             : }

Generated by: LCOV version 1.14