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

Generated by: LCOV version 1.14