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 572 0.0 %
Date: 2024-11-21 22:18:42 Functions: 0 32 0.0 %

          Line data    Source code
       1             : /*******************************************************************************
       2             :  *  Project: NextGIS Web Driver
       3             :  *  Purpose: Implements NextGIS Web Driver
       4             :  *  Author: Dmitry Baryshnikov, dmitry.baryshnikov@nextgis.com
       5             :  *  Language: C++
       6             :  *******************************************************************************
       7             :  *  The MIT License (MIT)
       8             :  *
       9             :  *  Copyright (c) 2018-2020, NextGIS <info@nextgis.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  *******************************************************************************/
      13             : 
      14             : #include "ogr_ngw.h"
      15             : 
      16             : #include "cpl_http.h"
      17             : #include "gdal_proxy.h"
      18             : 
      19             : #include <limits>
      20             : 
      21             : class NGWWrapperRasterBand : public GDALProxyRasterBand
      22             : {
      23             :     GDALRasterBand *poBaseBand;
      24             : 
      25             :   protected:
      26             :     virtual GDALRasterBand *
      27           0 :     RefUnderlyingRasterBand(bool /*bForceOpen*/) const override
      28             :     {
      29           0 :         return poBaseBand;
      30             :     }
      31             : 
      32             :   public:
      33           0 :     explicit NGWWrapperRasterBand(GDALRasterBand *poBaseBandIn)
      34           0 :         : poBaseBand(poBaseBandIn)
      35             :     {
      36           0 :         eDataType = poBaseBand->GetRasterDataType();
      37           0 :         poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
      38           0 :     }
      39             : 
      40           0 :     virtual ~NGWWrapperRasterBand()
      41           0 :     {
      42           0 :     }
      43             : };
      44             : 
      45             : /*
      46             :  * OGRNGWDataset()
      47             :  */
      48           0 : OGRNGWDataset::OGRNGWDataset()
      49             :     : nBatchSize(-1), nPageSize(-1), bFetchedPermissions(false),
      50             :       bHasFeaturePaging(false), bExtInNativeData(false), bMetadataDerty(false),
      51             :       papoLayers(nullptr), nLayers(0), poRasterDS(nullptr), nRasters(0),
      52             :       nCacheExpires(604800),    // 7 days
      53             :       nCacheMaxSize(67108864),  // 64 MB
      54           0 :       osJsonDepth("32")
      55             : {
      56           0 : }
      57             : 
      58             : /*
      59             :  * ~OGRNGWDataset()
      60             :  */
      61           0 : OGRNGWDataset::~OGRNGWDataset()
      62             : {
      63             :     // Last sync with server.
      64           0 :     OGRNGWDataset::FlushCache(true);
      65             : 
      66           0 :     if (poRasterDS != nullptr)
      67             :     {
      68           0 :         GDALClose(poRasterDS);
      69           0 :         poRasterDS = nullptr;
      70             :     }
      71             : 
      72           0 :     for (int i = 0; i < nLayers; ++i)
      73             :     {
      74           0 :         delete papoLayers[i];
      75             :     }
      76           0 :     CPLFree(papoLayers);
      77           0 : }
      78             : 
      79             : /*
      80             :  * FetchPermissions()
      81             :  */
      82           0 : void OGRNGWDataset::FetchPermissions()
      83             : {
      84           0 :     if (bFetchedPermissions)
      85             :     {
      86           0 :         return;
      87             :     }
      88             : 
      89           0 :     if (IsUpdateMode())
      90             :     {
      91             :         // Check connection and is it read only.
      92           0 :         char **papszHTTPOptions = GetHeaders();
      93             :         stPermissions = NGWAPI::CheckPermissions(
      94           0 :             osUrl, osResourceId, papszHTTPOptions, IsUpdateMode());
      95           0 :         CSLDestroy(papszHTTPOptions);
      96             :     }
      97             :     else
      98             :     {
      99           0 :         stPermissions.bDataCanRead = true;
     100           0 :         stPermissions.bResourceCanRead = true;
     101           0 :         stPermissions.bDatastructCanRead = true;
     102           0 :         stPermissions.bMetadataCanRead = true;
     103             :     }
     104           0 :     bFetchedPermissions = true;
     105             : }
     106             : 
     107             : /*
     108             :  * TestCapability()
     109             :  */
     110           0 : int OGRNGWDataset::TestCapability(const char *pszCap)
     111             : {
     112           0 :     FetchPermissions();
     113           0 :     if (EQUAL(pszCap, ODsCCreateLayer))
     114             :     {
     115           0 :         return stPermissions.bResourceCanCreate;
     116             :     }
     117           0 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     118             :     {
     119           0 :         return stPermissions.bResourceCanDelete;
     120             :     }
     121           0 :     else if (EQUAL(pszCap, "RenameLayer"))
     122             :     {
     123           0 :         return stPermissions.bResourceCanUpdate;
     124             :     }
     125           0 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     126             :     {
     127           0 :         return stPermissions.bDataCanWrite;  // FIXME: Check on resource level
     128             :                                              // is this permission set?
     129             :     }
     130           0 :     else if (EQUAL(pszCap, ODsCRandomLayerRead))
     131             :     {
     132           0 :         return stPermissions.bDataCanRead;
     133             :     }
     134           0 :     else if (EQUAL(pszCap, ODsCZGeometries))
     135             :     {
     136           0 :         return TRUE;
     137             :     }
     138             :     else
     139             :     {
     140           0 :         return FALSE;
     141             :     }
     142             : }
     143             : 
     144             : /*
     145             :  * GetLayer()
     146             :  */
     147           0 : OGRLayer *OGRNGWDataset::GetLayer(int iLayer)
     148             : {
     149           0 :     if (iLayer < 0 || iLayer >= nLayers)
     150             :     {
     151           0 :         return nullptr;
     152             :     }
     153             :     else
     154             :     {
     155           0 :         return papoLayers[iLayer];
     156             :     }
     157             : }
     158             : 
     159             : /*
     160             :  * Open()
     161             :  */
     162           0 : bool OGRNGWDataset::Open(const std::string &osUrlIn,
     163             :                          const std::string &osResourceIdIn,
     164             :                          char **papszOpenOptionsIn, bool bUpdateIn,
     165             :                          int nOpenFlagsIn)
     166             : {
     167           0 :     osUrl = osUrlIn;
     168           0 :     osResourceId = osResourceIdIn;
     169             : 
     170           0 :     eAccess = bUpdateIn ? GA_Update : GA_ReadOnly;
     171             : 
     172             :     osUserPwd = CSLFetchNameValueDef(papszOpenOptionsIn, "USERPWD",
     173           0 :                                      CPLGetConfigOption("NGW_USERPWD", ""));
     174             : 
     175           0 :     nBatchSize =
     176           0 :         atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "BATCH_SIZE",
     177             :                                   CPLGetConfigOption("NGW_BATCH_SIZE", "-1")));
     178             : 
     179           0 :     nPageSize =
     180           0 :         atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "PAGE_SIZE",
     181             :                                   CPLGetConfigOption("NGW_PAGE_SIZE", "-1")));
     182           0 :     if (nPageSize == 0)
     183             :     {
     184           0 :         nPageSize = -1;
     185             :     }
     186             : 
     187           0 :     nCacheExpires = atoi(CSLFetchNameValueDef(
     188             :         papszOpenOptionsIn, "CACHE_EXPIRES",
     189             :         CPLGetConfigOption("NGW_CACHE_EXPIRES", "604800")));
     190             : 
     191           0 :     nCacheMaxSize = atoi(CSLFetchNameValueDef(
     192             :         papszOpenOptionsIn, "CACHE_MAX_SIZE",
     193             :         CPLGetConfigOption("NGW_CACHE_MAX_SIZE", "67108864")));
     194             : 
     195           0 :     bExtInNativeData =
     196           0 :         CPLFetchBool(papszOpenOptionsIn, "NATIVE_DATA",
     197           0 :                      CPLTestBool(CPLGetConfigOption("NGW_NATIVE_DATA", "NO")));
     198             : 
     199             :     osJsonDepth =
     200             :         CSLFetchNameValueDef(papszOpenOptionsIn, "JSON_DEPTH",
     201           0 :                              CPLGetConfigOption("NGW_JSON_DEPTH", "32"));
     202             : 
     203             :     osExtensions =
     204             :         CSLFetchNameValueDef(papszOpenOptionsIn, "EXTENSIONS",
     205           0 :                              CPLGetConfigOption("NGW_EXTENSIONS", ""));
     206             : 
     207           0 :     if (osExtensions.empty())
     208             :     {
     209           0 :         bExtInNativeData = false;
     210             :     }
     211             : 
     212           0 :     return Init(nOpenFlagsIn);
     213             : }
     214             : 
     215             : /*
     216             :  * Open()
     217             :  *
     218             :  * The pszFilename templates:
     219             :  *      - NGW:http://some.nextgis.com/resource/0
     220             :  *      - NGW:http://some.nextgis.com:8000/test/resource/0
     221             :  */
     222           0 : bool OGRNGWDataset::Open(const char *pszFilename, char **papszOpenOptionsIn,
     223             :                          bool bUpdateIn, int nOpenFlagsIn)
     224             : {
     225           0 :     NGWAPI::Uri stUri = NGWAPI::ParseUri(pszFilename);
     226             : 
     227           0 :     if (stUri.osPrefix != "NGW")
     228             :     {
     229           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s",
     230             :                  pszFilename);
     231           0 :         return false;
     232             :     }
     233             : 
     234           0 :     osUrl = stUri.osAddress;
     235           0 :     osResourceId = stUri.osResourceId;
     236             : 
     237           0 :     return Open(stUri.osAddress, stUri.osResourceId, papszOpenOptionsIn,
     238           0 :                 bUpdateIn, nOpenFlagsIn);
     239             : }
     240             : 
     241             : /*
     242             :  * Init()
     243             :  */
     244           0 : bool OGRNGWDataset::Init(int nOpenFlagsIn)
     245             : {
     246             :     // NOTE: Skip check API version at that moment. We expected API v3.
     247             : 
     248             :     // Get resource details.
     249           0 :     CPLJSONDocument oResourceDetailsReq;
     250           0 :     char **papszHTTPOptions = GetHeaders();
     251           0 :     bool bResult = oResourceDetailsReq.LoadUrl(
     252           0 :         NGWAPI::GetResource(osUrl, osResourceId), papszHTTPOptions);
     253             : 
     254           0 :     CPLDebug("NGW", "Get resource %s details %s", osResourceId.c_str(),
     255             :              bResult ? "success" : "failed");
     256             : 
     257           0 :     if (bResult)
     258             :     {
     259           0 :         CPLJSONObject oRoot = oResourceDetailsReq.GetRoot();
     260             : 
     261           0 :         if (oRoot.IsValid())
     262             :         {
     263           0 :             std::string osResourceType = oRoot.GetString("resource/cls");
     264           0 :             FillMetadata(oRoot);
     265             : 
     266           0 :             if (osResourceType == "resource_group")
     267             :             {
     268             :                 // Check feature paging.
     269           0 :                 FillCapabilities(papszHTTPOptions);
     270           0 :                 if (oRoot.GetBool("resource/children", false))
     271             :                 {
     272             :                     // Get child resources.
     273           0 :                     bResult = FillResources(papszHTTPOptions, nOpenFlagsIn);
     274             :                 }
     275             :             }
     276           0 :             else if ((osResourceType == "vector_layer" ||
     277           0 :                       osResourceType == "postgis_layer"))
     278             :             {
     279             :                 // Check feature paging.
     280           0 :                 FillCapabilities(papszHTTPOptions);
     281             :                 // Add vector layer.
     282           0 :                 AddLayer(oRoot, papszHTTPOptions, nOpenFlagsIn);
     283             :             }
     284           0 :             else if (osResourceType == "mapserver_style" ||
     285           0 :                      osResourceType == "qgis_vector_style" ||
     286           0 :                      osResourceType == "raster_style" ||
     287           0 :                      osResourceType == "qgis_raster_style" ||
     288           0 :                      osResourceType == "wmsclient_layer")
     289             :             {
     290             :                 // GetExtent from parent.
     291           0 :                 OGREnvelope stExtent;
     292           0 :                 std::string osParentId = oRoot.GetString("resource/parent/id");
     293           0 :                 bool bExtentResult = NGWAPI::GetExtent(
     294           0 :                     osUrl, osParentId, papszHTTPOptions, 3857, stExtent);
     295             : 
     296           0 :                 if (!bExtentResult)
     297             :                 {
     298             :                     // Set full extent for EPSG:3857.
     299           0 :                     stExtent.MinX = -20037508.34;
     300           0 :                     stExtent.MaxX = 20037508.34;
     301           0 :                     stExtent.MinY = -20037508.34;
     302           0 :                     stExtent.MaxY = 20037508.34;
     303             :                 }
     304             : 
     305           0 :                 CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f",
     306             :                          stExtent.MinX, stExtent.MinY, stExtent.MaxX,
     307             :                          stExtent.MaxY);
     308             : 
     309           0 :                 int nEPSG = 3857;
     310             :                 // Get parent details. We can skip this as default SRS in NGW is
     311             :                 // 3857.
     312           0 :                 if (osResourceType == "wmsclient_layer")
     313             :                 {
     314           0 :                     nEPSG = oRoot.GetInteger("wmsclient_layer/srs/id", nEPSG);
     315             :                 }
     316             :                 else
     317             :                 {
     318           0 :                     CPLJSONDocument oResourceReq;
     319           0 :                     bResult = oResourceReq.LoadUrl(
     320           0 :                         NGWAPI::GetResource(osUrl, osResourceId),
     321             :                         papszHTTPOptions);
     322             : 
     323           0 :                     if (bResult)
     324             :                     {
     325           0 :                         CPLJSONObject oParentRoot = oResourceReq.GetRoot();
     326           0 :                         if (osResourceType == "mapserver_style" ||
     327           0 :                             osResourceType == "qgis_vector_style")
     328             :                         {
     329           0 :                             nEPSG = oParentRoot.GetInteger(
     330             :                                 "vector_layer/srs/id", nEPSG);
     331             :                         }
     332           0 :                         else if (osResourceType == "raster_style" ||
     333           0 :                                  osResourceType == "qgis_raster_style")
     334             :                         {
     335           0 :                             nEPSG = oParentRoot.GetInteger(
     336             :                                 "raster_layer/srs/id", nEPSG);
     337             :                         }
     338             :                     }
     339             :                 }
     340             : 
     341             :                 // Create raster dataset.
     342           0 :                 std::string osRasterUrl = NGWAPI::GetTMS(osUrl, osResourceId);
     343             :                 char *pszRasterUrl =
     344           0 :                     CPLEscapeString(osRasterUrl.c_str(), -1, CPLES_XML);
     345           0 :                 const char *pszConnStr = CPLSPrintf(
     346             :                     "<GDAL_WMS><Service name=\"TMS\">"
     347             :                     "<ServerUrl>%s</ServerUrl></Service><DataWindow>"
     348             :                     "<UpperLeftX>-20037508.34</"
     349             :                     "UpperLeftX><UpperLeftY>20037508.34</UpperLeftY>"
     350             :                     "<LowerRightX>20037508.34</"
     351             :                     "LowerRightX><LowerRightY>-20037508.34</LowerRightY>"
     352             :                     "<TileLevel>%d</TileLevel><TileCountX>1</TileCountX>"
     353             :                     "<TileCountY>1</TileCountY><YOrigin>top</YOrigin></"
     354             :                     "DataWindow>"
     355             :                     "<Projection>EPSG:%d</Projection><BlockSizeX>256</"
     356             :                     "BlockSizeX>"
     357             :                     "<BlockSizeY>256</BlockSizeY><BandsCount>%d</BandsCount>"
     358             :                     "<Cache><Type>file</Type><Expires>%d</Expires><MaxSize>%d</"
     359             :                     "MaxSize>"
     360             :                     "</Cache><ZeroBlockHttpCodes>204,404</ZeroBlockHttpCodes></"
     361             :                     "GDAL_WMS>",
     362             :                     pszRasterUrl,
     363             :                     22,     // NOTE: We have no limit in zoom levels.
     364             :                     nEPSG,  // NOTE: Default SRS is EPSG:3857.
     365             :                     4, nCacheExpires, nCacheMaxSize);
     366             : 
     367           0 :                 CPLFree(pszRasterUrl);
     368             : 
     369           0 :                 poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
     370             :                     pszConnStr,
     371             :                     GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     372             :                     nullptr, nullptr, nullptr));
     373             : 
     374           0 :                 if (poRasterDS)
     375             :                 {
     376           0 :                     bResult = true;
     377           0 :                     nRasterXSize = poRasterDS->GetRasterXSize();
     378           0 :                     nRasterYSize = poRasterDS->GetRasterYSize();
     379             : 
     380           0 :                     for (int iBand = 1; iBand <= poRasterDS->GetRasterCount();
     381             :                          iBand++)
     382             :                     {
     383           0 :                         SetBand(iBand, new NGWWrapperRasterBand(
     384           0 :                                            poRasterDS->GetRasterBand(iBand)));
     385             :                     }
     386             : 
     387             :                     // Set pixel limits.
     388           0 :                     bool bHasTransform = false;
     389           0 :                     double geoTransform[6] = {0.0};
     390           0 :                     double invGeoTransform[6] = {0.0};
     391           0 :                     if (poRasterDS->GetGeoTransform(geoTransform) == CE_None)
     392             :                     {
     393           0 :                         bHasTransform =
     394           0 :                             GDALInvGeoTransform(geoTransform,
     395             :                                                 invGeoTransform) == TRUE;
     396             :                     }
     397             : 
     398           0 :                     if (bHasTransform)
     399             :                     {
     400           0 :                         GDALApplyGeoTransform(
     401             :                             invGeoTransform, stExtent.MinX, stExtent.MinY,
     402             :                             &stPixelExtent.MinX, &stPixelExtent.MaxY);
     403             : 
     404           0 :                         GDALApplyGeoTransform(
     405             :                             invGeoTransform, stExtent.MaxX, stExtent.MaxY,
     406             :                             &stPixelExtent.MaxX, &stPixelExtent.MinY);
     407             : 
     408           0 :                         CPLDebug("NGW",
     409             :                                  "Raster extent in px is: %f, %f, %f, %f",
     410             :                                  stPixelExtent.MinX, stPixelExtent.MinY,
     411             :                                  stPixelExtent.MaxX, stPixelExtent.MaxY);
     412             :                     }
     413             :                     else
     414             :                     {
     415           0 :                         stPixelExtent.MinX = 0.0;
     416           0 :                         stPixelExtent.MinY = 0.0;
     417           0 :                         stPixelExtent.MaxX = std::numeric_limits<double>::max();
     418           0 :                         stPixelExtent.MaxY = std::numeric_limits<double>::max();
     419             :                     }
     420             :                 }
     421             :                 else
     422             :                 {
     423           0 :                     bResult = false;
     424             :                 }
     425             :             }
     426           0 :             else if (osResourceType ==
     427             :                      "raster_layer")  // FIXME: Do we need this check? &&
     428             :                                       // nOpenFlagsIn & GDAL_OF_RASTER )
     429             :             {
     430           0 :                 AddRaster(oRoot, papszHTTPOptions);
     431             :             }
     432             :             else
     433             :             {
     434           0 :                 bResult = false;
     435             :             }
     436             :             // TODO: Add support for baselayers, webmap, wfsserver_service,
     437             :             // wmsserver_service.
     438             :         }
     439             :     }
     440             : 
     441           0 :     CSLDestroy(papszHTTPOptions);
     442           0 :     return bResult;
     443             : }
     444             : 
     445             : /*
     446             :  * FillResources()
     447             :  */
     448           0 : bool OGRNGWDataset::FillResources(char **papszOptions, int nOpenFlagsIn)
     449             : {
     450           0 :     CPLJSONDocument oResourceDetailsReq;
     451           0 :     bool bResult = oResourceDetailsReq.LoadUrl(
     452           0 :         NGWAPI::GetChildren(osUrl, osResourceId), papszOptions);
     453             : 
     454           0 :     if (bResult)
     455             :     {
     456           0 :         CPLJSONArray oChildren(oResourceDetailsReq.GetRoot());
     457           0 :         for (int i = 0; i < oChildren.Size(); ++i)
     458             :         {
     459           0 :             CPLJSONObject oChild = oChildren[i];
     460           0 :             std::string osResourceType = oChild.GetString("resource/cls");
     461           0 :             if ((osResourceType == "vector_layer" ||
     462           0 :                  osResourceType == "postgis_layer"))
     463             :             {
     464             :                 // Add vector layer. If failed, try next layer.
     465           0 :                 AddLayer(oChild, papszOptions, nOpenFlagsIn);
     466             :             }
     467           0 :             else if ((osResourceType == "raster_layer" ||
     468           0 :                       osResourceType == "wmsclient_layer") &&
     469           0 :                      nOpenFlagsIn & GDAL_OF_RASTER)
     470             :             {
     471           0 :                 AddRaster(oChild, papszOptions);
     472             :             }
     473             :             // TODO: Add support for baselayers, webmap, wfsserver_service,
     474             :             // wmsserver_service.
     475             :         }
     476             :     }
     477           0 :     return bResult;
     478             : }
     479             : 
     480             : /*
     481             :  * AddLayer()
     482             :  */
     483           0 : void OGRNGWDataset::AddLayer(const CPLJSONObject &oResourceJsonObject,
     484             :                              char **papszOptions, int nOpenFlagsIn)
     485             : {
     486           0 :     std::string osLayerResourceId;
     487           0 :     if (nOpenFlagsIn & GDAL_OF_VECTOR)
     488             :     {
     489           0 :         OGRNGWLayer *poLayer = new OGRNGWLayer(this, oResourceJsonObject);
     490           0 :         papoLayers = (OGRNGWLayer **)CPLRealloc(
     491           0 :             papoLayers, (nLayers + 1) * sizeof(OGRNGWLayer *));
     492           0 :         papoLayers[nLayers++] = poLayer;
     493           0 :         osLayerResourceId = poLayer->GetResourceId();
     494             :     }
     495             :     else
     496             :     {
     497           0 :         osLayerResourceId = oResourceJsonObject.GetString("resource/id");
     498             :     }
     499             : 
     500             :     // Check styles exist and add them as rasters.
     501           0 :     if (nOpenFlagsIn & GDAL_OF_RASTER &&
     502           0 :         oResourceJsonObject.GetBool("resource/children", false))
     503             :     {
     504           0 :         CPLJSONDocument oResourceChildReq;
     505           0 :         bool bResult = oResourceChildReq.LoadUrl(
     506           0 :             NGWAPI::GetChildren(osUrl, osLayerResourceId), papszOptions);
     507             : 
     508           0 :         if (bResult)
     509             :         {
     510           0 :             CPLJSONArray oChildren(oResourceChildReq.GetRoot());
     511           0 :             for (int i = 0; i < oChildren.Size(); ++i)
     512             :             {
     513           0 :                 AddRaster(oChildren[i], papszOptions);
     514             :             }
     515             :         }
     516             :     }
     517           0 : }
     518             : 
     519             : /*
     520             :  * AddRaster()
     521             :  */
     522           0 : void OGRNGWDataset::AddRaster(const CPLJSONObject &oRasterJsonObj,
     523             :                               char **papszOptions)
     524             : {
     525           0 :     std::string osOutResourceId;
     526           0 :     std::string osOutResourceName;
     527           0 :     std::string osResourceType = oRasterJsonObj.GetString("resource/cls");
     528           0 :     if (osResourceType == "mapserver_style" ||
     529           0 :         osResourceType == "qgis_vector_style" ||
     530           0 :         osResourceType == "raster_style" ||
     531           0 :         osResourceType == "qgis_raster_style" ||
     532           0 :         osResourceType == "wmsclient_layer")
     533             :     {
     534           0 :         osOutResourceId = oRasterJsonObj.GetString("resource/id");
     535           0 :         osOutResourceName = oRasterJsonObj.GetString("resource/display_name");
     536             :     }
     537           0 :     else if (osResourceType == "raster_layer")
     538             :     {
     539             :         std::string osRasterResourceId =
     540           0 :             oRasterJsonObj.GetString("resource/id");
     541           0 :         CPLJSONDocument oResourceRequest;
     542           0 :         bool bResult = oResourceRequest.LoadUrl(
     543           0 :             NGWAPI::GetChildren(osUrl, osRasterResourceId), papszOptions);
     544             : 
     545           0 :         if (bResult)
     546             :         {
     547           0 :             CPLJSONArray oChildren(oResourceRequest.GetRoot());
     548           0 :             for (int i = 0; i < oChildren.Size(); ++i)
     549             :             {
     550           0 :                 CPLJSONObject oChild = oChildren[i];
     551           0 :                 osResourceType = oChild.GetString("resource/cls");
     552           0 :                 if (osResourceType == "raster_style" ||
     553           0 :                     osResourceType == "qgis_raster_style")
     554             :                 {
     555           0 :                     AddRaster(oChild, papszOptions);
     556             :                 }
     557             :             }
     558             :         }
     559             :     }
     560             : 
     561           0 :     if (!osOutResourceId.empty())
     562             :     {
     563           0 :         if (osOutResourceName.empty())
     564             :         {
     565           0 :             osOutResourceName = "raster_" + osOutResourceId;
     566             :         }
     567             : 
     568           0 :         CPLDebug("NGW", "Add raster %s: %s", osOutResourceId.c_str(),
     569             :                  osOutResourceName.c_str());
     570             : 
     571           0 :         GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_NAME", nRasters),
     572             :                                      CPLSPrintf("NGW:%s/resource/%s",
     573             :                                                 osUrl.c_str(),
     574             :                                                 osOutResourceId.c_str()),
     575             :                                      "SUBDATASETS");
     576           0 :         GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_DESC", nRasters),
     577             :                                      osOutResourceName.c_str(), "SUBDATASETS");
     578           0 :         nRasters++;
     579             :     }
     580           0 : }
     581             : 
     582             : /*
     583             :  * ICreateLayer
     584             :  */
     585           0 : OGRLayer *OGRNGWDataset::ICreateLayer(const char *pszNameIn,
     586             :                                       const OGRGeomFieldDefn *poGeomFieldDefn,
     587             :                                       CSLConstList papszOptions)
     588             : {
     589           0 :     if (!IsUpdateMode())
     590             :     {
     591           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     592             :                  "Operation not available in read-only mode");
     593           0 :         return nullptr;
     594             :     }
     595             : 
     596           0 :     const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     597             :     const auto poSpatialRef =
     598           0 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     599             : 
     600             :     // Check permissions as we create new layer in memory and will create in
     601             :     // during SyncToDisk.
     602           0 :     FetchPermissions();
     603             : 
     604           0 :     if (!stPermissions.bResourceCanCreate)
     605             :     {
     606           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
     607           0 :         return nullptr;
     608             :     }
     609             : 
     610             :     // Check input parameters.
     611           0 :     if ((eGType < wkbPoint || eGType > wkbMultiPolygon) &&
     612           0 :         (eGType < wkbPoint25D || eGType > wkbMultiPolygon25D))
     613             :     {
     614           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unsupported geometry type: %s",
     615             :                  OGRGeometryTypeToName(eGType));
     616           0 :         return nullptr;
     617             :     }
     618             : 
     619           0 :     if (!poSpatialRef)
     620             :     {
     621           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Undefined spatial reference");
     622           0 :         return nullptr;
     623             :     }
     624             : 
     625           0 :     OGRSpatialReference *poSRSClone = poSpatialRef->Clone();
     626           0 :     poSRSClone->AutoIdentifyEPSG();
     627           0 :     const char *pszEPSG = poSRSClone->GetAuthorityCode(nullptr);
     628           0 :     int nEPSG = -1;
     629           0 :     if (pszEPSG != nullptr)
     630             :     {
     631           0 :         nEPSG = atoi(pszEPSG);
     632             :     }
     633             : 
     634           0 :     if (nEPSG != 3857)  // TODO: Check NextGIS Web supported SRS.
     635             :     {
     636           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     637             :                  "Unsupported spatial reference EPSG code: %d", nEPSG);
     638           0 :         poSRSClone->Release();
     639           0 :         return nullptr;
     640             :     }
     641             : 
     642             :     // Do we already have this layer?  If so, should we blow it away?
     643           0 :     bool bOverwrite = CPLFetchBool(papszOptions, "OVERWRITE", false);
     644           0 :     for (int iLayer = 0; iLayer < nLayers; ++iLayer)
     645             :     {
     646           0 :         if (EQUAL(pszNameIn, papoLayers[iLayer]->GetName()))
     647             :         {
     648           0 :             if (bOverwrite)
     649             :             {
     650           0 :                 DeleteLayer(iLayer);
     651           0 :                 break;
     652             :             }
     653             :             else
     654             :             {
     655           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     656             :                          "Layer %s already exists, CreateLayer failed.\n"
     657             :                          "Use the layer creation option OVERWRITE=YES to "
     658             :                          "replace it.",
     659             :                          pszNameIn);
     660           0 :                 poSRSClone->Release();
     661           0 :                 return nullptr;
     662             :             }
     663             :         }
     664             :     }
     665             : 
     666             :     // Create layer.
     667           0 :     std::string osKey = CSLFetchNameValueDef(papszOptions, "KEY", "");
     668           0 :     std::string osDesc = CSLFetchNameValueDef(papszOptions, "DESCRIPTION", "");
     669           0 :     poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     670             :     OGRNGWLayer *poLayer =
     671           0 :         new OGRNGWLayer(this, pszNameIn, poSRSClone, eGType, osKey, osDesc);
     672           0 :     poSRSClone->Release();
     673           0 :     papoLayers = (OGRNGWLayer **)CPLRealloc(
     674           0 :         papoLayers, (nLayers + 1) * sizeof(OGRNGWLayer *));
     675           0 :     papoLayers[nLayers++] = poLayer;
     676           0 :     return poLayer;
     677             : }
     678             : 
     679             : /*
     680             :  * DeleteLayer()
     681             :  */
     682           0 : OGRErr OGRNGWDataset::DeleteLayer(int iLayer)
     683             : {
     684           0 :     if (!IsUpdateMode())
     685             :     {
     686           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     687             :                  "Operation not available in read-only mode.");
     688           0 :         return OGRERR_FAILURE;
     689             :     }
     690             : 
     691           0 :     if (iLayer < 0 || iLayer >= nLayers)
     692             :     {
     693           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     694             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
     695           0 :                  nLayers - 1);
     696           0 :         return OGRERR_FAILURE;
     697             :     }
     698             : 
     699           0 :     OGRNGWLayer *poLayer = static_cast<OGRNGWLayer *>(papoLayers[iLayer]);
     700             : 
     701           0 :     if (poLayer->GetResourceId() != "-1")
     702             :     {
     703             :         // For layers from server we can check permissions.
     704             : 
     705             :         // We can skip check permissions here as papoLayers[iLayer]->Delete()
     706             :         // will return false if no delete permission available.
     707           0 :         FetchPermissions();
     708             : 
     709           0 :         if (!stPermissions.bResourceCanDelete)
     710             :         {
     711           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
     712           0 :             return OGRERR_FAILURE;
     713             :         }
     714             :     }
     715             : 
     716           0 :     if (poLayer->Delete())
     717             :     {
     718           0 :         delete poLayer;
     719           0 :         memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
     720           0 :                 sizeof(void *) * (nLayers - iLayer - 1));
     721           0 :         nLayers--;
     722             :     }
     723             : 
     724           0 :     return OGRERR_NONE;
     725             : }
     726             : 
     727             : /*
     728             :  * FillMetadata()
     729             :  */
     730           0 : void OGRNGWDataset::FillMetadata(const CPLJSONObject &oRootObject)
     731             : {
     732           0 :     std::string osCreateDate = oRootObject.GetString("resource/creation_date");
     733           0 :     if (!osCreateDate.empty())
     734             :     {
     735           0 :         GDALDataset::SetMetadataItem("creation_date", osCreateDate.c_str());
     736             :     }
     737           0 :     osName = oRootObject.GetString("resource/display_name");
     738           0 :     SetDescription(osName.c_str());
     739           0 :     GDALDataset::SetMetadataItem("display_name", osName.c_str());
     740           0 :     std::string osDescription = oRootObject.GetString("resource/description");
     741           0 :     if (!osDescription.empty())
     742             :     {
     743           0 :         GDALDataset::SetMetadataItem("description", osDescription.c_str());
     744             :     }
     745           0 :     std::string osResourceType = oRootObject.GetString("resource/cls");
     746           0 :     if (!osResourceType.empty())
     747             :     {
     748           0 :         GDALDataset::SetMetadataItem("resource_type", osResourceType.c_str());
     749             :     }
     750             :     std::string osResourceParentId =
     751           0 :         oRootObject.GetString("resource/parent/id");
     752           0 :     if (!osResourceParentId.empty())
     753             :     {
     754           0 :         GDALDataset::SetMetadataItem("parent_id", osResourceParentId.c_str());
     755             :     }
     756           0 :     GDALDataset::SetMetadataItem("id", osResourceId.c_str());
     757             : 
     758             :     std::vector<CPLJSONObject> items =
     759           0 :         oRootObject.GetObj("resmeta/items").GetChildren();
     760             : 
     761           0 :     for (const CPLJSONObject &item : items)
     762             :     {
     763           0 :         std::string osSuffix = NGWAPI::GetResmetaSuffix(item.GetType());
     764           0 :         GDALDataset::SetMetadataItem((item.GetName() + osSuffix).c_str(),
     765           0 :                                      item.ToString().c_str(), "NGW");
     766             :     }
     767           0 : }
     768             : 
     769             : /*
     770             :  * FlushMetadata()
     771             :  */
     772           0 : bool OGRNGWDataset::FlushMetadata(char **papszMetadata)
     773             : {
     774           0 :     if (!bMetadataDerty)
     775             :     {
     776           0 :         return true;
     777             :     }
     778             : 
     779             :     bool bResult =
     780           0 :         NGWAPI::FlushMetadata(osUrl, osResourceId, papszMetadata, GetHeaders());
     781           0 :     if (bResult)
     782             :     {
     783           0 :         bMetadataDerty = false;
     784             :     }
     785             : 
     786           0 :     return bResult;
     787             : }
     788             : 
     789             : /*
     790             :  * SetMetadata()
     791             :  */
     792           0 : CPLErr OGRNGWDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
     793             : {
     794           0 :     FetchPermissions();
     795           0 :     if (!stPermissions.bMetadataCanWrite)
     796             :     {
     797           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
     798           0 :         return CE_Failure;
     799             :     }
     800             : 
     801           0 :     CPLErr eResult = GDALDataset::SetMetadata(papszMetadata, pszDomain);
     802           0 :     if (eResult == CE_None && pszDomain != nullptr && EQUAL(pszDomain, "NGW"))
     803             :     {
     804           0 :         eResult = FlushMetadata(papszMetadata) ? CE_None : CE_Failure;
     805             :     }
     806           0 :     return eResult;
     807             : }
     808             : 
     809             : /*
     810             :  * SetMetadataItem()
     811             :  */
     812           0 : CPLErr OGRNGWDataset::SetMetadataItem(const char *pszName, const char *pszValue,
     813             :                                       const char *pszDomain)
     814             : {
     815           0 :     FetchPermissions();
     816           0 :     if (!stPermissions.bMetadataCanWrite)
     817             :     {
     818           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
     819           0 :         return CE_Failure;
     820             :     }
     821           0 :     if (pszDomain != nullptr && EQUAL(pszDomain, "NGW"))
     822             :     {
     823           0 :         bMetadataDerty = true;
     824             :     }
     825           0 :     return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
     826             : }
     827             : 
     828             : /*
     829             :  * FlushCache()
     830             :  */
     831           0 : CPLErr OGRNGWDataset::FlushCache(bool bAtClosing)
     832             : {
     833           0 :     CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
     834           0 :     if (!FlushMetadata(GetMetadata("NGW")))
     835           0 :         eErr = CE_Failure;
     836           0 :     return eErr;
     837             : }
     838             : 
     839             : /*
     840             :  * GetHeaders()
     841             :  */
     842           0 : char **OGRNGWDataset::GetHeaders() const
     843             : {
     844           0 :     char **papszOptions = nullptr;
     845           0 :     papszOptions = CSLAddString(papszOptions, "HEADERS=Accept: */*");
     846             :     papszOptions =
     847           0 :         CSLAddNameValue(papszOptions, "JSON_DEPTH", osJsonDepth.c_str());
     848           0 :     if (!osUserPwd.empty())
     849             :     {
     850           0 :         papszOptions = CSLAddString(papszOptions, "HTTPAUTH=BASIC");
     851           0 :         std::string osUserPwdOption("USERPWD=");
     852           0 :         osUserPwdOption += osUserPwd;
     853           0 :         papszOptions = CSLAddString(papszOptions, osUserPwdOption.c_str());
     854             :     }
     855           0 :     return papszOptions;
     856             : }
     857             : 
     858             : /*
     859             :  * SQLUnescape()
     860             :  * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
     861             :  * dependency on sqlite
     862             :  */
     863           0 : static CPLString SQLUnescape(const char *pszVal)
     864             : {
     865           0 :     char chQuoteChar = pszVal[0];
     866           0 :     if (chQuoteChar != '\'' && chQuoteChar != '"')
     867           0 :         return pszVal;
     868             : 
     869           0 :     CPLString osRet;
     870           0 :     pszVal++;
     871           0 :     while (*pszVal != '\0')
     872             :     {
     873           0 :         if (*pszVal == chQuoteChar)
     874             :         {
     875           0 :             if (pszVal[1] == chQuoteChar)
     876           0 :                 pszVal++;
     877             :             else
     878           0 :                 break;
     879             :         }
     880           0 :         osRet += *pszVal;
     881           0 :         pszVal++;
     882             :     }
     883           0 :     return osRet;
     884             : }
     885             : 
     886             : /*
     887             :  * SQLTokenize()
     888             :  * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
     889             :  * dependency on sqlite
     890             :  */
     891           0 : static char **SQLTokenize(const char *pszStr)
     892             : {
     893           0 :     char **papszTokens = nullptr;
     894           0 :     bool bInQuote = false;
     895           0 :     char chQuoteChar = '\0';
     896           0 :     bool bInSpace = true;
     897           0 :     CPLString osCurrentToken;
     898           0 :     while (*pszStr != '\0')
     899             :     {
     900           0 :         if (*pszStr == ' ' && !bInQuote)
     901             :         {
     902           0 :             if (!bInSpace)
     903             :             {
     904           0 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
     905           0 :                 osCurrentToken.clear();
     906             :             }
     907           0 :             bInSpace = true;
     908             :         }
     909           0 :         else if ((*pszStr == '(' || *pszStr == ')' || *pszStr == ',') &&
     910           0 :                  !bInQuote)
     911             :         {
     912           0 :             if (!bInSpace)
     913             :             {
     914           0 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
     915           0 :                 osCurrentToken.clear();
     916             :             }
     917           0 :             osCurrentToken.clear();
     918           0 :             osCurrentToken += *pszStr;
     919           0 :             papszTokens = CSLAddString(papszTokens, osCurrentToken);
     920           0 :             osCurrentToken.clear();
     921           0 :             bInSpace = true;
     922             :         }
     923           0 :         else if (*pszStr == '"' || *pszStr == '\'')
     924             :         {
     925           0 :             if (bInQuote && *pszStr == chQuoteChar && pszStr[1] == chQuoteChar)
     926             :             {
     927           0 :                 osCurrentToken += *pszStr;
     928           0 :                 osCurrentToken += *pszStr;
     929           0 :                 pszStr += 2;
     930           0 :                 continue;
     931             :             }
     932           0 :             else if (bInQuote && *pszStr == chQuoteChar)
     933             :             {
     934           0 :                 osCurrentToken += *pszStr;
     935           0 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
     936           0 :                 osCurrentToken.clear();
     937           0 :                 bInSpace = true;
     938           0 :                 bInQuote = false;
     939           0 :                 chQuoteChar = '\0';
     940             :             }
     941           0 :             else if (bInQuote)
     942             :             {
     943           0 :                 osCurrentToken += *pszStr;
     944             :             }
     945             :             else
     946             :             {
     947           0 :                 chQuoteChar = *pszStr;
     948           0 :                 osCurrentToken.clear();
     949           0 :                 osCurrentToken += chQuoteChar;
     950           0 :                 bInQuote = true;
     951           0 :                 bInSpace = false;
     952             :             }
     953             :         }
     954             :         else
     955             :         {
     956           0 :             osCurrentToken += *pszStr;
     957           0 :             bInSpace = false;
     958             :         }
     959           0 :         pszStr++;
     960             :     }
     961             : 
     962           0 :     if (!osCurrentToken.empty())
     963           0 :         papszTokens = CSLAddString(papszTokens, osCurrentToken);
     964             : 
     965           0 :     return papszTokens;
     966             : }
     967             : 
     968             : /*
     969             :  * ExecuteSQL()
     970             :  */
     971           0 : OGRLayer *OGRNGWDataset::ExecuteSQL(const char *pszStatement,
     972             :                                     OGRGeometry *poSpatialFilter,
     973             :                                     const char *pszDialect)
     974             : {
     975             :     // Clean statement string.
     976           0 :     CPLString osStatement(pszStatement);
     977           0 :     osStatement = osStatement.Trim().replaceAll("  ", " ");
     978             : 
     979           0 :     if (STARTS_WITH_CI(osStatement, "DELLAYER:"))
     980             :     {
     981           0 :         CPLString osLayerName = osStatement.substr(strlen("DELLAYER:"));
     982           0 :         if (osLayerName.endsWith(";"))
     983             :         {
     984           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
     985           0 :             osLayerName.Trim();
     986             :         }
     987             : 
     988           0 :         CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
     989             : 
     990           0 :         for (int iLayer = 0; iLayer < nLayers; ++iLayer)
     991             :         {
     992           0 :             if (EQUAL(papoLayers[iLayer]->GetName(), osLayerName))
     993             :             {
     994           0 :                 DeleteLayer(iLayer);
     995           0 :                 return nullptr;
     996             :             }
     997             :         }
     998           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
     999             :                  osLayerName.c_str());
    1000             : 
    1001           0 :         return nullptr;
    1002             :     }
    1003             : 
    1004           0 :     if (STARTS_WITH_CI(osStatement, "DELETE FROM"))
    1005             :     {
    1006             :         // Get layer name from pszStatement DELETE FROM layer;.
    1007           0 :         CPLString osLayerName = osStatement.substr(strlen("DELETE FROM "));
    1008           0 :         if (osLayerName.endsWith(";"))
    1009             :         {
    1010           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
    1011           0 :             osLayerName.Trim();
    1012             :         }
    1013             : 
    1014           0 :         CPLDebug("NGW", "Delete features from layer with name %s.",
    1015             :                  osLayerName.c_str());
    1016             : 
    1017             :         OGRNGWLayer *poLayer =
    1018           0 :             static_cast<OGRNGWLayer *>(GetLayerByName(osLayerName));
    1019           0 :         if (poLayer)
    1020             :         {
    1021           0 :             poLayer->DeleteAllFeatures();
    1022             :         }
    1023             :         else
    1024             :         {
    1025           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
    1026             :                      osLayerName.c_str());
    1027             :         }
    1028           0 :         return nullptr;
    1029             :     }
    1030             : 
    1031           0 :     if (STARTS_WITH_CI(osStatement, "DROP TABLE"))
    1032             :     {
    1033             :         // Get layer name from pszStatement DELETE FROM layer;.
    1034           0 :         CPLString osLayerName = osStatement.substr(strlen("DROP TABLE "));
    1035           0 :         if (osLayerName.endsWith(";"))
    1036             :         {
    1037           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
    1038           0 :             osLayerName.Trim();
    1039             :         }
    1040             : 
    1041           0 :         CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
    1042             : 
    1043           0 :         for (int iLayer = 0; iLayer < nLayers; ++iLayer)
    1044             :         {
    1045           0 :             if (EQUAL(papoLayers[iLayer]->GetName(), osLayerName))
    1046             :             {
    1047           0 :                 DeleteLayer(iLayer);
    1048           0 :                 return nullptr;
    1049             :             }
    1050             :         }
    1051             : 
    1052           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
    1053             :                  osLayerName.c_str());
    1054             : 
    1055           0 :         return nullptr;
    1056             :     }
    1057             : 
    1058           0 :     if (STARTS_WITH_CI(osStatement, "ALTER TABLE "))
    1059             :     {
    1060           0 :         if (osStatement.endsWith(";"))
    1061             :         {
    1062           0 :             osStatement = osStatement.substr(0, osStatement.size() - 1);
    1063           0 :             osStatement.Trim();
    1064             :         }
    1065             : 
    1066           0 :         CPLStringList aosTokens(SQLTokenize(osStatement));
    1067             :         /* ALTER TABLE src_table RENAME TO dst_table */
    1068           0 :         if (aosTokens.size() == 6 && EQUAL(aosTokens[3], "RENAME") &&
    1069           0 :             EQUAL(aosTokens[4], "TO"))
    1070             :         {
    1071           0 :             const char *pszSrcTableName = aosTokens[2];
    1072           0 :             const char *pszDstTableName = aosTokens[5];
    1073             : 
    1074             :             OGRNGWLayer *poLayer = static_cast<OGRNGWLayer *>(
    1075           0 :                 GetLayerByName(SQLUnescape(pszSrcTableName)));
    1076           0 :             if (poLayer)
    1077             :             {
    1078           0 :                 poLayer->Rename(SQLUnescape(pszDstTableName));
    1079           0 :                 return nullptr;
    1080             :             }
    1081             : 
    1082           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
    1083             :                      pszSrcTableName);
    1084             :         }
    1085             :         else
    1086             :         {
    1087           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1088             :                      "Unsupported alter table operation. Only rename table to "
    1089             :                      "... support.");
    1090             :         }
    1091           0 :         return nullptr;
    1092             :     }
    1093             : 
    1094             :     // SELECT xxxxx FROM yyyy WHERE zzzzzz;
    1095           0 :     if (STARTS_WITH_CI(osStatement, "SELECT "))
    1096             :     {
    1097           0 :         swq_select oSelect;
    1098           0 :         CPLDebug("NGW", "Select statement: %s", osStatement.c_str());
    1099           0 :         if (oSelect.preparse(osStatement) != CE_None)
    1100             :         {
    1101           0 :             return nullptr;
    1102             :         }
    1103             : 
    1104           0 :         if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
    1105           0 :             oSelect.table_count == 1 && oSelect.order_specs == 0)
    1106             :         {
    1107             :             OGRNGWLayer *poLayer = reinterpret_cast<OGRNGWLayer *>(
    1108           0 :                 GetLayerByName(oSelect.table_defs[0].table_name));
    1109           0 :             if (nullptr == poLayer)
    1110             :             {
    1111           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1112             :                          "Layer %s not found in dataset.",
    1113           0 :                          oSelect.table_defs[0].table_name);
    1114           0 :                 return nullptr;
    1115             :             }
    1116             : 
    1117           0 :             std::set<std::string> aosFields;
    1118           0 :             bool bSkip = false;
    1119           0 :             for (int i = 0; i < oSelect.result_columns(); ++i)
    1120             :             {
    1121           0 :                 swq_col_func col_func = oSelect.column_defs[i].col_func;
    1122           0 :                 if (col_func != SWQCF_NONE)
    1123             :                 {
    1124           0 :                     bSkip = true;
    1125           0 :                     break;
    1126             :                 }
    1127             : 
    1128           0 :                 if (oSelect.column_defs[i].distinct_flag)
    1129             :                 {
    1130           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1131             :                              "Distinct not supported.");
    1132           0 :                     bSkip = true;
    1133           0 :                     break;
    1134             :                 }
    1135             : 
    1136           0 :                 if (oSelect.column_defs[i].field_name != nullptr)
    1137             :                 {
    1138           0 :                     if (EQUAL(oSelect.column_defs[i].field_name, "*"))
    1139             :                     {
    1140           0 :                         aosFields.clear();
    1141           0 :                         aosFields.emplace(oSelect.column_defs[i].field_name);
    1142           0 :                         break;
    1143             :                     }
    1144             :                     else
    1145             :                     {
    1146           0 :                         aosFields.emplace(oSelect.column_defs[i].field_name);
    1147             :                     }
    1148             :                 }
    1149             :             }
    1150             : 
    1151           0 :             std::string osNgwSelect;
    1152           0 :             for (int iKey = 0; iKey < oSelect.order_specs; iKey++)
    1153             :             {
    1154           0 :                 swq_order_def *psKeyDef = oSelect.order_defs + iKey;
    1155           0 :                 if (iKey > 0)
    1156             :                 {
    1157           0 :                     osNgwSelect += ",";
    1158             :                 }
    1159             : 
    1160           0 :                 if (psKeyDef->ascending_flag == TRUE)
    1161             :                 {
    1162           0 :                     osNgwSelect += psKeyDef->field_name;
    1163             :                 }
    1164             :                 else
    1165             :                 {
    1166           0 :                     osNgwSelect += "-" + std::string(psKeyDef->field_name);
    1167             :                 }
    1168             :             }
    1169             : 
    1170           0 :             if (oSelect.where_expr != nullptr)
    1171             :             {
    1172           0 :                 if (!osNgwSelect.empty())
    1173             :                 {
    1174           0 :                     osNgwSelect += "&";
    1175             :                 }
    1176             :                 osNgwSelect +=
    1177           0 :                     OGRNGWLayer::TranslateSQLToFilter(oSelect.where_expr);
    1178             :             }
    1179             : 
    1180           0 :             if (osNgwSelect.empty())
    1181             :             {
    1182           0 :                 bSkip = true;
    1183             :             }
    1184             : 
    1185           0 :             if (!bSkip)
    1186             :             {
    1187           0 :                 if (aosFields.empty())
    1188             :                 {
    1189           0 :                     CPLError(
    1190             :                         CE_Failure, CPLE_AppDefined,
    1191             :                         "SELECT statement is invalid: field list is empty.");
    1192           0 :                     return nullptr;
    1193             :                 }
    1194             : 
    1195           0 :                 if (poLayer->SyncToDisk() != OGRERR_NONE)
    1196             :                 {
    1197           0 :                     return nullptr;
    1198             :                 }
    1199             : 
    1200           0 :                 OGRNGWLayer *poOutLayer = poLayer->Clone();
    1201           0 :                 if (aosFields.size() == 1 && *(aosFields.begin()) == "*")
    1202             :                 {
    1203           0 :                     poOutLayer->SetIgnoredFields(nullptr);
    1204             :                 }
    1205             :                 else
    1206             :                 {
    1207           0 :                     poOutLayer->SetSelectedFields(aosFields);
    1208             :                 }
    1209           0 :                 poOutLayer->SetSpatialFilter(poSpatialFilter);
    1210             : 
    1211           0 :                 if (osNgwSelect
    1212           0 :                         .empty())  // If we here oSelect.where_expr is empty
    1213             :                 {
    1214           0 :                     poOutLayer->SetAttributeFilter(nullptr);
    1215             :                 }
    1216             :                 else
    1217             :                 {
    1218           0 :                     std::string osAttributeFilte = "NGW:" + osNgwSelect;
    1219           0 :                     poOutLayer->SetAttributeFilter(osAttributeFilte.c_str());
    1220             :                 }
    1221           0 :                 return poOutLayer;
    1222             :             }
    1223             :         }
    1224             :     }
    1225             : 
    1226           0 :     return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter, pszDialect);
    1227             : }
    1228             : 
    1229             : /*
    1230             :  * GetProjectionRef()
    1231             :  */
    1232           0 : const OGRSpatialReference *OGRNGWDataset::GetSpatialRef() const
    1233             : {
    1234           0 :     if (poRasterDS != nullptr)
    1235             :     {
    1236           0 :         return poRasterDS->GetSpatialRef();
    1237             :     }
    1238           0 :     return GDALDataset::GetSpatialRef();
    1239             : }
    1240             : 
    1241             : /*
    1242             :  * GetGeoTransform()
    1243             :  */
    1244           0 : CPLErr OGRNGWDataset::GetGeoTransform(double *padfTransform)
    1245             : {
    1246           0 :     if (poRasterDS != nullptr)
    1247             :     {
    1248           0 :         return poRasterDS->GetGeoTransform(padfTransform);
    1249             :     }
    1250           0 :     return GDALDataset::GetGeoTransform(padfTransform);
    1251             : }
    1252             : 
    1253             : /*
    1254             :  * IRasterIO()
    1255             :  */
    1256           0 : CPLErr OGRNGWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    1257             :                                 int nXSize, int nYSize, void *pData,
    1258             :                                 int nBufXSize, int nBufYSize,
    1259             :                                 GDALDataType eBufType, int nBandCount,
    1260             :                                 BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
    1261             :                                 GSpacing nLineSpace, GSpacing nBandSpace,
    1262             :                                 GDALRasterIOExtraArg *psExtraArg)
    1263             : {
    1264           0 :     if (poRasterDS != nullptr)
    1265             :     {
    1266           0 :         if (stPixelExtent.IsInit())
    1267             :         {
    1268           0 :             OGREnvelope stTestExtent;
    1269           0 :             stTestExtent.MinX = static_cast<double>(nXOff);
    1270           0 :             stTestExtent.MinY = static_cast<double>(nYOff);
    1271           0 :             stTestExtent.MaxX = static_cast<double>(nXOff + nXSize);
    1272           0 :             stTestExtent.MaxY = static_cast<double>(nYOff + nYSize);
    1273             : 
    1274           0 :             if (!stPixelExtent.Intersects(stTestExtent))
    1275             :             {
    1276           0 :                 CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f",
    1277             :                          stPixelExtent.MinX, stPixelExtent.MinY,
    1278             :                          stPixelExtent.MaxX, stPixelExtent.MaxY);
    1279           0 :                 CPLDebug("NGW", "RasterIO extent is: %f, %f, %f, %f",
    1280             :                          stTestExtent.MinX, stTestExtent.MinY,
    1281             :                          stTestExtent.MaxX, stTestExtent.MaxY);
    1282             : 
    1283             :                 // Fill buffer transparent color.
    1284           0 :                 memset(pData, 0,
    1285           0 :                        static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount *
    1286           0 :                            GDALGetDataTypeSizeBytes(eBufType));
    1287           0 :                 return CE_None;
    1288             :             }
    1289             :         }
    1290             :     }
    1291           0 :     return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
    1292             :                                   nBufXSize, nBufYSize, eBufType, nBandCount,
    1293             :                                   panBandMap, nPixelSpace, nLineSpace,
    1294           0 :                                   nBandSpace, psExtraArg);
    1295             : }
    1296             : 
    1297             : /*
    1298             :  * FillCapabilities()
    1299             :  */
    1300           0 : void OGRNGWDataset::FillCapabilities(char **papszOptions)
    1301             : {
    1302             :     // Check NGW version. Paging available from 3.1
    1303           0 :     CPLJSONDocument oRouteReq;
    1304           0 :     if (oRouteReq.LoadUrl(NGWAPI::GetVersion(osUrl), papszOptions))
    1305             :     {
    1306           0 :         CPLJSONObject oRoot = oRouteReq.GetRoot();
    1307             : 
    1308           0 :         if (oRoot.IsValid())
    1309             :         {
    1310           0 :             std::string osVersion = oRoot.GetString("nextgisweb", "0.0");
    1311           0 :             bHasFeaturePaging = NGWAPI::CheckVersion(osVersion, 3, 1);
    1312             : 
    1313           0 :             CPLDebug("NGW", "Is feature paging supported: %s",
    1314           0 :                      bHasFeaturePaging ? "yes" : "no");
    1315             :         }
    1316             :     }
    1317           0 : }
    1318             : 
    1319             : /*
    1320             :  * Extensions()
    1321             :  */
    1322           0 : std::string OGRNGWDataset::Extensions() const
    1323             : {
    1324           0 :     return osExtensions;
    1325             : }

Generated by: LCOV version 1.14