LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/ngw - ngw_api.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 5 482 1.0 %
Date: 2025-10-01 17:07:58 Functions: 1 38 2.6 %

          Line data    Source code
       1             : /*******************************************************************************
       2             :  *  Project: NextGIS Web Driver
       3             :  *  Purpose: Implements NextGIS Web Driver
       4             :  *  Author: Dmitry Baryshnikov, dmitry.baryshnikov@nextgis.com
       5             :  *  Language: C++
       6             :  *******************************************************************************
       7             :  *  The MIT License (MIT)
       8             :  *
       9             :  *  Copyright (c) 2018-2025, NextGIS
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  *******************************************************************************/
      13             : 
      14             : #include "ogr_ngw.h"
      15             : 
      16             : #include "cpl_http.h"
      17             : #include "cpl_multiproc.h"  // CPLSleep()
      18             : 
      19             : #include <limits>
      20             : 
      21             : namespace NGWAPI
      22             : {
      23             : 
      24           0 : static std::string GetErrorMessage(const CPLJSONObject &oRoot,
      25             :                                    const std::string &osErrorMessage)
      26             : {
      27           0 :     if (oRoot.IsValid())
      28             :     {
      29             :         std::string osErrorMessageInt =
      30           0 :             oRoot.GetString("message", osErrorMessage);
      31           0 :         if (!osErrorMessageInt.empty())
      32             :         {
      33           0 :             return osErrorMessageInt;
      34             :         }
      35             :     }
      36           0 :     return osErrorMessage;
      37             : }
      38             : 
      39           0 : bool CheckRequestResult(bool bResult, const CPLJSONObject &oRoot,
      40             :                         const std::string &osErrorMessage)
      41             : {
      42           0 :     if (!bResult)
      43             :     {
      44           0 :         auto osMsg = GetErrorMessage(oRoot, osErrorMessage);
      45             : 
      46           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      47             :                  "NGW driver failed to fetch data with error: %s",
      48             :                  osMsg.c_str());
      49           0 :         return false;
      50             :     }
      51             : 
      52           0 :     return true;
      53             : }
      54             : 
      55           0 : static void ReportError(const GByte *pabyData, int nDataLen,
      56             :                         const std::string &osErrorMessage)
      57             : {
      58           0 :     CPLJSONDocument oResult;
      59           0 :     if (oResult.LoadMemory(pabyData, nDataLen))
      60             :     {
      61           0 :         CPLJSONObject oRoot = oResult.GetRoot();
      62           0 :         auto osMsg = GetErrorMessage(oRoot, osErrorMessage);
      63           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", osMsg.c_str());
      64             :     }
      65             :     else
      66             :     {
      67           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMessage.c_str());
      68             :     }
      69           0 : }
      70             : 
      71           0 : bool CheckSupportedType(bool bIsRaster, const std::string &osType)
      72             : {
      73             :     //TODO: Add "raster_mosaic", "tileset", "wfsserver_service" and "wmsserver_service"
      74           0 :     if (bIsRaster)
      75             :     {
      76           0 :         if (osType == "mapserver_style" || osType == "qgis_vector_style" ||
      77           0 :             osType == "raster_style" || osType == "qgis_raster_style" ||
      78           0 :             osType == "basemap_layer" || osType == "webmap" ||
      79           0 :             osType == "wmsclient_layer" || osType == "raster_layer")
      80             :         {
      81           0 :             return true;
      82             :         }
      83             :     }
      84             :     else
      85             :     {
      86           0 :         if (osType == "vector_layer" || osType == "postgis_layer")
      87             :         {
      88           0 :             return true;
      89             :         }
      90             :     }
      91           0 :     return false;
      92             : }
      93             : 
      94           0 : std::string GetPermissionsURL(const std::string &osUrl,
      95             :                               const std::string &osResourceId)
      96             : {
      97           0 :     return osUrl + "/api/resource/" + osResourceId + "/permission";
      98             : }
      99             : 
     100           0 : std::string GetResourceURL(const std::string &osUrl,
     101             :                            const std::string &osResourceId)
     102             : {
     103           0 :     return osUrl + "/api/resource/" + osResourceId;
     104             : }
     105             : 
     106           0 : std::string GetChildrenURL(const std::string &osUrl,
     107             :                            const std::string &osResourceId)
     108             : {
     109           0 :     return osUrl + "/api/resource/?parent=" + osResourceId;
     110             : }
     111             : 
     112           0 : std::string GetFeatureURL(const std::string &osUrl,
     113             :                           const std::string &osResourceId)
     114             : {
     115           0 :     return osUrl + "/api/resource/" + osResourceId + "/feature/";
     116             : }
     117             : 
     118           0 : std::string GetTMSURL(const std::string &osUrl, const std::string &osResourceId)
     119             : {
     120           0 :     return osUrl +
     121             :            "/api/component/render/"
     122             :            "tile?z=${z}&amp;x=${x}&amp;y=${y}&amp;resource=" +
     123           0 :            osResourceId;
     124             : }
     125             : 
     126           0 : std::string GetSearchURL(const std::string &osUrl, const std::string &osKey,
     127             :                          const std::string &osValue)
     128             : {
     129           0 :     return osUrl + "/api/resource/search/?" + osKey + "=" + osValue;
     130             : }
     131             : 
     132             : std::string
     133           0 : GetFeaturePageURL(const std::string &osUrl, const std::string &osResourceId,
     134             :                   GIntBig nStart, int nCount, const std::string &osFields,
     135             :                   const std::string &osWhere, const std::string &osSpatialWhere,
     136             :                   const std::string &osExtensions, bool IsGeometryIgnored)
     137             : {
     138           0 :     std::string osFeatureUrl = GetFeatureURL(osUrl, osResourceId);
     139           0 :     bool bParamAdd = false;
     140           0 :     if (nCount > 0)
     141             :     {
     142           0 :         osFeatureUrl += "?offset=" + std::to_string(nStart) +
     143           0 :                         "&limit=" + std::to_string(nCount);
     144           0 :         bParamAdd = true;
     145             :     }
     146             : 
     147           0 :     if (!osFields.empty())
     148             :     {
     149           0 :         if (bParamAdd)
     150             :         {
     151           0 :             osFeatureUrl += "&fields=" + osFields;
     152             :         }
     153             :         else
     154             :         {
     155           0 :             osFeatureUrl += "?fields=" + osFields;
     156           0 :             bParamAdd = true;
     157             :         }
     158             :     }
     159             : 
     160           0 :     if (!osWhere.empty())
     161             :     {
     162           0 :         if (bParamAdd)
     163             :         {
     164           0 :             osFeatureUrl += "&" + osWhere;
     165             :         }
     166             :         else
     167             :         {
     168           0 :             osFeatureUrl += "?" + osWhere;
     169           0 :             bParamAdd = true;
     170             :         }
     171             :     }
     172             : 
     173           0 :     if (!osSpatialWhere.empty())
     174             :     {
     175           0 :         if (bParamAdd)
     176             :         {
     177           0 :             osFeatureUrl += "&intersects=" + osSpatialWhere;
     178             :         }
     179             :         else
     180             :         {
     181           0 :             osFeatureUrl += "?intersects=" + osSpatialWhere;
     182           0 :             bParamAdd = true;
     183             :         }
     184             :     }
     185             : 
     186           0 :     if (IsGeometryIgnored)
     187             :     {
     188           0 :         if (bParamAdd)
     189             :         {
     190           0 :             osFeatureUrl += "&geom=no";
     191             :         }
     192             :         else
     193             :         {
     194           0 :             osFeatureUrl += "?geom=no";
     195           0 :             bParamAdd = true;
     196             :         }
     197             :     }
     198             : 
     199           0 :     if (bParamAdd)
     200             :     {
     201           0 :         osFeatureUrl += "&extensions=" + osExtensions;
     202             :     }
     203             :     else
     204             :     {
     205           0 :         osFeatureUrl += "?extensions=" + osExtensions;
     206             :     }
     207             : 
     208           0 :     return osFeatureUrl;
     209             : }
     210             : 
     211           0 : std::string GetRouteURL(const std::string &osUrl)
     212             : {
     213           0 :     return osUrl + "/api/component/pyramid/route";
     214             : }
     215             : 
     216           0 : std::string GetUploadURL(const std::string &osUrl)
     217             : {
     218           0 :     return osUrl + "/api/component/file_upload/upload";
     219             : }
     220             : 
     221           0 : std::string GetVersionURL(const std::string &osUrl)
     222             : {
     223           0 :     return osUrl + "/api/component/pyramid/pkg_version";
     224             : }
     225             : 
     226           0 : std::string GetCOGURL(const std::string &osUrl, const std::string &osResourceId)
     227             : {
     228           0 :     return osUrl + "/api/resource/" + osResourceId + "/cog";
     229             : }
     230             : 
     231           0 : bool CheckVersion(const std::string &osVersion, int nMajor, int nMinor,
     232             :                   int nPatch)
     233             : {
     234           0 :     int nCurrentMajor(0);
     235           0 :     int nCurrentMinor(0);
     236           0 :     int nCurrentPatch(0);
     237             : 
     238           0 :     CPLStringList aosList(CSLTokenizeString2(osVersion.c_str(), ".", 0));
     239           0 :     if (aosList.size() > 2)
     240             :     {
     241           0 :         nCurrentMajor = atoi(aosList[0]);
     242           0 :         nCurrentMinor = atoi(aosList[1]);
     243           0 :         nCurrentPatch = atoi(aosList[2]);
     244             :     }
     245           0 :     else if (aosList.size() > 1)
     246             :     {
     247           0 :         nCurrentMajor = atoi(aosList[0]);
     248           0 :         nCurrentMinor = atoi(aosList[1]);
     249             :     }
     250           0 :     else if (aosList.size() > 0)
     251             :     {
     252           0 :         nCurrentMajor = atoi(aosList[0]);
     253             :     }
     254             : 
     255           0 :     int nCheckVersion = nMajor * 1000 + nMinor * 100 + nPatch;
     256           0 :     int nCurrentVersion =
     257           0 :         nCurrentMajor * 1000 + nCurrentMinor * 100 + nCurrentPatch;
     258           0 :     return nCurrentVersion >= nCheckVersion;
     259             : }
     260             : 
     261          50 : Uri ParseUri(const std::string &osUrl)
     262             : {
     263          50 :     Uri stOut;
     264          50 :     std::size_t nFound = osUrl.find(":");
     265          50 :     if (nFound == std::string::npos)
     266             :     {
     267          50 :         return stOut;
     268             :     }
     269             : 
     270           0 :     stOut.osPrefix = osUrl.substr(0, nFound);
     271           0 :     std::string osUrlInt = CPLString(osUrl.substr(nFound + 1)).tolower();
     272             : 
     273           0 :     nFound = osUrlInt.find("/resource/");
     274           0 :     if (nFound == std::string::npos)
     275             :     {
     276           0 :         return stOut;
     277             :     }
     278             : 
     279           0 :     stOut.osAddress = osUrlInt.substr(0, nFound);
     280             : 
     281             :     std::string osResourceId =
     282           0 :         CPLString(osUrlInt.substr(nFound + strlen("/resource/"))).Trim();
     283             : 
     284           0 :     nFound = osResourceId.find('/');
     285           0 :     if (nFound != std::string::npos)
     286             :     {
     287           0 :         stOut.osResourceId = osResourceId.substr(0, nFound);
     288           0 :         stOut.osNewResourceName = osResourceId.substr(nFound + 1);
     289             :     }
     290             :     else
     291             :     {
     292           0 :         stOut.osResourceId = std::move(osResourceId);
     293             :     }
     294             : 
     295           0 :     return stOut;
     296             : }
     297             : 
     298           0 : std::string CreateResource(const std::string &osUrl,
     299             :                            const std::string &osPayload,
     300             :                            const CPLStringList &aosHTTPOptions)
     301             : {
     302           0 :     CPLErrorReset();
     303           0 :     std::string osPayloadInt = "POSTFIELDS=" + osPayload;
     304             : 
     305           0 :     CPLStringList aosHTTPOptionsInt(aosHTTPOptions);
     306             : 
     307           0 :     aosHTTPOptionsInt.AddString("CUSTOMREQUEST=POST");
     308           0 :     aosHTTPOptionsInt.AddString(osPayloadInt.c_str());
     309             :     aosHTTPOptionsInt.AddString(
     310           0 :         "HEADERS=Content-Type: application/json\r\nAccept: */*");
     311             : 
     312           0 :     CPLDebug("NGW", "CreateResource request payload: %s", osPayload.c_str());
     313             : 
     314           0 :     CPLJSONDocument oCreateReq;
     315             :     bool bResult =
     316           0 :         oCreateReq.LoadUrl(GetResourceURL(osUrl, ""), aosHTTPOptionsInt);
     317           0 :     std::string osResourceId("-1");
     318           0 :     CPLJSONObject oRoot = oCreateReq.GetRoot();
     319           0 :     if (CheckRequestResult(bResult, oRoot, "CreateResource request failed"))
     320             :     {
     321           0 :         osResourceId = oRoot.GetString("id", "-1");
     322             :     }
     323           0 :     return osResourceId;
     324             : }
     325             : 
     326           0 : bool UpdateResource(const std::string &osUrl, const std::string &osResourceId,
     327             :                     const std::string &osPayload,
     328             :                     const CPLStringList &aosHTTPOptions)
     329             : {
     330           0 :     CPLErrorReset();
     331           0 :     std::string osPayloadInt = "POSTFIELDS=" + osPayload;
     332             : 
     333           0 :     CPLStringList aosHTTPOptionsInt(aosHTTPOptions);
     334           0 :     aosHTTPOptionsInt.AddString("CUSTOMREQUEST=PUT");
     335           0 :     aosHTTPOptionsInt.AddString(osPayloadInt.c_str());
     336             :     aosHTTPOptionsInt.AddString(
     337           0 :         "HEADERS=Content-Type: application/json\r\nAccept: */*");
     338             : 
     339           0 :     CPLDebug("NGW", "UpdateResource request payload: %s", osPayload.c_str());
     340             : 
     341           0 :     CPLHTTPResult *psResult = CPLHTTPFetch(
     342           0 :         GetResourceURL(osUrl, osResourceId).c_str(), aosHTTPOptionsInt);
     343           0 :     bool bResult = false;
     344           0 :     if (psResult)
     345             :     {
     346           0 :         bResult = psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
     347             : 
     348             :         // Get error message.
     349           0 :         if (!bResult)
     350             :         {
     351           0 :             ReportError(psResult->pabyData, psResult->nDataLen,
     352             :                         "UpdateResource request failed");
     353             :         }
     354           0 :         CPLHTTPDestroyResult(psResult);
     355             :     }
     356             :     else
     357             :     {
     358           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Update resource %s failed",
     359             :                  osResourceId.c_str());
     360             :     }
     361           0 :     return bResult;
     362             : }
     363             : 
     364           0 : bool DeleteResource(const std::string &osUrl, const std::string &osResourceId,
     365             :                     const CPLStringList &aosHTTPOptions)
     366             : {
     367           0 :     CPLErrorReset();
     368           0 :     CPLStringList aosHTTPOptionsInt(aosHTTPOptions);
     369             : 
     370           0 :     aosHTTPOptionsInt.AddString("CUSTOMREQUEST=DELETE");
     371           0 :     auto osUrlNew = GetResourceURL(osUrl, osResourceId);
     372           0 :     CPLHTTPResult *psResult = CPLHTTPFetch(osUrlNew.c_str(), aosHTTPOptionsInt);
     373           0 :     bool bResult = false;
     374           0 :     if (psResult)
     375             :     {
     376           0 :         bResult = psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
     377             :         // Get error message.
     378           0 :         if (!bResult)
     379             :         {
     380           0 :             ReportError(psResult->pabyData, psResult->nDataLen,
     381             :                         "DeleteResource request failed");
     382             :         }
     383           0 :         CPLHTTPDestroyResult(psResult);
     384             :     }
     385           0 :     return bResult;
     386             : }
     387             : 
     388           0 : bool RenameResource(const std::string &osUrl, const std::string &osResourceId,
     389             :                     const std::string &osNewName,
     390             :                     const CPLStringList &aosHTTPOptions)
     391             : {
     392           0 :     CPLJSONObject oPayload;
     393           0 :     CPLJSONObject oResource("resource", oPayload);
     394           0 :     oResource.Add("display_name", osNewName);
     395           0 :     std::string osPayload = oPayload.Format(CPLJSONObject::PrettyFormat::Plain);
     396             : 
     397           0 :     return UpdateResource(osUrl, osResourceId, osPayload, aosHTTPOptions);
     398             : }
     399             : 
     400           0 : OGRwkbGeometryType NGWGeomTypeToOGRGeomType(const std::string &osGeomType)
     401             : {
     402             :     // http://docs.nextgis.com/docs_ngweb_dev/doc/developer/vector_data_types.html#nextgisweb.feature_layer.interface.GEOM_TYPE
     403           0 :     if (osGeomType == "POINT")
     404           0 :         return wkbPoint;
     405           0 :     else if (osGeomType == "LINESTRING")
     406           0 :         return wkbLineString;
     407           0 :     else if (osGeomType == "POLYGON")
     408           0 :         return wkbPolygon;
     409           0 :     else if (osGeomType == "MULTIPOINT")
     410           0 :         return wkbMultiPoint;
     411           0 :     else if (osGeomType == "MULTILINESTRING")
     412           0 :         return wkbMultiLineString;
     413           0 :     else if (osGeomType == "MULTIPOLYGON")
     414           0 :         return wkbMultiPolygon;
     415           0 :     else if (osGeomType == "POINTZ")
     416           0 :         return wkbPoint25D;
     417           0 :     else if (osGeomType == "LINESTRINGZ")
     418           0 :         return wkbLineString25D;
     419           0 :     else if (osGeomType == "POLYGONZ")
     420           0 :         return wkbPolygon25D;
     421           0 :     else if (osGeomType == "MULTIPOINTZ")
     422           0 :         return wkbMultiPoint25D;
     423           0 :     else if (osGeomType == "MULTILINESTRINGZ")
     424           0 :         return wkbMultiLineString25D;
     425           0 :     else if (osGeomType == "MULTIPOLYGONZ")
     426           0 :         return wkbMultiPolygon25D;
     427             :     else
     428           0 :         return wkbUnknown;
     429             : }
     430             : 
     431           0 : std::string OGRGeomTypeToNGWGeomType(OGRwkbGeometryType eType)
     432             : {
     433           0 :     switch (eType)
     434             :     {  // Don't flatten
     435           0 :         case wkbPoint:
     436           0 :             return "POINT";
     437           0 :         case wkbLineString:
     438           0 :             return "LINESTRING";
     439           0 :         case wkbPolygon:
     440           0 :             return "POLYGON";
     441           0 :         case wkbMultiPoint:
     442           0 :             return "MULTIPOINT";
     443           0 :         case wkbMultiLineString:
     444           0 :             return "MULTILINESTRING";
     445           0 :         case wkbMultiPolygon:
     446           0 :             return "MULTIPOLYGON";
     447           0 :         case wkbPoint25D:
     448           0 :             return "POINTZ";
     449           0 :         case wkbLineString25D:
     450           0 :             return "LINESTRINGZ";
     451           0 :         case wkbPolygon25D:
     452           0 :             return "POLYGONZ";
     453           0 :         case wkbMultiPoint25D:
     454           0 :             return "MULTIPOINTZ";
     455           0 :         case wkbMultiLineString25D:
     456           0 :             return "MULTILINESTRINGZ";
     457           0 :         case wkbMultiPolygon25D:
     458           0 :             return "MULTIPOLYGONZ";
     459           0 :         default:
     460           0 :             return "";
     461             :     }
     462             : }
     463             : 
     464           0 : OGRFieldType NGWFieldTypeToOGRFieldType(const std::string &osFieldType)
     465             : {
     466             :     // http://docs.nextgis.com/docs_ngweb_dev/doc/developer/vector_data_types.html#nextgisweb.feature_layer.interface.FIELD_TYPE
     467           0 :     if (osFieldType == "INTEGER")
     468           0 :         return OFTInteger;
     469           0 :     else if (osFieldType == "BIGINT")
     470           0 :         return OFTInteger64;
     471           0 :     else if (osFieldType == "REAL")
     472           0 :         return OFTReal;
     473           0 :     else if (osFieldType == "STRING")
     474           0 :         return OFTString;
     475           0 :     else if (osFieldType == "DATE")
     476           0 :         return OFTDate;
     477           0 :     else if (osFieldType == "TIME")
     478           0 :         return OFTTime;
     479           0 :     else if (osFieldType == "DATETIME")
     480           0 :         return OFTDateTime;
     481             :     else
     482           0 :         return OFTString;
     483             : }
     484             : 
     485           0 : std::string OGRFieldTypeToNGWFieldType(OGRFieldType eType)
     486             : {
     487           0 :     switch (eType)
     488             :     {
     489           0 :         case OFTInteger:
     490           0 :             return "INTEGER";
     491           0 :         case OFTInteger64:
     492           0 :             return "BIGINT";
     493           0 :         case OFTReal:
     494           0 :             return "REAL";
     495           0 :         case OFTString:
     496           0 :             return "STRING";
     497           0 :         case OFTDate:
     498           0 :             return "DATE";
     499           0 :         case OFTTime:
     500           0 :             return "TIME";
     501           0 :         case OFTDateTime:
     502           0 :             return "DATETIME";
     503           0 :         default:
     504           0 :             return "STRING";
     505             :     }
     506             : }
     507             : 
     508           0 : Permissions CheckPermissions(const std::string &osUrl,
     509             :                              const std::string &osResourceId,
     510             :                              const CPLStringList &aosHTTPOptions,
     511             :                              bool bReadWrite)
     512             : {
     513           0 :     Permissions stOut;
     514           0 :     CPLErrorReset();
     515           0 :     CPLJSONDocument oPermissionReq;
     516             : 
     517           0 :     auto osUrlNew = GetPermissionsURL(osUrl, osResourceId);
     518           0 :     bool bResult = oPermissionReq.LoadUrl(osUrlNew, aosHTTPOptions);
     519             : 
     520           0 :     CPLJSONObject oRoot = oPermissionReq.GetRoot();
     521           0 :     if (CheckRequestResult(bResult, oRoot, "Get permissions failed"))
     522             :     {
     523           0 :         stOut.bResourceCanRead = oRoot.GetBool("resource/read", true);
     524           0 :         stOut.bResourceCanCreate = oRoot.GetBool("resource/create", bReadWrite);
     525           0 :         stOut.bResourceCanUpdate = oRoot.GetBool("resource/update", bReadWrite);
     526           0 :         stOut.bResourceCanDelete = oRoot.GetBool("resource/delete", bReadWrite);
     527             : 
     528           0 :         stOut.bDatastructCanRead = oRoot.GetBool("datastruct/read", true);
     529           0 :         stOut.bDatastructCanWrite =
     530           0 :             oRoot.GetBool("datastruct/write", bReadWrite);
     531             : 
     532           0 :         stOut.bDataCanRead = oRoot.GetBool("data/read", true);
     533           0 :         stOut.bDataCanWrite = oRoot.GetBool("data/write", bReadWrite);
     534             : 
     535           0 :         stOut.bMetadataCanRead = oRoot.GetBool("metadata/read", true);
     536           0 :         stOut.bMetadataCanWrite = oRoot.GetBool("metadata/write", bReadWrite);
     537             : 
     538           0 :         CPLErrorReset();  // If we are here no error occurred
     539           0 :         return stOut;
     540             :     }
     541             : 
     542           0 :     return stOut;
     543             : }
     544             : 
     545           0 : std::string GetFeatureCount(const std::string &osUrl,
     546             :                             const std::string &osResourceId)
     547             : {
     548           0 :     return osUrl + "/api/resource/" + osResourceId + "/feature_count";
     549             : }
     550             : 
     551           0 : std::string GetLayerExtent(const std::string &osUrl,
     552             :                            const std::string &osResourceId)
     553             : {
     554           0 :     return osUrl + "/api/resource/" + osResourceId + "/extent";
     555             : }
     556             : 
     557           0 : std::string GetResmetaSuffix(CPLJSONObject::Type eType)
     558             : {
     559           0 :     switch (eType)
     560             :     {
     561           0 :         case CPLJSONObject::Type::Integer:
     562             :         case CPLJSONObject::Type::Long:
     563           0 :             return ".d";
     564           0 :         case CPLJSONObject::Type::Double:
     565           0 :             return ".f";
     566           0 :         default:
     567           0 :             return "";
     568             :     }
     569             : }
     570             : 
     571           0 : void FillResmeta(const CPLJSONObject &oRoot, char **papszMetadata)
     572             : {
     573           0 :     CPLJSONObject oResMeta("resmeta", oRoot);
     574           0 :     CPLJSONObject oResMetaItems("items", oResMeta);
     575           0 :     CPLStringList oaMetadata(papszMetadata, FALSE);
     576           0 :     for (int i = 0; i < oaMetadata.size(); ++i)
     577             :     {
     578           0 :         std::string osItem = oaMetadata[i];
     579           0 :         size_t nPos = osItem.find("=");
     580           0 :         if (nPos != std::string::npos)
     581             :         {
     582           0 :             std::string osItemName = osItem.substr(0, nPos);
     583           0 :             CPLString osItemValue = osItem.substr(nPos + 1);
     584             : 
     585           0 :             if (osItemName.size() > 2)
     586             :             {
     587           0 :                 size_t nSuffixPos = osItemName.size() - 2;
     588           0 :                 std::string osSuffix = osItemName.substr(nSuffixPos);
     589           0 :                 if (osSuffix == ".d")
     590             :                 {
     591           0 :                     GInt64 nVal = CPLAtoGIntBig(osItemValue.c_str());
     592           0 :                     oResMetaItems.Add(osItemName.substr(0, nSuffixPos), nVal);
     593           0 :                     continue;
     594             :                 }
     595             : 
     596           0 :                 if (osSuffix == ".f")
     597             :                 {
     598           0 :                     oResMetaItems.Add(osItemName.substr(0, nSuffixPos),
     599             :                                       CPLAtofM(osItemValue.c_str()));
     600           0 :                     continue;
     601             :                 }
     602             :             }
     603             : 
     604           0 :             oResMetaItems.Add(osItemName, osItemValue);
     605             :         }
     606             :     }
     607           0 : }
     608             : 
     609           0 : bool FlushMetadata(const std::string &osUrl, const std::string &osResourceId,
     610             :                    char **papszMetadata, const CPLStringList &aosHTTPOptions)
     611             : {
     612           0 :     if (nullptr == papszMetadata)
     613             :     {
     614           0 :         return true;
     615             :     }
     616           0 :     CPLJSONObject oMetadataJson;
     617           0 :     FillResmeta(oMetadataJson, papszMetadata);
     618             : 
     619           0 :     return UpdateResource(
     620             :         osUrl, osResourceId,
     621           0 :         oMetadataJson.Format(CPLJSONObject::PrettyFormat::Plain),
     622           0 :         aosHTTPOptions);
     623             : }
     624             : 
     625           0 : bool DeleteFeature(const std::string &osUrl, const std::string &osResourceId,
     626             :                    const std::string &osFeatureId,
     627             :                    const CPLStringList &aosHTTPOptions)
     628             : {
     629           0 :     CPLErrorReset();
     630           0 :     CPLStringList aosHTTPOptionsInt(aosHTTPOptions);
     631           0 :     aosHTTPOptionsInt.AddString("CUSTOMREQUEST=DELETE");
     632           0 :     std::string osUrlInt = GetFeatureURL(osUrl, osResourceId) + osFeatureId;
     633           0 :     CPLHTTPResult *psResult = CPLHTTPFetch(osUrlInt.c_str(), aosHTTPOptionsInt);
     634           0 :     bool bResult = false;
     635           0 :     if (psResult)
     636             :     {
     637           0 :         bResult = psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
     638             :         // Get error message.
     639           0 :         if (!bResult)
     640             :         {
     641           0 :             ReportError(psResult->pabyData, psResult->nDataLen,
     642             :                         "DeleteFeature request failed");
     643             :         }
     644           0 :         CPLHTTPDestroyResult(psResult);
     645             :     }
     646           0 :     return bResult;
     647             : }
     648             : 
     649           0 : bool DeleteFeatures(const std::string &osUrl, const std::string &osResourceId,
     650             :                     const std::string &osFeaturesIDJson,
     651             :                     const CPLStringList &aosHTTPOptions)
     652             : {
     653           0 :     CPLErrorReset();
     654           0 :     std::string osPayloadInt = "POSTFIELDS=" + osFeaturesIDJson;
     655             : 
     656           0 :     CPLStringList aosHTTPOptionsInt(aosHTTPOptions);
     657           0 :     aosHTTPOptionsInt.AddString("CUSTOMREQUEST=DELETE");
     658           0 :     aosHTTPOptionsInt.AddString(osPayloadInt.c_str());
     659             :     aosHTTPOptionsInt.AddString(
     660           0 :         "HEADERS=Content-Type: application/json\r\nAccept: */*");
     661             : 
     662           0 :     std::string osUrlInt = GetFeatureURL(osUrl, osResourceId);
     663           0 :     CPLHTTPResult *psResult = CPLHTTPFetch(osUrlInt.c_str(), aosHTTPOptionsInt);
     664           0 :     bool bResult = false;
     665           0 :     if (psResult)
     666             :     {
     667           0 :         bResult = psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
     668             :         // Get error message.
     669           0 :         if (!bResult)
     670             :         {
     671           0 :             ReportError(psResult->pabyData, psResult->nDataLen,
     672             :                         "DeleteFeatures request failed");
     673             :         }
     674           0 :         CPLHTTPDestroyResult(psResult);
     675             :     }
     676           0 :     return bResult;
     677             : }
     678             : 
     679           0 : GIntBig CreateFeature(const std::string &osUrl, const std::string &osResourceId,
     680             :                       const std::string &osFeatureJson,
     681             :                       const CPLStringList &aosHTTPOptions)
     682             : {
     683           0 :     CPLErrorReset();
     684           0 :     std::string osPayloadInt = "POSTFIELDS=" + osFeatureJson;
     685             : 
     686           0 :     CPLStringList aosHTTPOptionsInt(aosHTTPOptions);
     687           0 :     aosHTTPOptionsInt.AddString("CUSTOMREQUEST=POST");
     688           0 :     aosHTTPOptionsInt.AddString(osPayloadInt.c_str());
     689             :     aosHTTPOptionsInt.AddString(
     690           0 :         "HEADERS=Content-Type: application/json\r\nAccept: */*");
     691             : 
     692           0 :     CPLDebug("NGW", "CreateFeature request payload: %s", osFeatureJson.c_str());
     693             : 
     694           0 :     std::string osUrlInt = GetFeatureURL(osUrl, osResourceId);
     695             : 
     696           0 :     CPLJSONDocument oCreateFeatureReq;
     697           0 :     bool bResult = oCreateFeatureReq.LoadUrl(osUrlInt, aosHTTPOptionsInt);
     698             : 
     699           0 :     CPLJSONObject oRoot = oCreateFeatureReq.GetRoot();
     700           0 :     GIntBig nOutFID = OGRNullFID;
     701           0 :     if (CheckRequestResult(bResult, oRoot, "Create new feature failed"))
     702             :     {
     703           0 :         nOutFID = oRoot.GetLong("id", OGRNullFID);
     704             :     }
     705             : 
     706           0 :     CPLDebug("NGW", "CreateFeature new FID: " CPL_FRMT_GIB, nOutFID);
     707           0 :     return nOutFID;
     708             : }
     709             : 
     710           0 : bool UpdateFeature(const std::string &osUrl, const std::string &osResourceId,
     711             :                    const std::string &osFeatureId,
     712             :                    const std::string &osFeatureJson,
     713             :                    const CPLStringList &aosHTTPOptions)
     714             : {
     715           0 :     CPLErrorReset();
     716           0 :     std::string osPayloadInt = "POSTFIELDS=" + osFeatureJson;
     717             : 
     718           0 :     CPLStringList aosHTTPOptionsInt(aosHTTPOptions);
     719           0 :     aosHTTPOptionsInt.AddString("CUSTOMREQUEST=PUT");
     720           0 :     aosHTTPOptionsInt.AddString(osPayloadInt.c_str());
     721             :     aosHTTPOptionsInt.AddString(
     722           0 :         "HEADERS=Content-Type: application/json\r\nAccept: */*");
     723             : 
     724           0 :     CPLDebug("NGW", "UpdateFeature request payload: %s", osFeatureJson.c_str());
     725             : 
     726           0 :     std::string osUrlInt = GetFeatureURL(osUrl, osResourceId) + osFeatureId;
     727           0 :     CPLHTTPResult *psResult = CPLHTTPFetch(osUrlInt.c_str(), aosHTTPOptionsInt);
     728           0 :     bool bResult = false;
     729           0 :     if (psResult)
     730             :     {
     731           0 :         bResult = psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
     732             : 
     733             :         // Get error message.
     734           0 :         if (!bResult)
     735             :         {
     736           0 :             ReportError(psResult->pabyData, psResult->nDataLen,
     737             :                         "UpdateFeature request failed");
     738             :         }
     739           0 :         CPLHTTPDestroyResult(psResult);
     740             :     }
     741           0 :     return bResult;
     742             : }
     743             : 
     744           0 : std::vector<GIntBig> PatchFeatures(const std::string &osUrl,
     745             :                                    const std::string &osResourceId,
     746             :                                    const std::string &osFeaturesJson,
     747             :                                    const CPLStringList &aosHTTPOptions)
     748             : {
     749           0 :     std::vector<GIntBig> aoFIDs;
     750           0 :     CPLErrorReset();
     751           0 :     std::string osPayloadInt = "POSTFIELDS=" + osFeaturesJson;
     752             : 
     753           0 :     CPLStringList aosHTTPOptionsInt(aosHTTPOptions);
     754           0 :     aosHTTPOptionsInt.AddString("CUSTOMREQUEST=PATCH");
     755           0 :     aosHTTPOptionsInt.AddString(osPayloadInt.c_str());
     756             :     aosHTTPOptionsInt.AddString(
     757           0 :         "HEADERS=Content-Type: application/json\r\nAccept: */*");
     758             : 
     759           0 :     CPLDebug("NGW", "PatchFeatures request payload: %s",
     760             :              osFeaturesJson.c_str());
     761             : 
     762           0 :     std::string osUrlInt = GetFeatureURL(osUrl, osResourceId);
     763           0 :     CPLJSONDocument oPatchFeatureReq;
     764           0 :     bool bResult = oPatchFeatureReq.LoadUrl(osUrlInt, aosHTTPOptionsInt);
     765             : 
     766           0 :     CPLJSONObject oRoot = oPatchFeatureReq.GetRoot();
     767           0 :     if (CheckRequestResult(bResult, oRoot, "Patch features failed"))
     768             :     {
     769           0 :         CPLJSONArray aoJSONIDs = oRoot.ToArray();
     770           0 :         for (int i = 0; i < aoJSONIDs.Size(); ++i)
     771             :         {
     772           0 :             GIntBig nOutFID = aoJSONIDs[i].GetLong("id", OGRNullFID);
     773           0 :             aoFIDs.push_back(nOutFID);
     774             :         }
     775             :     }
     776           0 :     return aoFIDs;
     777             : }
     778             : 
     779           0 : bool GetExtent(const std::string &osUrl, const std::string &osResourceId,
     780             :                const CPLStringList &aosHTTPOptions, int nEPSG,
     781             :                OGREnvelope &stExtent)
     782             : {
     783           0 :     CPLErrorReset();
     784           0 :     CPLJSONDocument oExtentReq;
     785             :     double dfRetryDelaySecs =
     786           0 :         CPLAtof(aosHTTPOptions.FetchNameValueDef("RETRY_DELAY", "2.5"));
     787           0 :     int nMaxRetries = atoi(aosHTTPOptions.FetchNameValueDef("MAX_RETRY", "0"));
     788           0 :     int nRetryCount = 0;
     789             :     while (true)
     790             :     {
     791           0 :         auto osUrlNew = GetLayerExtent(osUrl, osResourceId);
     792           0 :         bool bResult = oExtentReq.LoadUrl(osUrlNew, aosHTTPOptions);
     793             : 
     794           0 :         CPLJSONObject oRoot = oExtentReq.GetRoot();
     795           0 :         if (CheckRequestResult(bResult, oRoot, "Get extent failed"))
     796             :         {
     797             :             // Response extent spatial reference is EPSG:4326.
     798             : 
     799           0 :             double dfMinX = oRoot.GetDouble("extent/minLon");
     800           0 :             double dfMinY = oRoot.GetDouble("extent/minLat");
     801           0 :             double dfMaxX = oRoot.GetDouble("extent/maxLon");
     802           0 :             double dfMaxY = oRoot.GetDouble("extent/maxLat");
     803             : 
     804             :             double adfCoordinatesX[4];
     805             :             double adfCoordinatesY[4];
     806           0 :             adfCoordinatesX[0] = dfMinX;
     807           0 :             adfCoordinatesY[0] = dfMinY;
     808           0 :             adfCoordinatesX[1] = dfMinX;
     809           0 :             adfCoordinatesY[1] = dfMaxY;
     810           0 :             adfCoordinatesX[2] = dfMaxX;
     811           0 :             adfCoordinatesY[2] = dfMaxY;
     812           0 :             adfCoordinatesX[3] = dfMaxX;
     813           0 :             adfCoordinatesY[3] = dfMinY;
     814             : 
     815           0 :             OGRSpatialReference o4326SRS;
     816           0 :             o4326SRS.SetWellKnownGeogCS("WGS84");
     817           0 :             o4326SRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     818           0 :             OGRSpatialReference o3857SRS;
     819           0 :             o3857SRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     820           0 :             if (o3857SRS.importFromEPSG(nEPSG) != OGRERR_NONE)
     821             :             {
     822           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     823             :                          "Project extent SRS to EPSG:3857 failed");
     824           0 :                 return false;
     825             :             }
     826             : 
     827             :             OGRCoordinateTransformation *poTransform =
     828           0 :                 OGRCreateCoordinateTransformation(&o4326SRS, &o3857SRS);
     829           0 :             if (poTransform)
     830             :             {
     831           0 :                 poTransform->Transform(4, adfCoordinatesX, adfCoordinatesY);
     832           0 :                 delete poTransform;
     833             : 
     834           0 :                 stExtent.MinX = std::numeric_limits<double>::max();
     835           0 :                 stExtent.MaxX = std::numeric_limits<double>::min();
     836           0 :                 stExtent.MinY = std::numeric_limits<double>::max();
     837           0 :                 stExtent.MaxY = std::numeric_limits<double>::min();
     838             : 
     839           0 :                 for (int i = 1; i < 4; ++i)
     840             :                 {
     841           0 :                     if (stExtent.MinX > adfCoordinatesX[i])
     842             :                     {
     843           0 :                         stExtent.MinX = adfCoordinatesX[i];
     844             :                     }
     845           0 :                     if (stExtent.MaxX < adfCoordinatesX[i])
     846             :                     {
     847           0 :                         stExtent.MaxX = adfCoordinatesX[i];
     848             :                     }
     849           0 :                     if (stExtent.MinY > adfCoordinatesY[i])
     850             :                     {
     851           0 :                         stExtent.MinY = adfCoordinatesY[i];
     852             :                     }
     853           0 :                     if (stExtent.MaxY < adfCoordinatesY[i])
     854             :                     {
     855           0 :                         stExtent.MaxY = adfCoordinatesY[i];
     856             :                     }
     857             :                 }
     858             :             }
     859           0 :             CPLErrorReset();  // If we are here no error occurred
     860           0 :             return true;
     861             :         }
     862             : 
     863           0 :         if (nRetryCount >= nMaxRetries)
     864             :         {
     865           0 :             break;
     866             :         }
     867             : 
     868           0 :         CPLSleep(dfRetryDelaySecs);
     869           0 :         nRetryCount++;
     870           0 :     }
     871           0 :     return false;
     872             : }
     873             : 
     874           0 : CPLJSONObject UploadFile(const std::string &osUrl,
     875             :                          const std::string &osFilePath,
     876             :                          const CPLStringList &aosHTTPOptions,
     877             :                          GDALProgressFunc pfnProgress, void *pProgressData)
     878             : {
     879           0 :     CPLErrorReset();
     880           0 :     CPLStringList aosHTTPOptionsInt(aosHTTPOptions);
     881             :     aosHTTPOptionsInt.AddString(
     882           0 :         CPLSPrintf("FORM_FILE_PATH=%s", osFilePath.c_str()));
     883           0 :     aosHTTPOptionsInt.AddString("FORM_FILE_NAME=file");
     884             : 
     885           0 :     const char *pszFormFileName = CPLGetFilename(osFilePath.c_str());
     886           0 :     aosHTTPOptionsInt.AddString("FORM_KEY_0=name");
     887           0 :     aosHTTPOptionsInt.AddString(CPLSPrintf("FORM_VALUE_0=%s", pszFormFileName));
     888           0 :     aosHTTPOptionsInt.AddString("FORM_ITEM_COUNT=1");
     889             : 
     890             :     CPLHTTPResult *psResult =
     891           0 :         CPLHTTPFetchEx(GetUploadURL(osUrl).c_str(), aosHTTPOptionsInt,
     892           0 :                        pfnProgress, pProgressData, nullptr, nullptr);
     893           0 :     CPLJSONObject oResult;
     894           0 :     if (psResult)
     895             :     {
     896           0 :         const bool bResult =
     897           0 :             psResult->nStatus == 0 && psResult->pszErrBuf == nullptr;
     898             : 
     899             :         // Get error message.
     900           0 :         if (!bResult)
     901             :         {
     902           0 :             ReportError(psResult->pabyData, psResult->nDataLen,
     903             :                         "Upload file request failed");
     904             :         }
     905             :         else
     906             :         {
     907           0 :             CPLJSONDocument oFileJson;
     908           0 :             if (oFileJson.LoadMemory(psResult->pabyData, psResult->nDataLen))
     909             :             {
     910           0 :                 oResult = oFileJson.GetRoot();
     911             :             }
     912             :         }
     913           0 :         CPLHTTPDestroyResult(psResult);
     914             :     }
     915             :     else
     916             :     {
     917           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Upload file %s failed",
     918             :                  osFilePath.c_str());
     919             :     }
     920           0 :     return oResult;
     921             : }
     922             : 
     923             : }  // namespace NGWAPI

Generated by: LCOV version 1.14