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

Generated by: LCOV version 1.14