LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/ngw - gdalngwdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 813 0.0 %
Date: 2025-02-18 14:19:29 Functions: 0 43 0.0 %

          Line data    Source code
       1             : /*******************************************************************************
       2             :  *  Project: NextGIS Web Driver
       3             :  *  Purpose: Implements NextGIS Web Driver
       4             :  *  Author: Dmitry Baryshnikov, dmitry.baryshnikov@nextgis.com
       5             :  *  Language: C++
       6             :  *******************************************************************************
       7             :  *  The MIT License (MIT)
       8             :  *
       9             :  *  Copyright (c) 2018-2025, NextGIS <info@nextgis.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  *******************************************************************************/
      13             : 
      14             : #include "ogr_ngw.h"
      15             : 
      16             : #include "cpl_http.h"
      17             : #include "gdal_proxy.h"
      18             : 
      19             : #include <limits>
      20             : 
      21             : class NGWWrapperRasterBand : public GDALProxyRasterBand
      22             : {
      23             :     GDALRasterBand *poBaseBand;
      24             : 
      25             :   protected:
      26             :     virtual GDALRasterBand *
      27           0 :     RefUnderlyingRasterBand(bool /*bForceOpen*/) const override
      28             :     {
      29           0 :         return poBaseBand;
      30             :     }
      31             : 
      32             :   public:
      33           0 :     explicit NGWWrapperRasterBand(GDALRasterBand *poBaseBandIn)
      34           0 :         : poBaseBand(poBaseBandIn)
      35             :     {
      36           0 :         eDataType = poBaseBand->GetRasterDataType();
      37           0 :         poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
      38           0 :     }
      39             : 
      40           0 :     virtual ~NGWWrapperRasterBand()
      41           0 :     {
      42           0 :     }
      43             : };
      44             : 
      45           0 : static const char *FormGDALTMSConnectionString(const std::string &osUrl,
      46             :                                                const std::string &osResourceId,
      47             :                                                int nEPSG, int nCacheExpires,
      48             :                                                int nCacheMaxSize)
      49             : {
      50           0 :     std::string osRasterUrl = NGWAPI::GetTMSURL(osUrl, osResourceId);
      51           0 :     char *pszRasterUrl = CPLEscapeString(osRasterUrl.c_str(), -1, CPLES_XML);
      52             :     const char *pszConnStr =
      53           0 :         CPLSPrintf("<GDAL_WMS><Service name=\"TMS\">"
      54             :                    "<ServerUrl>%s</ServerUrl></Service><DataWindow>"
      55             :                    "<UpperLeftX>-20037508.34</"
      56             :                    "UpperLeftX><UpperLeftY>20037508.34</UpperLeftY>"
      57             :                    "<LowerRightX>20037508.34</"
      58             :                    "LowerRightX><LowerRightY>-20037508.34</LowerRightY>"
      59             :                    "<TileLevel>%d</TileLevel><TileCountX>1</TileCountX>"
      60             :                    "<TileCountY>1</TileCountY><YOrigin>top</YOrigin></"
      61             :                    "DataWindow>"
      62             :                    "<Projection>EPSG:%d</Projection><BlockSizeX>256</"
      63             :                    "BlockSizeX>"
      64             :                    "<BlockSizeY>256</BlockSizeY><BandsCount>%d</BandsCount>"
      65             :                    "<Cache><Type>file</Type><Expires>%d</Expires><MaxSize>%d</"
      66             :                    "MaxSize>"
      67             :                    "</Cache><ZeroBlockHttpCodes>204,404</ZeroBlockHttpCodes></"
      68             :                    "GDAL_WMS>",
      69             :                    pszRasterUrl,
      70             :                    22,     // NOTE: We have no limit in zoom levels.
      71             :                    nEPSG,  // NOTE: Default SRS is EPSG:3857.
      72             :                    4, nCacheExpires, nCacheMaxSize);
      73             : 
      74           0 :     CPLFree(pszRasterUrl);
      75           0 :     return pszConnStr;
      76             : }
      77             : 
      78           0 : static std::string GetStylesIdentifiers(const CPLJSONArray &aoStyles, int nDeep)
      79             : {
      80           0 :     std::string sOut;
      81           0 :     if (nDeep > 255)
      82             :     {
      83           0 :         return sOut;
      84             :     }
      85             : 
      86           0 :     for (const auto &subobj : aoStyles)
      87             :     {
      88           0 :         auto sType = subobj.GetString("item_type");
      89           0 :         if (sType == "layer")
      90             :         {
      91           0 :             auto sId = subobj.GetString("layer_style_id");
      92           0 :             if (!sId.empty())
      93             :             {
      94           0 :                 if (sOut.empty())
      95             :                 {
      96           0 :                     sOut = sId;
      97             :                 }
      98             :                 else
      99             :                 {
     100           0 :                     sOut += "," + sId;
     101             :                 }
     102             :             }
     103             :         }
     104             :         else
     105             :         {
     106           0 :             auto aoChildren = subobj.GetArray("children");
     107           0 :             auto sId = GetStylesIdentifiers(aoChildren, nDeep + 1);
     108           0 :             if (!sId.empty())
     109             :             {
     110           0 :                 if (sOut.empty())
     111             :                 {
     112           0 :                     sOut = sId;
     113             :                 }
     114             :                 else
     115             :                 {
     116           0 :                     sOut += "," + sId;
     117             :                 }
     118             :             }
     119             :         }
     120             :     }
     121           0 :     return sOut;
     122             : }
     123             : 
     124             : /*
     125             :  * OGRNGWDataset()
     126             :  */
     127           0 : OGRNGWDataset::OGRNGWDataset()
     128             :     : nBatchSize(-1), nPageSize(-1), bFetchedPermissions(false),
     129             :       bHasFeaturePaging(false), bExtInNativeData(false), bMetadataDerty(false),
     130             :       poRasterDS(nullptr), nRasters(0), nCacheExpires(604800),  // 7 days
     131             :       nCacheMaxSize(67108864),                                  // 64 MB
     132           0 :       osJsonDepth("32")
     133             : {
     134           0 : }
     135             : 
     136             : /*
     137             :  * ~OGRNGWDataset()
     138             :  */
     139           0 : OGRNGWDataset::~OGRNGWDataset()
     140             : {
     141             :     // Last sync with server.
     142           0 :     OGRNGWDataset::FlushCache(true);
     143             : 
     144           0 :     if (poRasterDS != nullptr)
     145             :     {
     146           0 :         GDALClose(poRasterDS);
     147           0 :         poRasterDS = nullptr;
     148             :     }
     149           0 : }
     150             : 
     151             : /*
     152             :  * FetchPermissions()
     153             :  */
     154           0 : void OGRNGWDataset::FetchPermissions()
     155             : {
     156           0 :     if (bFetchedPermissions)
     157             :     {
     158           0 :         return;
     159             :     }
     160             : 
     161           0 :     if (IsUpdateMode())
     162             :     {
     163             :         // Check connection and is it read only.
     164             :         stPermissions = NGWAPI::CheckPermissions(
     165           0 :             osUrl, osResourceId, GetHeaders(false), IsUpdateMode());
     166             :     }
     167             :     else
     168             :     {
     169           0 :         stPermissions.bDataCanRead = true;
     170           0 :         stPermissions.bResourceCanRead = true;
     171           0 :         stPermissions.bDatastructCanRead = true;
     172           0 :         stPermissions.bMetadataCanRead = true;
     173             :     }
     174           0 :     bFetchedPermissions = true;
     175             : }
     176             : 
     177             : /*
     178             :  * TestCapability()
     179             :  */
     180           0 : int OGRNGWDataset::TestCapability(const char *pszCap)
     181             : {
     182           0 :     FetchPermissions();
     183           0 :     if (EQUAL(pszCap, ODsCCreateLayer))
     184             :     {
     185           0 :         return stPermissions.bResourceCanCreate;
     186             :     }
     187           0 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     188             :     {
     189           0 :         return stPermissions.bResourceCanDelete;
     190             :     }
     191           0 :     else if (EQUAL(pszCap, "RenameLayer"))
     192             :     {
     193           0 :         return stPermissions.bResourceCanUpdate;
     194             :     }
     195           0 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     196             :     {
     197           0 :         return stPermissions.bDataCanWrite;  // FIXME: Check on resource level
     198             :                                              // is this permission set?
     199             :     }
     200           0 :     else if (EQUAL(pszCap, ODsCRandomLayerRead))
     201             :     {
     202           0 :         return stPermissions.bDataCanRead;
     203             :     }
     204           0 :     else if (EQUAL(pszCap, ODsCZGeometries))
     205             :     {
     206           0 :         return TRUE;
     207             :     }
     208           0 :     else if (EQUAL(pszCap, ODsCAddFieldDomain))
     209             :     {
     210           0 :         return stPermissions.bResourceCanCreate;
     211             :     }
     212           0 :     else if (EQUAL(pszCap, ODsCDeleteFieldDomain))
     213             :     {
     214           0 :         return stPermissions.bResourceCanDelete;
     215             :     }
     216           0 :     else if (EQUAL(pszCap, ODsCUpdateFieldDomain))
     217             :     {
     218           0 :         return stPermissions.bResourceCanUpdate;
     219             :     }
     220             :     else
     221             :     {
     222           0 :         return FALSE;
     223             :     }
     224             : }
     225             : 
     226             : /*
     227             :  * GetLayer()
     228             :  */
     229           0 : OGRLayer *OGRNGWDataset::GetLayer(int iLayer)
     230             : {
     231           0 :     if (iLayer < 0 || iLayer >= GetLayerCount())
     232             :     {
     233           0 :         return nullptr;
     234             :     }
     235             :     else
     236             :     {
     237           0 :         return aoLayers[iLayer].get();
     238             :     }
     239             : }
     240             : 
     241             : /*
     242             :  * Open()
     243             :  */
     244           0 : bool OGRNGWDataset::Open(const std::string &osUrlIn,
     245             :                          const std::string &osResourceIdIn,
     246             :                          char **papszOpenOptionsIn, bool bUpdateIn,
     247             :                          int nOpenFlagsIn)
     248             : {
     249           0 :     osUrl = osUrlIn;
     250           0 :     osResourceId = osResourceIdIn;
     251             : 
     252           0 :     eAccess = bUpdateIn ? GA_Update : GA_ReadOnly;
     253             : 
     254             :     osUserPwd = CSLFetchNameValueDef(papszOpenOptionsIn, "USERPWD",
     255           0 :                                      CPLGetConfigOption("NGW_USERPWD", ""));
     256             : 
     257           0 :     nBatchSize =
     258           0 :         atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "BATCH_SIZE",
     259             :                                   CPLGetConfigOption("NGW_BATCH_SIZE", "-1")));
     260             : 
     261           0 :     nPageSize =
     262           0 :         atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "PAGE_SIZE",
     263             :                                   CPLGetConfigOption("NGW_PAGE_SIZE", "-1")));
     264           0 :     if (nPageSize == 0)
     265             :     {
     266           0 :         nPageSize = -1;
     267             :     }
     268             : 
     269           0 :     nCacheExpires = atoi(CSLFetchNameValueDef(
     270             :         papszOpenOptionsIn, "CACHE_EXPIRES",
     271             :         CPLGetConfigOption("NGW_CACHE_EXPIRES", "604800")));
     272             : 
     273           0 :     nCacheMaxSize = atoi(CSLFetchNameValueDef(
     274             :         papszOpenOptionsIn, "CACHE_MAX_SIZE",
     275             :         CPLGetConfigOption("NGW_CACHE_MAX_SIZE", "67108864")));
     276             : 
     277           0 :     bExtInNativeData =
     278           0 :         CPLFetchBool(papszOpenOptionsIn, "NATIVE_DATA",
     279           0 :                      CPLTestBool(CPLGetConfigOption("NGW_NATIVE_DATA", "NO")));
     280             : 
     281             :     osJsonDepth =
     282             :         CSLFetchNameValueDef(papszOpenOptionsIn, "JSON_DEPTH",
     283           0 :                              CPLGetConfigOption("NGW_JSON_DEPTH", "32"));
     284             : 
     285             :     osExtensions =
     286             :         CSLFetchNameValueDef(papszOpenOptionsIn, "EXTENSIONS",
     287           0 :                              CPLGetConfigOption("NGW_EXTENSIONS", ""));
     288             : 
     289             :     osConnectTimeout =
     290             :         CSLFetchNameValueDef(papszOpenOptionsIn, "CONNECTTIMEOUT",
     291           0 :                              CPLGetConfigOption("NGW_CONNECTTIMEOUT", ""));
     292             :     osTimeout = CSLFetchNameValueDef(papszOpenOptionsIn, "TIMEOUT",
     293           0 :                                      CPLGetConfigOption("NGW_TIMEOUT", ""));
     294             :     osRetryCount =
     295             :         CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_RETRY",
     296           0 :                              CPLGetConfigOption("NGW_MAX_RETRY", ""));
     297             :     osRetryDelay =
     298             :         CSLFetchNameValueDef(papszOpenOptionsIn, "RETRY_DELAY",
     299           0 :                              CPLGetConfigOption("NGW_RETRY_DELAY", ""));
     300             : 
     301           0 :     if (osExtensions.empty())
     302             :     {
     303           0 :         bExtInNativeData = false;
     304             :     }
     305             : 
     306           0 :     CPLDebug("NGW",
     307             :              "Open options:\n"
     308             :              "  BATCH_SIZE %d\n"
     309             :              "  PAGE_SIZE %d\n"
     310             :              "  CACHE_EXPIRES %d\n"
     311             :              "  CACHE_MAX_SIZE %d\n"
     312             :              "  JSON_DEPTH %s\n"
     313             :              "  EXTENSIONS %s\n"
     314             :              "  CONNECTTIMEOUT %s\n"
     315             :              "  TIMEOUT %s\n"
     316             :              "  MAX_RETRY %s\n"
     317             :              "  RETRY_DELAY %s",
     318             :              nBatchSize, nPageSize, nCacheExpires, nCacheMaxSize,
     319             :              osJsonDepth.c_str(), osExtensions.c_str(),
     320             :              osConnectTimeout.c_str(), osTimeout.c_str(), osRetryCount.c_str(),
     321             :              osRetryDelay.c_str());
     322             : 
     323           0 :     return Init(nOpenFlagsIn);
     324             : }
     325             : 
     326             : /*
     327             :  * Open()
     328             :  *
     329             :  * The pszFilename templates:
     330             :  *      - NGW:http://some.nextgis.com/resource/0
     331             :  *      - NGW:http://some.nextgis.com:8000/test/resource/0
     332             :  */
     333           0 : bool OGRNGWDataset::Open(const char *pszFilename, char **papszOpenOptionsIn,
     334             :                          bool bUpdateIn, int nOpenFlagsIn)
     335             : {
     336           0 :     NGWAPI::Uri stUri = NGWAPI::ParseUri(pszFilename);
     337             : 
     338           0 :     if (stUri.osPrefix != "NGW")
     339             :     {
     340           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s",
     341             :                  pszFilename);
     342           0 :         return false;
     343             :     }
     344             : 
     345           0 :     osUrl = stUri.osAddress;
     346           0 :     osResourceId = stUri.osResourceId;
     347             : 
     348           0 :     return Open(stUri.osAddress, stUri.osResourceId, papszOpenOptionsIn,
     349           0 :                 bUpdateIn, nOpenFlagsIn);
     350             : }
     351             : 
     352             : /*
     353             :  * SetupRasterDSWrapper()
     354             :  */
     355           0 : void OGRNGWDataset::SetupRasterDSWrapper(const OGREnvelope &stExtent)
     356             : {
     357           0 :     if (poRasterDS)
     358             :     {
     359           0 :         nRasterXSize = poRasterDS->GetRasterXSize();
     360           0 :         nRasterYSize = poRasterDS->GetRasterYSize();
     361             : 
     362           0 :         for (int iBand = 1; iBand <= poRasterDS->GetRasterCount(); iBand++)
     363             :         {
     364           0 :             SetBand(iBand,
     365           0 :                     new NGWWrapperRasterBand(poRasterDS->GetRasterBand(iBand)));
     366             :         }
     367             : 
     368           0 :         if (stExtent.IsInit())
     369             :         {
     370             :             // Set pixel limits.
     371           0 :             bool bHasTransform = false;
     372           0 :             double geoTransform[6] = {0.0};
     373           0 :             double invGeoTransform[6] = {0.0};
     374           0 :             if (poRasterDS->GetGeoTransform(geoTransform) == CE_None)
     375             :             {
     376           0 :                 bHasTransform =
     377           0 :                     GDALInvGeoTransform(geoTransform, invGeoTransform) == TRUE;
     378             :             }
     379             : 
     380           0 :             if (bHasTransform)
     381             :             {
     382           0 :                 GDALApplyGeoTransform(invGeoTransform, stExtent.MinX,
     383           0 :                                       stExtent.MinY, &stPixelExtent.MinX,
     384             :                                       &stPixelExtent.MaxY);
     385             : 
     386           0 :                 GDALApplyGeoTransform(invGeoTransform, stExtent.MaxX,
     387           0 :                                       stExtent.MaxY, &stPixelExtent.MaxX,
     388             :                                       &stPixelExtent.MinY);
     389             : 
     390           0 :                 CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f",
     391             :                          stPixelExtent.MinX, stPixelExtent.MinY,
     392             :                          stPixelExtent.MaxX, stPixelExtent.MaxY);
     393             :             }
     394             :             else
     395             :             {
     396           0 :                 stPixelExtent.MinX = 0.0;
     397           0 :                 stPixelExtent.MinY = 0.0;
     398           0 :                 stPixelExtent.MaxX = std::numeric_limits<double>::max();
     399           0 :                 stPixelExtent.MaxY = std::numeric_limits<double>::max();
     400             :             }
     401             :         }
     402             :     }
     403           0 : }
     404             : 
     405             : /*
     406             :  * Init()
     407             :  */
     408           0 : bool OGRNGWDataset::Init(int nOpenFlagsIn)
     409             : {
     410             :     // NOTE: Skip check API version at that moment. We expected API v3 or higher.
     411             : 
     412             :     // Get resource details.
     413           0 :     CPLJSONDocument oResourceDetailsReq;
     414           0 :     auto aosHTTPOptions = GetHeaders(false);
     415           0 :     bool bResult = oResourceDetailsReq.LoadUrl(
     416           0 :         NGWAPI::GetResourceURL(osUrl, osResourceId), aosHTTPOptions);
     417             : 
     418           0 :     CPLDebug("NGW", "Get resource %s details %s", osResourceId.c_str(),
     419             :              bResult ? "success" : "failed");
     420             : 
     421           0 :     if (bResult)
     422             :     {
     423           0 :         CPLJSONObject oRoot = oResourceDetailsReq.GetRoot();
     424             : 
     425           0 :         if (oRoot.IsValid())
     426             :         {
     427           0 :             auto osResourceType = oRoot.GetString("resource/cls");
     428           0 :             FillMetadata(oRoot);
     429             : 
     430           0 :             if (osResourceType == "resource_group")
     431             :             {
     432             :                 // Check feature paging.
     433           0 :                 FillCapabilities(aosHTTPOptions);
     434           0 :                 if (oRoot.GetBool("resource/children", false))
     435             :                 {
     436             :                     // Get child resources.
     437           0 :                     bResult = FillResources(aosHTTPOptions, nOpenFlagsIn);
     438             :                 }
     439             :             }
     440           0 :             else if (NGWAPI::CheckSupportedType(false, osResourceType))
     441             :             {
     442             :                 // Check feature paging.
     443           0 :                 FillCapabilities(aosHTTPOptions);
     444             :                 // Add vector layer.
     445           0 :                 AddLayer(oRoot, aosHTTPOptions, nOpenFlagsIn);
     446             :             }
     447           0 :             else if (osResourceType == "mapserver_style" ||
     448           0 :                      osResourceType == "qgis_vector_style" ||
     449           0 :                      osResourceType == "raster_style" ||
     450           0 :                      osResourceType == "qgis_raster_style")
     451             :             {
     452             :                 // GetExtent from parent.
     453           0 :                 OGREnvelope stExtent;
     454           0 :                 std::string osParentId = oRoot.GetString("resource/parent/id");
     455           0 :                 bool bExtentResult = NGWAPI::GetExtent(
     456           0 :                     osUrl, osParentId, aosHTTPOptions, 3857, stExtent);
     457             : 
     458           0 :                 if (!bExtentResult)
     459             :                 {
     460             :                     // Set full extent for EPSG:3857.
     461           0 :                     stExtent.MinX = -20037508.34;
     462           0 :                     stExtent.MaxX = 20037508.34;
     463           0 :                     stExtent.MinY = -20037508.34;
     464           0 :                     stExtent.MaxY = 20037508.34;
     465             :                 }
     466             : 
     467           0 :                 CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f",
     468             :                          stExtent.MinX, stExtent.MinY, stExtent.MaxX,
     469             :                          stExtent.MaxY);
     470             : 
     471           0 :                 int nEPSG = 3857;
     472             :                 // NOTE: Get parent details. We can skip this as default SRS in
     473             :                 // NGW is 3857.
     474           0 :                 CPLJSONDocument oResourceReq;
     475           0 :                 bResult = oResourceReq.LoadUrl(
     476           0 :                     NGWAPI::GetResourceURL(osUrl, osResourceId),
     477           0 :                     aosHTTPOptions);
     478             : 
     479           0 :                 if (bResult)
     480             :                 {
     481           0 :                     CPLJSONObject oParentRoot = oResourceReq.GetRoot();
     482           0 :                     if (osResourceType == "mapserver_style" ||
     483           0 :                         osResourceType == "qgis_vector_style")
     484             :                     {
     485           0 :                         nEPSG = oParentRoot.GetInteger("vector_layer/srs/id",
     486             :                                                        nEPSG);
     487             :                     }
     488           0 :                     else if (osResourceType == "raster_style" ||
     489           0 :                              osResourceType == "qgis_raster_style")
     490             :                     {
     491           0 :                         nEPSG = oParentRoot.GetInteger("raster_layer/srs/id",
     492             :                                                        nEPSG);
     493             :                     }
     494             :                 }
     495             : 
     496           0 :                 const char *pszConnStr = FormGDALTMSConnectionString(
     497           0 :                     osUrl, osResourceId, nEPSG, nCacheExpires, nCacheMaxSize);
     498           0 :                 CPLDebug("NGW", "Open %s as '%s'", osResourceType.c_str(),
     499             :                          pszConnStr);
     500           0 :                 poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
     501             :                     pszConnStr,
     502             :                     GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     503             :                     nullptr, nullptr, nullptr));
     504           0 :                 SetupRasterDSWrapper(stExtent);
     505             :             }
     506           0 :             else if (osResourceType == "wmsclient_layer")
     507             :             {
     508           0 :                 OGREnvelope stExtent;
     509             :                 // Set full extent for EPSG:3857.
     510           0 :                 stExtent.MinX = -20037508.34;
     511           0 :                 stExtent.MaxX = 20037508.34;
     512           0 :                 stExtent.MinY = -20037508.34;
     513           0 :                 stExtent.MaxY = 20037508.34;
     514             : 
     515           0 :                 CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f",
     516             :                          stExtent.MinX, stExtent.MinY, stExtent.MaxX,
     517             :                          stExtent.MaxY);
     518             : 
     519           0 :                 int nEPSG = oRoot.GetInteger("wmsclient_layer/srs/id", 3857);
     520             : 
     521           0 :                 const char *pszConnStr = FormGDALTMSConnectionString(
     522           0 :                     osUrl, osResourceId, nEPSG, nCacheExpires, nCacheMaxSize);
     523           0 :                 poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
     524             :                     pszConnStr,
     525             :                     GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     526             :                     nullptr, nullptr, nullptr));
     527           0 :                 SetupRasterDSWrapper(stExtent);
     528             :             }
     529           0 :             else if (osResourceType == "basemap_layer")
     530             :             {
     531           0 :                 auto osTMSURL = oRoot.GetString("basemap_layer/url");
     532           0 :                 int nEPSG = 3857;
     533           0 :                 auto osQMS = oRoot.GetString("basemap_layer/qms");
     534           0 :                 if (!osQMS.empty())
     535             :                 {
     536           0 :                     CPLJSONDocument oDoc;
     537           0 :                     if (oDoc.LoadMemory(osQMS))
     538             :                     {
     539           0 :                         auto oQMLRoot = oDoc.GetRoot();
     540           0 :                         nEPSG = oQMLRoot.GetInteger("epsg");
     541             :                     }
     542             :                 }
     543             : 
     544             :                 // TODO: for EPSG != 3857 need to calc full extent
     545           0 :                 if (nEPSG != 3857)
     546             :                 {
     547           0 :                     bResult = false;
     548             :                 }
     549             :                 else
     550             :                 {
     551           0 :                     OGREnvelope stExtent;
     552             :                     // Set full extent for EPSG:3857.
     553           0 :                     stExtent.MinX = -20037508.34;
     554           0 :                     stExtent.MaxX = 20037508.34;
     555           0 :                     stExtent.MinY = -20037508.34;
     556           0 :                     stExtent.MaxY = 20037508.34;
     557             : 
     558           0 :                     const char *pszConnStr = FormGDALTMSConnectionString(
     559           0 :                         osTMSURL, osResourceId, nEPSG, nCacheExpires,
     560             :                         nCacheMaxSize);
     561           0 :                     poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
     562             :                         pszConnStr,
     563             :                         GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     564             :                         nullptr, nullptr, nullptr));
     565           0 :                     SetupRasterDSWrapper(stExtent);
     566             :                 }
     567             :             }
     568           0 :             else if (osResourceType == "webmap")
     569             :             {
     570           0 :                 OGREnvelope stExtent;
     571             :                 // Set full extent for EPSG:3857.
     572           0 :                 stExtent.MinX = -20037508.34;
     573           0 :                 stExtent.MaxX = 20037508.34;
     574           0 :                 stExtent.MinY = -20037508.34;
     575           0 :                 stExtent.MaxY = 20037508.34;
     576             : 
     577             :                 // Get all styles
     578           0 :                 auto aoChildren = oRoot.GetArray("webmap/children");
     579           0 :                 auto sIdentifiers = GetStylesIdentifiers(aoChildren, 0);
     580             : 
     581           0 :                 const char *pszConnStr = FormGDALTMSConnectionString(
     582           0 :                     osUrl, sIdentifiers, 3857, nCacheExpires, nCacheMaxSize);
     583           0 :                 poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
     584             :                     pszConnStr,
     585             :                     GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     586             :                     nullptr, nullptr, nullptr));
     587           0 :                 SetupRasterDSWrapper(stExtent);
     588             :             }
     589           0 :             else if (osResourceType == "raster_layer")
     590             :             {
     591           0 :                 auto osCogURL = NGWAPI::GetCOGURL(osUrl, osResourceId);
     592           0 :                 auto osConnStr = std::string("/vsicurl/") + osCogURL;
     593             : 
     594           0 :                 CPLDebug("NGW", "Raster url is: %s", osConnStr.c_str());
     595             : 
     596           0 :                 poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
     597             :                     osConnStr.c_str(),
     598             :                     GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     599             :                     nullptr, nullptr, nullptr));
     600             : 
     601             :                 // Add styles if exists
     602           0 :                 auto osRasterResourceId = oRoot.GetString("resource/id");
     603           0 :                 CPLJSONDocument oResourceRequest;
     604           0 :                 bool bLoadResult = oResourceRequest.LoadUrl(
     605           0 :                     NGWAPI::GetChildrenURL(osUrl, osRasterResourceId),
     606           0 :                     aosHTTPOptions);
     607           0 :                 if (bLoadResult)
     608             :                 {
     609           0 :                     CPLJSONArray oChildren(oResourceRequest.GetRoot());
     610           0 :                     for (const auto &oChild : oChildren)
     611             :                     {
     612           0 :                         AddRaster(oChild);
     613             :                     }
     614             :                 }
     615             : 
     616           0 :                 SetupRasterDSWrapper(OGREnvelope());
     617             :             }
     618             :             else
     619             :             {
     620           0 :                 bResult = false;
     621             :             }
     622             : 
     623             :             // TODO: Add support for wfsserver_service, wmsserver_service,
     624             :             // raster_mosaic, tileset.
     625             :         }
     626             :     }
     627             : 
     628           0 :     return bResult;
     629             : }
     630             : 
     631             : /*
     632             :  * FillResources()
     633             :  */
     634           0 : bool OGRNGWDataset::FillResources(const CPLStringList &aosHTTPOptions,
     635             :                                   int nOpenFlagsIn)
     636             : {
     637           0 :     CPLJSONDocument oResourceDetailsReq;
     638             :     // Fill domains
     639           0 :     bool bResult = oResourceDetailsReq.LoadUrl(
     640           0 :         NGWAPI::GetSearchURL(osUrl, "cls", "lookup_table"), aosHTTPOptions);
     641           0 :     if (bResult)
     642             :     {
     643           0 :         CPLJSONArray oChildren(oResourceDetailsReq.GetRoot());
     644           0 :         for (const auto &oChild : oChildren)
     645             :         {
     646           0 :             OGRNGWCodedFieldDomain oDomain(oChild);
     647           0 :             if (oDomain.GetID() > 0)
     648             :             {
     649           0 :                 moDomains[oDomain.GetID()] = oDomain;
     650             :             }
     651             :         }
     652             :     }
     653             : 
     654             :     // Fill child resources
     655           0 :     bResult = oResourceDetailsReq.LoadUrl(
     656           0 :         NGWAPI::GetChildrenURL(osUrl, osResourceId), aosHTTPOptions);
     657             : 
     658           0 :     if (bResult)
     659             :     {
     660           0 :         CPLJSONArray oChildren(oResourceDetailsReq.GetRoot());
     661           0 :         for (const auto &oChild : oChildren)
     662             :         {
     663           0 :             if (nOpenFlagsIn & GDAL_OF_VECTOR)
     664             :             {
     665             :                 // Add vector layer. If failed, try next layer.
     666           0 :                 AddLayer(oChild, aosHTTPOptions, nOpenFlagsIn);
     667             :             }
     668             : 
     669           0 :             if (nOpenFlagsIn & GDAL_OF_RASTER)
     670             :             {
     671           0 :                 AddRaster(oChild);
     672             :             }
     673             :         }
     674             :     }
     675           0 :     return bResult;
     676             : }
     677             : 
     678             : /*
     679             :  * AddLayer()
     680             :  */
     681           0 : void OGRNGWDataset::AddLayer(const CPLJSONObject &oResourceJsonObject,
     682             :                              const CPLStringList &aosHTTPOptions,
     683             :                              int nOpenFlagsIn)
     684             : {
     685           0 :     auto osResourceType = oResourceJsonObject.GetString("resource/cls");
     686           0 :     if (!NGWAPI::CheckSupportedType(false, osResourceType))
     687             :     {
     688             :         // NOTE: Only vector_layer and postgis_layer types now supported
     689           0 :         return;
     690             :     }
     691             : 
     692           0 :     auto osLayerResourceId = oResourceJsonObject.GetString("resource/id");
     693           0 :     if (nOpenFlagsIn & GDAL_OF_VECTOR)
     694             :     {
     695           0 :         OGRNGWLayerPtr poLayer(new OGRNGWLayer(this, oResourceJsonObject));
     696           0 :         aoLayers.emplace_back(poLayer);
     697           0 :         osLayerResourceId = poLayer->GetResourceId();
     698             :     }
     699             : 
     700             :     // Check styles exist and add them as rasters.
     701           0 :     if (nOpenFlagsIn & GDAL_OF_RASTER &&
     702           0 :         oResourceJsonObject.GetBool("resource/children", false))
     703             :     {
     704           0 :         CPLJSONDocument oResourceChildReq;
     705           0 :         bool bResult = oResourceChildReq.LoadUrl(
     706           0 :             NGWAPI::GetChildrenURL(osUrl, osLayerResourceId), aosHTTPOptions);
     707             : 
     708           0 :         if (bResult)
     709             :         {
     710           0 :             CPLJSONArray oChildren(oResourceChildReq.GetRoot());
     711           0 :             for (const auto &oChild : oChildren)
     712             :             {
     713           0 :                 AddRaster(oChild);
     714             :             }
     715             :         }
     716             :     }
     717             : }
     718             : 
     719             : /*
     720             :  * AddRaster()
     721             :  */
     722           0 : void OGRNGWDataset::AddRaster(const CPLJSONObject &oRasterJsonObj)
     723             : {
     724           0 :     auto osResourceType = oRasterJsonObj.GetString("resource/cls");
     725           0 :     if (!NGWAPI::CheckSupportedType(true, osResourceType))
     726             :     {
     727           0 :         return;
     728             :     }
     729             : 
     730           0 :     auto osOutResourceId = oRasterJsonObj.GetString("resource/id");
     731           0 :     auto osOutResourceName = oRasterJsonObj.GetString("resource/display_name");
     732             : 
     733           0 :     if (osOutResourceName.empty())
     734             :     {
     735           0 :         osOutResourceName = "raster_" + osOutResourceId;
     736             :     }
     737             : 
     738           0 :     CPLDebug("NGW", "Add raster %s: %s", osOutResourceId.c_str(),
     739             :              osOutResourceName.c_str());
     740             : 
     741           0 :     GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_NAME", nRasters + 1),
     742             :                                  CPLSPrintf("NGW:%s/resource/%s", osUrl.c_str(),
     743             :                                             osOutResourceId.c_str()),
     744             :                                  "SUBDATASETS");
     745           0 :     GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_DESC", nRasters + 1),
     746             :                                  CPLSPrintf("%s (%s)",
     747             :                                             osOutResourceName.c_str(),
     748             :                                             osResourceType.c_str()),
     749             :                                  "SUBDATASETS");
     750           0 :     nRasters++;
     751             : }
     752             : 
     753             : /*
     754             :  * ICreateLayer
     755             :  */
     756           0 : OGRLayer *OGRNGWDataset::ICreateLayer(const char *pszNameIn,
     757             :                                       const OGRGeomFieldDefn *poGeomFieldDefn,
     758             :                                       CSLConstList papszOptions)
     759             : {
     760           0 :     if (!IsUpdateMode())
     761             :     {
     762           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     763             :                  "Operation not available in read-only mode");
     764           0 :         return nullptr;
     765             :     }
     766             : 
     767           0 :     const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     768             :     const auto poSpatialRef =
     769           0 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     770             : 
     771             :     // Check permissions as we create new layer in memory and will create in
     772             :     // during SyncToDisk.
     773           0 :     FetchPermissions();
     774             : 
     775           0 :     if (!stPermissions.bResourceCanCreate)
     776             :     {
     777           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
     778           0 :         return nullptr;
     779             :     }
     780             : 
     781             :     // Check input parameters.
     782           0 :     if ((eGType < wkbPoint || eGType > wkbMultiPolygon) &&
     783           0 :         (eGType < wkbPoint25D || eGType > wkbMultiPolygon25D))
     784             :     {
     785           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unsupported geometry type: %s",
     786             :                  OGRGeometryTypeToName(eGType));
     787           0 :         return nullptr;
     788             :     }
     789             : 
     790           0 :     if (!poSpatialRef)
     791             :     {
     792           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Undefined spatial reference");
     793           0 :         return nullptr;
     794             :     }
     795             : 
     796           0 :     OGRSpatialReference *poSRSClone = poSpatialRef->Clone();
     797           0 :     poSRSClone->AutoIdentifyEPSG();
     798           0 :     const char *pszEPSG = poSRSClone->GetAuthorityCode(nullptr);
     799           0 :     int nEPSG = -1;
     800           0 :     if (pszEPSG != nullptr)
     801             :     {
     802           0 :         nEPSG = atoi(pszEPSG);
     803             :     }
     804             : 
     805           0 :     if (nEPSG != 3857)  // TODO: Check NextGIS Web supported SRS.
     806             :     {
     807           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     808             :                  "Unsupported spatial reference EPSG code: %d", nEPSG);
     809           0 :         poSRSClone->Release();
     810           0 :         return nullptr;
     811             :     }
     812             : 
     813             :     // Do we already have this layer?  If so, should we blow it away?
     814           0 :     bool bOverwrite = CPLFetchBool(papszOptions, "OVERWRITE", false);
     815           0 :     for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer)
     816             :     {
     817           0 :         if (EQUAL(pszNameIn, aoLayers[iLayer]->GetName()))
     818             :         {
     819           0 :             if (bOverwrite)
     820             :             {
     821           0 :                 DeleteLayer(iLayer);
     822           0 :                 break;
     823             :             }
     824             :             else
     825             :             {
     826           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     827             :                          "Layer %s already exists, CreateLayer failed.\n"
     828             :                          "Use the layer creation option OVERWRITE=YES to "
     829             :                          "replace it.",
     830             :                          pszNameIn);
     831           0 :                 poSRSClone->Release();
     832           0 :                 return nullptr;
     833             :             }
     834             :         }
     835             :     }
     836             : 
     837             :     // Create layer.
     838           0 :     std::string osKey = CSLFetchNameValueDef(papszOptions, "KEY", "");
     839           0 :     std::string osDesc = CSLFetchNameValueDef(papszOptions, "DESCRIPTION", "");
     840           0 :     poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     841             :     OGRNGWLayerPtr poLayer(
     842           0 :         new OGRNGWLayer(this, pszNameIn, poSRSClone, eGType, osKey, osDesc));
     843           0 :     poSRSClone->Release();
     844           0 :     aoLayers.emplace_back(poLayer);
     845           0 :     return poLayer.get();
     846             : }
     847             : 
     848             : /*
     849             :  * DeleteLayer()
     850             :  */
     851           0 : OGRErr OGRNGWDataset::DeleteLayer(int iLayer)
     852             : {
     853           0 :     if (!IsUpdateMode())
     854             :     {
     855           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     856             :                  "Operation not available in read-only mode.");
     857           0 :         return OGRERR_FAILURE;
     858             :     }
     859             : 
     860           0 :     if (iLayer < 0 || iLayer >= GetLayerCount())
     861             :     {
     862           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     863             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
     864           0 :                  GetLayerCount() - 1);
     865           0 :         return OGRERR_FAILURE;
     866             :     }
     867             : 
     868           0 :     auto poLayer = aoLayers[iLayer];
     869           0 :     if (poLayer->GetResourceId() != "-1")
     870             :     {
     871             :         // For layers from server we can check permissions.
     872             : 
     873             :         // We can skip check permissions here as papoLayers[iLayer]->Delete()
     874             :         // will return false if no delete permission available.
     875           0 :         FetchPermissions();
     876             : 
     877           0 :         if (!stPermissions.bResourceCanDelete)
     878             :         {
     879           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
     880           0 :             return OGRERR_FAILURE;
     881             :         }
     882             :     }
     883             : 
     884           0 :     if (poLayer->Delete())
     885             :     {
     886           0 :         aoLayers.erase(aoLayers.begin() + iLayer);
     887             :     }
     888             : 
     889           0 :     return OGRERR_NONE;
     890             : }
     891             : 
     892             : /*
     893             :  * FillMetadata()
     894             :  */
     895           0 : void OGRNGWDataset::FillMetadata(const CPLJSONObject &oRootObject)
     896             : {
     897           0 :     std::string osCreateDate = oRootObject.GetString("resource/creation_date");
     898           0 :     if (!osCreateDate.empty())
     899             :     {
     900           0 :         GDALDataset::SetMetadataItem("creation_date", osCreateDate.c_str());
     901             :     }
     902           0 :     osName = oRootObject.GetString("resource/display_name");
     903           0 :     SetDescription(osName.c_str());
     904           0 :     GDALDataset::SetMetadataItem("display_name", osName.c_str());
     905           0 :     std::string osDescription = oRootObject.GetString("resource/description");
     906           0 :     if (!osDescription.empty())
     907             :     {
     908           0 :         GDALDataset::SetMetadataItem("description", osDescription.c_str());
     909             :     }
     910           0 :     std::string osResourceType = oRootObject.GetString("resource/cls");
     911           0 :     if (!osResourceType.empty())
     912             :     {
     913           0 :         GDALDataset::SetMetadataItem("resource_type", osResourceType.c_str());
     914             :     }
     915             :     std::string osResourceParentId =
     916           0 :         oRootObject.GetString("resource/parent/id");
     917           0 :     if (!osResourceParentId.empty())
     918             :     {
     919           0 :         GDALDataset::SetMetadataItem("parent_id", osResourceParentId.c_str());
     920             :     }
     921           0 :     GDALDataset::SetMetadataItem("id", osResourceId.c_str());
     922             : 
     923             :     std::vector<CPLJSONObject> items =
     924           0 :         oRootObject.GetObj("resmeta/items").GetChildren();
     925             : 
     926           0 :     for (const CPLJSONObject &item : items)
     927             :     {
     928           0 :         std::string osSuffix = NGWAPI::GetResmetaSuffix(item.GetType());
     929           0 :         GDALDataset::SetMetadataItem((item.GetName() + osSuffix).c_str(),
     930           0 :                                      item.ToString().c_str(), "NGW");
     931             :     }
     932           0 : }
     933             : 
     934             : /*
     935             :  * FlushMetadata()
     936             :  */
     937           0 : bool OGRNGWDataset::FlushMetadata(char **papszMetadata)
     938             : {
     939           0 :     if (!bMetadataDerty)
     940             :     {
     941           0 :         return true;
     942             :     }
     943             : 
     944           0 :     bool bResult = NGWAPI::FlushMetadata(osUrl, osResourceId, papszMetadata,
     945           0 :                                          GetHeaders(false));
     946           0 :     if (bResult)
     947             :     {
     948           0 :         bMetadataDerty = false;
     949             :     }
     950             : 
     951           0 :     return bResult;
     952             : }
     953             : 
     954             : /*
     955             :  * SetMetadata()
     956             :  */
     957           0 : CPLErr OGRNGWDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
     958             : {
     959           0 :     FetchPermissions();
     960           0 :     if (!stPermissions.bMetadataCanWrite)
     961             :     {
     962           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
     963           0 :         return CE_Failure;
     964             :     }
     965             : 
     966           0 :     CPLErr eResult = GDALDataset::SetMetadata(papszMetadata, pszDomain);
     967           0 :     if (eResult == CE_None && pszDomain != nullptr && EQUAL(pszDomain, "NGW"))
     968             :     {
     969           0 :         eResult = FlushMetadata(papszMetadata) ? CE_None : CE_Failure;
     970             :     }
     971           0 :     return eResult;
     972             : }
     973             : 
     974             : /*
     975             :  * SetMetadataItem()
     976             :  */
     977           0 : CPLErr OGRNGWDataset::SetMetadataItem(const char *pszName, const char *pszValue,
     978             :                                       const char *pszDomain)
     979             : {
     980           0 :     FetchPermissions();
     981           0 :     if (!stPermissions.bMetadataCanWrite)
     982             :     {
     983           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
     984           0 :         return CE_Failure;
     985             :     }
     986           0 :     if (pszDomain != nullptr && EQUAL(pszDomain, "NGW"))
     987             :     {
     988           0 :         bMetadataDerty = true;
     989             :     }
     990           0 :     return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
     991             : }
     992             : 
     993             : /*
     994             :  * FlushCache()
     995             :  */
     996           0 : CPLErr OGRNGWDataset::FlushCache(bool bAtClosing)
     997             : {
     998           0 :     CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
     999           0 :     if (!FlushMetadata(GetMetadata("NGW")))
    1000           0 :         eErr = CE_Failure;
    1001           0 :     return eErr;
    1002             : }
    1003             : 
    1004             : /*
    1005             :  * GetHeaders()
    1006             :  */
    1007           0 : CPLStringList OGRNGWDataset::GetHeaders(bool bSkipRetry) const
    1008             : {
    1009           0 :     CPLStringList aosOptions;
    1010           0 :     aosOptions.AddNameValue("HEADERS", "Accept: */*");
    1011           0 :     aosOptions.AddNameValue("JSON_DEPTH", osJsonDepth.c_str());
    1012           0 :     if (!osUserPwd.empty())
    1013             :     {
    1014           0 :         aosOptions.AddNameValue("HTTPAUTH", "BASIC");
    1015           0 :         aosOptions.AddNameValue("USERPWD", osUserPwd.c_str());
    1016             :     }
    1017             : 
    1018           0 :     if (!osConnectTimeout.empty())
    1019             :     {
    1020           0 :         aosOptions.AddNameValue("CONNECTTIMEOUT", osConnectTimeout.c_str());
    1021             :     }
    1022             : 
    1023           0 :     if (!osTimeout.empty())
    1024             :     {
    1025           0 :         aosOptions.AddNameValue("TIMEOUT", osTimeout.c_str());
    1026             :     }
    1027             : 
    1028           0 :     if (!bSkipRetry)
    1029             :     {
    1030           0 :         if (!osRetryCount.empty())
    1031             :         {
    1032           0 :             aosOptions.AddNameValue("MAX_RETRY", osRetryCount.c_str());
    1033             :         }
    1034           0 :         if (!osRetryDelay.empty())
    1035             :         {
    1036           0 :             aosOptions.AddNameValue("RETRY_DELAY", osRetryDelay.c_str());
    1037             :         }
    1038             :     }
    1039           0 :     return aosOptions;
    1040             : }
    1041             : 
    1042             : /*
    1043             :  * SQLUnescape()
    1044             :  * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
    1045             :  * dependency on sqlite
    1046             :  */
    1047           0 : static CPLString SQLUnescape(const char *pszVal)
    1048             : {
    1049           0 :     char chQuoteChar = pszVal[0];
    1050           0 :     if (chQuoteChar != '\'' && chQuoteChar != '"')
    1051           0 :         return pszVal;
    1052             : 
    1053           0 :     CPLString osRet;
    1054           0 :     pszVal++;
    1055           0 :     while (*pszVal != '\0')
    1056             :     {
    1057           0 :         if (*pszVal == chQuoteChar)
    1058             :         {
    1059           0 :             if (pszVal[1] == chQuoteChar)
    1060           0 :                 pszVal++;
    1061             :             else
    1062           0 :                 break;
    1063             :         }
    1064           0 :         osRet += *pszVal;
    1065           0 :         pszVal++;
    1066             :     }
    1067           0 :     return osRet;
    1068             : }
    1069             : 
    1070             : /*
    1071             :  * SQLTokenize()
    1072             :  * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
    1073             :  * dependency on sqlite
    1074             :  */
    1075           0 : static char **SQLTokenize(const char *pszStr)
    1076             : {
    1077           0 :     char **papszTokens = nullptr;
    1078           0 :     bool bInQuote = false;
    1079           0 :     char chQuoteChar = '\0';
    1080           0 :     bool bInSpace = true;
    1081           0 :     CPLString osCurrentToken;
    1082           0 :     while (*pszStr != '\0')
    1083             :     {
    1084           0 :         if (*pszStr == ' ' && !bInQuote)
    1085             :         {
    1086           0 :             if (!bInSpace)
    1087             :             {
    1088           0 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
    1089           0 :                 osCurrentToken.clear();
    1090             :             }
    1091           0 :             bInSpace = true;
    1092             :         }
    1093           0 :         else if ((*pszStr == '(' || *pszStr == ')' || *pszStr == ',') &&
    1094           0 :                  !bInQuote)
    1095             :         {
    1096           0 :             if (!bInSpace)
    1097             :             {
    1098           0 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
    1099           0 :                 osCurrentToken.clear();
    1100             :             }
    1101           0 :             osCurrentToken.clear();
    1102           0 :             osCurrentToken += *pszStr;
    1103           0 :             papszTokens = CSLAddString(papszTokens, osCurrentToken);
    1104           0 :             osCurrentToken.clear();
    1105           0 :             bInSpace = true;
    1106             :         }
    1107           0 :         else if (*pszStr == '"' || *pszStr == '\'')
    1108             :         {
    1109           0 :             if (bInQuote && *pszStr == chQuoteChar && pszStr[1] == chQuoteChar)
    1110             :             {
    1111           0 :                 osCurrentToken += *pszStr;
    1112           0 :                 osCurrentToken += *pszStr;
    1113           0 :                 pszStr += 2;
    1114           0 :                 continue;
    1115             :             }
    1116           0 :             else if (bInQuote && *pszStr == chQuoteChar)
    1117             :             {
    1118           0 :                 osCurrentToken += *pszStr;
    1119           0 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
    1120           0 :                 osCurrentToken.clear();
    1121           0 :                 bInSpace = true;
    1122           0 :                 bInQuote = false;
    1123           0 :                 chQuoteChar = '\0';
    1124             :             }
    1125           0 :             else if (bInQuote)
    1126             :             {
    1127           0 :                 osCurrentToken += *pszStr;
    1128             :             }
    1129             :             else
    1130             :             {
    1131           0 :                 chQuoteChar = *pszStr;
    1132           0 :                 osCurrentToken.clear();
    1133           0 :                 osCurrentToken += chQuoteChar;
    1134           0 :                 bInQuote = true;
    1135           0 :                 bInSpace = false;
    1136             :             }
    1137             :         }
    1138             :         else
    1139             :         {
    1140           0 :             osCurrentToken += *pszStr;
    1141           0 :             bInSpace = false;
    1142             :         }
    1143           0 :         pszStr++;
    1144             :     }
    1145             : 
    1146           0 :     if (!osCurrentToken.empty())
    1147           0 :         papszTokens = CSLAddString(papszTokens, osCurrentToken);
    1148             : 
    1149           0 :     return papszTokens;
    1150             : }
    1151             : 
    1152             : /*
    1153             :  * ExecuteSQL()
    1154             :  */
    1155           0 : OGRLayer *OGRNGWDataset::ExecuteSQL(const char *pszStatement,
    1156             :                                     OGRGeometry *poSpatialFilter,
    1157             :                                     const char *pszDialect)
    1158             : {
    1159             :     // Clean statement string.
    1160           0 :     CPLString osStatement(pszStatement);
    1161           0 :     osStatement = osStatement.Trim().replaceAll("  ", " ");
    1162             : 
    1163           0 :     if (STARTS_WITH_CI(osStatement, "DELLAYER:"))
    1164             :     {
    1165           0 :         CPLString osLayerName = osStatement.substr(strlen("DELLAYER:"));
    1166           0 :         if (osLayerName.endsWith(";"))
    1167             :         {
    1168           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
    1169           0 :             osLayerName.Trim();
    1170             :         }
    1171             : 
    1172           0 :         CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
    1173             : 
    1174           0 :         for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer)
    1175             :         {
    1176           0 :             if (EQUAL(aoLayers[iLayer]->GetName(), osLayerName))
    1177             :             {
    1178           0 :                 DeleteLayer(iLayer);
    1179           0 :                 return nullptr;
    1180             :             }
    1181             :         }
    1182           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
    1183             :                  osLayerName.c_str());
    1184             : 
    1185           0 :         return nullptr;
    1186             :     }
    1187             : 
    1188           0 :     if (STARTS_WITH_CI(osStatement, "DELETE FROM"))
    1189             :     {
    1190           0 :         osStatement = osStatement.substr(strlen("DELETE FROM "));
    1191           0 :         if (osStatement.endsWith(";"))
    1192             :         {
    1193           0 :             osStatement = osStatement.substr(0, osStatement.size() - 1);
    1194           0 :             osStatement.Trim();
    1195             :         }
    1196             : 
    1197           0 :         std::size_t found = osStatement.find("WHERE");
    1198           0 :         CPLString osLayerName;
    1199           0 :         if (found == std::string::npos)
    1200             :         {  // No where clause
    1201           0 :             osLayerName = osStatement;
    1202           0 :             osStatement.clear();
    1203             :         }
    1204             :         else
    1205             :         {
    1206           0 :             osLayerName = osStatement.substr(0, found);
    1207           0 :             osLayerName.Trim();
    1208           0 :             osStatement = osStatement.substr(found + strlen("WHERE "));
    1209             :         }
    1210             : 
    1211             :         OGRNGWLayer *poLayer =
    1212           0 :             reinterpret_cast<OGRNGWLayer *>(GetLayerByName(osLayerName));
    1213           0 :         if (nullptr == poLayer)
    1214             :         {
    1215           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1216             :                      "Layer %s not found in dataset.", osName.c_str());
    1217           0 :             return nullptr;
    1218             :         }
    1219             : 
    1220           0 :         if (osStatement.empty())
    1221             :         {
    1222           0 :             poLayer->DeleteAllFeatures();
    1223             :         }
    1224             :         else
    1225             :         {
    1226           0 :             CPLDebug("NGW", "Delete features with statement %s",
    1227             :                      osStatement.c_str());
    1228           0 :             OGRFeatureQuery oQuery;
    1229           0 :             OGRErr eErr = oQuery.Compile(poLayer->GetLayerDefn(), osStatement);
    1230           0 :             if (eErr != OGRERR_NONE)
    1231             :             {
    1232           0 :                 return nullptr;
    1233             :             }
    1234             : 
    1235             :             // Ignore all fields except first and ignore geometry
    1236           0 :             auto poLayerDefn = poLayer->GetLayerDefn();
    1237           0 :             poLayerDefn->SetGeometryIgnored(TRUE);
    1238           0 :             if (poLayerDefn->GetFieldCount() > 0)
    1239             :             {
    1240           0 :                 std::set<std::string> osFields;
    1241           0 :                 OGRFieldDefn *poFieldDefn = poLayerDefn->GetFieldDefn(0);
    1242           0 :                 osFields.insert(poFieldDefn->GetNameRef());
    1243           0 :                 poLayer->SetSelectedFields(osFields);
    1244             :             }
    1245             :             CPLString osNgwDelete =
    1246           0 :                 "NGW:" +
    1247           0 :                 OGRNGWLayer::TranslateSQLToFilter(
    1248           0 :                     reinterpret_cast<swq_expr_node *>(oQuery.GetSWQExpr()));
    1249             : 
    1250           0 :             poLayer->SetAttributeFilter(osNgwDelete);
    1251             : 
    1252           0 :             std::vector<GIntBig> aiFeaturesIDs;
    1253             :             OGRFeature *poFeat;
    1254           0 :             while ((poFeat = poLayer->GetNextFeature()) != nullptr)
    1255             :             {
    1256           0 :                 aiFeaturesIDs.push_back(poFeat->GetFID());
    1257           0 :                 OGRFeature::DestroyFeature(poFeat);
    1258             :             }
    1259             : 
    1260           0 :             poLayer->DeleteFeatures(aiFeaturesIDs);
    1261             : 
    1262             :             // Reset all filters and ignores
    1263           0 :             poLayerDefn->SetGeometryIgnored(FALSE);
    1264           0 :             poLayer->SetAttributeFilter(nullptr);
    1265           0 :             poLayer->SetIgnoredFields(nullptr);
    1266             :         }
    1267           0 :         return nullptr;
    1268             :     }
    1269             : 
    1270           0 :     if (STARTS_WITH_CI(osStatement, "DROP TABLE"))
    1271             :     {
    1272             :         // Get layer name from pszStatement DELETE FROM layer;.
    1273           0 :         CPLString osLayerName = osStatement.substr(strlen("DROP TABLE "));
    1274           0 :         if (osLayerName.endsWith(";"))
    1275             :         {
    1276           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
    1277           0 :             osLayerName.Trim();
    1278             :         }
    1279             : 
    1280           0 :         CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
    1281             : 
    1282           0 :         for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer)
    1283             :         {
    1284           0 :             if (EQUAL(aoLayers[iLayer]->GetName(), osLayerName))
    1285             :             {
    1286           0 :                 DeleteLayer(iLayer);
    1287           0 :                 return nullptr;
    1288             :             }
    1289             :         }
    1290             : 
    1291           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
    1292             :                  osLayerName.c_str());
    1293             : 
    1294           0 :         return nullptr;
    1295             :     }
    1296             : 
    1297           0 :     if (STARTS_WITH_CI(osStatement, "ALTER TABLE "))
    1298             :     {
    1299           0 :         if (osStatement.endsWith(";"))
    1300             :         {
    1301           0 :             osStatement = osStatement.substr(0, osStatement.size() - 1);
    1302           0 :             osStatement.Trim();
    1303             :         }
    1304             : 
    1305           0 :         CPLStringList aosTokens(SQLTokenize(osStatement));
    1306             :         /* ALTER TABLE src_table RENAME TO dst_table */
    1307           0 :         if (aosTokens.size() == 6 && EQUAL(aosTokens[3], "RENAME") &&
    1308           0 :             EQUAL(aosTokens[4], "TO"))
    1309             :         {
    1310           0 :             const char *pszSrcTableName = aosTokens[2];
    1311           0 :             const char *pszDstTableName = aosTokens[5];
    1312             : 
    1313             :             OGRNGWLayer *poLayer = static_cast<OGRNGWLayer *>(
    1314           0 :                 GetLayerByName(SQLUnescape(pszSrcTableName)));
    1315           0 :             if (poLayer)
    1316             :             {
    1317           0 :                 poLayer->Rename(SQLUnescape(pszDstTableName));
    1318           0 :                 return nullptr;
    1319             :             }
    1320             : 
    1321           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
    1322             :                      pszSrcTableName);
    1323             :         }
    1324             :         else
    1325             :         {
    1326           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1327             :                      "Unsupported alter table operation. Only rename table to "
    1328             :                      "... support.");
    1329             :         }
    1330           0 :         return nullptr;
    1331             :     }
    1332             : 
    1333             :     // SELECT xxxxx FROM yyyy WHERE zzzzzz;
    1334           0 :     if (STARTS_WITH_CI(osStatement, "SELECT "))
    1335             :     {
    1336           0 :         swq_select oSelect;
    1337           0 :         CPLDebug("NGW", "Select statement: %s", osStatement.c_str());
    1338           0 :         if (oSelect.preparse(osStatement) != CE_None)
    1339             :         {
    1340           0 :             return nullptr;
    1341             :         }
    1342             : 
    1343           0 :         if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
    1344           0 :             oSelect.table_count == 1 && oSelect.order_specs == 0)
    1345             :         {
    1346             :             OGRNGWLayer *poLayer = reinterpret_cast<OGRNGWLayer *>(
    1347           0 :                 GetLayerByName(oSelect.table_defs[0].table_name));
    1348           0 :             if (nullptr == poLayer)
    1349             :             {
    1350           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1351             :                          "Layer %s not found in dataset.",
    1352           0 :                          oSelect.table_defs[0].table_name);
    1353           0 :                 return nullptr;
    1354             :             }
    1355             : 
    1356           0 :             std::set<std::string> aosFields;
    1357           0 :             bool bSkip = false;
    1358           0 :             for (int i = 0; i < oSelect.result_columns(); ++i)
    1359             :             {
    1360           0 :                 swq_col_func col_func = oSelect.column_defs[i].col_func;
    1361           0 :                 if (col_func != SWQCF_NONE)
    1362             :                 {
    1363           0 :                     bSkip = true;
    1364           0 :                     break;
    1365             :                 }
    1366             : 
    1367           0 :                 if (oSelect.column_defs[i].distinct_flag)
    1368             :                 {
    1369           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1370             :                              "Distinct not supported.");
    1371           0 :                     bSkip = true;
    1372           0 :                     break;
    1373             :                 }
    1374             : 
    1375           0 :                 if (oSelect.column_defs[i].field_name != nullptr)
    1376             :                 {
    1377           0 :                     if (EQUAL(oSelect.column_defs[i].field_name, "*"))
    1378             :                     {
    1379           0 :                         aosFields.clear();
    1380           0 :                         aosFields.emplace(oSelect.column_defs[i].field_name);
    1381           0 :                         break;
    1382             :                     }
    1383             :                     else
    1384             :                     {
    1385           0 :                         aosFields.emplace(oSelect.column_defs[i].field_name);
    1386             :                     }
    1387             :                 }
    1388             :             }
    1389             : 
    1390           0 :             std::string osNgwSelect;
    1391           0 :             for (int iKey = 0; iKey < oSelect.order_specs; iKey++)
    1392             :             {
    1393           0 :                 swq_order_def *psKeyDef = oSelect.order_defs + iKey;
    1394           0 :                 if (iKey > 0)
    1395             :                 {
    1396           0 :                     osNgwSelect += ",";
    1397             :                 }
    1398             : 
    1399           0 :                 if (psKeyDef->ascending_flag == TRUE)
    1400             :                 {
    1401           0 :                     osNgwSelect += psKeyDef->field_name;
    1402             :                 }
    1403             :                 else
    1404             :                 {
    1405           0 :                     osNgwSelect += "-" + std::string(psKeyDef->field_name);
    1406             :                 }
    1407             :             }
    1408             : 
    1409           0 :             if (oSelect.where_expr != nullptr)
    1410             :             {
    1411           0 :                 if (!osNgwSelect.empty())
    1412             :                 {
    1413           0 :                     osNgwSelect += "&";
    1414             :                 }
    1415             :                 osNgwSelect +=
    1416           0 :                     OGRNGWLayer::TranslateSQLToFilter(oSelect.where_expr);
    1417             :             }
    1418             : 
    1419           0 :             if (osNgwSelect.empty())
    1420             :             {
    1421           0 :                 bSkip = true;
    1422             :             }
    1423             : 
    1424           0 :             if (!bSkip)
    1425             :             {
    1426           0 :                 if (aosFields.empty())
    1427             :                 {
    1428           0 :                     CPLError(
    1429             :                         CE_Failure, CPLE_AppDefined,
    1430             :                         "SELECT statement is invalid: field list is empty.");
    1431           0 :                     return nullptr;
    1432             :                 }
    1433             : 
    1434           0 :                 if (poLayer->SyncToDisk() != OGRERR_NONE)
    1435             :                 {
    1436           0 :                     return nullptr;
    1437             :                 }
    1438             : 
    1439           0 :                 OGRNGWLayer *poOutLayer = poLayer->Clone();
    1440           0 :                 if (aosFields.size() == 1 && *(aosFields.begin()) == "*")
    1441             :                 {
    1442           0 :                     poOutLayer->SetIgnoredFields(nullptr);
    1443             :                 }
    1444             :                 else
    1445             :                 {
    1446           0 :                     poOutLayer->SetSelectedFields(aosFields);
    1447             :                 }
    1448           0 :                 poOutLayer->SetSpatialFilter(poSpatialFilter);
    1449             : 
    1450           0 :                 if (osNgwSelect
    1451           0 :                         .empty())  // If we here oSelect.where_expr is empty
    1452             :                 {
    1453           0 :                     poOutLayer->SetAttributeFilter(nullptr);
    1454             :                 }
    1455             :                 else
    1456             :                 {
    1457           0 :                     std::string osAttributeFilte = "NGW:" + osNgwSelect;
    1458           0 :                     poOutLayer->SetAttributeFilter(osAttributeFilte.c_str());
    1459             :                 }
    1460           0 :                 return poOutLayer;
    1461             :             }
    1462             :         }
    1463             :     }
    1464             : 
    1465           0 :     return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter, pszDialect);
    1466             : }
    1467             : 
    1468             : /*
    1469             :  * GetProjectionRef()
    1470             :  */
    1471           0 : const OGRSpatialReference *OGRNGWDataset::GetSpatialRef() const
    1472             : {
    1473           0 :     if (poRasterDS != nullptr)
    1474             :     {
    1475           0 :         return poRasterDS->GetSpatialRef();
    1476             :     }
    1477           0 :     return GDALDataset::GetSpatialRef();
    1478             : }
    1479             : 
    1480             : /*
    1481             :  * GetGeoTransform()
    1482             :  */
    1483           0 : CPLErr OGRNGWDataset::GetGeoTransform(double *padfTransform)
    1484             : {
    1485           0 :     if (poRasterDS != nullptr)
    1486             :     {
    1487           0 :         return poRasterDS->GetGeoTransform(padfTransform);
    1488             :     }
    1489           0 :     return GDALDataset::GetGeoTransform(padfTransform);
    1490             : }
    1491             : 
    1492             : /*
    1493             :  * IRasterIO()
    1494             :  */
    1495           0 : CPLErr OGRNGWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    1496             :                                 int nXSize, int nYSize, void *pData,
    1497             :                                 int nBufXSize, int nBufYSize,
    1498             :                                 GDALDataType eBufType, int nBandCount,
    1499             :                                 BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
    1500             :                                 GSpacing nLineSpace, GSpacing nBandSpace,
    1501             :                                 GDALRasterIOExtraArg *psExtraArg)
    1502             : {
    1503           0 :     if (poRasterDS != nullptr)
    1504             :     {
    1505           0 :         if (stPixelExtent.IsInit())
    1506             :         {
    1507           0 :             OGREnvelope stTestExtent;
    1508           0 :             stTestExtent.MinX = static_cast<double>(nXOff);
    1509           0 :             stTestExtent.MinY = static_cast<double>(nYOff);
    1510           0 :             stTestExtent.MaxX = static_cast<double>(nXOff + nXSize);
    1511           0 :             stTestExtent.MaxY = static_cast<double>(nYOff + nYSize);
    1512             : 
    1513           0 :             if (!stPixelExtent.Intersects(stTestExtent))
    1514             :             {
    1515           0 :                 CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f",
    1516             :                          stPixelExtent.MinX, stPixelExtent.MinY,
    1517             :                          stPixelExtent.MaxX, stPixelExtent.MaxY);
    1518           0 :                 CPLDebug("NGW", "RasterIO extent is: %f, %f, %f, %f",
    1519             :                          stTestExtent.MinX, stTestExtent.MinY,
    1520             :                          stTestExtent.MaxX, stTestExtent.MaxY);
    1521             : 
    1522             :                 // Fill buffer transparent color.
    1523           0 :                 memset(pData, 0,
    1524           0 :                        static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount *
    1525           0 :                            GDALGetDataTypeSizeBytes(eBufType));
    1526           0 :                 return CE_None;
    1527             :             }
    1528             :         }
    1529             :     }
    1530           0 :     return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    1531             :                                   nBufXSize, nBufYSize, eBufType, nBandCount,
    1532             :                                   panBandMap, nPixelSpace, nLineSpace,
    1533           0 :                                   nBandSpace, psExtraArg);
    1534             : }
    1535             : 
    1536             : /*
    1537             :  * FillCapabilities()
    1538             :  */
    1539           0 : void OGRNGWDataset::FillCapabilities(const CPLStringList &aosHTTPOptions)
    1540             : {
    1541             :     // Check NGW version. Paging available from 3.1
    1542           0 :     CPLJSONDocument oRouteReq;
    1543           0 :     if (oRouteReq.LoadUrl(NGWAPI::GetVersionURL(osUrl), aosHTTPOptions))
    1544             :     {
    1545           0 :         CPLJSONObject oRoot = oRouteReq.GetRoot();
    1546             : 
    1547           0 :         if (oRoot.IsValid())
    1548             :         {
    1549           0 :             std::string osVersion = oRoot.GetString("nextgisweb", "0.0");
    1550           0 :             bHasFeaturePaging = NGWAPI::CheckVersion(osVersion, 3, 1);
    1551             : 
    1552           0 :             CPLDebug("NGW", "Is feature paging supported: %s",
    1553           0 :                      bHasFeaturePaging ? "yes" : "no");
    1554             :         }
    1555             :     }
    1556           0 : }
    1557             : 
    1558             : /*
    1559             :  * Extensions()
    1560             :  */
    1561           0 : std::string OGRNGWDataset::Extensions() const
    1562             : {
    1563           0 :     return osExtensions;
    1564             : }
    1565             : 
    1566             : /*
    1567             :  * GetFieldDomainNames()
    1568             :  */
    1569           0 : std::vector<std::string> OGRNGWDataset::GetFieldDomainNames(CSLConstList) const
    1570             : {
    1571           0 :     std::vector<std::string> oDomainNamesList;
    1572           0 :     std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger,
    1573             :                                              OFTInteger64};
    1574           0 :     for (auto const &oDom : moDomains)
    1575             :     {
    1576           0 :         for (auto eFieldType : aeFieldTypes)
    1577             :         {
    1578           0 :             auto pOgrDom = oDom.second.ToFieldDomain(eFieldType);
    1579           0 :             if (pOgrDom != nullptr)
    1580             :             {
    1581           0 :                 oDomainNamesList.emplace_back(pOgrDom->GetName());
    1582             :             }
    1583             :         }
    1584             :     }
    1585           0 :     return oDomainNamesList;
    1586             : }
    1587             : 
    1588             : /*
    1589             :  * GetFieldDomain()
    1590             :  */
    1591             : const OGRFieldDomain *
    1592           0 : OGRNGWDataset::GetFieldDomain(const std::string &name) const
    1593             : {
    1594           0 :     std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger,
    1595             :                                              OFTInteger64};
    1596           0 :     for (auto const &oDom : moDomains)
    1597             :     {
    1598           0 :         for (auto eFieldType : aeFieldTypes)
    1599             :         {
    1600           0 :             auto pOgrDom = oDom.second.ToFieldDomain(eFieldType);
    1601           0 :             if (pOgrDom != nullptr)
    1602             :             {
    1603           0 :                 if (pOgrDom->GetName() == name)
    1604             :                 {
    1605           0 :                     return pOgrDom;
    1606             :                 }
    1607             :             }
    1608             :         }
    1609             :     }
    1610           0 :     return nullptr;
    1611             : }
    1612             : 
    1613             : /*
    1614             :  * DeleteFieldDomain()
    1615             :  */
    1616           0 : bool OGRNGWDataset::DeleteFieldDomain(const std::string &name,
    1617             :                                       std::string &failureReason)
    1618             : {
    1619           0 :     if (eAccess != GA_Update)
    1620             :     {
    1621             :         failureReason =
    1622           0 :             "DeleteFieldDomain() not supported on read-only dataset";
    1623           0 :         return false;
    1624             :     }
    1625             : 
    1626           0 :     std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger,
    1627             :                                              OFTInteger64};
    1628           0 :     for (auto const &oDom : moDomains)
    1629             :     {
    1630           0 :         for (auto eFieldType : aeFieldTypes)
    1631             :         {
    1632           0 :             auto pOgrDom = oDom.second.ToFieldDomain(eFieldType);
    1633           0 :             if (pOgrDom != nullptr)
    1634             :             {
    1635           0 :                 if (pOgrDom->GetName() == name)
    1636             :                 {
    1637           0 :                     auto nResourceID = oDom.second.GetID();
    1638             : 
    1639           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1640             :                              "Delete following domains with common "
    1641             :                              "identifier " CPL_FRMT_GIB ": %s.",
    1642             :                              nResourceID,
    1643           0 :                              oDom.second.GetDomainsNames().c_str());
    1644             : 
    1645           0 :                     auto result = NGWAPI::DeleteResource(
    1646           0 :                         GetUrl(), std::to_string(nResourceID),
    1647           0 :                         GetHeaders(false));
    1648           0 :                     if (!result)
    1649             :                     {
    1650           0 :                         failureReason = CPLGetLastErrorMsg();
    1651           0 :                         return result;
    1652             :                     }
    1653             : 
    1654           0 :                     moDomains.erase(nResourceID);
    1655             : 
    1656             :                     // Remove domain from fields definitions
    1657           0 :                     for (const auto &oLayer : aoLayers)
    1658             :                     {
    1659           0 :                         for (int i = 0;
    1660           0 :                              i < oLayer->GetLayerDefn()->GetFieldCount(); ++i)
    1661             :                         {
    1662             :                             OGRFieldDefn *poFieldDefn =
    1663           0 :                                 oLayer->GetLayerDefn()->GetFieldDefn(i);
    1664           0 :                             if (oDom.second.HasDomainName(
    1665             :                                     poFieldDefn->GetDomainName()))
    1666             :                             {
    1667             :                                 auto oTemporaryUnsealer(
    1668           0 :                                     poFieldDefn->GetTemporaryUnsealer());
    1669           0 :                                 poFieldDefn->SetDomainName(std::string());
    1670             :                             }
    1671             :                         }
    1672             :                     }
    1673           0 :                     return true;
    1674             :                 }
    1675             :             }
    1676             :         }
    1677             :     }
    1678           0 :     failureReason = "Domain does not exist";
    1679           0 :     return false;
    1680             : }
    1681             : 
    1682             : /*
    1683             :  * CreateNGWLookupTableJson()
    1684             :  */
    1685           0 : static std::string CreateNGWLookupTableJson(OGRCodedFieldDomain *pDomain,
    1686             :                                             GIntBig nResourceId)
    1687             : {
    1688           0 :     CPLJSONObject oResourceJson;
    1689             :     // Add resource json item.
    1690           0 :     CPLJSONObject oResource("resource", oResourceJson);
    1691           0 :     oResource.Add("cls", "lookup_table");
    1692           0 :     CPLJSONObject oResourceParent("parent", oResource);
    1693           0 :     oResourceParent.Add("id", nResourceId);
    1694           0 :     oResource.Add("display_name", pDomain->GetName());
    1695           0 :     oResource.Add("description", pDomain->GetDescription());
    1696             : 
    1697             :     // Add vector_layer json item.
    1698           0 :     CPLJSONObject oLookupTable("lookup_table", oResourceJson);
    1699           0 :     CPLJSONObject oLookupTableItems("items", oLookupTable);
    1700           0 :     const auto enumeration = pDomain->GetEnumeration();
    1701           0 :     for (int i = 0; enumeration[i].pszCode != nullptr; ++i)
    1702             :     {
    1703           0 :         const char *pszValCurrent = "";
    1704             :         // NGW not supported null as coded value, so set it as ""
    1705           0 :         if (enumeration[i].pszValue != nullptr)
    1706             :         {
    1707           0 :             pszValCurrent = enumeration[i].pszValue;
    1708             :         }
    1709           0 :         oLookupTableItems.Add(enumeration[i].pszCode, pszValCurrent);
    1710             :     }
    1711             : 
    1712           0 :     return oResourceJson.Format(CPLJSONObject::PrettyFormat::Plain);
    1713             : }
    1714             : 
    1715             : /*
    1716             :  * AddFieldDomain()
    1717             :  */
    1718           0 : bool OGRNGWDataset::AddFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain,
    1719             :                                    std::string &failureReason)
    1720             : {
    1721           0 :     const std::string domainName(domain->GetName());
    1722           0 :     if (eAccess != GA_Update)
    1723             :     {
    1724           0 :         failureReason = "Add field domain not supported on read-only dataset";
    1725           0 :         return false;
    1726             :     }
    1727             : 
    1728           0 :     if (GetFieldDomain(domainName) != nullptr)
    1729             :     {
    1730           0 :         failureReason = "A domain of identical name already exists";
    1731           0 :         return false;
    1732             :     }
    1733             : 
    1734           0 :     if (domain->GetDomainType() != OFDT_CODED)
    1735             :     {
    1736           0 :         failureReason = "Unsupported domain type";
    1737           0 :         return false;
    1738             :     }
    1739             : 
    1740             :     auto osPalyload = CreateNGWLookupTableJson(
    1741           0 :         dynamic_cast<OGRCodedFieldDomain *>(domain.get()),
    1742           0 :         static_cast<GIntBig>(std::stol(osResourceId)));
    1743             : 
    1744             :     std::string osResourceIdInt =
    1745           0 :         NGWAPI::CreateResource(osUrl, osPalyload, GetHeaders());
    1746           0 :     if (osResourceIdInt == "-1")
    1747             :     {
    1748           0 :         failureReason = CPLGetLastErrorMsg();
    1749           0 :         return false;
    1750             :     }
    1751           0 :     auto osNewResourceUrl = NGWAPI::GetResourceURL(osUrl, osResourceIdInt);
    1752           0 :     CPLJSONDocument oResourceDetailsReq;
    1753             :     bool bResult =
    1754           0 :         oResourceDetailsReq.LoadUrl(osNewResourceUrl, GetHeaders(false));
    1755           0 :     if (!bResult)
    1756             :     {
    1757           0 :         failureReason = CPLGetLastErrorMsg();
    1758           0 :         return false;
    1759             :     }
    1760             : 
    1761           0 :     OGRNGWCodedFieldDomain oDomain(oResourceDetailsReq.GetRoot());
    1762           0 :     if (oDomain.GetID() == 0)
    1763             :     {
    1764           0 :         failureReason = "Failed to parse domain detailes from NGW";
    1765           0 :         return false;
    1766             :     }
    1767           0 :     moDomains[oDomain.GetID()] = oDomain;
    1768           0 :     return true;
    1769             : }
    1770             : 
    1771             : /*
    1772             :  * UpdateFieldDomain()
    1773             :  */
    1774           0 : bool OGRNGWDataset::UpdateFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain,
    1775             :                                       std::string &failureReason)
    1776             : {
    1777           0 :     const std::string domainName(domain->GetName());
    1778           0 :     if (eAccess != GA_Update)
    1779             :     {
    1780           0 :         failureReason = "Add field domain not supported on read-only dataset";
    1781           0 :         return false;
    1782             :     }
    1783             : 
    1784           0 :     if (GetFieldDomain(domainName) == nullptr)
    1785             :     {
    1786           0 :         failureReason = "The domain should already exist to be updated";
    1787           0 :         return false;
    1788             :     }
    1789             : 
    1790           0 :     if (domain->GetDomainType() != OFDT_CODED)
    1791             :     {
    1792           0 :         failureReason = "Unsupported domain type";
    1793           0 :         return false;
    1794             :     }
    1795             : 
    1796           0 :     auto nResourceId = GetDomainIdByName(domainName);
    1797           0 :     if (nResourceId == 0)
    1798             :     {
    1799           0 :         failureReason = "Failed get NGW domain identifier";
    1800           0 :         return false;
    1801             :     }
    1802             : 
    1803             :     auto osPayload = CreateNGWLookupTableJson(
    1804           0 :         dynamic_cast<OGRCodedFieldDomain *>(domain.get()),
    1805           0 :         static_cast<GIntBig>(std::stol(osResourceId)));
    1806             : 
    1807           0 :     if (!NGWAPI::UpdateResource(osUrl, osResourceId, osPayload, GetHeaders()))
    1808             :     {
    1809           0 :         failureReason = CPLGetLastErrorMsg();
    1810           0 :         return false;
    1811             :     }
    1812             : 
    1813           0 :     auto osNewResourceUrl = NGWAPI::GetResourceURL(osUrl, osResourceId);
    1814           0 :     CPLJSONDocument oResourceDetailsReq;
    1815             :     bool bResult =
    1816           0 :         oResourceDetailsReq.LoadUrl(osNewResourceUrl, GetHeaders(false));
    1817           0 :     if (!bResult)
    1818             :     {
    1819           0 :         failureReason = CPLGetLastErrorMsg();
    1820           0 :         return false;
    1821             :     }
    1822             : 
    1823           0 :     OGRNGWCodedFieldDomain oDomain(oResourceDetailsReq.GetRoot());
    1824           0 :     if (oDomain.GetID() == 0)
    1825             :     {
    1826           0 :         failureReason = "Failed to parse domain detailes from NGW";
    1827           0 :         return false;
    1828             :     }
    1829           0 :     moDomains[oDomain.GetID()] = oDomain;
    1830           0 :     return true;
    1831             : }
    1832             : 
    1833             : /*
    1834             :  * GetDomainByID()
    1835             :  */
    1836           0 : OGRNGWCodedFieldDomain OGRNGWDataset::GetDomainByID(GIntBig id) const
    1837             : {
    1838           0 :     auto pos = moDomains.find(id);
    1839           0 :     if (pos == moDomains.end())
    1840             :     {
    1841           0 :         return OGRNGWCodedFieldDomain();
    1842             :     }
    1843             :     else
    1844             :     {
    1845           0 :         return pos->second;
    1846             :     }
    1847             : }
    1848             : 
    1849             : /*
    1850             :  *  GetDomainIdByName()
    1851             :  */
    1852           0 : GIntBig OGRNGWDataset::GetDomainIdByName(const std::string &osDomainName) const
    1853             : {
    1854           0 :     for (auto oDom : moDomains)
    1855             :     {
    1856           0 :         if (oDom.second.HasDomainName(osDomainName))
    1857             :         {
    1858           0 :             return oDom.first;
    1859             :         }
    1860             :     }
    1861           0 :     return 0L;
    1862             : }

Generated by: LCOV version 1.14