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

Generated by: LCOV version 1.14