LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/ngw - ogrngwdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 54 225 24.0 %
Date: 2024-11-21 22:18:42 Functions: 4 8 50.0 %

          Line data    Source code
       1             : /*******************************************************************************
       2             :  *  Project: NextGIS Web Driver
       3             :  *  Purpose: Implements NextGIS Web Driver
       4             :  *  Author: Dmitry Baryshnikov, dmitry.baryshnikov@nextgis.com
       5             :  *  Language: C++
       6             :  *******************************************************************************
       7             :  *  The MIT License (MIT)
       8             :  *
       9             :  *  Copyright (c) 2018-2020, NextGIS <info@nextgis.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  *******************************************************************************/
      13             : 
      14             : #include "ogr_ngw.h"
      15             : 
      16             : /*
      17             :  * GetHeaders()
      18             :  */
      19           0 : static char **GetHeaders(const std::string &osUserPwdIn = "")
      20             : {
      21           0 :     char **papszOptions = nullptr;
      22           0 :     papszOptions = CSLAddString(papszOptions, "HEADERS=Accept: */*");
      23           0 :     std::string osUserPwd;
      24           0 :     if (osUserPwdIn.empty())
      25             :     {
      26           0 :         osUserPwd = CPLGetConfigOption("NGW_USERPWD", "");
      27             :     }
      28             :     else
      29             :     {
      30           0 :         osUserPwd = osUserPwdIn;
      31             :     }
      32             : 
      33           0 :     if (!osUserPwd.empty())
      34             :     {
      35           0 :         papszOptions = CSLAddString(papszOptions, "HTTPAUTH=BASIC");
      36           0 :         std::string osUserPwdOption("USERPWD=");
      37           0 :         osUserPwdOption += osUserPwd;
      38           0 :         papszOptions = CSLAddString(papszOptions, osUserPwdOption.c_str());
      39             :     }
      40           0 :     return papszOptions;
      41             : }
      42             : 
      43             : /*
      44             :  * OGRNGWDriverIdentify()
      45             :  */
      46             : 
      47       51624 : static int OGRNGWDriverIdentify(GDALOpenInfo *poOpenInfo)
      48             : {
      49       51624 :     return STARTS_WITH_CI(poOpenInfo->pszFilename, "NGW:");
      50             : }
      51             : 
      52             : /*
      53             :  * OGRNGWDriverOpen()
      54             :  */
      55             : 
      56           0 : static GDALDataset *OGRNGWDriverOpen(GDALOpenInfo *poOpenInfo)
      57             : {
      58           0 :     if (OGRNGWDriverIdentify(poOpenInfo) == 0)
      59             :     {
      60           0 :         return nullptr;
      61             :     }
      62             : 
      63           0 :     OGRNGWDataset *poDS = new OGRNGWDataset();
      64           0 :     if (!poDS->Open(poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions,
      65           0 :                     poOpenInfo->eAccess == GA_Update, poOpenInfo->nOpenFlags))
      66             :     {
      67           0 :         delete poDS;
      68           0 :         poDS = nullptr;
      69             :     }
      70             : 
      71           0 :     return poDS;
      72             : }
      73             : 
      74             : /*
      75             :  * OGRNGWDriverCreate()
      76             :  *
      77             :  * Add new datasource name at the end of URL:
      78             :  * NGW:http://some.nextgis.com/resource/0/new_name
      79             :  * NGW:http://some.nextgis.com:8000/test/resource/0/new_name
      80             :  */
      81             : 
      82             : static GDALDataset *
      83          32 : OGRNGWDriverCreate(const char *pszName, CPL_UNUSED int nBands,
      84             :                    CPL_UNUSED int nXSize, CPL_UNUSED int nYSize,
      85             :                    CPL_UNUSED GDALDataType eDT, char **papszOptions)
      86             : 
      87             : {
      88          96 :     NGWAPI::Uri stUri = NGWAPI::ParseUri(pszName);
      89          32 :     CPLErrorReset();
      90          32 :     if (stUri.osPrefix != "NGW")
      91             :     {
      92          32 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s", pszName);
      93          32 :         return nullptr;
      94             :     }
      95             : 
      96           0 :     CPLDebug("NGW", "Parse uri result. URL: %s, ID: %s, New name: %s",
      97             :              stUri.osAddress.c_str(), stUri.osResourceId.c_str(),
      98             :              stUri.osNewResourceName.c_str());
      99             : 
     100           0 :     std::string osKey = CSLFetchNameValueDef(papszOptions, "KEY", "");
     101           0 :     std::string osDesc = CSLFetchNameValueDef(papszOptions, "DESCRIPTION", "");
     102             :     std::string osUserPwd = CSLFetchNameValueDef(
     103           0 :         papszOptions, "USERPWD", CPLGetConfigOption("NGW_USERPWD", ""));
     104             : 
     105           0 :     CPLJSONObject oPayload;
     106           0 :     CPLJSONObject oResource("resource", oPayload);
     107           0 :     oResource.Add("cls", "resource_group");
     108           0 :     oResource.Add("display_name", stUri.osNewResourceName);
     109           0 :     if (!osKey.empty())
     110             :     {
     111           0 :         oResource.Add("keyname", osKey);
     112             :     }
     113             : 
     114           0 :     if (!osDesc.empty())
     115             :     {
     116           0 :         oResource.Add("description", osDesc);
     117             :     }
     118             : 
     119           0 :     CPLJSONObject oParent("parent", oResource);
     120           0 :     oParent.Add("id", atoi(stUri.osResourceId.c_str()));
     121             : 
     122             :     std::string osNewResourceId = NGWAPI::CreateResource(
     123           0 :         stUri.osAddress, oPayload.Format(CPLJSONObject::PrettyFormat::Plain),
     124           0 :         GetHeaders(osUserPwd));
     125           0 :     if (osNewResourceId == "-1")
     126             :     {
     127           0 :         return nullptr;
     128             :     }
     129             : 
     130           0 :     OGRNGWDataset *poDS = new OGRNGWDataset();
     131             : 
     132           0 :     if (!poDS->Open(stUri.osAddress, osNewResourceId, papszOptions, true,
     133             :                     GDAL_OF_RASTER | GDAL_OF_VECTOR))  // TODO: GDAL_OF_GNM
     134             :     {
     135           0 :         delete poDS;
     136           0 :         poDS = nullptr;
     137             :     }
     138             : 
     139           0 :     return poDS;
     140             : }
     141             : 
     142             : /*
     143             :  * OGRNGWDriverDelete()
     144             :  */
     145           0 : static CPLErr OGRNGWDriverDelete(const char *pszName)
     146             : {
     147           0 :     NGWAPI::Uri stUri = NGWAPI::ParseUri(pszName);
     148           0 :     CPLErrorReset();
     149           0 :     if (!stUri.osNewResourceName.empty())
     150             :     {
     151           0 :         CPLError(CE_Warning, CPLE_NotSupported,
     152             :                  "Cannot delete new resource with name %s", pszName);
     153           0 :         return CE_Failure;
     154             :     }
     155             : 
     156           0 :     if (stUri.osPrefix != "NGW")
     157             :     {
     158           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s", pszName);
     159           0 :         return CE_Failure;
     160             :     }
     161             : 
     162           0 :     if (stUri.osResourceId == "0")
     163             :     {
     164           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot delete resource 0");
     165           0 :         return CE_Failure;
     166             :     }
     167             : 
     168           0 :     char **papszOptions = GetHeaders();
     169             :     // NGWAPI::Permissions stPermissions =
     170             :     // NGWAPI::CheckPermissions(stUri.osAddress,
     171             :     //     stUri.osResourceId, papszOptions, true);
     172             :     // if( stPermissions.bResourceCanDelete )
     173             :     // {
     174           0 :     return NGWAPI::DeleteResource(stUri.osAddress, stUri.osResourceId,
     175             :                                   papszOptions)
     176           0 :                ? CE_None
     177           0 :                : CE_Failure;
     178             :     // }
     179             :     // CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
     180             :     // return CE_Failure;
     181             : }
     182             : 
     183             : /*
     184             :  * OGRNGWDriverRename()
     185             :  */
     186           0 : static CPLErr OGRNGWDriverRename(const char *pszNewName, const char *pszOldName)
     187             : {
     188           0 :     NGWAPI::Uri stUri = NGWAPI::ParseUri(pszOldName);
     189           0 :     CPLErrorReset();
     190           0 :     if (stUri.osPrefix != "NGW")
     191             :     {
     192           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s",
     193             :                  pszOldName);
     194           0 :         return CE_Failure;
     195             :     }
     196           0 :     CPLDebug("NGW", "Parse uri result. URL: %s, ID: %s, New name: %s",
     197             :              stUri.osAddress.c_str(), stUri.osResourceId.c_str(), pszNewName);
     198           0 :     char **papszOptions = GetHeaders();
     199             :     // NGWAPI::Permissions stPermissions =
     200             :     // NGWAPI::CheckPermissions(stUri.osAddress,
     201             :     //     stUri.osResourceId, papszOptions, true);
     202             :     // if( stPermissions.bResourceCanUpdate )
     203             :     // {
     204           0 :     return NGWAPI::RenameResource(stUri.osAddress, stUri.osResourceId,
     205             :                                   pszNewName, papszOptions)
     206           0 :                ? CE_None
     207           0 :                : CE_Failure;
     208             :     // }
     209             :     // CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
     210             :     // return CE_Failure;
     211             : }
     212             : 
     213             : /*
     214             :  * OGRNGWDriverCreateCopy()
     215             :  */
     216          18 : static GDALDataset *OGRNGWDriverCreateCopy(const char *pszFilename,
     217             :                                            GDALDataset *poSrcDS, int bStrict,
     218             :                                            char **papszOptions,
     219             :                                            GDALProgressFunc pfnProgress,
     220             :                                            void *pProgressData)
     221             : {
     222             :     // Check destination dataset,
     223          54 :     NGWAPI::Uri stUri = NGWAPI::ParseUri(pszFilename);
     224          18 :     CPLErrorReset();
     225          18 :     if (stUri.osPrefix != "NGW")
     226             :     {
     227          18 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s",
     228             :                  pszFilename);
     229          18 :         return nullptr;
     230             :     }
     231             : 
     232             :     // NGW v3.1 supported different raster types: 1 band and 16/32 bit, RGB/RGBA
     233             :     // rasters and etc.
     234             :     // For RGB/RGBA rasters we can create default raster_style.
     235             :     // For other types - qml style file path is mandatory.
     236             :     std::string osQMLPath =
     237           0 :         CSLFetchNameValueDef(papszOptions, "RASTER_QML_PATH", "");
     238             : 
     239             :     // Check bands count.
     240           0 :     const int nBands = poSrcDS->GetRasterCount();
     241           0 :     if (nBands < 3 || nBands > 4)
     242             :     {
     243           0 :         if (osQMLPath.empty())
     244             :         {
     245           0 :             CPLError(
     246             :                 CE_Failure, CPLE_NotSupported,
     247             :                 "Default NGW raster style supports only 3 (RGB) or 4 (RGBA). "
     248             :                 "Raster has %d bands. You must provide QML file with raster "
     249             :                 "style.",
     250             :                 nBands);
     251           0 :             return nullptr;
     252             :         }
     253             :     }
     254             : 
     255             :     // Check band data type.
     256           0 :     if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
     257             :     {
     258           0 :         if (osQMLPath.empty())
     259             :         {
     260           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     261             :                      "Default NGW raster style supports only 8 bit byte bands. "
     262             :                      "Raster has data type %s. You must provide QML file with "
     263             :                      "raster style.",
     264             :                      GDALGetDataTypeName(
     265             :                          poSrcDS->GetRasterBand(1)->GetRasterDataType()));
     266           0 :             return nullptr;
     267             :         }
     268             :     }
     269             : 
     270           0 :     bool bCloseDS = false;
     271           0 :     std::string osFilename;
     272             : 
     273             :     // Check if source GDALDataset is tiff.
     274           0 :     if (EQUAL(poSrcDS->GetDriverName(), "GTiff") == FALSE)
     275             :     {
     276           0 :         GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
     277             :         // Compress to minimize network transfer.
     278           0 :         const char *apszOptions[] = {"COMPRESS=LZW", "NUM_THREADS=ALL_CPUS",
     279             :                                      nullptr};
     280           0 :         std::string osTempFilename = CPLGenerateTempFilename("ngw_tmp");
     281           0 :         osTempFilename += ".tif";
     282           0 :         GDALDataset *poTmpDS = poDriver->CreateCopy(
     283             :             osTempFilename.c_str(), poSrcDS, bStrict,
     284             :             const_cast<char **>(apszOptions), pfnProgress, pProgressData);
     285             : 
     286           0 :         if (poTmpDS != nullptr)
     287             :         {
     288           0 :             bCloseDS = true;
     289           0 :             osFilename = std::move(osTempFilename);
     290           0 :             poSrcDS = poTmpDS;
     291             :         }
     292             :         else
     293             :         {
     294           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     295             :                      "NGW driver doesn't support %s source raster.",
     296           0 :                      poSrcDS->GetDriverName());
     297           0 :             return nullptr;
     298             :         }
     299             :     }
     300             : 
     301           0 :     if (osFilename.empty())
     302             :     {
     303             :         // Check if source tiff is local file.
     304           0 :         CPLStringList oaFiles(poSrcDS->GetFileList());
     305           0 :         for (int i = 0; i < oaFiles.size(); ++i)
     306             :         {
     307             :             // Check extension tif
     308           0 :             const char *pszExt = CPLGetExtension(oaFiles[i]);
     309           0 :             if (pszExt && EQUALN(pszExt, "tif", 3))
     310             :             {
     311           0 :                 osFilename = oaFiles[i];
     312           0 :                 break;
     313             :             }
     314             :         }
     315             :     }
     316             : 
     317           0 :     if (bCloseDS)
     318             :     {
     319           0 :         GDALClose((GDALDatasetH)poSrcDS);
     320             :     }
     321             : 
     322           0 :     std::string osKey = CSLFetchNameValueDef(papszOptions, "KEY", "");
     323           0 :     std::string osDesc = CSLFetchNameValueDef(papszOptions, "DESCRIPTION", "");
     324             :     std::string osUserPwd = CSLFetchNameValueDef(
     325           0 :         papszOptions, "USERPWD", CPLGetConfigOption("NGW_USERPWD", ""));
     326             :     std::string osStyleName =
     327           0 :         CSLFetchNameValueDef(papszOptions, "RASTER_STYLE_NAME", "");
     328             : 
     329             :     // Send file
     330           0 :     char **papszHTTPOptions = GetHeaders(osUserPwd);
     331             :     CPLJSONObject oFileJson =
     332             :         NGWAPI::UploadFile(stUri.osAddress, osFilename, papszHTTPOptions,
     333           0 :                            pfnProgress, pProgressData);
     334             : 
     335           0 :     if (bCloseDS)  // Delete temp tiff file.
     336             :     {
     337           0 :         VSIUnlink(osFilename.c_str());
     338             :     }
     339             : 
     340           0 :     if (!oFileJson.IsValid())
     341             :     {
     342           0 :         return nullptr;
     343             :     }
     344             : 
     345           0 :     CPLJSONArray oUploadMeta = oFileJson.GetArray("upload_meta");
     346           0 :     if (!oUploadMeta.IsValid() || oUploadMeta.Size() == 0)
     347             :     {
     348           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Get unexpected response: %s.",
     349           0 :                  oFileJson.Format(CPLJSONObject::PrettyFormat::Plain).c_str());
     350           0 :         return nullptr;
     351             :     }
     352             : 
     353             :     // Create raster layer
     354             :     // Create payload
     355           0 :     CPLJSONObject oPayloadRaster;
     356           0 :     CPLJSONObject oResource("resource", oPayloadRaster);
     357           0 :     oResource.Add("cls", "raster_layer");
     358           0 :     oResource.Add("display_name", stUri.osNewResourceName);
     359           0 :     if (!osKey.empty())
     360             :     {
     361           0 :         oResource.Add("keyname", osKey);
     362             :     }
     363             : 
     364           0 :     if (!osDesc.empty())
     365             :     {
     366           0 :         oResource.Add("description", osDesc);
     367             :     }
     368             : 
     369           0 :     CPLJSONObject oParent("parent", oResource);
     370           0 :     oParent.Add("id", atoi(stUri.osResourceId.c_str()));
     371             : 
     372           0 :     CPLJSONObject oRasterLayer("raster_layer", oPayloadRaster);
     373           0 :     oRasterLayer.Add("source", oUploadMeta[0]);
     374             : 
     375           0 :     CPLJSONObject oSrs("srs", oRasterLayer);
     376           0 :     oSrs.Add("id", 3857);  // Now only Web Mercator supported.
     377             : 
     378           0 :     papszHTTPOptions = GetHeaders(osUserPwd);
     379             :     std::string osNewResourceId = NGWAPI::CreateResource(
     380             :         stUri.osAddress,
     381           0 :         oPayloadRaster.Format(CPLJSONObject::PrettyFormat::Plain),
     382           0 :         papszHTTPOptions);
     383           0 :     if (osNewResourceId == "-1")
     384             :     {
     385           0 :         return nullptr;
     386             :     }
     387             : 
     388             :     // Create raster style
     389           0 :     CPLJSONObject oPayloadRasterStyle;
     390           0 :     CPLJSONObject oResourceStyle("resource", oPayloadRasterStyle);
     391           0 :     if (osQMLPath.empty())
     392             :     {
     393           0 :         oResourceStyle.Add("cls", "raster_style");
     394             :     }
     395             :     else
     396             :     {
     397           0 :         oResourceStyle.Add("cls", "qgis_raster_style");
     398             : 
     399             :         // Upload QML file
     400           0 :         papszHTTPOptions = GetHeaders(osUserPwd);
     401             :         oFileJson =
     402           0 :             NGWAPI::UploadFile(stUri.osAddress, osQMLPath, papszHTTPOptions,
     403           0 :                                pfnProgress, pProgressData);
     404           0 :         oUploadMeta = oFileJson.GetArray("upload_meta");
     405           0 :         if (!oUploadMeta.IsValid() || oUploadMeta.Size() == 0)
     406             :         {
     407           0 :             CPLError(
     408             :                 CE_Failure, CPLE_AppDefined, "Get unexpected response: %s.",
     409           0 :                 oFileJson.Format(CPLJSONObject::PrettyFormat::Plain).c_str());
     410           0 :             return nullptr;
     411             :         }
     412             :         CPLJSONObject oQGISRasterStyle("qgis_raster_style",
     413           0 :                                        oPayloadRasterStyle);
     414           0 :         oQGISRasterStyle.Add("file_upload", oUploadMeta[0]);
     415             :     }
     416             : 
     417           0 :     if (osStyleName.empty())
     418             :     {
     419           0 :         osStyleName = stUri.osNewResourceName;
     420             :     }
     421           0 :     oResourceStyle.Add("display_name", osStyleName);
     422           0 :     CPLJSONObject oParentRaster("parent", oResourceStyle);
     423           0 :     oParentRaster.Add("id", atoi(osNewResourceId.c_str()));
     424             : 
     425           0 :     papszHTTPOptions = GetHeaders(osUserPwd);
     426           0 :     osNewResourceId = NGWAPI::CreateResource(
     427             :         stUri.osAddress,
     428           0 :         oPayloadRasterStyle.Format(CPLJSONObject::PrettyFormat::Plain),
     429           0 :         papszHTTPOptions);
     430           0 :     if (osNewResourceId == "-1")
     431             :     {
     432           0 :         return nullptr;
     433             :     }
     434             : 
     435           0 :     OGRNGWDataset *poDS = new OGRNGWDataset();
     436             : 
     437           0 :     if (!poDS->Open(stUri.osAddress, osNewResourceId, papszOptions, true,
     438             :                     GDAL_OF_RASTER))
     439             :     {
     440           0 :         delete poDS;
     441           0 :         poDS = nullptr;
     442             :     }
     443             : 
     444           0 :     return poDS;
     445             : }
     446             : 
     447             : /*
     448             :  * RegisterOGRNGW()
     449             :  */
     450             : 
     451        1595 : void RegisterOGRNGW()
     452             : {
     453        1595 :     if (GDALGetDriverByName("NGW") != nullptr)
     454             :     {
     455         302 :         return;
     456             :     }
     457             : 
     458        1293 :     GDALDriver *poDriver = new GDALDriver();
     459             : 
     460        1293 :     poDriver->SetDescription("NGW");
     461        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "NextGIS Web");
     462        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     463        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     464        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
     465        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_DELETE_LAYER, "YES");
     466        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
     467        1293 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
     468        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/ngw.html");
     469        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "NGW:");
     470        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
     471        1293 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS,
     472        1293 :                               "NATIVE OGRSQL SQLITE");
     473             : 
     474        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
     475        1293 :     poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS, "Name");
     476        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
     477             : 
     478        1293 :     poDriver->SetMetadataItem(
     479             :         GDAL_DMD_OPENOPTIONLIST,
     480             :         "<OpenOptionList>"
     481             :         "   <Option name='USERPWD' scope='raster,vector' type='string' "
     482             :         "description='Username and password, separated by colon'/>"
     483             :         "   <Option name='PAGE_SIZE' scope='vector' type='integer' "
     484             :         "description='Limit feature count while fetching from server. Default "
     485             :         "value is -1 - no limit' default='-1'/>"
     486             :         "   <Option name='BATCH_SIZE' scope='vector' type='integer' "
     487             :         "description='Size of feature insert and update operations cache "
     488             :         "before send to server. If batch size is -1 batch mode is disabled' "
     489             :         "default='-1'/>"
     490             :         "   <Option name='NATIVE_DATA' scope='vector' type='boolean' "
     491             :         "description='Whether to store the native Json representation of "
     492             :         "extensions key. If EXTENSIONS not set or empty, NATIVE_DATA defaults "
     493             :         "to NO' default='NO'/>"
     494             :         "   <Option name='CACHE_EXPIRES' scope='raster' type='integer' "
     495             :         "description='Time in seconds cached files will stay valid. If cached "
     496             :         "file expires it is deleted when maximum size of cache is reached. "
     497             :         "Also expired file can be overwritten by the new one from web' "
     498             :         "default='604800'/>"
     499             :         "   <Option name='CACHE_MAX_SIZE' scope='raster' type='integer' "
     500             :         "description='The cache maximum size in bytes. If cache reached "
     501             :         "maximum size, expired cached files will be deleted' "
     502             :         "default='67108864'/>"
     503             :         "   <Option name='JSON_DEPTH' scope='raster,vector' type='integer' "
     504             :         "description='The depth of json response that can be parsed. If depth "
     505             :         "is greater than this value, parse error occurs' default='32'/>"
     506             :         "   <Option name='EXTENSIONS' scope='vector' type='string' "
     507             :         "description='Comma separated extensions list. Available are "
     508             :         "description and attachment' default=''/>"
     509        1293 :         "</OpenOptionList>");
     510             : 
     511        1293 :     poDriver->SetMetadataItem(
     512             :         GDAL_DMD_CREATIONOPTIONLIST,
     513             :         "<CreationOptionList>"
     514             :         "   <Option name='KEY' scope='raster,vector' type='string' "
     515             :         "description='Key value. Must be unique in whole NextGIS Web "
     516             :         "instance'/>"
     517             :         "   <Option name='DESCRIPTION' scope='raster,vector' type='string' "
     518             :         "description='Resource description'/>"
     519             :         "   <Option name='RASTER_STYLE_NAME' scope='raster' type='string' "
     520             :         "description='Raster layer style name'/>"
     521             :         "   <Option name='USERPWD' scope='raster,vector' type='string' "
     522             :         "description='Username and password, separated by colon'/>"
     523             :         "   <Option name='PAGE_SIZE' scope='vector' type='integer' "
     524             :         "description='Limit feature count while fetching from server. Default "
     525             :         "value is -1 - no limit' default='-1'/>"
     526             :         "   <Option name='BATCH_SIZE' scope='vector' type='integer' "
     527             :         "description='Size of feature insert and update operations cache "
     528             :         "before send to server. If batch size is -1 batch mode is disabled' "
     529             :         "default='-1'/>"
     530             :         "   <Option name='NATIVE_DATA' scope='vector' type='boolean' "
     531             :         "description='Whether to store the native Json representation of "
     532             :         "extensions key. If EXTENSIONS not set or empty, NATIVE_DATA defaults "
     533             :         "to NO' default='NO'/>"
     534             :         "   <Option name='CACHE_EXPIRES' scope='raster' type='integer' "
     535             :         "description='Time in seconds cached files will stay valid. If cached "
     536             :         "file expires it is deleted when maximum size of cache is reached. "
     537             :         "Also expired file can be overwritten by the new one from web' "
     538             :         "default='604800'/>"
     539             :         "   <Option name='CACHE_MAX_SIZE' scope='raster' type='integer' "
     540             :         "description='The cache maximum size in bytes. If cache reached "
     541             :         "maximum size, expired cached files will be deleted' "
     542             :         "default='67108864'/>"
     543             :         "   <Option name='JSON_DEPTH' scope='raster,vector' type='integer' "
     544             :         "description='The depth of json response that can be parsed. If depth "
     545             :         "is greater than this value, parse error occurs' default='32'/>"
     546             :         "   <Option name='RASTER_QML_PATH' scope='raster' type='string' "
     547             :         "description='Raster QMS style path'/>"
     548             :         "   <Option name='EXTENSIONS' scope='vector' type='string' "
     549             :         "description='Comma separated extensions list. Available are "
     550             :         "description and attachment' default=''/>"
     551        1293 :         "</CreationOptionList>");
     552             : 
     553        1293 :     poDriver->SetMetadataItem(
     554             :         GDAL_DS_LAYER_CREATIONOPTIONLIST,
     555             :         "<LayerCreationOptionList>"
     556             :         "   <Option name='OVERWRITE' type='boolean' description='Whether to "
     557             :         "overwrite an existing table with the layer name to be created' "
     558             :         "default='NO'/>"
     559             :         "   <Option name='KEY' type='string' description='Key value. Must be "
     560             :         "unique in whole NextGIS Web instance'/>"
     561             :         "   <Option name='DESCRIPTION' type='string' description='Resource "
     562             :         "description'/>"
     563        1293 :         "</LayerCreationOptionList>");
     564             : 
     565        1293 :     poDriver->SetMetadataItem(
     566             :         GDAL_DMD_CREATIONFIELDDATATYPES,
     567        1293 :         "Integer Integer64 Real String Date DateTime Time");
     568        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
     569        1293 :                               "AlternativeName");
     570        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES");
     571        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, "YES");
     572        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RENAME_LAYERS, "YES");
     573             : 
     574        1293 :     poDriver->pfnOpen = OGRNGWDriverOpen;
     575        1293 :     poDriver->pfnIdentify = OGRNGWDriverIdentify;
     576        1293 :     poDriver->pfnCreate = OGRNGWDriverCreate;
     577        1293 :     poDriver->pfnCreateCopy = OGRNGWDriverCreateCopy;
     578        1293 :     poDriver->pfnDelete = OGRNGWDriverDelete;
     579        1293 :     poDriver->pfnRename = OGRNGWDriverRename;
     580             : 
     581        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     582             : }

Generated by: LCOV version 1.14