LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/plscenes - ogrplscenesdatav1dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 385 416 92.5 %
Date: 2025-10-24 23:03:13 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PlanetLabs scene driver
       4             :  * Purpose:  Implements OGRPLScenesDataV1Dataset
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2016, Planet Labs
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_multiproc.h"  // CPLSleep()
      14             : 
      15             : #include "ogr_plscenes.h"
      16             : #include "ogrlibjsonutils.h"
      17             : #include <time.h>
      18             : 
      19             : /************************************************************************/
      20             : /*                       OGRPLScenesDataV1Dataset()                     */
      21             : /************************************************************************/
      22             : 
      23          29 : OGRPLScenesDataV1Dataset::OGRPLScenesDataV1Dataset()
      24             :     : m_bLayerListInitialized(false), m_bMustCleanPersistent(false),
      25          29 :       m_nLayers(0), m_papoLayers(nullptr), m_bFollowLinks(false)
      26             : {
      27          29 : }
      28             : 
      29             : /************************************************************************/
      30             : /*                       ~OGRPLScenesDataV1Dataset()                    */
      31             : /************************************************************************/
      32             : 
      33          58 : OGRPLScenesDataV1Dataset::~OGRPLScenesDataV1Dataset()
      34             : {
      35          41 :     for (int i = 0; i < m_nLayers; i++)
      36          12 :         delete m_papoLayers[i];
      37          29 :     CPLFree(m_papoLayers);
      38             : 
      39          29 :     if (m_bMustCleanPersistent)
      40             :     {
      41          20 :         char **papszOptions = CSLSetNameValue(nullptr, "CLOSE_PERSISTENT",
      42             :                                               CPLSPrintf("PLSCENES:%p", this));
      43          20 :         CPLHTTPDestroyResult(CPLHTTPFetch(m_osBaseURL, papszOptions));
      44          20 :         CSLDestroy(papszOptions);
      45             :     }
      46          58 : }
      47             : 
      48             : /************************************************************************/
      49             : /*                              GetLayer()                              */
      50             : /************************************************************************/
      51             : 
      52          27 : const OGRLayer *OGRPLScenesDataV1Dataset::GetLayer(int idx) const
      53             : {
      54          27 :     if (idx < 0 || idx >= GetLayerCount())
      55           2 :         return nullptr;
      56          25 :     return m_papoLayers[idx];
      57             : }
      58             : 
      59             : /************************************************************************/
      60             : /*                           GetLayerCount()                            */
      61             : /************************************************************************/
      62             : 
      63          99 : int OGRPLScenesDataV1Dataset::GetLayerCount() const
      64             : {
      65          99 :     if (!m_bLayerListInitialized)
      66             :     {
      67           8 :         m_bLayerListInitialized = true;
      68           8 :         const_cast<OGRPLScenesDataV1Dataset *>(this)->EstablishLayerList();
      69             :     }
      70          99 :     return m_nLayers;
      71             : }
      72             : 
      73             : /************************************************************************/
      74             : /*                          ParseItemType()                             */
      75             : /************************************************************************/
      76             : 
      77          17 : OGRLayer *OGRPLScenesDataV1Dataset::ParseItemType(json_object *poItemType)
      78             : {
      79          33 :     if (poItemType == nullptr ||
      80          16 :         json_object_get_type(poItemType) != json_type_object)
      81           2 :         return nullptr;
      82          15 :     json_object *poId = CPL_json_object_object_get(poItemType, "id");
      83          15 :     if (poId == nullptr || json_object_get_type(poId) != json_type_string)
      84           2 :         return nullptr;
      85             : 
      86          26 :     CPLString osDisplayDescription;
      87             :     json_object *poDisplayDescription =
      88          13 :         CPL_json_object_object_get(poItemType, "display_description");
      89          16 :     if (poDisplayDescription != nullptr &&
      90           3 :         json_object_get_type(poDisplayDescription) == json_type_string)
      91           3 :         osDisplayDescription = json_object_get_string(poDisplayDescription);
      92          26 :     CPLString osDisplayName;
      93             :     json_object *poDisplayName =
      94          13 :         CPL_json_object_object_get(poItemType, "display_name");
      95          16 :     if (poDisplayName != nullptr &&
      96           3 :         json_object_get_type(poDisplayName) == json_type_string)
      97           3 :         osDisplayName = json_object_get_string(poDisplayName);
      98             : 
      99          13 :     const char *pszId = json_object_get_string(poId);
     100             : 
     101             :     // The layer might already exist if GetLayerByName() is called before
     102             :     // GetLayer()/GetLayerCount() is
     103             : 
     104             :     // Prevent GetLayerCount() from calling EstablishLayerList()
     105          13 :     bool bLayerListInitializedBackup = m_bLayerListInitialized;
     106          13 :     m_bLayerListInitialized = true;
     107          13 :     OGRLayer *poExistingLayer = GDALDataset::GetLayerByName(pszId);
     108          13 :     m_bLayerListInitialized = bLayerListInitializedBackup;
     109          13 :     if (poExistingLayer != nullptr)
     110           1 :         return poExistingLayer;
     111             : 
     112          12 :     OGRPLScenesDataV1Layer *poPLLayer = new OGRPLScenesDataV1Layer(this, pszId);
     113          12 :     if (!osDisplayName.empty())
     114           3 :         poPLLayer->SetMetadataItem("SHORT_DESCRIPTION", osDisplayName.c_str());
     115          12 :     if (!osDisplayDescription.empty())
     116           3 :         poPLLayer->SetMetadataItem("DESCRIPTION", osDisplayDescription.c_str());
     117          24 :     m_papoLayers = (OGRPLScenesDataV1Layer **)CPLRealloc(
     118          12 :         m_papoLayers, sizeof(OGRPLScenesDataV1Layer *) * (m_nLayers + 1));
     119          12 :     m_papoLayers[m_nLayers++] = poPLLayer;
     120          12 :     return poPLLayer;
     121             : }
     122             : 
     123             : /************************************************************************/
     124             : /*                          ParseItemTypes()                         */
     125             : /************************************************************************/
     126             : 
     127          10 : bool OGRPLScenesDataV1Dataset::ParseItemTypes(json_object *poObj,
     128             :                                               CPLString &osNext)
     129             : {
     130          10 :     json_object *poItemTypes = CPL_json_object_object_get(poObj, "item_types");
     131          19 :     if (poItemTypes == nullptr ||
     132           9 :         json_object_get_type(poItemTypes) != json_type_array)
     133             :     {
     134           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     135             :                  "Missing item_types object, or not of type array");
     136           1 :         return false;
     137             :     }
     138           9 :     const auto nCatalogsLength = json_object_array_length(poItemTypes);
     139          22 :     for (auto i = decltype(nCatalogsLength){0}; i < nCatalogsLength; i++)
     140             :     {
     141          13 :         json_object *poItemType = json_object_array_get_idx(poItemTypes, i);
     142          13 :         ParseItemType(poItemType);
     143             :     }
     144             : 
     145             :     // Is there a next page ?
     146           9 :     osNext = "";
     147           9 :     json_object *poLinks = CPL_json_object_object_get(poObj, "_links");
     148           9 :     if (poLinks && json_object_get_type(poLinks) == json_type_object)
     149             :     {
     150           2 :         json_object *poNext = CPL_json_object_object_get(poLinks, "_next");
     151           2 :         if (poNext && json_object_get_type(poNext) == json_type_string)
     152             :         {
     153           2 :             osNext = json_object_get_string(poNext);
     154             :         }
     155             :     }
     156             : 
     157           9 :     return true;
     158             : }
     159             : 
     160             : /************************************************************************/
     161             : /*                          EstablishLayerList()                        */
     162             : /************************************************************************/
     163             : 
     164           8 : void OGRPLScenesDataV1Dataset::EstablishLayerList()
     165             : {
     166          16 :     CPLString osURL(m_osNextItemTypesPageURL);
     167           8 :     m_osNextItemTypesPageURL = "";
     168             : 
     169           9 :     while (!osURL.empty())
     170             :     {
     171           2 :         json_object *poObj = RunRequest(osURL);
     172           2 :         if (poObj == nullptr)
     173           1 :             break;
     174           1 :         if (!ParseItemTypes(poObj, osURL))
     175             :         {
     176           0 :             json_object_put(poObj);
     177           0 :             break;
     178             :         }
     179           1 :         json_object_put(poObj);
     180             :     }
     181           8 : }
     182             : 
     183             : /************************************************************************/
     184             : /*                          GetLayerByName()                            */
     185             : /************************************************************************/
     186             : 
     187          14 : OGRLayer *OGRPLScenesDataV1Dataset::GetLayerByName(const char *pszName)
     188             : {
     189             :     // Prevent GetLayerCount() from calling EstablishLayerList()
     190          14 :     bool bLayerListInitializedBackup = m_bLayerListInitialized;
     191          14 :     m_bLayerListInitialized = true;
     192          14 :     OGRLayer *poRet = GDALDataset::GetLayerByName(pszName);
     193          14 :     m_bLayerListInitialized = bLayerListInitializedBackup;
     194          14 :     if (poRet != nullptr)
     195           3 :         return poRet;
     196             : 
     197          33 :     CPLString osURL(m_osBaseURL + "item-types/" + pszName);
     198          11 :     json_object *poObj = RunRequest(osURL);
     199          11 :     if (poObj == nullptr)
     200           7 :         return nullptr;
     201           4 :     poRet = ParseItemType(poObj);
     202           4 :     json_object_put(poObj);
     203           4 :     return poRet;
     204             : }
     205             : 
     206             : /************************************************************************/
     207             : /*                          GetBaseHTTPOptions()                         */
     208             : /************************************************************************/
     209             : 
     210          75 : char **OGRPLScenesDataV1Dataset::GetBaseHTTPOptions()
     211             : {
     212          75 :     m_bMustCleanPersistent = true;
     213             : 
     214          75 :     char **papszOptions = nullptr;
     215             :     papszOptions =
     216          75 :         CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=PLSCENES:%p", this));
     217          75 :     papszOptions = CSLAddString(
     218             :         papszOptions,
     219             :         CPLSPrintf("HEADERS=Authorization: api-key %s", m_osAPIKey.c_str()));
     220             : 
     221          75 :     return papszOptions;
     222             : }
     223             : 
     224             : /************************************************************************/
     225             : /*                               RunRequest()                           */
     226             : /************************************************************************/
     227             : 
     228          75 : json_object *OGRPLScenesDataV1Dataset::RunRequest(const char *pszURL,
     229             :                                                   int bQuiet404Error,
     230             :                                                   const char *pszHTTPVerb,
     231             :                                                   bool bExpectJSonReturn,
     232             :                                                   const char *pszPostContent)
     233             : {
     234          75 :     char **papszOptions = CSLAddString(GetBaseHTTPOptions(), nullptr);
     235             :     // We need to set it each time as CURL would reuse the previous value
     236             :     // if reusing the same connection
     237          75 :     papszOptions = CSLSetNameValue(papszOptions, "CUSTOMREQUEST", pszHTTPVerb);
     238          75 :     if (pszPostContent != nullptr)
     239             :     {
     240          18 :         CPLString osHeaders = CSLFetchNameValueDef(papszOptions, "HEADERS", "");
     241          18 :         if (!osHeaders.empty())
     242          18 :             osHeaders += "\r\n";
     243          18 :         osHeaders += "Content-Type: application/json";
     244          18 :         papszOptions = CSLSetNameValue(papszOptions, "HEADERS", osHeaders);
     245             :         papszOptions =
     246          18 :             CSLSetNameValue(papszOptions, "POSTFIELDS", pszPostContent);
     247             :     }
     248          75 :     papszOptions = CSLSetNameValue(papszOptions, "MAX_RETRY", "3");
     249          75 :     CPLHTTPResult *psResult = nullptr;
     250          75 :     if (STARTS_WITH(m_osBaseURL, "/vsimem/") && STARTS_WITH(pszURL, "/vsimem/"))
     251             :     {
     252          11 :         psResult = (CPLHTTPResult *)CPLCalloc(1, sizeof(CPLHTTPResult));
     253          11 :         vsi_l_offset nDataLengthLarge = 0;
     254          22 :         CPLString osURL(pszURL);
     255          11 :         if (osURL.back() == '/')
     256           6 :             osURL.pop_back();
     257          11 :         if (pszPostContent != nullptr)
     258             :         {
     259           3 :             osURL += "&POSTFIELDS=";
     260           3 :             osURL += pszPostContent;
     261             :         }
     262          11 :         CPLDebug("PLSCENES", "Fetching %s", osURL.c_str());
     263          11 :         GByte *pabyBuf = VSIGetMemFileBuffer(osURL, &nDataLengthLarge, FALSE);
     264          11 :         size_t nDataLength = static_cast<size_t>(nDataLengthLarge);
     265          11 :         if (pabyBuf)
     266             :         {
     267           8 :             psResult->pabyData = (GByte *)VSI_MALLOC_VERBOSE(1 + nDataLength);
     268           8 :             if (psResult->pabyData)
     269             :             {
     270           8 :                 memcpy(psResult->pabyData, pabyBuf, nDataLength);
     271           8 :                 psResult->pabyData[nDataLength] = 0;
     272             :             }
     273             :         }
     274             :         else
     275             :         {
     276           3 :             psResult->pszErrBuf = CPLStrdup(
     277             :                 CPLSPrintf("Error 404. Cannot find %s", osURL.c_str()));
     278             :         }
     279             :     }
     280             :     else
     281             :     {
     282          64 :         if (bQuiet404Error)
     283           0 :             CPLPushErrorHandler(CPLQuietErrorHandler);
     284          64 :         psResult = CPLHTTPFetch(pszURL, papszOptions);
     285          64 :         if (bQuiet404Error)
     286           0 :             CPLPopErrorHandler();
     287             :     }
     288          75 :     CSLDestroy(papszOptions);
     289             : 
     290          75 :     if (pszPostContent != nullptr && m_bMustCleanPersistent)
     291             :     {
     292          18 :         papszOptions = CSLSetNameValue(nullptr, "CLOSE_PERSISTENT",
     293             :                                        CPLSPrintf("PLSCENES:%p", this));
     294          18 :         CPLHTTPDestroyResult(CPLHTTPFetch(m_osBaseURL, papszOptions));
     295          18 :         CSLDestroy(papszOptions);
     296          18 :         m_bMustCleanPersistent = false;
     297             :     }
     298             : 
     299          75 :     if (psResult->pszErrBuf != nullptr)
     300             :     {
     301          15 :         if (!(bQuiet404Error && strstr(psResult->pszErrBuf, "404")))
     302             :         {
     303          15 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
     304          15 :                      psResult->pabyData ? (const char *)psResult->pabyData
     305             :                                         : psResult->pszErrBuf);
     306             :         }
     307          15 :         CPLHTTPDestroyResult(psResult);
     308          15 :         return nullptr;
     309             :     }
     310             : 
     311          60 :     if (!bExpectJSonReturn &&
     312           0 :         (psResult->pabyData == nullptr || psResult->nDataLen == 0))
     313             :     {
     314           0 :         CPLHTTPDestroyResult(psResult);
     315           0 :         return nullptr;
     316             :     }
     317             : 
     318          60 :     if (psResult->pabyData == nullptr)
     319             :     {
     320           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     321             :                  "Empty content returned by server");
     322           2 :         CPLHTTPDestroyResult(psResult);
     323           2 :         return nullptr;
     324             :     }
     325             : 
     326          58 :     const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
     327             : #ifdef DEBUG_VERBOSE
     328             :     CPLDebug("PLScenes", "%s", pszText);
     329             : #endif
     330             : 
     331          58 :     json_object *poObj = nullptr;
     332          58 :     if (!OGRJSonParse(pszText, &poObj, true))
     333             :     {
     334           2 :         CPLHTTPDestroyResult(psResult);
     335           2 :         return nullptr;
     336             :     }
     337             : 
     338          56 :     CPLHTTPDestroyResult(psResult);
     339             : 
     340          56 :     if (json_object_get_type(poObj) != json_type_object)
     341             :     {
     342           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     343             :                  "Return is not a JSON dictionary");
     344           0 :         json_object_put(poObj);
     345           0 :         poObj = nullptr;
     346             :     }
     347             : 
     348          56 :     return poObj;
     349             : }
     350             : 
     351             : /************************************************************************/
     352             : /*                           InsertAPIKeyInURL()                        */
     353             : /************************************************************************/
     354             : 
     355           5 : CPLString OGRPLScenesDataV1Dataset::InsertAPIKeyInURL(CPLString osURL)
     356             : {
     357           5 :     if (STARTS_WITH(osURL, "http://"))
     358             :     {
     359           4 :         osURL = "http://" + m_osAPIKey + ":@" + osURL.substr(strlen("http://"));
     360             :     }
     361           1 :     else if (STARTS_WITH(osURL, "https://"))
     362             :     {
     363             :         osURL =
     364           0 :             "https://" + m_osAPIKey + ":@" + osURL.substr(strlen("https://"));
     365             :     }
     366           5 :     return osURL;
     367             : }
     368             : 
     369             : /************************************************************************/
     370             : /*                            OpenRasterScene()                         */
     371             : /************************************************************************/
     372             : 
     373          13 : GDALDataset *OGRPLScenesDataV1Dataset::OpenRasterScene(GDALOpenInfo *poOpenInfo,
     374             :                                                        CPLString osScene,
     375             :                                                        char **papszOptions)
     376             : {
     377          13 :     if (!(poOpenInfo->nOpenFlags & GDAL_OF_RASTER))
     378             :     {
     379           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     380             :                  "The scene option must only be used with vector access");
     381           0 :         return nullptr;
     382             :     }
     383             : 
     384          26 :     int nActivationTimeout = atoi(CSLFetchNameValueDef(
     385          13 :         poOpenInfo->papszOpenOptions, "ACTIVATION_TIMEOUT", "3600"));
     386             : 
     387          13 :     for (char **papszIter = papszOptions; papszIter && *papszIter; papszIter++)
     388             :     {
     389           1 :         char *pszKey = nullptr;
     390           1 :         const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
     391           1 :         if (pszValue != nullptr)
     392             :         {
     393           1 :             if (!EQUAL(pszKey, "api_key") && !EQUAL(pszKey, "scene") &&
     394           1 :                 !EQUAL(pszKey, "product_type") && !EQUAL(pszKey, "asset") &&
     395           1 :                 !EQUAL(pszKey, "catalog") && !EQUAL(pszKey, "itemtypes") &&
     396           1 :                 !EQUAL(pszKey, "version") && !EQUAL(pszKey, "follow_links") &&
     397           1 :                 !EQUAL(pszKey, "metadata"))
     398             :             {
     399           1 :                 CPLError(CE_Failure, CPLE_NotSupported, "Unsupported option %s",
     400             :                          pszKey);
     401           1 :                 CPLFree(pszKey);
     402           1 :                 return nullptr;
     403             :             }
     404           0 :             CPLFree(pszKey);
     405             :         }
     406             :     }
     407             : 
     408          12 :     const char *pszCatalog = CSLFetchNameValueDef(
     409             :         papszOptions, "itemtypes",
     410             :         CSLFetchNameValueDef(
     411             :             papszOptions, "catalog",
     412             :             CSLFetchNameValueDef(
     413          12 :                 poOpenInfo->papszOpenOptions, "ITEMTYPES",
     414          12 :                 CSLFetchNameValue(poOpenInfo->papszOpenOptions, "CATALOG"))));
     415          12 :     if (pszCatalog == nullptr)
     416             :     {
     417           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Missing catalog");
     418           1 :         return nullptr;
     419             :     }
     420             : 
     421          11 :     const char *pszProductType = CSLFetchNameValueDef(
     422             :         papszOptions, "asset",
     423             :         CSLFetchNameValueDef(
     424             :             papszOptions, "product_type",
     425          11 :             CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "ASSET",
     426          11 :                                  CSLFetchNameValue(poOpenInfo->papszOpenOptions,
     427             :                                                    "PRODUCT_TYPE"))));
     428             : 
     429          22 :     CPLString osRasterURL;
     430          11 :     osRasterURL = m_osBaseURL;
     431          11 :     osRasterURL += "item-types/";
     432          11 :     osRasterURL += pszCatalog;
     433          11 :     osRasterURL += "/items/";
     434          11 :     osRasterURL += osScene;
     435          11 :     osRasterURL += "/assets/";
     436             : 
     437          11 :     time_t nStartTime = time(nullptr);
     438          18 : retry:
     439          18 :     time_t nCurrentTime = time(nullptr);
     440          18 :     if (nCurrentTime - nStartTime > nActivationTimeout)
     441             :     {
     442           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Activation timeout reached");
     443           2 :         return nullptr;
     444             :     }
     445          16 :     json_object *poObj = RunRequest(osRasterURL);
     446          16 :     if (poObj == nullptr)
     447           1 :         return nullptr;
     448             : 
     449          15 :     json_object *poSubObj = CPL_json_object_object_get(
     450             :         poObj, pszProductType ? pszProductType : "visual");
     451          15 :     if (poSubObj == nullptr)
     452             :     {
     453           2 :         if (pszProductType != nullptr && !EQUAL(pszProductType, "LIST"))
     454             :         {
     455           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find asset %s",
     456             :                      pszProductType);
     457           1 :             json_object_put(poObj);
     458             :         }
     459             :         else
     460             :         {
     461             :             json_object_iter it;
     462           1 :             it.key = nullptr;
     463           1 :             it.val = nullptr;
     464           1 :             it.entry = nullptr;
     465           1 :             char **papszSubdatasets = nullptr;
     466           1 :             int nSubDataset = 0;
     467           2 :             json_object_object_foreachC(poObj, it)
     468             :             {
     469           1 :                 ++nSubDataset;
     470           1 :                 papszSubdatasets = CSLSetNameValue(
     471             :                     papszSubdatasets,
     472             :                     CPLSPrintf("SUBDATASET_%d_NAME", nSubDataset),
     473             :                     CPLSPrintf("Scene=%s of item types %s, asset %s",
     474             :                                osScene.c_str(), pszCatalog, it.key));
     475           1 :                 papszSubdatasets = CSLSetNameValue(
     476             :                     papszSubdatasets,
     477             :                     CPLSPrintf("SUBDATASET_%d_DESC", nSubDataset),
     478             :                     CPLSPrintf("PLScenes:version=Data_V1,itemtypes=%s,scene=%s,"
     479             :                                "asset=%s",
     480             :                                pszCatalog, osScene.c_str(), it.key));
     481             :             }
     482           1 :             json_object_put(poObj);
     483           1 :             if (nSubDataset != 0)
     484             :             {
     485           1 :                 GDALDataset *poDS = new OGRPLScenesDataV1Dataset();
     486           1 :                 poDS->SetMetadata(papszSubdatasets, "SUBDATASETS");
     487           1 :                 CSLDestroy(papszSubdatasets);
     488           1 :                 return poDS;
     489             :             }
     490             :         }
     491           1 :         return nullptr;
     492             :     }
     493             : 
     494          13 :     if (json_object_get_type(poSubObj) != json_type_object)
     495             :     {
     496           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find link");
     497           1 :         json_object_put(poObj);
     498           1 :         return nullptr;
     499             :     }
     500             : 
     501             :     json_object *poPermissions =
     502          12 :         CPL_json_object_object_get(poSubObj, "_permissions");
     503          12 :     if (poPermissions != nullptr)
     504             :     {
     505             :         const char *pszPermissions =
     506          12 :             json_object_to_json_string_ext(poPermissions, 0);
     507          12 :         if (pszPermissions && strstr(pszPermissions, "download") == nullptr)
     508             :         {
     509           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     510             :                      "You don't have download permissions for this product");
     511             :         }
     512             :     }
     513             : 
     514          12 :     json_object *poLocation = CPL_json_object_object_get(poSubObj, "location");
     515          12 :     json_object *poStatus = CPL_json_object_object_get(poSubObj, "status");
     516          12 :     bool bActive = false;
     517          24 :     if (poStatus != nullptr &&
     518          12 :         json_object_get_type(poStatus) == json_type_string)
     519             :     {
     520          12 :         const char *pszStatus = json_object_get_string(poStatus);
     521          12 :         if (EQUAL(pszStatus, "activating"))
     522             :         {
     523           4 :             CPLDebug("PLScenes", "The product is in activation. Retrying...");
     524           4 :             CPLSleep(nActivationTimeout == 1 ? 0.5 : 1.0);
     525           4 :             poLocation = nullptr;
     526           4 :             json_object_put(poObj);
     527           4 :             goto retry;
     528             :         }
     529           8 :         bActive = EQUAL(pszStatus, "active");
     530             :     }
     531          13 :     if (poLocation == nullptr ||
     532           8 :         json_object_get_type(poLocation) != json_type_string || !bActive)
     533             :     {
     534           3 :         CPLDebug("PLScenes", "The product isn't activated yet. Activating it");
     535             :         json_object *poActivate =
     536           3 :             json_ex_get_object_by_path(poSubObj, "_links.activate");
     537           6 :         if (poActivate == nullptr ||
     538           3 :             json_object_get_type(poActivate) != json_type_string)
     539             :         {
     540           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     541             :                      "Cannot find link to activate scene %s", osScene.c_str());
     542           0 :             json_object_put(poObj);
     543           0 :             return nullptr;
     544             :         }
     545           3 :         CPLString osActivate = json_object_get_string(poActivate);
     546           3 :         poLocation = nullptr;
     547           3 :         json_object_put(poObj);
     548           3 :         poObj = RunRequest(osActivate, FALSE, "GET", false);
     549           3 :         if (poObj != nullptr)
     550           0 :             json_object_put(poObj);
     551           3 :         poObj = nullptr;
     552           3 :         CPLSleep(nActivationTimeout == 1 ? 0.5 : 1.0);
     553           3 :         goto retry;
     554             :     }
     555             : 
     556           5 :     const char *pszLink = json_object_get_string(poLocation);
     557             : 
     558           5 :     osRasterURL = pszLink ? pszLink : "";
     559           5 :     json_object_put(poObj);
     560           5 :     if (osRasterURL.empty())
     561             :     {
     562           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find link to scene %s",
     563             :                  osScene.c_str());
     564           0 :         return nullptr;
     565             :     }
     566             : 
     567           5 :     osRasterURL = InsertAPIKeyInURL(osRasterURL);
     568             : 
     569             :     const bool bUseVSICURL =
     570           5 :         CPLFetchBool(poOpenInfo->papszOpenOptions, "RANDOM_ACCESS", true);
     571           5 :     if (bUseVSICURL && !(STARTS_WITH(m_osBaseURL, "/vsimem/")))
     572             :     {
     573           5 :         char *pszEscapedURL = CPLEscapeString(osRasterURL, -1, CPLES_URL);
     574             :         CPLString osTmpURL("/vsicurl?use_head=no&max_retry=3&empty_dir=yes&use_"
     575          10 :                            "redirect_url_if_no_query_string_params=yes&url=");
     576           5 :         osTmpURL += pszEscapedURL;
     577           5 :         CPLFree(pszEscapedURL);
     578           5 :         CPLDebug("PLSCENES", "URL = %s", osTmpURL.c_str());
     579             : 
     580             :         VSIStatBufL sStat;
     581           5 :         if (VSIStatL(osTmpURL, &sStat) == 0 && sStat.st_size > 0)
     582             :         {
     583           4 :             osRasterURL = std::move(osTmpURL);
     584             :         }
     585             :         else
     586             :         {
     587           1 :             CPLDebug("PLSCENES", "Cannot use random access for that file");
     588             :         }
     589             :     }
     590             : 
     591           5 :     char **papszAllowedDrivers = nullptr;
     592           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "HTTP");
     593           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "GTiff");
     594           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "PNG");
     595           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JPEG");
     596           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "NITF");
     597           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2KAK");
     598           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2ECW");
     599           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2MrSID");
     600           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2OpenJPEG");
     601           5 :     GDALDataset *poOutDS = (GDALDataset *)GDALOpenEx(
     602             :         osRasterURL, GDAL_OF_RASTER, papszAllowedDrivers, nullptr, nullptr);
     603           5 :     CSLDestroy(papszAllowedDrivers);
     604           5 :     if (poOutDS)
     605             :     {
     606           3 :         if (CPLFetchBool(
     607             :                 papszOptions, "metadata",
     608           3 :                 CPLFetchBool(poOpenInfo->papszOpenOptions, "METADATA", true)))
     609             :         {
     610           3 :             OGRLayer *poLayer = GetLayerByName(pszCatalog);
     611           3 :             if (poLayer != nullptr)
     612             :             {
     613             :                 // Set a dummy name so that PAM goes here
     614           2 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
     615             : 
     616             :                 std::string osTmpFilename =
     617           4 :                     VSIMemGenerateHiddenFilename("ogrplscenesDataV1");
     618             : 
     619           2 :                 poOutDS->SetDescription(osTmpFilename.c_str());
     620             : 
     621             :                 /* Attach scene metadata. */
     622           2 :                 poLayer->SetAttributeFilter(
     623           2 :                     CPLSPrintf("id = '%s'", osScene.c_str()));
     624           2 :                 OGRFeature *poFeat = poLayer->GetNextFeature();
     625           2 :                 if (poFeat)
     626             :                 {
     627          41 :                     for (int i = 0; i < poFeat->GetFieldCount(); i++)
     628             :                     {
     629          40 :                         if (poFeat->IsFieldSetAndNotNull(i))
     630             :                         {
     631             :                             const char *pszKey =
     632           2 :                                 poFeat->GetFieldDefnRef(i)->GetNameRef();
     633           2 :                             const char *pszVal = poFeat->GetFieldAsString(i);
     634           2 :                             if (strncmp(pszKey, "asset_", strlen("asset_")) ==
     635           2 :                                     0 ||
     636           2 :                                 strstr(pszVal, "https://") != nullptr ||
     637           2 :                                 strcmp(pszKey, "columns") == 0 ||
     638           2 :                                 strcmp(pszKey, "rows") == 0 ||
     639           2 :                                 strcmp(pszKey, "epsg_code") == 0 ||
     640           2 :                                 strcmp(pszKey, "origin_x") == 0 ||
     641           2 :                                 strcmp(pszKey, "origin_y") == 0 ||
     642           2 :                                 strcmp(pszKey, "permissions") == 0 ||
     643           2 :                                 strcmp(pszKey, "acquired") ==
     644             :                                     0  // Redundant with TIFFTAG_DATETIME
     645             :                             )
     646             :                             {
     647           0 :                                 continue;
     648             :                             }
     649           2 :                             poOutDS->SetMetadataItem(pszKey, pszVal);
     650             :                         }
     651             :                     }
     652             :                 }
     653           2 :                 delete poFeat;
     654             : 
     655           2 :                 poOutDS->FlushCache(false);
     656           2 :                 VSIUnlink(osTmpFilename.c_str());
     657           2 :                 VSIUnlink(
     658           4 :                     std::string(osTmpFilename).append(".aux.xml").c_str());
     659           2 :                 CPLPopErrorHandler();
     660           2 :                 CPL_IGNORE_RET_VAL(osTmpFilename);
     661             :             }
     662             :         }
     663             : 
     664           3 :         CPLErrorReset();
     665           3 :         poOutDS->SetDescription(poOpenInfo->pszFilename);
     666             :     }
     667           2 :     else if (CPLGetLastErrorType() == CE_None)
     668             :     {
     669           2 :         poObj = RunRequest(osRasterURL);
     670           2 :         if (poObj == nullptr)
     671             :         {
     672           2 :             CPLError(
     673             :                 CE_Failure, CPLE_AppDefined,
     674             :                 "The generation of the product is in progress. Retry later");
     675             :         }
     676             :         else
     677             :         {
     678           0 :             CPLError(
     679             :                 CE_Failure, CPLE_AppDefined, "%s",
     680             :                 json_object_to_json_string_ext(poObj, JSON_C_TO_STRING_PRETTY));
     681           0 :             json_object_put(poObj);
     682             :         }
     683             :     }
     684             : 
     685           5 :     return poOutDS;
     686             : }
     687             : 
     688             : /************************************************************************/
     689             : /*                                Open()                                */
     690             : /************************************************************************/
     691             : 
     692          28 : GDALDataset *OGRPLScenesDataV1Dataset::Open(GDALOpenInfo *poOpenInfo)
     693             : {
     694          28 :     OGRPLScenesDataV1Dataset *poDS = new OGRPLScenesDataV1Dataset();
     695             : 
     696             :     poDS->m_osBaseURL =
     697          28 :         CPLGetConfigOption("PL_URL", "https://api.planet.com/data/v1/");
     698             : 
     699          56 :     char **papszOptions = CSLTokenizeStringComplex(
     700          28 :         poOpenInfo->pszFilename + strlen("PLScenes:"), ",", TRUE, FALSE);
     701             : 
     702             :     poDS->m_osAPIKey = CSLFetchNameValueDef(
     703             :         papszOptions, "api_key",
     704          28 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "API_KEY",
     705          28 :                              CPLGetConfigOption("PL_API_KEY", "")));
     706          28 :     if (poDS->m_osAPIKey.empty())
     707             :     {
     708           1 :         CPLError(
     709             :             CE_Failure, CPLE_AppDefined,
     710             :             "Missing PL_API_KEY configuration option or API_KEY open option");
     711           1 :         delete poDS;
     712           1 :         CSLDestroy(papszOptions);
     713           1 :         return nullptr;
     714             :     }
     715             : 
     716          27 :     poDS->m_bFollowLinks = CPLTestBool(
     717             :         CSLFetchNameValueDef(papszOptions, "follow_links",
     718          27 :                              CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     719             :                                                   "FOLLOW_LINKS", "FALSE")));
     720             : 
     721             :     poDS->m_osFilter = CSLFetchNameValueDef(
     722             :         papszOptions, "filter",
     723          27 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "FILTER", ""));
     724          27 :     poDS->m_osFilter.Trim();
     725             : 
     726          27 :     const char *pszScene = CSLFetchNameValueDef(
     727             :         papszOptions, "scene",
     728          27 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "SCENE"));
     729          27 :     if (pszScene)
     730             :     {
     731             :         GDALDataset *poRasterDS =
     732          13 :             poDS->OpenRasterScene(poOpenInfo, pszScene, papszOptions);
     733          13 :         delete poDS;
     734          13 :         CSLDestroy(papszOptions);
     735          13 :         return poRasterDS;
     736             :     }
     737          14 :     else if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) &&
     738           0 :              !(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR))
     739             :     {
     740           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Missing scene");
     741           0 :         delete poDS;
     742           0 :         CSLDestroy(papszOptions);
     743           0 :         return nullptr;
     744             :     }
     745             : 
     746          19 :     for (char **papszIter = papszOptions; papszIter && *papszIter; papszIter++)
     747             :     {
     748           6 :         char *pszKey = nullptr;
     749           6 :         const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
     750           6 :         if (pszValue != nullptr)
     751             :         {
     752           6 :             if (!EQUAL(pszKey, "api_key") && !EQUAL(pszKey, "version") &&
     753           2 :                 !EQUAL(pszKey, "catalog") && !EQUAL(pszKey, "itemtypes") &&
     754           2 :                 !EQUAL(pszKey, "follow_links") && !EQUAL(pszKey, "filter"))
     755             :             {
     756           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
     757             :                          "Unsupported option '%s'", pszKey);
     758           1 :                 CPLFree(pszKey);
     759           1 :                 delete poDS;
     760           1 :                 CSLDestroy(papszOptions);
     761           1 :                 return nullptr;
     762             :             }
     763           5 :             CPLFree(pszKey);
     764             :         }
     765             :     }
     766             : 
     767             :     json_object *poObj =
     768          13 :         poDS->RunRequest((poDS->m_osBaseURL + "item-types/").c_str());
     769          13 :     if (poObj == nullptr)
     770             :     {
     771           2 :         delete poDS;
     772           2 :         CSLDestroy(papszOptions);
     773           2 :         return nullptr;
     774             :     }
     775             : 
     776          11 :     const char *pszCatalog = CSLFetchNameValueDef(
     777             :         papszOptions, "itemtypes",
     778             :         CSLFetchNameValueDef(
     779             :             papszOptions, "catalog",
     780             :             CSLFetchNameValueDef(
     781          11 :                 poOpenInfo->papszOpenOptions, "ITEMTYPES",
     782          11 :                 CSLFetchNameValue(poOpenInfo->papszOpenOptions, "CATALOG"))));
     783          11 :     if (pszCatalog == nullptr)
     784             :     {
     785             :         // Establish (partial if there are other pages) layer list.
     786           9 :         if (!poDS->ParseItemTypes(poObj, poDS->m_osNextItemTypesPageURL))
     787             :         {
     788           1 :             delete poDS;
     789           1 :             poDS = nullptr;
     790             :         }
     791             :     }
     792             :     else
     793             :     {
     794           2 :         if (poDS->GetLayerByName(pszCatalog) == nullptr)
     795             :         {
     796           1 :             delete poDS;
     797           1 :             poDS = nullptr;
     798             :         }
     799             :     }
     800             : 
     801          11 :     json_object_put(poObj);
     802             : 
     803          11 :     CSLDestroy(papszOptions);
     804             : 
     805          11 :     if (!(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR))
     806             :     {
     807           0 :         delete poDS;
     808           0 :         return nullptr;
     809             :     }
     810             : 
     811          11 :     return poDS;
     812             : }

Generated by: LCOV version 1.14