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 932 0.0 %
Date: 2025-02-20 10:14:44 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/desciption");
     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 oFieldCommnent;
    1004           0 :         oFieldCommnent.Add("id", oField.GetLong("id"));
    1005           0 :         oFieldCommnent.Add("label_field", oField.GetBool("label_field"));
    1006           0 :         oFieldCommnent.Add("grid_visibility",
    1007           0 :                            oField.GetBool("grid_visibility"));
    1008           0 :         oFieldCommnent.Add("text_search", oField.GetBool("text_search"));
    1009           0 :         oFieldDefn.SetComment(
    1010           0 :             oFieldCommnent.Format(CPLJSONObject::PrettyFormat::Plain));
    1011             : 
    1012             :         // Domain
    1013           0 :         auto nDomainID = oField.GetInteger("lookup_table/id", -1);
    1014           0 :         if (nDomainID != -1)
    1015             :         {
    1016           0 :             auto oDom = poDS->GetDomainByID(nDomainID);
    1017           0 :             auto pOgrDom = oDom.ToFieldDomain(eFieldtype);
    1018           0 :             if (pOgrDom != nullptr)
    1019             :             {
    1020           0 :                 oFieldDefn.SetDomainName(pOgrDom->GetName());
    1021             :             }
    1022             :         }
    1023             : 
    1024           0 :         poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1025             :     }
    1026             : 
    1027           0 :     OGRLayer::SetIgnoredFields(soIgnoredFieldsNames);
    1028           0 : }
    1029             : 
    1030             : /*
    1031             :  * GetMaxFeatureCount()
    1032             :  */
    1033           0 : GIntBig OGRNGWLayer::GetMaxFeatureCount(bool bForce)
    1034             : {
    1035           0 :     if (nFeatureCount < 0 || bForce)
    1036             :     {
    1037             :         CPLJSONObject oRoot =
    1038           0 :             LoadUrl(NGWAPI::GetFeatureCount(poDS->GetUrl(), osResourceId));
    1039           0 :         if (!oRoot.IsValid())
    1040             :         {
    1041           0 :             return nFeatureCount;
    1042             :         }
    1043             : 
    1044           0 :         nFeatureCount = oRoot.GetLong("total_count");
    1045           0 :         nFeatureCount += GetNewFeaturesCount();
    1046             :     }
    1047           0 :     return nFeatureCount;
    1048             : }
    1049             : 
    1050             : /*
    1051             :  * GetFeatureCount()
    1052             :  */
    1053           0 : GIntBig OGRNGWLayer::GetFeatureCount(int bForce)
    1054             : {
    1055           0 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
    1056             :     {
    1057           0 :         return GetMaxFeatureCount(CPL_TO_BOOL(bForce));
    1058             :     }
    1059             :     else
    1060             :     {
    1061           0 :         return OGRLayer::GetFeatureCount(bForce);
    1062             :     }
    1063             : }
    1064             : 
    1065             : /*
    1066             :  * IGetExtent()
    1067             :  */
    1068           0 : OGRErr OGRNGWLayer::IGetExtent(int /* iGeomField */, OGREnvelope *psExtent,
    1069             :                                bool bForce)
    1070             : {
    1071           0 :     if (!stExtent.IsInit() || CPL_TO_BOOL(bForce))
    1072             :     {
    1073           0 :         auto aosHTTPOptions = poDS->GetHeaders(false);
    1074           0 :         bool bResult = NGWAPI::GetExtent(poDS->GetUrl(), osResourceId,
    1075           0 :                                          aosHTTPOptions, 3857, stExtent);
    1076           0 :         if (!bResult)
    1077             :         {
    1078           0 :             return OGRERR_FAILURE;
    1079             :         }
    1080             :     }
    1081           0 :     *psExtent = stExtent;
    1082           0 :     return OGRERR_NONE;
    1083             : }
    1084             : 
    1085             : /*
    1086             :  * FetchPermissions()
    1087             :  */
    1088           0 : void OGRNGWLayer::FetchPermissions()
    1089             : {
    1090           0 :     if (bFetchedPermissions || osResourceId == "-1")
    1091             :     {
    1092           0 :         return;
    1093             :     }
    1094             : 
    1095           0 :     if (poDS->IsUpdateMode())
    1096             :     {
    1097           0 :         auto aosHTTPOptions = poDS->GetHeaders(false);
    1098             :         stPermissions = NGWAPI::CheckPermissions(
    1099           0 :             poDS->GetUrl(), osResourceId, aosHTTPOptions, poDS->IsUpdateMode());
    1100             :     }
    1101             :     else
    1102             :     {
    1103           0 :         stPermissions.bDataCanRead = true;
    1104           0 :         stPermissions.bResourceCanRead = true;
    1105           0 :         stPermissions.bDatastructCanRead = true;
    1106           0 :         stPermissions.bMetadataCanRead = true;
    1107             :     }
    1108           0 :     bFetchedPermissions = true;
    1109             : }
    1110             : 
    1111             : /*
    1112             :  * CreateField()
    1113             :  */
    1114           0 : OGRErr OGRNGWLayer::CreateField(const OGRFieldDefn *poField,
    1115             :                                 CPL_UNUSED int bApproxOK)
    1116             : {
    1117           0 :     CPLAssert(nullptr != poField);
    1118             : 
    1119           0 :     if (!CheckFieldNameUnique(poFeatureDefn, -1, poField->GetNameRef()))
    1120             :     {
    1121           0 :         return OGRERR_FAILURE;
    1122             :     }
    1123             : 
    1124           0 :     OGRFieldDefn oModFieldDefn(poField);
    1125           0 :     poFeatureDefn->AddFieldDefn(&oModFieldDefn);
    1126           0 :     bNeedSyncStructure = true;
    1127           0 :     return OGRERR_NONE;
    1128             : }
    1129             : 
    1130             : /*
    1131             :  * DeleteField()
    1132             :  */
    1133           0 : OGRErr OGRNGWLayer::DeleteField(int iField)
    1134             : {
    1135           0 :     if (osResourceId != "-1")
    1136             :     {
    1137           0 :         auto poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1138           0 :         if (poFieldDefn == nullptr)
    1139             :         {
    1140           0 :             return OGRERR_FAILURE;
    1141             :         }
    1142             :         // Get field NGW ID
    1143           0 :         CPLJSONDocument oComment;
    1144           0 :         if (oComment.LoadMemory(poFieldDefn->GetComment()))
    1145             :         {
    1146           0 :             auto oRoot = oComment.GetRoot();
    1147           0 :             auto nNGWID = oRoot.GetLong("id", -1);
    1148           0 :             if (nNGWID != -1)
    1149             :             {
    1150           0 :                 soDeletedFieldsIds.insert(nNGWID);
    1151             :             }
    1152             :         }
    1153             :     }
    1154           0 :     return poFeatureDefn->DeleteFieldDefn(iField);
    1155             : }
    1156             : 
    1157             : /*
    1158             :  * ReorderFields()
    1159             :  */
    1160           0 : OGRErr OGRNGWLayer::ReorderFields(int *panMap)
    1161             : {
    1162           0 :     return poFeatureDefn->ReorderFieldDefns(panMap);
    1163             : }
    1164             : 
    1165             : /*
    1166             :  * AlterFieldDefn()
    1167             :  */
    1168           0 : OGRErr OGRNGWLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
    1169             :                                    int nFlagsIn)
    1170             : {
    1171           0 :     CPLDebug("NGW", "AlterFieldDefn fld #%d", iField);
    1172           0 :     CPLAssert(nullptr != poNewFieldDefn);
    1173             : 
    1174           0 :     if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
    1175             :     {
    1176           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    1177           0 :         return OGRERR_FAILURE;
    1178             :     }
    1179             : 
    1180           0 :     FetchPermissions();
    1181           0 :     if (!stPermissions.bDatastructCanWrite)
    1182             :     {
    1183           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Insufficient permissions");
    1184           0 :         return OGRERR_FAILURE;
    1185             :     }
    1186             : 
    1187           0 :     if (!poDS->IsUpdateMode())
    1188             :     {
    1189           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Layer is read only");
    1190           0 :         return OGRERR_FAILURE;
    1191             :     }
    1192             : 
    1193           0 :     OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1194             :     // Check new field name is not equal for another fields.
    1195           0 :     if (!CheckFieldNameUnique(poFeatureDefn, iField,
    1196             :                               poNewFieldDefn->GetNameRef()))
    1197             :     {
    1198           0 :         return OGRERR_FAILURE;
    1199             :     }
    1200             : 
    1201           0 :     if (osResourceId == "-1")  // Can full alter field only on new layers
    1202             :                                // (not synced with server).
    1203             :     {
    1204             :         // Field name 'id' forbidden.
    1205           0 :         OGRFieldDefn oModFieldDefn(poNewFieldDefn);
    1206             : 
    1207           0 :         poFieldDefn->SetName(oModFieldDefn.GetNameRef());
    1208           0 :         poFieldDefn->SetAlternativeName(oModFieldDefn.GetAlternativeNameRef());
    1209           0 :         poFieldDefn->SetComment(oModFieldDefn.GetComment());
    1210           0 :         poFieldDefn->SetType(oModFieldDefn.GetType());
    1211           0 :         poFieldDefn->SetSubType(oModFieldDefn.GetSubType());
    1212           0 :         poFieldDefn->SetWidth(oModFieldDefn.GetWidth());
    1213           0 :         poFieldDefn->SetPrecision(oModFieldDefn.GetPrecision());
    1214             :     }
    1215             :     else
    1216             :     {
    1217           0 :         if (nFlagsIn & ALTER_NAME_FLAG)
    1218             :         {
    1219           0 :             bNeedSyncStructure = true;
    1220           0 :             poFieldDefn->SetName(poNewFieldDefn->GetNameRef());
    1221             :         }
    1222           0 :         if (nFlagsIn & ALTER_DOMAIN_FLAG)
    1223             :         {
    1224           0 :             bNeedSyncStructure = true;
    1225           0 :             poFieldDefn->SetDomainName(poNewFieldDefn->GetDomainName());
    1226             :         }
    1227           0 :         if (nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG)
    1228             :         {
    1229           0 :             bNeedSyncStructure = true;
    1230           0 :             poFieldDefn->SetAlternativeName(
    1231             :                 poNewFieldDefn->GetAlternativeNameRef());
    1232             :         }
    1233           0 :         if (nFlagsIn & ALTER_COMMENT_FLAG)
    1234             :         {
    1235           0 :             bNeedSyncStructure = true;
    1236           0 :             poFieldDefn->SetComment(poNewFieldDefn->GetComment());
    1237             :         }
    1238             :     }
    1239           0 :     ResetReading();
    1240           0 :     return OGRERR_NONE;
    1241             : }
    1242             : 
    1243             : /*
    1244             :  * SetMetadata()
    1245             :  */
    1246           0 : CPLErr OGRNGWLayer::SetMetadata(char **papszMetadata, const char *pszDomain)
    1247             : {
    1248           0 :     bNeedSyncStructure = true;
    1249           0 :     return OGRLayer::SetMetadata(papszMetadata, pszDomain);
    1250             : }
    1251             : 
    1252             : /*
    1253             :  * SetMetadataItem()
    1254             :  */
    1255           0 : CPLErr OGRNGWLayer::SetMetadataItem(const char *pszName, const char *pszValue,
    1256             :                                     const char *pszDomain)
    1257             : {
    1258           0 :     bNeedSyncStructure = true;
    1259           0 :     return OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
    1260             : }
    1261             : 
    1262             : /*
    1263             :  * CreateNGWResourceJson()
    1264             :  */
    1265           0 : std::string OGRNGWLayer::CreateNGWResourceJson()
    1266             : {
    1267           0 :     CPLJSONObject oResourceJson;
    1268             : 
    1269             :     // Add resource json item.
    1270           0 :     CPLJSONObject oResource("resource", oResourceJson);
    1271           0 :     oResource.Add("cls", "vector_layer");
    1272           0 :     CPLJSONObject oResourceParent("parent", oResource);
    1273           0 :     oResourceParent.Add("id",
    1274           0 :                         static_cast<GIntBig>(std::stol(poDS->GetResourceId())));
    1275           0 :     oResource.Add("display_name", GetName());
    1276           0 :     const char *pszKeyName = GetMetadataItem("keyname");
    1277           0 :     if (pszKeyName)
    1278             :     {
    1279           0 :         oResource.Add("keyname", pszKeyName);
    1280             :     }
    1281           0 :     const char *pszDescription = GetMetadataItem("description");
    1282           0 :     if (pszDescription)
    1283             :     {
    1284           0 :         oResource.Add("description", pszDescription);
    1285             :     }
    1286             : 
    1287             :     // Add vector_layer json item.
    1288           0 :     CPLJSONObject oVectorLayer("vector_layer", oResourceJson);
    1289           0 :     CPLJSONObject oVectorLayerSrs("srs", oVectorLayer);
    1290             : 
    1291           0 :     OGRSpatialReference *poSpatialRef = GetSpatialRef();
    1292           0 :     int nEPSG = 3857;
    1293           0 :     if (poSpatialRef)
    1294             :     {
    1295           0 :         poSpatialRef->AutoIdentifyEPSG();
    1296           0 :         const char *pszEPSG = poSpatialRef->GetAuthorityCode(nullptr);
    1297           0 :         if (pszEPSG != nullptr)
    1298             :         {
    1299           0 :             nEPSG = atoi(pszEPSG);
    1300             :         }
    1301             :     }
    1302           0 :     oVectorLayerSrs.Add("id", nEPSG);
    1303             :     // In OGRNGWDataset::ICreateLayer we limit supported geometry types.
    1304           0 :     oVectorLayer.Add("geometry_type",
    1305           0 :                      NGWAPI::OGRGeomTypeToNGWGeomType(GetGeomType()));
    1306             : 
    1307             :     // Fill fields
    1308           0 :     CPLJSONArray oVectorLayerFields;
    1309           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
    1310             :     {
    1311           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1312           0 :         CPLJSONObject oField;
    1313           0 :         auto const &sComment = poFieldDefn->GetComment();
    1314           0 :         if (!sComment.empty())
    1315             :         {
    1316           0 :             CPLJSONDocument oComment;
    1317           0 :             if (oComment.LoadMemory(sComment))
    1318             :             {
    1319           0 :                 oField = oComment.GetRoot();
    1320             :             }
    1321             :         }
    1322           0 :         oField.Add("keyname", poFieldDefn->GetNameRef());
    1323           0 :         oField.Add("datatype",
    1324           0 :                    NGWAPI::OGRFieldTypeToNGWFieldType(poFieldDefn->GetType()));
    1325           0 :         std::string osFieldAliasName = poFieldDefn->GetAlternativeNameRef();
    1326             :         // Get alias from metadata.
    1327           0 :         if (osFieldAliasName.empty())
    1328             :         {
    1329           0 :             osFieldAliasName = poFieldDefn->GetNameRef();
    1330             :         }
    1331           0 :         oField.Add("display_name", osFieldAliasName);
    1332             : 
    1333           0 :         if (poFieldDefn->GetDomainName().empty())
    1334             :         {
    1335           0 :             oField.AddNull("lookup_table");
    1336             :         }
    1337             :         else
    1338             :         {
    1339           0 :             CPLJSONObject oFieldDom("lookup_table", oField);
    1340           0 :             auto nDomId = poDS->GetDomainIdByName(poFieldDefn->GetDomainName());
    1341           0 :             oFieldDom.Add("id", nDomId);
    1342             :         }
    1343             : 
    1344           0 :         oVectorLayerFields.Add(oField);
    1345             :     }
    1346           0 :     for (auto nFieldID : soDeletedFieldsIds)
    1347             :     {
    1348           0 :         CPLJSONObject oField;
    1349           0 :         oField.Add("id", nFieldID);
    1350           0 :         oField.Add("delete", true);
    1351           0 :         oVectorLayerFields.Add(oField);
    1352             :     }
    1353             : 
    1354           0 :     CPLJSONObject oFeatureLayer("feature_layer", oResourceJson);
    1355           0 :     oFeatureLayer.Add("fields", oVectorLayerFields);
    1356             : 
    1357             :     // Add resmeta json item.
    1358           0 :     NGWAPI::FillResmeta(oResourceJson, GetMetadata("NGW"));
    1359             : 
    1360           0 :     return oResourceJson.Format(CPLJSONObject::PrettyFormat::Plain);
    1361             : }
    1362             : 
    1363             : /*
    1364             :  * SyncFeatures()
    1365             :  */
    1366           0 : OGRErr OGRNGWLayer::SyncFeatures()
    1367             : {
    1368           0 :     if (!bNeedSyncData)
    1369             :     {
    1370           0 :         return OGRERR_NONE;
    1371             :     }
    1372             : 
    1373           0 :     CPLJSONArray oFeatureJsonArray;
    1374           0 :     std::vector<GIntBig> aoPatchedFIDs;
    1375           0 :     for (GIntBig nFID : soChangedIds)
    1376             :     {
    1377           0 :         if (moFeatures[nFID] != nullptr)
    1378             :         {
    1379           0 :             oFeatureJsonArray.Add(FeatureToJson(moFeatures[nFID]));
    1380           0 :             aoPatchedFIDs.push_back(nFID);
    1381             :         }
    1382             :     }
    1383             : 
    1384           0 :     if (!aoPatchedFIDs.empty())
    1385             :     {
    1386             :         auto osIDs = NGWAPI::PatchFeatures(
    1387           0 :             poDS->GetUrl(), osResourceId,
    1388           0 :             oFeatureJsonArray.Format(CPLJSONObject::PrettyFormat::Plain),
    1389           0 :             poDS->GetHeaders());
    1390           0 :         if (!osIDs.empty())
    1391             :         {
    1392           0 :             bNeedSyncData = false;
    1393           0 :             nFeatureCount += GetNewFeaturesCount();
    1394           0 :             soChangedIds.clear();
    1395           0 :             if (osIDs.size() !=
    1396           0 :                 aoPatchedFIDs.size())  // Expected equal identifier count.
    1397             :             {
    1398           0 :                 CPLDebug("NGW", "Patched feature count is not equal. Reload "
    1399             :                                 "features from server.");
    1400           0 :                 FreeMap(moFeatures);
    1401             :             }
    1402             :             else  // Just update identifiers.
    1403             :             {
    1404           0 :                 int nCounter = 0;
    1405           0 :                 for (GIntBig nFID : aoPatchedFIDs)
    1406             :                 {
    1407           0 :                     GIntBig nNewFID = osIDs[nCounter++];
    1408           0 :                     OGRFeature *poFeature = moFeatures[nFID];
    1409           0 :                     poFeature->SetFID(nNewFID);
    1410           0 :                     moFeatures.erase(nFID);
    1411           0 :                     moFeatures[nNewFID] = poFeature;
    1412             :                 }
    1413             :             }
    1414             :         }
    1415             :         else
    1416             :         {
    1417             :             // Error message should set in NGWAPI::PatchFeatures function.
    1418           0 :             if (CPLGetLastErrorNo() != 0)
    1419             :             {
    1420           0 :                 return OGRERR_FAILURE;
    1421             :             }
    1422             :         }
    1423             :     }
    1424           0 :     return OGRERR_NONE;
    1425             : }
    1426             : 
    1427             : /*
    1428             :  * SyncToDisk()
    1429             :  */
    1430           0 : OGRErr OGRNGWLayer::SyncToDisk()
    1431             : {
    1432           0 :     if (osResourceId == "-1")  // Create vector layer at NextGIS Web.
    1433             :     {
    1434           0 :         bNeedSyncData = !moFeatures.empty();
    1435             :         std::string osResourceIdInt = NGWAPI::CreateResource(
    1436           0 :             poDS->GetUrl(), CreateNGWResourceJson(), poDS->GetHeaders());
    1437           0 :         if (osResourceIdInt == "-1")
    1438             :         {
    1439             :             // Error message should set in CreateResource.
    1440           0 :             return OGRERR_FAILURE;
    1441             :         }
    1442           0 :         osResourceId = std::move(osResourceIdInt);
    1443           0 :         OGRLayer::SetMetadataItem("id", osResourceId.c_str());
    1444           0 :         FetchPermissions();
    1445           0 :         bNeedSyncStructure = false;
    1446             :     }
    1447           0 :     else if (bNeedSyncStructure)  // Update vector layer at NextGIS Web.
    1448             :     {
    1449           0 :         if (!NGWAPI::UpdateResource(poDS->GetUrl(), GetResourceId(),
    1450           0 :                                     CreateNGWResourceJson(),
    1451           0 :                                     poDS->GetHeaders()))
    1452             :         {
    1453             :             // Error message should set in UpdateResource.
    1454           0 :             return OGRERR_FAILURE;
    1455             :         }
    1456           0 :         bNeedSyncStructure = false;
    1457           0 :         soDeletedFieldsIds.clear();
    1458             :     }
    1459             : 
    1460           0 :     auto oRoot = LoadUrl(NGWAPI::GetResourceURL(poDS->GetUrl(), osResourceId));
    1461           0 :     if (!oRoot.IsValid())
    1462             :     {
    1463           0 :         return OGRERR_FAILURE;
    1464             :     }
    1465           0 :     Fill(oRoot);
    1466             : 
    1467             :     // Update NGW layer state
    1468           0 :     FetchPermissions();
    1469             : 
    1470             :     // Sync features.
    1471           0 :     return SyncFeatures();
    1472             : }
    1473             : 
    1474             : /*
    1475             :  * DeleteFeature()
    1476             :  */
    1477           0 : OGRErr OGRNGWLayer::DeleteFeature(GIntBig nFID)
    1478             : {
    1479           0 :     CPLErrorReset();
    1480           0 :     if (nFID < 0)
    1481             :     {
    1482           0 :         if (moFeatures[nFID] != nullptr)
    1483             :         {
    1484           0 :             OGRFeature::DestroyFeature(moFeatures[nFID]);
    1485           0 :             moFeatures[nFID] = nullptr;
    1486           0 :             nFeatureCount--;
    1487           0 :             soChangedIds.erase(nFID);
    1488           0 :             return OGRERR_NONE;
    1489             :         }
    1490           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1491             :                  "Feature with id " CPL_FRMT_GIB " not found.", nFID);
    1492           0 :         return OGRERR_FAILURE;
    1493             :     }
    1494             :     else
    1495             :     {
    1496           0 :         FetchPermissions();
    1497           0 :         if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
    1498             :         {
    1499           0 :             bool bResult = NGWAPI::DeleteFeature(poDS->GetUrl(), osResourceId,
    1500           0 :                                                  std::to_string(nFID),
    1501           0 :                                                  poDS->GetHeaders(false));
    1502           0 :             if (bResult)
    1503             :             {
    1504           0 :                 if (moFeatures[nFID] != nullptr)
    1505             :                 {
    1506           0 :                     OGRFeature::DestroyFeature(moFeatures[nFID]);
    1507           0 :                     moFeatures[nFID] = nullptr;
    1508             :                 }
    1509           0 :                 nFeatureCount--;
    1510           0 :                 soChangedIds.erase(nFID);
    1511           0 :                 return OGRERR_NONE;
    1512             :             }
    1513           0 :             return OGRERR_FAILURE;
    1514             :         }
    1515           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1516             :                  "Delete feature " CPL_FRMT_GIB " operation is not permitted.",
    1517             :                  nFID);
    1518           0 :         return OGRERR_FAILURE;
    1519             :     }
    1520             : }
    1521             : 
    1522             : /*
    1523             :  * DeleteFeatures()
    1524             :  */
    1525           0 : OGRErr OGRNGWLayer::DeleteFeatures(const std::vector<GIntBig> &vFeaturesID)
    1526             : {
    1527           0 :     CPLErrorReset();
    1528             : 
    1529             :     // Try to delete local features (not synchronized with NGW)
    1530           0 :     std::vector<GIntBig> vFeaturesIDInt(vFeaturesID);
    1531           0 :     for (size_t i = 0; i < vFeaturesIDInt.size(); i++)
    1532             :     {
    1533           0 :         auto nFID = vFeaturesIDInt[i];
    1534           0 :         if (nFID < 0)
    1535             :         {
    1536           0 :             if (moFeatures[nFID] != nullptr)
    1537             :             {
    1538           0 :                 OGRFeature::DestroyFeature(moFeatures[nFID]);
    1539           0 :                 moFeatures[nFID] = nullptr;
    1540           0 :                 nFeatureCount--;
    1541           0 :                 soChangedIds.erase(nFID);
    1542           0 :                 vFeaturesIDInt.erase(vFeaturesIDInt.begin() + i);
    1543             :             }
    1544             :         }
    1545             :     }
    1546             : 
    1547           0 :     FetchPermissions();
    1548           0 :     if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
    1549             :     {
    1550           0 :         bool bResult = NGWAPI::DeleteFeatures(
    1551           0 :             poDS->GetUrl(), osResourceId,
    1552           0 :             FeaturesIDToJsonString(vFeaturesIDInt), poDS->GetHeaders(false));
    1553           0 :         if (bResult)
    1554             :         {
    1555           0 :             for (GIntBig nFID : vFeaturesIDInt)
    1556             :             {
    1557           0 :                 if (moFeatures[nFID] != nullptr)
    1558             :                 {
    1559           0 :                     OGRFeature::DestroyFeature(moFeatures[nFID]);
    1560           0 :                     moFeatures[nFID] = nullptr;
    1561           0 :                     nFeatureCount--;
    1562           0 :                     soChangedIds.erase(nFID);
    1563             :                 }
    1564           0 :                 nFeatureCount--;
    1565           0 :                 soChangedIds.erase(nFID);
    1566             :             }
    1567           0 :             return OGRERR_NONE;
    1568             :         }
    1569             :     }
    1570           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Delete features failed");
    1571           0 :     return OGRERR_FAILURE;
    1572             : }
    1573             : 
    1574             : /*
    1575             :  * DeleteAllFeatures()
    1576             :  */
    1577           0 : bool OGRNGWLayer::DeleteAllFeatures()
    1578             : {
    1579           0 :     if (osResourceId == "-1")
    1580             :     {
    1581           0 :         soChangedIds.clear();
    1582           0 :         bNeedSyncData = false;
    1583           0 :         FreeFeaturesCache();
    1584           0 :         nFeatureCount = 0;
    1585           0 :         return true;
    1586             :     }
    1587             :     else
    1588             :     {
    1589           0 :         FetchPermissions();
    1590           0 :         if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
    1591             :         {
    1592           0 :             bool bResult = NGWAPI::DeleteFeature(poDS->GetUrl(), osResourceId,
    1593           0 :                                                  "", poDS->GetHeaders(false));
    1594           0 :             if (bResult)
    1595             :             {
    1596           0 :                 soChangedIds.clear();
    1597           0 :                 bNeedSyncData = false;
    1598           0 :                 FreeFeaturesCache();
    1599           0 :                 nFeatureCount = 0;
    1600             :             }
    1601           0 :             return bResult;
    1602             :         }
    1603             :     }
    1604           0 :     CPLErrorReset();
    1605           0 :     CPLError(CE_Failure, CPLE_AppDefined,
    1606             :              "Delete all features operation is not permitted.");
    1607           0 :     return false;
    1608             : }
    1609             : 
    1610             : /*
    1611             :  * ISetFeature()
    1612             :  */
    1613           0 : OGRErr OGRNGWLayer::ISetFeature(OGRFeature *poFeature)
    1614             : {
    1615           0 :     if (poDS->IsBatchMode())
    1616             :     {
    1617           0 :         if (moFeatures[poFeature->GetFID()] == nullptr)
    1618             :         {
    1619           0 :             if (poFeature->GetFID() < 0)
    1620             :             {
    1621           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1622             :                          "Cannot update not existing feature " CPL_FRMT_GIB,
    1623             :                          poFeature->GetFID());
    1624           0 :                 return OGRERR_FAILURE;
    1625             :             }
    1626             :         }
    1627             :         else
    1628             :         {
    1629           0 :             OGRFeature::DestroyFeature(moFeatures[poFeature->GetFID()]);
    1630             :         }
    1631           0 :         moFeatures[poFeature->GetFID()] = poFeature->Clone();
    1632           0 :         soChangedIds.insert(poFeature->GetFID());
    1633             : 
    1634           0 :         if (soChangedIds.size() > static_cast<size_t>(poDS->GetBatchSize()))
    1635             :         {
    1636           0 :             bNeedSyncData = true;
    1637             :         }
    1638             : 
    1639           0 :         return SyncToDisk();
    1640             :     }
    1641             :     else
    1642             :     {
    1643             :         OGRErr eResult =
    1644           0 :             SyncToDisk();  // For create new layer if not yet created.
    1645           0 :         if (eResult == OGRERR_NONE)
    1646             :         {
    1647           0 :             if (poFeature->GetFID() < 0)
    1648             :             {
    1649           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1650             :                          "Cannot update not existing feature " CPL_FRMT_GIB,
    1651             :                          poFeature->GetFID());
    1652           0 :                 return OGRERR_FAILURE;
    1653             :             }
    1654             : 
    1655           0 :             bool bResult = NGWAPI::UpdateFeature(
    1656           0 :                 poDS->GetUrl(), osResourceId,
    1657           0 :                 std::to_string(poFeature->GetFID()),
    1658           0 :                 FeatureToJsonString(poFeature), poDS->GetHeaders());
    1659           0 :             if (bResult)
    1660             :             {
    1661           0 :                 CPLDebug("NGW", "ISetFeature with FID " CPL_FRMT_GIB,
    1662             :                          poFeature->GetFID());
    1663             : 
    1664           0 :                 OGRFeature::DestroyFeature(moFeatures[poFeature->GetFID()]);
    1665           0 :                 moFeatures[poFeature->GetFID()] = poFeature->Clone();
    1666           0 :                 return OGRERR_NONE;
    1667             :             }
    1668             :             else
    1669             :             {
    1670             :                 // CPLError should be set in NGWAPI::UpdateFeature.
    1671           0 :                 return OGRERR_FAILURE;
    1672             :             }
    1673             :         }
    1674             :         else
    1675             :         {
    1676           0 :             return eResult;
    1677             :         }
    1678             :     }
    1679             : }
    1680             : 
    1681             : /*
    1682             :  * ICreateFeature()
    1683             :  */
    1684           0 : OGRErr OGRNGWLayer::ICreateFeature(OGRFeature *poFeature)
    1685             : {
    1686           0 :     if (poDS->IsBatchMode())
    1687             :     {
    1688           0 :         GIntBig nNewFID = -1;
    1689           0 :         if (!soChangedIds.empty())
    1690             :         {
    1691           0 :             nNewFID = *(soChangedIds.begin()) - 1;
    1692             :         }
    1693           0 :         poFeature->SetFID(nNewFID);
    1694           0 :         moFeatures[nNewFID] = poFeature->Clone();
    1695           0 :         soChangedIds.insert(nNewFID);
    1696           0 :         nFeatureCount++;
    1697             : 
    1698           0 :         if (soChangedIds.size() > static_cast<size_t>(poDS->GetBatchSize()))
    1699             :         {
    1700           0 :             bNeedSyncData = true;
    1701             :         }
    1702             : 
    1703           0 :         return SyncToDisk();
    1704             :     }
    1705             :     else
    1706             :     {
    1707             :         OGRErr eResult =
    1708           0 :             SyncToDisk();  // For create new layer if not yet created.
    1709           0 :         if (eResult == OGRERR_NONE)
    1710             :         {
    1711           0 :             GIntBig nNewFID = NGWAPI::CreateFeature(
    1712           0 :                 poDS->GetUrl(), osResourceId, FeatureToJsonString(poFeature),
    1713           0 :                 poDS->GetHeaders());
    1714           0 :             if (nNewFID >= 0)
    1715             :             {
    1716           0 :                 poFeature->SetFID(nNewFID);
    1717           0 :                 moFeatures[nNewFID] = poFeature->Clone();
    1718           0 :                 nFeatureCount++;
    1719           0 :                 return OGRERR_NONE;
    1720             :             }
    1721             :             else
    1722             :             {
    1723             :                 // CPLError should be set in NGWAPI::CreateFeature.
    1724           0 :                 return OGRERR_FAILURE;
    1725             :             }
    1726             :         }
    1727             :         else
    1728             :         {
    1729           0 :             return eResult;
    1730             :         }
    1731             :     }
    1732             : }
    1733             : 
    1734             : /*
    1735             :  * SetIgnoredFields()
    1736             :  */
    1737           0 : OGRErr OGRNGWLayer::SetIgnoredFields(CSLConstList papszFields)
    1738             : {
    1739           0 :     OGRErr eResult = OGRLayer::SetIgnoredFields(papszFields);
    1740           0 :     if (eResult != OGRERR_NONE)
    1741             :     {
    1742           0 :         return eResult;
    1743             :     }
    1744             : 
    1745           0 :     osFields.clear();
    1746             : 
    1747           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
    1748             :     {
    1749           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1750           0 :         if (poFieldDefn->IsIgnored())
    1751             :         {
    1752           0 :             CPLDebug("NGW", "SetIgnoredFields: Field '%s' set to ignored",
    1753             :                      poFieldDefn->GetNameRef());
    1754           0 :             continue;
    1755             :         }
    1756             : 
    1757           0 :         if (osFields.empty())
    1758             :         {
    1759           0 :             osFields = poFieldDefn->GetNameRef();
    1760             :         }
    1761             :         else
    1762             :         {
    1763           0 :             osFields += "," + std::string(poFieldDefn->GetNameRef());
    1764             :         }
    1765             :     }
    1766             : 
    1767             :     // Encode osFields string for URL
    1768           0 :     if (!osFields.empty())
    1769             :     {
    1770           0 :         char *pszValuesEncoded = CPLEscapeString(
    1771           0 :             osFields.c_str(), static_cast<int>(osFields.size()), CPLES_URL);
    1772           0 :         osFields = pszValuesEncoded;
    1773           0 :         CPLFree(pszValuesEncoded);
    1774             :     }
    1775             : 
    1776           0 :     CPLDebug("NGW", "SetIgnoredFields: NGW fields filter set to '%s'",
    1777             :              osFields.c_str());
    1778             : 
    1779           0 :     ResetReading();
    1780           0 :     return OGRERR_NONE;
    1781             : }
    1782             : 
    1783             : /*
    1784             :  * ISetSpatialFilter()
    1785             :  */
    1786           0 : OGRErr OGRNGWLayer::ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom)
    1787             : {
    1788           0 :     OGRLayer::ISetSpatialFilter(iGeomField, poGeom);
    1789             : 
    1790           0 :     if (nullptr == m_poFilterGeom)
    1791             :     {
    1792           0 :         CPLDebug("NGW", "Spatial filter unset");
    1793           0 :         osSpatialFilter.clear();
    1794             :     }
    1795             :     else
    1796             :     {
    1797           0 :         OGREnvelope sEnvelope;
    1798           0 :         m_poFilterGeom->getEnvelope(&sEnvelope);
    1799             : 
    1800           0 :         OGREnvelope sBigEnvelope;
    1801           0 :         sBigEnvelope.MinX = -40000000.0;
    1802           0 :         sBigEnvelope.MinY = -40000000.0;
    1803           0 :         sBigEnvelope.MaxX = 40000000.0;
    1804           0 :         sBigEnvelope.MaxY = 40000000.0;
    1805             : 
    1806             :         // Case for infinity filter
    1807           0 :         if (sEnvelope.Contains(sBigEnvelope) == TRUE)
    1808             :         {
    1809           0 :             CPLDebug("NGW", "Spatial filter unset as filter envelope covers "
    1810             :                             "whole features.");
    1811           0 :             osSpatialFilter.clear();
    1812             :         }
    1813             :         else
    1814             :         {
    1815           0 :             if (sEnvelope.MinX == sEnvelope.MaxX &&
    1816           0 :                 sEnvelope.MinY == sEnvelope.MaxY)
    1817             :             {
    1818           0 :                 OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
    1819           0 :                 InstallFilter(&p);
    1820             :             }
    1821             : 
    1822           0 :             osSpatialFilter = OGRGeometryToWKT(m_poFilterGeom);
    1823           0 :             CPLDebug("NGW", "Spatial filter: %s", osSpatialFilter.c_str());
    1824           0 :             char *pszSpatFilterEncoded = CPLEscapeString(
    1825             :                 osSpatialFilter.c_str(),
    1826           0 :                 static_cast<int>(osSpatialFilter.size()), CPLES_URL);
    1827           0 :             osSpatialFilter = pszSpatFilterEncoded;
    1828           0 :             CPLFree(pszSpatFilterEncoded);
    1829             :         }
    1830             :     }
    1831             : 
    1832           0 :     ResetReading();
    1833             : 
    1834           0 :     return OGRERR_NONE;
    1835             : }
    1836             : 
    1837             : /*
    1838             :  * SetAttributeFilter()
    1839             :  */
    1840           0 : OGRErr OGRNGWLayer::SetAttributeFilter(const char *pszQuery)
    1841             : {
    1842           0 :     OGRErr eResult = OGRERR_NONE;
    1843           0 :     if (nullptr == pszQuery)
    1844             :     {
    1845           0 :         eResult = OGRLayer::SetAttributeFilter(pszQuery);
    1846           0 :         osWhere.clear();
    1847           0 :         bClientSideAttributeFilter = false;
    1848             :     }
    1849           0 :     else if (STARTS_WITH_CI(pszQuery,
    1850             :                             "NGW:"))  // Already formatted for NGW REST API
    1851             :     {
    1852           0 :         osWhere = pszQuery + strlen("NGW:");
    1853           0 :         bClientSideAttributeFilter = false;
    1854             :     }
    1855             :     else
    1856             :     {
    1857           0 :         eResult = OGRLayer::SetAttributeFilter(pszQuery);
    1858           0 :         if (eResult == OGRERR_NONE && m_poAttrQuery != nullptr)
    1859             :         {
    1860             :             swq_expr_node *poNode =
    1861           0 :                 reinterpret_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
    1862           0 :             std::string osWhereIn = TranslateSQLToFilter(poNode);
    1863           0 :             if (osWhereIn.empty())
    1864             :             {
    1865           0 :                 osWhere.clear();
    1866           0 :                 bClientSideAttributeFilter = true;
    1867           0 :                 CPLDebug(
    1868             :                     "NGW",
    1869             :                     "Attribute filter '%s' will be evaluated on client side.",
    1870             :                     pszQuery);
    1871             :             }
    1872             :             else
    1873             :             {
    1874           0 :                 bClientSideAttributeFilter = false;
    1875           0 :                 CPLDebug("NGW", "Attribute filter: %s", osWhereIn.c_str());
    1876           0 :                 osWhere = osWhereIn;
    1877             :             }
    1878             :         }
    1879             :     }
    1880             : 
    1881           0 :     ResetReading();
    1882           0 :     return eResult;
    1883             : }
    1884             : 
    1885             : /*
    1886             :  * SetSelectedFields()
    1887             :  */
    1888           0 : OGRErr OGRNGWLayer::SetSelectedFields(const std::set<std::string> &aosFields)
    1889             : {
    1890           0 :     CPLStringList aosIgnoreFields;
    1891           0 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
    1892             :     {
    1893           0 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1894           0 :         if (aosFields.find(poFieldDefn->GetNameRef()) != aosFields.end())
    1895             :         {
    1896           0 :             continue;
    1897             :         }
    1898           0 :         aosIgnoreFields.AddString(poFieldDefn->GetNameRef());
    1899             :     }
    1900           0 :     return SetIgnoredFields(aosIgnoreFields.List());
    1901             : }
    1902             : 
    1903             : /*
    1904             :  * Clone()
    1905             :  */
    1906           0 : OGRNGWLayer *OGRNGWLayer::Clone() const
    1907             : {
    1908           0 :     return new OGRNGWLayer(osResourceId, poDS, stPermissions,
    1909           0 :                            poFeatureDefn->Clone(), nFeatureCount, stExtent);
    1910             : }
    1911             : 
    1912             : /*
    1913             :  * GetNewFeaturesCount()
    1914             :  */
    1915           0 : GIntBig OGRNGWLayer::GetNewFeaturesCount() const
    1916             : {
    1917           0 :     if (soChangedIds.empty())
    1918             :     {
    1919           0 :         return 0;
    1920             :     }
    1921             : 
    1922           0 :     if (*soChangedIds.begin() >= 0)
    1923             :     {
    1924           0 :         return 0;
    1925             :     }
    1926             : 
    1927             :     // The lowest negative identifier equal new feature count
    1928           0 :     return *soChangedIds.begin() * -1;
    1929             : }

Generated by: LCOV version 1.14