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 931 0.0 %
Date: 2025-03-28 11:40:40 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 :     OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
     161           0 :     CPLJSONObject oFieldsJson("fields", oFeatureJson);
     162           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
     163             :     {
     164           0 :         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 :     SyncToDisk();
     595           0 :     FreeFeaturesCache();
     596           0 :     if (poDS->GetPageSize() > 0)
     597             :     {
     598           0 :         nPageStart = 0;
     599             :     }
     600           0 :     oNextPos = moFeatures.begin();
     601           0 : }
     602             : 
     603           0 : CPLJSONObject OGRNGWLayer::LoadUrl(const std::string &osUrl) const
     604             : {
     605           0 :     CPLErrorReset();
     606           0 :     auto aosHTTPOptions = poDS->GetHeaders(false);
     607           0 :     CPLJSONDocument oFeatureReq;
     608             : 
     609           0 :     bool bResult = oFeatureReq.LoadUrl(osUrl, aosHTTPOptions);
     610           0 :     CPLJSONObject oRoot = oFeatureReq.GetRoot();
     611           0 :     if (NGWAPI::CheckRequestResult(bResult, oRoot,
     612             :                                    "GetFeatures request failed"))
     613             :     {
     614           0 :         CPLErrorReset();  // If we are here no error occurred
     615           0 :         return oRoot;
     616             :     }
     617             : 
     618           0 :     return CPLJSONObject();
     619             : }
     620             : 
     621             : /*
     622             :  * FillFeatures()
     623             :  */
     624           0 : bool OGRNGWLayer::FillFeatures(const std::string &osUrl)
     625             : {
     626           0 :     CPLDebug("NGW", "GetNextFeature: Url: %s", osUrl.c_str());
     627             : 
     628           0 :     CPLJSONObject oRoot = LoadUrl(osUrl);
     629           0 :     if (!oRoot.IsValid())
     630             :     {
     631           0 :         return false;
     632             :     }
     633             : 
     634           0 :     CPLJSONArray aoJSONFeatures = oRoot.ToArray();
     635           0 :     for (int i = 0; i < aoJSONFeatures.Size(); ++i)
     636             :     {
     637           0 :         OGRFeature *poFeature = JSONToFeature(aoJSONFeatures[i], poFeatureDefn,
     638           0 :                                               true, poDS->IsExtInNativeData());
     639           0 :         moFeatures[poFeature->GetFID()] = poFeature;
     640             :     }
     641             : 
     642           0 :     return true;
     643             : }
     644             : 
     645             : /*
     646             :  * SetNextByIndex()
     647             :  */
     648           0 : OGRErr OGRNGWLayer::SetNextByIndex(GIntBig nIndex)
     649             : {
     650           0 :     SyncToDisk();
     651           0 :     if (nIndex < 0)
     652             :     {
     653           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     654             :                  "Feature index must be greater or equal 0. Got " CPL_FRMT_GIB,
     655             :                  nIndex);
     656           0 :         return OGRERR_FAILURE;
     657             :     }
     658           0 :     if (poDS->GetPageSize() > 0)
     659             :     {
     660             :         // Check if index is in current cache
     661           0 :         if (nPageStart > nIndex && nIndex <= nPageStart - poDS->GetPageSize())
     662             :         {
     663           0 :             if (moFeatures.empty() ||
     664           0 :                 static_cast<GIntBig>(moFeatures.size()) <= nIndex)
     665             :             {
     666           0 :                 oNextPos = moFeatures.end();
     667             :             }
     668             :             else
     669             :             {
     670           0 :                 oNextPos = moFeatures.begin();
     671           0 :                 std::advance(oNextPos, static_cast<size_t>(nIndex));
     672             :             }
     673             :         }
     674             :         else
     675             :         {
     676           0 :             ResetReading();
     677           0 :             nPageStart = nIndex;
     678             :         }
     679             :     }
     680             :     else
     681             :     {
     682           0 :         if (moFeatures.empty() && GetMaxFeatureCount(false) > 0)
     683             :         {
     684           0 :             std::string osUrl;
     685           0 :             if (poDS->HasFeaturePaging())
     686             :             {
     687           0 :                 osUrl = NGWAPI::GetFeaturePageURL(
     688           0 :                     poDS->GetUrl(), osResourceId, 0, 0, osFields, osWhere,
     689           0 :                     osSpatialFilter, poDS->Extensions(),
     690           0 :                     poFeatureDefn->IsGeometryIgnored() == TRUE);
     691             :             }
     692             :             else
     693             :             {
     694           0 :                 osUrl = NGWAPI::GetFeatureURL(poDS->GetUrl(), osResourceId);
     695             :             }
     696             : 
     697           0 :             if (!FillFeatures(osUrl))
     698             :             {
     699           0 :                 return OGRERR_FAILURE;
     700             :             }
     701             :         }
     702             : 
     703           0 :         if (moFeatures.empty() ||
     704           0 :             static_cast<GIntBig>(moFeatures.size()) <= nIndex)
     705             :         {
     706           0 :             oNextPos = moFeatures.end();
     707             :         }
     708             :         else
     709             :         {
     710           0 :             oNextPos = moFeatures.begin();
     711           0 :             std::advance(oNextPos, static_cast<size_t>(nIndex));
     712             :         }
     713             :     }
     714           0 :     return OGRERR_NONE;
     715             : }
     716             : 
     717             : /*
     718             :  * GetNextFeature()
     719             :  */
     720           0 : OGRFeature *OGRNGWLayer::GetNextFeature()
     721             : {
     722           0 :     std::string osUrl;
     723             : 
     724           0 :     if (poDS->GetPageSize() > 0)
     725             :     {
     726           0 :         if (oNextPos == moFeatures.end() &&
     727           0 :             nPageStart < GetMaxFeatureCount(false))
     728             :         {
     729           0 :             FreeFeaturesCache();
     730             : 
     731           0 :             osUrl = NGWAPI::GetFeaturePageURL(
     732           0 :                 poDS->GetUrl(), osResourceId, nPageStart, poDS->GetPageSize(),
     733           0 :                 osFields, osWhere, osSpatialFilter, poDS->Extensions(),
     734           0 :                 poFeatureDefn->IsGeometryIgnored() == TRUE);
     735           0 :             nPageStart += poDS->GetPageSize();
     736             :         }
     737             :     }
     738           0 :     else if (moFeatures.empty() && GetMaxFeatureCount(false) > 0)
     739             :     {
     740           0 :         if (poDS->HasFeaturePaging())
     741             :         {
     742           0 :             osUrl = NGWAPI::GetFeaturePageURL(
     743           0 :                 poDS->GetUrl(), osResourceId, 0, 0, osFields, osWhere,
     744           0 :                 osSpatialFilter, poDS->Extensions(),
     745           0 :                 poFeatureDefn->IsGeometryIgnored() == TRUE);
     746             :         }
     747             :         else
     748             :         {
     749           0 :             osUrl = NGWAPI::GetFeatureURL(poDS->GetUrl(), osResourceId);
     750             :         }
     751             :     }
     752             : 
     753           0 :     bool bFinalRead = true;
     754           0 :     if (!osUrl.empty())
     755             :     {
     756           0 :         if (!FillFeatures(osUrl))
     757             :         {
     758           0 :             return nullptr;
     759             :         }
     760             : 
     761           0 :         oNextPos = moFeatures.begin();
     762             : 
     763           0 :         if (poDS->GetPageSize() < 1)
     764             :         {
     765             :             // Without paging we read all features at once.
     766           0 :             m_nFeaturesRead = moFeatures.size();
     767             :         }
     768             :         else
     769             :         {
     770           0 :             if (poDS->GetPageSize() - moFeatures.size() == 0)
     771             :             {
     772           0 :                 m_nFeaturesRead = nPageStart;
     773           0 :                 bFinalRead = false;
     774             :             }
     775             :             else
     776             :             {
     777           0 :                 m_nFeaturesRead =
     778           0 :                     nPageStart - poDS->GetPageSize() + moFeatures.size();
     779             :             }
     780             :         }
     781             :     }
     782             : 
     783           0 :     while (oNextPos != moFeatures.end())
     784             :     {
     785           0 :         OGRFeature *poFeature = oNextPos->second;
     786           0 :         ++oNextPos;
     787             : 
     788           0 :         if (poFeature == nullptr)  // Feature may be deleted.
     789             :         {
     790           0 :             continue;
     791             :         }
     792             : 
     793             :         // Check local filters only for new features which not send to server
     794             :         // yet or if attribute filter process on client side.
     795           0 :         if (poFeature->GetFID() < 0 || bClientSideAttributeFilter)
     796             :         {
     797           0 :             if ((m_poFilterGeom == nullptr ||
     798           0 :                  FilterGeometry(poFeature->GetGeometryRef())) &&
     799           0 :                 (m_poAttrQuery == nullptr ||
     800           0 :                  m_poAttrQuery->Evaluate(poFeature)))
     801             :             {
     802           0 :                 return poFeature->Clone();
     803             :             }
     804             :         }
     805             :         else
     806             :         {
     807           0 :             return poFeature->Clone();
     808             :         }
     809             :     }
     810             : 
     811           0 :     if (poDS->GetPageSize() > 0 && !bFinalRead)
     812             :     {
     813           0 :         return GetNextFeature();
     814             :     }
     815           0 :     return nullptr;
     816             : }
     817             : 
     818             : /*
     819             :  * GetFeature()
     820             :  */
     821           0 : OGRFeature *OGRNGWLayer::GetFeature(GIntBig nFID)
     822             : {
     823             :     // Check feature in cache.
     824           0 :     if (moFeatures[nFID] != nullptr)
     825             :     {
     826           0 :         return moFeatures[nFID]->Clone();
     827             :     }
     828           0 :     auto osUrl = NGWAPI::GetFeatureURL(poDS->GetUrl(), osResourceId) +
     829           0 :                  std::to_string(nFID);
     830           0 :     CPLJSONObject oRoot = LoadUrl(osUrl);
     831           0 :     if (!oRoot.IsValid())
     832             :     {
     833           0 :         return nullptr;
     834             :     }
     835             : 
     836             :     // Don't store feature in cache. This can broke sequence read.
     837           0 :     return JSONToFeature(oRoot, poFeatureDefn, true, poDS->IsExtInNativeData());
     838             : }
     839             : 
     840             : /*
     841             :  * GetLayerDefn()
     842             :  */
     843           0 : OGRFeatureDefn *OGRNGWLayer::GetLayerDefn()
     844             : {
     845           0 :     return poFeatureDefn;
     846             : }
     847             : 
     848             : /*
     849             :  * TestCapability()
     850             :  */
     851           0 : int OGRNGWLayer::TestCapability(const char *pszCap)
     852             : {
     853           0 :     FetchPermissions();
     854           0 :     if (EQUAL(pszCap, OLCRandomRead))
     855           0 :         return TRUE;
     856           0 :     else if (EQUAL(pszCap, OLCSequentialWrite))
     857           0 :         return stPermissions.bDataCanWrite && poDS->IsUpdateMode();
     858           0 :     else if (EQUAL(pszCap, OLCRandomWrite))
     859           0 :         return stPermissions.bDataCanWrite && poDS->IsUpdateMode();
     860           0 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
     861           0 :         return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
     862           0 :     else if (EQUAL(pszCap, OLCFastGetExtent))
     863           0 :         return TRUE;
     864           0 :     else if (EQUAL(pszCap, OLCAlterFieldDefn))
     865           0 :         return stPermissions.bDatastructCanWrite && poDS->IsUpdateMode();
     866           0 :     else if (EQUAL(pszCap, OLCDeleteFeature))
     867           0 :         return stPermissions.bDataCanWrite && poDS->IsUpdateMode();
     868           0 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
     869           0 :         return TRUE;
     870           0 :     else if (EQUAL(pszCap, OLCFastSetNextByIndex))
     871           0 :         return TRUE;
     872           0 :     else if (EQUAL(pszCap, OLCCreateField))
     873           0 :         return stPermissions.bDatastructCanWrite && poDS->IsUpdateMode();
     874           0 :     else if (EQUAL(pszCap, OLCIgnoreFields))
     875           0 :         return poDS->HasFeaturePaging();  // Ignore fields, paging support and
     876             :                                           // attribute/spatial filters were
     877             :                                           // introduced in NGW v3.1
     878           0 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
     879           0 :         return poDS->HasFeaturePaging();
     880           0 :     else if (EQUAL(pszCap, OLCRename))
     881           0 :         return poDS->IsUpdateMode();
     882           0 :     else if (EQUAL(pszCap, OLCZGeometries))
     883           0 :         return TRUE;
     884           0 :     return FALSE;
     885             : }
     886             : 
     887             : /*
     888             :  * Fill()
     889             :  */
     890           0 : void OGRNGWLayer::Fill(const CPLJSONObject &oRootObject)
     891             : {
     892           0 :     auto osName = oRootObject.GetString("resource/display_name");
     893           0 :     CPLStringList soIgnoredFieldsNames;
     894           0 :     if (nullptr != poFeatureDefn)
     895             :     {
     896           0 :         while (poFeatureDefn->GetFieldCount() > 0)
     897             :         {
     898           0 :             OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(0);
     899           0 :             if (poFieldDefn->IsIgnored())
     900             :             {
     901           0 :                 soIgnoredFieldsNames.AddString(poFieldDefn->GetNameRef());
     902             :             }
     903           0 :             poFeatureDefn->DeleteFieldDefn(0);
     904             :         }
     905           0 :         poFeatureDefn->SetName(osName.c_str());
     906             : 
     907           0 :         poFeatureDefn->SetGeomType(NGWAPI::NGWGeomTypeToOGRGeomType(
     908           0 :             oRootObject.GetString("vector_layer/geometry_type")));
     909             :     }
     910             :     else
     911             :     {
     912           0 :         poFeatureDefn = new OGRFeatureDefn(osName.c_str());
     913           0 :         poFeatureDefn->Reference();
     914             : 
     915           0 :         poFeatureDefn->SetGeomType(NGWAPI::NGWGeomTypeToOGRGeomType(
     916           0 :             oRootObject.GetString("vector_layer/geometry_type")));
     917             : 
     918           0 :         OGRSpatialReference *poSRS = new OGRSpatialReference;
     919           0 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     920           0 :         int nEPSG = oRootObject.GetInteger(
     921             :             "vector_layer/srs/id",
     922             :             3857);  // Default NGW SRS is Web mercator EPSG:3857.
     923           0 :         if (poSRS->importFromEPSG(nEPSG) == OGRERR_NONE)
     924             :         {
     925           0 :             if (poFeatureDefn->GetGeomFieldCount() != 0)
     926             :             {
     927           0 :                 poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
     928             :             }
     929             :         }
     930           0 :         poSRS->Release();
     931             :     }
     932             : 
     933           0 :     CPLJSONArray oFields = oRootObject.GetArray("feature_layer/fields");
     934           0 :     FillFields(oFields, soIgnoredFieldsNames);
     935           0 :     FillMetadata(oRootObject);
     936             : 
     937           0 :     auto osDescription = oRootObject.GetString("resource/description");
     938           0 :     SetDescription(osDescription.c_str());
     939             :     // SetDescription(poFeatureDefn->GetName());
     940           0 : }
     941             : 
     942             : /*
     943             :  * FillMetadata()
     944             :  */
     945           0 : void OGRNGWLayer::FillMetadata(const CPLJSONObject &oRootObject)
     946             : {
     947           0 :     std::string osCreateDate = oRootObject.GetString("resource/creation_date");
     948           0 :     if (!osCreateDate.empty())
     949             :     {
     950           0 :         OGRLayer::SetMetadataItem("creation_date", osCreateDate.c_str());
     951             :     }
     952           0 :     std::string osDescription = oRootObject.GetString("resource/description");
     953           0 :     if (!osDescription.empty())
     954             :     {
     955           0 :         OGRLayer::SetMetadataItem("description", osDescription.c_str());
     956             :     }
     957           0 :     std::string osKeyName = oRootObject.GetString("resource/keyname");
     958           0 :     if (!osKeyName.empty())
     959             :     {
     960           0 :         OGRLayer::SetMetadataItem("keyname", osKeyName.c_str());
     961             :     }
     962           0 :     std::string osResourceType = oRootObject.GetString("resource/cls");
     963           0 :     if (!osResourceType.empty())
     964             :     {
     965           0 :         OGRLayer::SetMetadataItem("resource_type", osResourceType.c_str());
     966             :     }
     967             :     std::string osResourceParentId =
     968           0 :         oRootObject.GetString("resource/parent/id");
     969           0 :     if (!osResourceParentId.empty())
     970             :     {
     971           0 :         OGRLayer::SetMetadataItem("parent_id", osResourceParentId.c_str());
     972             :     }
     973           0 :     OGRLayer::SetMetadataItem("id", osResourceId.c_str());
     974             : 
     975             :     std::vector<CPLJSONObject> items =
     976           0 :         oRootObject.GetObj("resmeta/items").GetChildren();
     977             : 
     978           0 :     for (const CPLJSONObject &item : items)
     979             :     {
     980           0 :         std::string osSuffix = NGWAPI::GetResmetaSuffix(item.GetType());
     981           0 :         OGRLayer::SetMetadataItem((item.GetName() + osSuffix).c_str(),
     982           0 :                                   item.ToString().c_str(), "NGW");
     983             :     }
     984           0 : }
     985             : 
     986             : /*
     987             :  * FillFields()
     988             :  */
     989           0 : void OGRNGWLayer::FillFields(const CPLJSONArray &oFields,
     990             :                              const CPLStringList &soIgnoredFieldsNames)
     991             : {
     992           0 :     for (const auto &oField : oFields)
     993             :     {
     994           0 :         std::string osFieldName = oField.GetString("keyname");
     995             :         OGRFieldType eFieldtype =
     996           0 :             NGWAPI::NGWFieldTypeToOGRFieldType(oField.GetString("datatype"));
     997             : 
     998           0 :         OGRFieldDefn oFieldDefn(osFieldName.c_str(), eFieldtype);
     999           0 :         std::string osFieldAlias = oField.GetString("display_name");
    1000           0 :         oFieldDefn.SetAlternativeName(osFieldAlias.c_str());
    1001             : 
    1002             :         // Add additional parameters to comment as JSON string
    1003           0 :         CPLJSONObject oFieldComment;
    1004           0 :         oFieldComment.Add("id", oField.GetLong("id"));
    1005           0 :         oFieldComment.Add("label_field", oField.GetBool("label_field"));
    1006           0 :         oFieldComment.Add("grid_visibility", oField.GetBool("grid_visibility"));
    1007           0 :         oFieldComment.Add("text_search", oField.GetBool("text_search"));
    1008           0 :         oFieldDefn.SetComment(
    1009           0 :             oFieldComment.Format(CPLJSONObject::PrettyFormat::Plain));
    1010             : 
    1011             :         // Domain
    1012           0 :         auto nDomainID = oField.GetInteger("lookup_table/id", -1);
    1013           0 :         if (nDomainID != -1)
    1014             :         {
    1015           0 :             auto oDom = poDS->GetDomainByID(nDomainID);
    1016           0 :             auto pOgrDom = oDom.ToFieldDomain(eFieldtype);
    1017           0 :             if (pOgrDom != nullptr)
    1018             :             {
    1019           0 :                 oFieldDefn.SetDomainName(pOgrDom->GetName());
    1020             :             }
    1021             :         }
    1022             : 
    1023           0 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1024             :     }
    1025             : 
    1026           0 :     OGRLayer::SetIgnoredFields(soIgnoredFieldsNames);
    1027           0 : }
    1028             : 
    1029             : /*
    1030             :  * GetMaxFeatureCount()
    1031             :  */
    1032           0 : GIntBig OGRNGWLayer::GetMaxFeatureCount(bool bForce)
    1033             : {
    1034           0 :     if (nFeatureCount < 0 || bForce)
    1035             :     {
    1036             :         CPLJSONObject oRoot =
    1037           0 :             LoadUrl(NGWAPI::GetFeatureCount(poDS->GetUrl(), osResourceId));
    1038           0 :         if (!oRoot.IsValid())
    1039             :         {
    1040           0 :             return nFeatureCount;
    1041             :         }
    1042             : 
    1043           0 :         nFeatureCount = oRoot.GetLong("total_count");
    1044           0 :         nFeatureCount += GetNewFeaturesCount();
    1045             :     }
    1046           0 :     return nFeatureCount;
    1047             : }
    1048             : 
    1049             : /*
    1050             :  * GetFeatureCount()
    1051             :  */
    1052           0 : GIntBig OGRNGWLayer::GetFeatureCount(int bForce)
    1053             : {
    1054           0 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
    1055             :     {
    1056           0 :         return GetMaxFeatureCount(CPL_TO_BOOL(bForce));
    1057             :     }
    1058             :     else
    1059             :     {
    1060           0 :         return OGRLayer::GetFeatureCount(bForce);
    1061             :     }
    1062             : }
    1063             : 
    1064             : /*
    1065             :  * IGetExtent()
    1066             :  */
    1067           0 : OGRErr OGRNGWLayer::IGetExtent(int /* iGeomField */, OGREnvelope *psExtent,
    1068             :                                bool bForce)
    1069             : {
    1070           0 :     if (!stExtent.IsInit() || CPL_TO_BOOL(bForce))
    1071             :     {
    1072           0 :         auto aosHTTPOptions = poDS->GetHeaders(false);
    1073           0 :         bool bResult = NGWAPI::GetExtent(poDS->GetUrl(), osResourceId,
    1074           0 :                                          aosHTTPOptions, 3857, stExtent);
    1075           0 :         if (!bResult)
    1076             :         {
    1077           0 :             return OGRERR_FAILURE;
    1078             :         }
    1079             :     }
    1080           0 :     *psExtent = stExtent;
    1081           0 :     return OGRERR_NONE;
    1082             : }
    1083             : 
    1084             : /*
    1085             :  * FetchPermissions()
    1086             :  */
    1087           0 : void OGRNGWLayer::FetchPermissions()
    1088             : {
    1089           0 :     if (bFetchedPermissions || osResourceId == "-1")
    1090             :     {
    1091           0 :         return;
    1092             :     }
    1093             : 
    1094           0 :     if (poDS->IsUpdateMode())
    1095             :     {
    1096           0 :         auto aosHTTPOptions = poDS->GetHeaders(false);
    1097             :         stPermissions = NGWAPI::CheckPermissions(
    1098           0 :             poDS->GetUrl(), osResourceId, aosHTTPOptions, poDS->IsUpdateMode());
    1099             :     }
    1100             :     else
    1101             :     {
    1102           0 :         stPermissions.bDataCanRead = true;
    1103           0 :         stPermissions.bResourceCanRead = true;
    1104           0 :         stPermissions.bDatastructCanRead = true;
    1105           0 :         stPermissions.bMetadataCanRead = true;
    1106             :     }
    1107           0 :     bFetchedPermissions = true;
    1108             : }
    1109             : 
    1110             : /*
    1111             :  * CreateField()
    1112             :  */
    1113           0 : OGRErr OGRNGWLayer::CreateField(const OGRFieldDefn *poField,
    1114             :                                 CPL_UNUSED int bApproxOK)
    1115             : {
    1116           0 :     CPLAssert(nullptr != poField);
    1117             : 
    1118           0 :     if (!CheckFieldNameUnique(poFeatureDefn, -1, poField->GetNameRef()))
    1119             :     {
    1120           0 :         return OGRERR_FAILURE;
    1121             :     }
    1122             : 
    1123           0 :     OGRFieldDefn oModFieldDefn(poField);
    1124           0 :     poFeatureDefn->AddFieldDefn(&oModFieldDefn);
    1125           0 :     bNeedSyncStructure = true;
    1126           0 :     return OGRERR_NONE;
    1127             : }
    1128             : 
    1129             : /*
    1130             :  * DeleteField()
    1131             :  */
    1132           0 : OGRErr OGRNGWLayer::DeleteField(int iField)
    1133             : {
    1134           0 :     if (osResourceId != "-1")
    1135             :     {
    1136           0 :         auto poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1137           0 :         if (poFieldDefn == nullptr)
    1138             :         {
    1139           0 :             return OGRERR_FAILURE;
    1140             :         }
    1141             :         // Get field NGW ID
    1142           0 :         CPLJSONDocument oComment;
    1143           0 :         if (oComment.LoadMemory(poFieldDefn->GetComment()))
    1144             :         {
    1145           0 :             auto oRoot = oComment.GetRoot();
    1146           0 :             auto nNGWID = oRoot.GetLong("id", -1);
    1147           0 :             if (nNGWID != -1)
    1148             :             {
    1149           0 :                 soDeletedFieldsIds.insert(nNGWID);
    1150             :             }
    1151             :         }
    1152             :     }
    1153           0 :     return poFeatureDefn->DeleteFieldDefn(iField);
    1154             : }
    1155             : 
    1156             : /*
    1157             :  * ReorderFields()
    1158             :  */
    1159           0 : OGRErr OGRNGWLayer::ReorderFields(int *panMap)
    1160             : {
    1161           0 :     return poFeatureDefn->ReorderFieldDefns(panMap);
    1162             : }
    1163             : 
    1164             : /*
    1165             :  * AlterFieldDefn()
    1166             :  */
    1167           0 : OGRErr OGRNGWLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
    1168             :                                    int nFlagsIn)
    1169             : {
    1170           0 :     CPLDebug("NGW", "AlterFieldDefn fld #%d", iField);
    1171           0 :     CPLAssert(nullptr != poNewFieldDefn);
    1172             : 
    1173           0 :     if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
    1174             :     {
    1175           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    1176           0 :         return OGRERR_FAILURE;
    1177             :     }
    1178             : 
    1179           0 :     FetchPermissions();
    1180           0 :     if (!stPermissions.bDatastructCanWrite)
    1181             :     {
    1182           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Insufficient permissions");
    1183           0 :         return OGRERR_FAILURE;
    1184             :     }
    1185             : 
    1186           0 :     if (!poDS->IsUpdateMode())
    1187             :     {
    1188           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Layer is read only");
    1189           0 :         return OGRERR_FAILURE;
    1190             :     }
    1191             : 
    1192           0 :     OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1193             :     // Check new field name is not equal for another fields.
    1194           0 :     if (!CheckFieldNameUnique(poFeatureDefn, iField,
    1195             :                               poNewFieldDefn->GetNameRef()))
    1196             :     {
    1197           0 :         return OGRERR_FAILURE;
    1198             :     }
    1199             : 
    1200           0 :     if (osResourceId == "-1")  // Can full alter field only on new layers
    1201             :                                // (not synced with server).
    1202             :     {
    1203             :         // Field name 'id' forbidden.
    1204           0 :         OGRFieldDefn oModFieldDefn(poNewFieldDefn);
    1205             : 
    1206           0 :         poFieldDefn->SetName(oModFieldDefn.GetNameRef());
    1207           0 :         poFieldDefn->SetAlternativeName(oModFieldDefn.GetAlternativeNameRef());
    1208           0 :         poFieldDefn->SetComment(oModFieldDefn.GetComment());
    1209           0 :         poFieldDefn->SetType(oModFieldDefn.GetType());
    1210           0 :         poFieldDefn->SetSubType(oModFieldDefn.GetSubType());
    1211           0 :         poFieldDefn->SetWidth(oModFieldDefn.GetWidth());
    1212           0 :         poFieldDefn->SetPrecision(oModFieldDefn.GetPrecision());
    1213             :     }
    1214             :     else
    1215             :     {
    1216           0 :         if (nFlagsIn & ALTER_NAME_FLAG)
    1217             :         {
    1218           0 :             bNeedSyncStructure = true;
    1219           0 :             poFieldDefn->SetName(poNewFieldDefn->GetNameRef());
    1220             :         }
    1221           0 :         if (nFlagsIn & ALTER_DOMAIN_FLAG)
    1222             :         {
    1223           0 :             bNeedSyncStructure = true;
    1224           0 :             poFieldDefn->SetDomainName(poNewFieldDefn->GetDomainName());
    1225             :         }
    1226           0 :         if (nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG)
    1227             :         {
    1228           0 :             bNeedSyncStructure = true;
    1229           0 :             poFieldDefn->SetAlternativeName(
    1230             :                 poNewFieldDefn->GetAlternativeNameRef());
    1231             :         }
    1232           0 :         if (nFlagsIn & ALTER_COMMENT_FLAG)
    1233             :         {
    1234           0 :             bNeedSyncStructure = true;
    1235           0 :             poFieldDefn->SetComment(poNewFieldDefn->GetComment());
    1236             :         }
    1237             :     }
    1238           0 :     ResetReading();
    1239           0 :     return OGRERR_NONE;
    1240             : }
    1241             : 
    1242             : /*
    1243             :  * SetMetadata()
    1244             :  */
    1245           0 : CPLErr OGRNGWLayer::SetMetadata(char **papszMetadata, const char *pszDomain)
    1246             : {
    1247           0 :     bNeedSyncStructure = true;
    1248           0 :     return OGRLayer::SetMetadata(papszMetadata, pszDomain);
    1249             : }
    1250             : 
    1251             : /*
    1252             :  * SetMetadataItem()
    1253             :  */
    1254           0 : CPLErr OGRNGWLayer::SetMetadataItem(const char *pszName, const char *pszValue,
    1255             :                                     const char *pszDomain)
    1256             : {
    1257           0 :     bNeedSyncStructure = true;
    1258           0 :     return OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
    1259             : }
    1260             : 
    1261             : /*
    1262             :  * CreateNGWResourceJson()
    1263             :  */
    1264           0 : std::string OGRNGWLayer::CreateNGWResourceJson()
    1265             : {
    1266           0 :     CPLJSONObject oResourceJson;
    1267             : 
    1268             :     // Add resource json item.
    1269           0 :     CPLJSONObject oResource("resource", oResourceJson);
    1270           0 :     oResource.Add("cls", "vector_layer");
    1271           0 :     CPLJSONObject oResourceParent("parent", oResource);
    1272           0 :     oResourceParent.Add("id",
    1273           0 :                         static_cast<GIntBig>(std::stol(poDS->GetResourceId())));
    1274           0 :     oResource.Add("display_name", GetName());
    1275           0 :     const char *pszKeyName = GetMetadataItem("keyname");
    1276           0 :     if (pszKeyName)
    1277             :     {
    1278           0 :         oResource.Add("keyname", pszKeyName);
    1279             :     }
    1280           0 :     const char *pszDescription = GetMetadataItem("description");
    1281           0 :     if (pszDescription)
    1282             :     {
    1283           0 :         oResource.Add("description", pszDescription);
    1284             :     }
    1285             : 
    1286             :     // Add vector_layer json item.
    1287           0 :     CPLJSONObject oVectorLayer("vector_layer", oResourceJson);
    1288           0 :     CPLJSONObject oVectorLayerSrs("srs", oVectorLayer);
    1289             : 
    1290           0 :     OGRSpatialReference *poSpatialRef = GetSpatialRef();
    1291           0 :     int nEPSG = 3857;
    1292           0 :     if (poSpatialRef)
    1293             :     {
    1294           0 :         poSpatialRef->AutoIdentifyEPSG();
    1295           0 :         const char *pszEPSG = poSpatialRef->GetAuthorityCode(nullptr);
    1296           0 :         if (pszEPSG != nullptr)
    1297             :         {
    1298           0 :             nEPSG = atoi(pszEPSG);
    1299             :         }
    1300             :     }
    1301           0 :     oVectorLayerSrs.Add("id", nEPSG);
    1302             :     // In OGRNGWDataset::ICreateLayer we limit supported geometry types.
    1303           0 :     oVectorLayer.Add("geometry_type",
    1304           0 :                      NGWAPI::OGRGeomTypeToNGWGeomType(GetGeomType()));
    1305             : 
    1306             :     // Fill fields
    1307           0 :     CPLJSONArray oVectorLayerFields;
    1308           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
    1309             :     {
    1310           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1311           0 :         CPLJSONObject oField;
    1312           0 :         auto const &sComment = poFieldDefn->GetComment();
    1313           0 :         if (!sComment.empty())
    1314             :         {
    1315           0 :             CPLJSONDocument oComment;
    1316           0 :             if (oComment.LoadMemory(sComment))
    1317             :             {
    1318           0 :                 oField = oComment.GetRoot();
    1319             :             }
    1320             :         }
    1321           0 :         oField.Add("keyname", poFieldDefn->GetNameRef());
    1322           0 :         oField.Add("datatype",
    1323           0 :                    NGWAPI::OGRFieldTypeToNGWFieldType(poFieldDefn->GetType()));
    1324           0 :         std::string osFieldAliasName = poFieldDefn->GetAlternativeNameRef();
    1325             :         // Get alias from metadata.
    1326           0 :         if (osFieldAliasName.empty())
    1327             :         {
    1328           0 :             osFieldAliasName = poFieldDefn->GetNameRef();
    1329             :         }
    1330           0 :         oField.Add("display_name", osFieldAliasName);
    1331             : 
    1332           0 :         if (poFieldDefn->GetDomainName().empty())
    1333             :         {
    1334           0 :             oField.AddNull("lookup_table");
    1335             :         }
    1336             :         else
    1337             :         {
    1338           0 :             CPLJSONObject oFieldDom("lookup_table", oField);
    1339           0 :             auto nDomId = poDS->GetDomainIdByName(poFieldDefn->GetDomainName());
    1340           0 :             oFieldDom.Add("id", nDomId);
    1341             :         }
    1342             : 
    1343           0 :         oVectorLayerFields.Add(oField);
    1344             :     }
    1345           0 :     for (auto nFieldID : soDeletedFieldsIds)
    1346             :     {
    1347           0 :         CPLJSONObject oField;
    1348           0 :         oField.Add("id", nFieldID);
    1349           0 :         oField.Add("delete", true);
    1350           0 :         oVectorLayerFields.Add(oField);
    1351             :     }
    1352             : 
    1353           0 :     CPLJSONObject oFeatureLayer("feature_layer", oResourceJson);
    1354           0 :     oFeatureLayer.Add("fields", oVectorLayerFields);
    1355             : 
    1356             :     // Add resmeta json item.
    1357           0 :     NGWAPI::FillResmeta(oResourceJson, GetMetadata("NGW"));
    1358             : 
    1359           0 :     return oResourceJson.Format(CPLJSONObject::PrettyFormat::Plain);
    1360             : }
    1361             : 
    1362             : /*
    1363             :  * SyncFeatures()
    1364             :  */
    1365           0 : OGRErr OGRNGWLayer::SyncFeatures()
    1366             : {
    1367           0 :     if (!bNeedSyncData)
    1368             :     {
    1369           0 :         return OGRERR_NONE;
    1370             :     }
    1371             : 
    1372           0 :     CPLJSONArray oFeatureJsonArray;
    1373           0 :     std::vector<GIntBig> aoPatchedFIDs;
    1374           0 :     for (GIntBig nFID : soChangedIds)
    1375             :     {
    1376           0 :         if (moFeatures[nFID] != nullptr)
    1377             :         {
    1378           0 :             oFeatureJsonArray.Add(FeatureToJson(moFeatures[nFID]));
    1379           0 :             aoPatchedFIDs.push_back(nFID);
    1380             :         }
    1381             :     }
    1382             : 
    1383           0 :     if (!aoPatchedFIDs.empty())
    1384             :     {
    1385             :         auto osIDs = NGWAPI::PatchFeatures(
    1386           0 :             poDS->GetUrl(), osResourceId,
    1387           0 :             oFeatureJsonArray.Format(CPLJSONObject::PrettyFormat::Plain),
    1388           0 :             poDS->GetHeaders());
    1389           0 :         if (!osIDs.empty())
    1390             :         {
    1391           0 :             bNeedSyncData = false;
    1392           0 :             nFeatureCount += GetNewFeaturesCount();
    1393           0 :             soChangedIds.clear();
    1394           0 :             if (osIDs.size() !=
    1395           0 :                 aoPatchedFIDs.size())  // Expected equal identifier count.
    1396             :             {
    1397           0 :                 CPLDebug("NGW", "Patched feature count is not equal. Reload "
    1398             :                                 "features from server.");
    1399           0 :                 FreeMap(moFeatures);
    1400             :             }
    1401             :             else  // Just update identifiers.
    1402             :             {
    1403           0 :                 int nCounter = 0;
    1404           0 :                 for (GIntBig nFID : aoPatchedFIDs)
    1405             :                 {
    1406           0 :                     GIntBig nNewFID = osIDs[nCounter++];
    1407           0 :                     OGRFeature *poFeature = moFeatures[nFID];
    1408           0 :                     poFeature->SetFID(nNewFID);
    1409           0 :                     moFeatures.erase(nFID);
    1410           0 :                     moFeatures[nNewFID] = poFeature;
    1411             :                 }
    1412             :             }
    1413             :         }
    1414             :         else
    1415             :         {
    1416             :             // Error message should set in NGWAPI::PatchFeatures function.
    1417           0 :             if (CPLGetLastErrorNo() != 0)
    1418             :             {
    1419           0 :                 return OGRERR_FAILURE;
    1420             :             }
    1421             :         }
    1422             :     }
    1423           0 :     return OGRERR_NONE;
    1424             : }
    1425             : 
    1426             : /*
    1427             :  * SyncToDisk()
    1428             :  */
    1429           0 : OGRErr OGRNGWLayer::SyncToDisk()
    1430             : {
    1431           0 :     if (osResourceId == "-1")  // Create vector layer at NextGIS Web.
    1432             :     {
    1433           0 :         bNeedSyncData = !moFeatures.empty();
    1434             :         std::string osResourceIdInt = NGWAPI::CreateResource(
    1435           0 :             poDS->GetUrl(), CreateNGWResourceJson(), poDS->GetHeaders());
    1436           0 :         if (osResourceIdInt == "-1")
    1437             :         {
    1438             :             // Error message should set in CreateResource.
    1439           0 :             return OGRERR_FAILURE;
    1440             :         }
    1441           0 :         osResourceId = std::move(osResourceIdInt);
    1442           0 :         OGRLayer::SetMetadataItem("id", osResourceId.c_str());
    1443           0 :         FetchPermissions();
    1444           0 :         bNeedSyncStructure = false;
    1445             :     }
    1446           0 :     else if (bNeedSyncStructure)  // Update vector layer at NextGIS Web.
    1447             :     {
    1448           0 :         if (!NGWAPI::UpdateResource(poDS->GetUrl(), GetResourceId(),
    1449           0 :                                     CreateNGWResourceJson(),
    1450           0 :                                     poDS->GetHeaders()))
    1451             :         {
    1452             :             // Error message should set in UpdateResource.
    1453           0 :             return OGRERR_FAILURE;
    1454             :         }
    1455           0 :         bNeedSyncStructure = false;
    1456           0 :         soDeletedFieldsIds.clear();
    1457             :     }
    1458             : 
    1459           0 :     auto oRoot = LoadUrl(NGWAPI::GetResourceURL(poDS->GetUrl(), osResourceId));
    1460           0 :     if (!oRoot.IsValid())
    1461             :     {
    1462           0 :         return OGRERR_FAILURE;
    1463             :     }
    1464           0 :     Fill(oRoot);
    1465             : 
    1466             :     // Update NGW layer state
    1467           0 :     FetchPermissions();
    1468             : 
    1469             :     // Sync features.
    1470           0 :     return SyncFeatures();
    1471             : }
    1472             : 
    1473             : /*
    1474             :  * DeleteFeature()
    1475             :  */
    1476           0 : OGRErr OGRNGWLayer::DeleteFeature(GIntBig nFID)
    1477             : {
    1478           0 :     CPLErrorReset();
    1479           0 :     if (nFID < 0)
    1480             :     {
    1481           0 :         if (moFeatures[nFID] != nullptr)
    1482             :         {
    1483           0 :             OGRFeature::DestroyFeature(moFeatures[nFID]);
    1484           0 :             moFeatures[nFID] = nullptr;
    1485           0 :             nFeatureCount--;
    1486           0 :             soChangedIds.erase(nFID);
    1487           0 :             return OGRERR_NONE;
    1488             :         }
    1489           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1490             :                  "Feature with id " CPL_FRMT_GIB " not found.", nFID);
    1491           0 :         return OGRERR_FAILURE;
    1492             :     }
    1493             :     else
    1494             :     {
    1495           0 :         FetchPermissions();
    1496           0 :         if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
    1497             :         {
    1498           0 :             bool bResult = NGWAPI::DeleteFeature(poDS->GetUrl(), osResourceId,
    1499           0 :                                                  std::to_string(nFID),
    1500           0 :                                                  poDS->GetHeaders(false));
    1501           0 :             if (bResult)
    1502             :             {
    1503           0 :                 if (moFeatures[nFID] != nullptr)
    1504             :                 {
    1505           0 :                     OGRFeature::DestroyFeature(moFeatures[nFID]);
    1506           0 :                     moFeatures[nFID] = nullptr;
    1507             :                 }
    1508           0 :                 nFeatureCount--;
    1509           0 :                 soChangedIds.erase(nFID);
    1510           0 :                 return OGRERR_NONE;
    1511             :             }
    1512           0 :             return OGRERR_FAILURE;
    1513             :         }
    1514           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1515             :                  "Delete feature " CPL_FRMT_GIB " operation is not permitted.",
    1516             :                  nFID);
    1517           0 :         return OGRERR_FAILURE;
    1518             :     }
    1519             : }
    1520             : 
    1521             : /*
    1522             :  * DeleteFeatures()
    1523             :  */
    1524           0 : OGRErr OGRNGWLayer::DeleteFeatures(const std::vector<GIntBig> &vFeaturesID)
    1525             : {
    1526           0 :     CPLErrorReset();
    1527             : 
    1528             :     // Try to delete local features (not synchronized with NGW)
    1529           0 :     std::vector<GIntBig> vFeaturesIDInt(vFeaturesID);
    1530           0 :     for (size_t i = 0; i < vFeaturesIDInt.size(); i++)
    1531             :     {
    1532           0 :         auto nFID = vFeaturesIDInt[i];
    1533           0 :         if (nFID < 0)
    1534             :         {
    1535           0 :             if (moFeatures[nFID] != nullptr)
    1536             :             {
    1537           0 :                 OGRFeature::DestroyFeature(moFeatures[nFID]);
    1538           0 :                 moFeatures[nFID] = nullptr;
    1539           0 :                 nFeatureCount--;
    1540           0 :                 soChangedIds.erase(nFID);
    1541           0 :                 vFeaturesIDInt.erase(vFeaturesIDInt.begin() + i);
    1542             :             }
    1543             :         }
    1544             :     }
    1545             : 
    1546           0 :     FetchPermissions();
    1547           0 :     if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
    1548             :     {
    1549           0 :         bool bResult = NGWAPI::DeleteFeatures(
    1550           0 :             poDS->GetUrl(), osResourceId,
    1551           0 :             FeaturesIDToJsonString(vFeaturesIDInt), poDS->GetHeaders(false));
    1552           0 :         if (bResult)
    1553             :         {
    1554           0 :             for (GIntBig nFID : vFeaturesIDInt)
    1555             :             {
    1556           0 :                 if (moFeatures[nFID] != nullptr)
    1557             :                 {
    1558           0 :                     OGRFeature::DestroyFeature(moFeatures[nFID]);
    1559           0 :                     moFeatures[nFID] = nullptr;
    1560           0 :                     nFeatureCount--;
    1561           0 :                     soChangedIds.erase(nFID);
    1562             :                 }
    1563           0 :                 nFeatureCount--;
    1564           0 :                 soChangedIds.erase(nFID);
    1565             :             }
    1566           0 :             return OGRERR_NONE;
    1567             :         }
    1568             :     }
    1569           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Delete features failed");
    1570           0 :     return OGRERR_FAILURE;
    1571             : }
    1572             : 
    1573             : /*
    1574             :  * DeleteAllFeatures()
    1575             :  */
    1576           0 : bool OGRNGWLayer::DeleteAllFeatures()
    1577             : {
    1578           0 :     if (osResourceId == "-1")
    1579             :     {
    1580           0 :         soChangedIds.clear();
    1581           0 :         bNeedSyncData = false;
    1582           0 :         FreeFeaturesCache();
    1583           0 :         nFeatureCount = 0;
    1584           0 :         return true;
    1585             :     }
    1586             :     else
    1587             :     {
    1588           0 :         FetchPermissions();
    1589           0 :         if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
    1590             :         {
    1591           0 :             bool bResult = NGWAPI::DeleteFeature(poDS->GetUrl(), osResourceId,
    1592           0 :                                                  "", poDS->GetHeaders(false));
    1593           0 :             if (bResult)
    1594             :             {
    1595           0 :                 soChangedIds.clear();
    1596           0 :                 bNeedSyncData = false;
    1597           0 :                 FreeFeaturesCache();
    1598           0 :                 nFeatureCount = 0;
    1599             :             }
    1600           0 :             return bResult;
    1601             :         }
    1602             :     }
    1603           0 :     CPLErrorReset();
    1604           0 :     CPLError(CE_Failure, CPLE_AppDefined,
    1605             :              "Delete all features operation is not permitted.");
    1606           0 :     return false;
    1607             : }
    1608             : 
    1609             : /*
    1610             :  * ISetFeature()
    1611             :  */
    1612           0 : OGRErr OGRNGWLayer::ISetFeature(OGRFeature *poFeature)
    1613             : {
    1614           0 :     if (poDS->IsBatchMode())
    1615             :     {
    1616           0 :         if (moFeatures[poFeature->GetFID()] == nullptr)
    1617             :         {
    1618           0 :             if (poFeature->GetFID() < 0)
    1619             :             {
    1620           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1621             :                          "Cannot update not existing feature " CPL_FRMT_GIB,
    1622             :                          poFeature->GetFID());
    1623           0 :                 return OGRERR_FAILURE;
    1624             :             }
    1625             :         }
    1626             :         else
    1627             :         {
    1628           0 :             OGRFeature::DestroyFeature(moFeatures[poFeature->GetFID()]);
    1629             :         }
    1630           0 :         moFeatures[poFeature->GetFID()] = poFeature->Clone();
    1631           0 :         soChangedIds.insert(poFeature->GetFID());
    1632             : 
    1633           0 :         if (soChangedIds.size() > static_cast<size_t>(poDS->GetBatchSize()))
    1634             :         {
    1635           0 :             bNeedSyncData = true;
    1636             :         }
    1637             : 
    1638           0 :         return SyncToDisk();
    1639             :     }
    1640             :     else
    1641             :     {
    1642             :         OGRErr eResult =
    1643           0 :             SyncToDisk();  // For create new layer if not yet created.
    1644           0 :         if (eResult == OGRERR_NONE)
    1645             :         {
    1646           0 :             if (poFeature->GetFID() < 0)
    1647             :             {
    1648           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1649             :                          "Cannot update not existing feature " CPL_FRMT_GIB,
    1650             :                          poFeature->GetFID());
    1651           0 :                 return OGRERR_FAILURE;
    1652             :             }
    1653             : 
    1654           0 :             bool bResult = NGWAPI::UpdateFeature(
    1655           0 :                 poDS->GetUrl(), osResourceId,
    1656           0 :                 std::to_string(poFeature->GetFID()),
    1657           0 :                 FeatureToJsonString(poFeature), poDS->GetHeaders());
    1658           0 :             if (bResult)
    1659             :             {
    1660           0 :                 CPLDebug("NGW", "ISetFeature with FID " CPL_FRMT_GIB,
    1661             :                          poFeature->GetFID());
    1662             : 
    1663           0 :                 OGRFeature::DestroyFeature(moFeatures[poFeature->GetFID()]);
    1664           0 :                 moFeatures[poFeature->GetFID()] = poFeature->Clone();
    1665           0 :                 return OGRERR_NONE;
    1666             :             }
    1667             :             else
    1668             :             {
    1669             :                 // CPLError should be set in NGWAPI::UpdateFeature.
    1670           0 :                 return OGRERR_FAILURE;
    1671             :             }
    1672             :         }
    1673             :         else
    1674             :         {
    1675           0 :             return eResult;
    1676             :         }
    1677             :     }
    1678             : }
    1679             : 
    1680             : /*
    1681             :  * ICreateFeature()
    1682             :  */
    1683           0 : OGRErr OGRNGWLayer::ICreateFeature(OGRFeature *poFeature)
    1684             : {
    1685           0 :     if (poDS->IsBatchMode())
    1686             :     {
    1687           0 :         GIntBig nNewFID = -1;
    1688           0 :         if (!soChangedIds.empty())
    1689             :         {
    1690           0 :             nNewFID = *(soChangedIds.begin()) - 1;
    1691             :         }
    1692           0 :         poFeature->SetFID(nNewFID);
    1693           0 :         moFeatures[nNewFID] = poFeature->Clone();
    1694           0 :         soChangedIds.insert(nNewFID);
    1695           0 :         nFeatureCount++;
    1696             : 
    1697           0 :         if (soChangedIds.size() > static_cast<size_t>(poDS->GetBatchSize()))
    1698             :         {
    1699           0 :             bNeedSyncData = true;
    1700             :         }
    1701             : 
    1702           0 :         return SyncToDisk();
    1703             :     }
    1704             :     else
    1705             :     {
    1706             :         OGRErr eResult =
    1707           0 :             SyncToDisk();  // For create new layer if not yet created.
    1708           0 :         if (eResult == OGRERR_NONE)
    1709             :         {
    1710           0 :             GIntBig nNewFID = NGWAPI::CreateFeature(
    1711           0 :                 poDS->GetUrl(), osResourceId, FeatureToJsonString(poFeature),
    1712           0 :                 poDS->GetHeaders());
    1713           0 :             if (nNewFID >= 0)
    1714             :             {
    1715           0 :                 poFeature->SetFID(nNewFID);
    1716           0 :                 moFeatures[nNewFID] = poFeature->Clone();
    1717           0 :                 nFeatureCount++;
    1718           0 :                 return OGRERR_NONE;
    1719             :             }
    1720             :             else
    1721             :             {
    1722             :                 // CPLError should be set in NGWAPI::CreateFeature.
    1723           0 :                 return OGRERR_FAILURE;
    1724             :             }
    1725             :         }
    1726             :         else
    1727             :         {
    1728           0 :             return eResult;
    1729             :         }
    1730             :     }
    1731             : }
    1732             : 
    1733             : /*
    1734             :  * SetIgnoredFields()
    1735             :  */
    1736           0 : OGRErr OGRNGWLayer::SetIgnoredFields(CSLConstList papszFields)
    1737             : {
    1738           0 :     OGRErr eResult = OGRLayer::SetIgnoredFields(papszFields);
    1739           0 :     if (eResult != OGRERR_NONE)
    1740             :     {
    1741           0 :         return eResult;
    1742             :     }
    1743             : 
    1744           0 :     osFields.clear();
    1745             : 
    1746           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
    1747             :     {
    1748           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1749           0 :         if (poFieldDefn->IsIgnored())
    1750             :         {
    1751           0 :             CPLDebug("NGW", "SetIgnoredFields: Field '%s' set to ignored",
    1752             :                      poFieldDefn->GetNameRef());
    1753           0 :             continue;
    1754             :         }
    1755             : 
    1756           0 :         if (osFields.empty())
    1757             :         {
    1758           0 :             osFields = poFieldDefn->GetNameRef();
    1759             :         }
    1760             :         else
    1761             :         {
    1762           0 :             osFields += "," + std::string(poFieldDefn->GetNameRef());
    1763             :         }
    1764             :     }
    1765             : 
    1766             :     // Encode osFields string for URL
    1767           0 :     if (!osFields.empty())
    1768             :     {
    1769           0 :         char *pszValuesEncoded = CPLEscapeString(
    1770           0 :             osFields.c_str(), static_cast<int>(osFields.size()), CPLES_URL);
    1771           0 :         osFields = pszValuesEncoded;
    1772           0 :         CPLFree(pszValuesEncoded);
    1773             :     }
    1774             : 
    1775           0 :     CPLDebug("NGW", "SetIgnoredFields: NGW fields filter set to '%s'",
    1776             :              osFields.c_str());
    1777             : 
    1778           0 :     ResetReading();
    1779           0 :     return OGRERR_NONE;
    1780             : }
    1781             : 
    1782             : /*
    1783             :  * ISetSpatialFilter()
    1784             :  */
    1785           0 : OGRErr OGRNGWLayer::ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom)
    1786             : {
    1787           0 :     OGRLayer::ISetSpatialFilter(iGeomField, poGeom);
    1788             : 
    1789           0 :     if (nullptr == m_poFilterGeom)
    1790             :     {
    1791           0 :         CPLDebug("NGW", "Spatial filter unset");
    1792           0 :         osSpatialFilter.clear();
    1793             :     }
    1794             :     else
    1795             :     {
    1796           0 :         OGREnvelope sEnvelope;
    1797           0 :         m_poFilterGeom->getEnvelope(&sEnvelope);
    1798             : 
    1799           0 :         OGREnvelope sBigEnvelope;
    1800           0 :         sBigEnvelope.MinX = -40000000.0;
    1801           0 :         sBigEnvelope.MinY = -40000000.0;
    1802           0 :         sBigEnvelope.MaxX = 40000000.0;
    1803           0 :         sBigEnvelope.MaxY = 40000000.0;
    1804             : 
    1805             :         // Case for infinity filter
    1806           0 :         if (sEnvelope.Contains(sBigEnvelope) == TRUE)
    1807             :         {
    1808           0 :             CPLDebug("NGW", "Spatial filter unset as filter envelope covers "
    1809             :                             "whole features.");
    1810           0 :             osSpatialFilter.clear();
    1811             :         }
    1812             :         else
    1813             :         {
    1814           0 :             if (sEnvelope.MinX == sEnvelope.MaxX &&
    1815           0 :                 sEnvelope.MinY == sEnvelope.MaxY)
    1816             :             {
    1817           0 :                 OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
    1818           0 :                 InstallFilter(&p);
    1819             :             }
    1820             : 
    1821           0 :             osSpatialFilter = OGRGeometryToWKT(m_poFilterGeom);
    1822           0 :             CPLDebug("NGW", "Spatial filter: %s", osSpatialFilter.c_str());
    1823           0 :             char *pszSpatFilterEncoded = CPLEscapeString(
    1824             :                 osSpatialFilter.c_str(),
    1825           0 :                 static_cast<int>(osSpatialFilter.size()), CPLES_URL);
    1826           0 :             osSpatialFilter = pszSpatFilterEncoded;
    1827           0 :             CPLFree(pszSpatFilterEncoded);
    1828             :         }
    1829             :     }
    1830             : 
    1831           0 :     ResetReading();
    1832             : 
    1833           0 :     return OGRERR_NONE;
    1834             : }
    1835             : 
    1836             : /*
    1837             :  * SetAttributeFilter()
    1838             :  */
    1839           0 : OGRErr OGRNGWLayer::SetAttributeFilter(const char *pszQuery)
    1840             : {
    1841           0 :     OGRErr eResult = OGRERR_NONE;
    1842           0 :     if (nullptr == pszQuery)
    1843             :     {
    1844           0 :         eResult = OGRLayer::SetAttributeFilter(pszQuery);
    1845           0 :         osWhere.clear();
    1846           0 :         bClientSideAttributeFilter = false;
    1847             :     }
    1848           0 :     else if (STARTS_WITH_CI(pszQuery,
    1849             :                             "NGW:"))  // Already formatted for NGW REST API
    1850             :     {
    1851           0 :         osWhere = pszQuery + strlen("NGW:");
    1852           0 :         bClientSideAttributeFilter = false;
    1853             :     }
    1854             :     else
    1855             :     {
    1856           0 :         eResult = OGRLayer::SetAttributeFilter(pszQuery);
    1857           0 :         if (eResult == OGRERR_NONE && m_poAttrQuery != nullptr)
    1858             :         {
    1859             :             swq_expr_node *poNode =
    1860           0 :                 reinterpret_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
    1861           0 :             std::string osWhereIn = TranslateSQLToFilter(poNode);
    1862           0 :             if (osWhereIn.empty())
    1863             :             {
    1864           0 :                 osWhere.clear();
    1865           0 :                 bClientSideAttributeFilter = true;
    1866           0 :                 CPLDebug(
    1867             :                     "NGW",
    1868             :                     "Attribute filter '%s' will be evaluated on client side.",
    1869             :                     pszQuery);
    1870             :             }
    1871             :             else
    1872             :             {
    1873           0 :                 bClientSideAttributeFilter = false;
    1874           0 :                 CPLDebug("NGW", "Attribute filter: %s", osWhereIn.c_str());
    1875           0 :                 osWhere = osWhereIn;
    1876             :             }
    1877             :         }
    1878             :     }
    1879             : 
    1880           0 :     ResetReading();
    1881           0 :     return eResult;
    1882             : }
    1883             : 
    1884             : /*
    1885             :  * SetSelectedFields()
    1886             :  */
    1887           0 : OGRErr OGRNGWLayer::SetSelectedFields(const std::set<std::string> &aosFields)
    1888             : {
    1889           0 :     CPLStringList aosIgnoreFields;
    1890           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
    1891             :     {
    1892           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1893           0 :         if (aosFields.find(poFieldDefn->GetNameRef()) != aosFields.end())
    1894             :         {
    1895           0 :             continue;
    1896             :         }
    1897           0 :         aosIgnoreFields.AddString(poFieldDefn->GetNameRef());
    1898             :     }
    1899           0 :     return SetIgnoredFields(aosIgnoreFields.List());
    1900             : }
    1901             : 
    1902             : /*
    1903             :  * Clone()
    1904             :  */
    1905           0 : OGRNGWLayer *OGRNGWLayer::Clone() const
    1906             : {
    1907           0 :     return new OGRNGWLayer(osResourceId, poDS, stPermissions,
    1908           0 :                            poFeatureDefn->Clone(), nFeatureCount, stExtent);
    1909             : }
    1910             : 
    1911             : /*
    1912             :  * GetNewFeaturesCount()
    1913             :  */
    1914           0 : GIntBig OGRNGWLayer::GetNewFeaturesCount() const
    1915             : {
    1916           0 :     if (soChangedIds.empty())
    1917             :     {
    1918           0 :         return 0;
    1919             :     }
    1920             : 
    1921           0 :     if (*soChangedIds.begin() >= 0)
    1922             :     {
    1923           0 :         return 0;
    1924             :     }
    1925             : 
    1926             :     // The lowest negative identifier equal new feature count
    1927           0 :     return *soChangedIds.begin() * -1;
    1928             : }

Generated by: LCOV version 1.14