LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/plscenes - ogrplscenesdatav1dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 381 411 92.7 %
Date: 2026-01-31 22:56:34 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          77 : char **OGRPLScenesDataV1Dataset::GetBaseHTTPOptions()
     211             : {
     212          77 :     m_bMustCleanPersistent = true;
     213             : 
     214          77 :     char **papszOptions = nullptr;
     215             :     papszOptions =
     216          77 :         CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=PLSCENES:%p", this));
     217          77 :     papszOptions = CSLAddString(
     218             :         papszOptions,
     219             :         CPLSPrintf("HEADERS=Authorization: api-key %s", m_osAPIKey.c_str()));
     220             : 
     221          77 :     return papszOptions;
     222             : }
     223             : 
     224             : /************************************************************************/
     225             : /*                             RunRequest()                             */
     226             : /************************************************************************/
     227             : 
     228          77 : json_object *OGRPLScenesDataV1Dataset::RunRequest(const char *pszURL,
     229             :                                                   int bQuiet404Error,
     230             :                                                   const char *pszHTTPVerb,
     231             :                                                   bool bExpectJSonReturn,
     232             :                                                   const char *pszPostContent)
     233             : {
     234          77 :     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          77 :     papszOptions = CSLSetNameValue(papszOptions, "CUSTOMREQUEST", pszHTTPVerb);
     238          77 :     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          77 :     papszOptions = CSLSetNameValue(papszOptions, "MAX_RETRY", "3");
     249          77 :     CPLHTTPResult *psResult = nullptr;
     250          77 :     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          66 :         if (bQuiet404Error)
     283           0 :             CPLPushErrorHandler(CPLQuietErrorHandler);
     284          66 :         psResult = CPLHTTPFetch(pszURL, papszOptions);
     285          66 :         if (bQuiet404Error)
     286           0 :             CPLPopErrorHandler();
     287             :     }
     288          77 :     CSLDestroy(papszOptions);
     289             : 
     290          77 :     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          77 :     if (psResult->pszErrBuf != nullptr)
     300             :     {
     301          16 :         if (!(bQuiet404Error && strstr(psResult->pszErrBuf, "404")))
     302             :         {
     303          16 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
     304          16 :                      psResult->pabyData ? (const char *)psResult->pabyData
     305             :                                         : psResult->pszErrBuf);
     306             :         }
     307          16 :         CPLHTTPDestroyResult(psResult);
     308          16 :         return nullptr;
     309             :     }
     310             : 
     311          61 :     if (!bExpectJSonReturn &&
     312           0 :         (psResult->pabyData == nullptr || psResult->nDataLen == 0))
     313             :     {
     314           0 :         CPLHTTPDestroyResult(psResult);
     315           0 :         return nullptr;
     316             :     }
     317             : 
     318          61 :     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          59 :     const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
     327             : #ifdef DEBUG_VERBOSE
     328             :     CPLDebug("PLScenes", "%s", pszText);
     329             : #endif
     330             : 
     331          59 :     json_object *poObj = nullptr;
     332          59 :     if (!OGRJSonParse(pszText, &poObj, true))
     333             :     {
     334           2 :         CPLHTTPDestroyResult(psResult);
     335           2 :         return nullptr;
     336             :     }
     337             : 
     338          57 :     CPLHTTPDestroyResult(psResult);
     339             : 
     340          57 :     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          57 :     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(
     374             :     GDALOpenInfo *poOpenInfo, CPLString osScene, CSLConstList papszOptions)
     375             : {
     376          13 :     if (!(poOpenInfo->nOpenFlags & GDAL_OF_RASTER))
     377             :     {
     378           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     379             :                  "The scene option must only be used with vector access");
     380           0 :         return nullptr;
     381             :     }
     382             : 
     383          26 :     int nActivationTimeout = atoi(CSLFetchNameValueDef(
     384          13 :         poOpenInfo->papszOpenOptions, "ACTIVATION_TIMEOUT", "3600"));
     385             : 
     386          13 :     for (const auto &[pszKey, pszValue] : cpl::IterateNameValue(papszOptions))
     387             :     {
     388           1 :         if (!EQUAL(pszKey, "api_key") && !EQUAL(pszKey, "scene") &&
     389           1 :             !EQUAL(pszKey, "product_type") && !EQUAL(pszKey, "asset") &&
     390           1 :             !EQUAL(pszKey, "catalog") && !EQUAL(pszKey, "itemtypes") &&
     391           1 :             !EQUAL(pszKey, "version") && !EQUAL(pszKey, "follow_links") &&
     392           1 :             !EQUAL(pszKey, "metadata"))
     393             :         {
     394           1 :             CPLError(CE_Failure, CPLE_NotSupported, "Unsupported option %s",
     395             :                      pszKey);
     396           1 :             return nullptr;
     397             :         }
     398             :     }
     399             : 
     400          12 :     const char *pszCatalog = CSLFetchNameValueDef(
     401             :         papszOptions, "itemtypes",
     402             :         CSLFetchNameValueDef(
     403             :             papszOptions, "catalog",
     404             :             CSLFetchNameValueDef(
     405          12 :                 poOpenInfo->papszOpenOptions, "ITEMTYPES",
     406          12 :                 CSLFetchNameValue(poOpenInfo->papszOpenOptions, "CATALOG"))));
     407          12 :     if (pszCatalog == nullptr)
     408             :     {
     409           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Missing catalog");
     410           1 :         return nullptr;
     411             :     }
     412             : 
     413          11 :     const char *pszProductType = CSLFetchNameValueDef(
     414             :         papszOptions, "asset",
     415             :         CSLFetchNameValueDef(
     416             :             papszOptions, "product_type",
     417          11 :             CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "ASSET",
     418          11 :                                  CSLFetchNameValue(poOpenInfo->papszOpenOptions,
     419             :                                                    "PRODUCT_TYPE"))));
     420             : 
     421          22 :     CPLString osRasterURL;
     422          11 :     osRasterURL = m_osBaseURL;
     423          11 :     osRasterURL += "item-types/";
     424          11 :     osRasterURL += pszCatalog;
     425          11 :     osRasterURL += "/items/";
     426          11 :     osRasterURL += osScene;
     427          11 :     osRasterURL += "/assets/";
     428             : 
     429          11 :     time_t nStartTime = time(nullptr);
     430          19 : retry:
     431          19 :     time_t nCurrentTime = time(nullptr);
     432          19 :     if (nCurrentTime - nStartTime > nActivationTimeout)
     433             :     {
     434           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Activation timeout reached");
     435           2 :         return nullptr;
     436             :     }
     437          17 :     json_object *poObj = RunRequest(osRasterURL);
     438          17 :     if (poObj == nullptr)
     439           1 :         return nullptr;
     440             : 
     441          16 :     json_object *poSubObj = CPL_json_object_object_get(
     442             :         poObj, pszProductType ? pszProductType : "visual");
     443          16 :     if (poSubObj == nullptr)
     444             :     {
     445           2 :         if (pszProductType != nullptr && !EQUAL(pszProductType, "LIST"))
     446             :         {
     447           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find asset %s",
     448             :                      pszProductType);
     449           1 :             json_object_put(poObj);
     450             :         }
     451             :         else
     452             :         {
     453             :             json_object_iter it;
     454           1 :             it.key = nullptr;
     455           1 :             it.val = nullptr;
     456           1 :             it.entry = nullptr;
     457           1 :             char **papszSubdatasets = nullptr;
     458           1 :             int nSubDataset = 0;
     459           2 :             json_object_object_foreachC(poObj, it)
     460             :             {
     461           1 :                 ++nSubDataset;
     462           1 :                 papszSubdatasets = CSLSetNameValue(
     463             :                     papszSubdatasets,
     464             :                     CPLSPrintf("SUBDATASET_%d_NAME", nSubDataset),
     465             :                     CPLSPrintf("Scene=%s of item types %s, asset %s",
     466             :                                osScene.c_str(), pszCatalog, it.key));
     467           1 :                 papszSubdatasets = CSLSetNameValue(
     468             :                     papszSubdatasets,
     469             :                     CPLSPrintf("SUBDATASET_%d_DESC", nSubDataset),
     470             :                     CPLSPrintf("PLScenes:version=Data_V1,itemtypes=%s,scene=%s,"
     471             :                                "asset=%s",
     472             :                                pszCatalog, osScene.c_str(), it.key));
     473             :             }
     474           1 :             json_object_put(poObj);
     475           1 :             if (nSubDataset != 0)
     476             :             {
     477           1 :                 GDALDataset *poDS = new OGRPLScenesDataV1Dataset();
     478           1 :                 poDS->SetMetadata(papszSubdatasets, "SUBDATASETS");
     479           1 :                 CSLDestroy(papszSubdatasets);
     480           1 :                 return poDS;
     481             :             }
     482             :         }
     483           1 :         return nullptr;
     484             :     }
     485             : 
     486          14 :     if (json_object_get_type(poSubObj) != json_type_object)
     487             :     {
     488           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find link");
     489           1 :         json_object_put(poObj);
     490           1 :         return nullptr;
     491             :     }
     492             : 
     493             :     json_object *poPermissions =
     494          13 :         CPL_json_object_object_get(poSubObj, "_permissions");
     495          13 :     if (poPermissions != nullptr)
     496             :     {
     497             :         const char *pszPermissions =
     498          13 :             json_object_to_json_string_ext(poPermissions, 0);
     499          13 :         if (pszPermissions && strstr(pszPermissions, "download") == nullptr)
     500             :         {
     501           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     502             :                      "You don't have download permissions for this product");
     503             :         }
     504             :     }
     505             : 
     506          13 :     json_object *poLocation = CPL_json_object_object_get(poSubObj, "location");
     507          13 :     json_object *poStatus = CPL_json_object_object_get(poSubObj, "status");
     508          13 :     bool bActive = false;
     509          26 :     if (poStatus != nullptr &&
     510          13 :         json_object_get_type(poStatus) == json_type_string)
     511             :     {
     512          13 :         const char *pszStatus = json_object_get_string(poStatus);
     513          13 :         if (EQUAL(pszStatus, "activating"))
     514             :         {
     515           4 :             CPLDebug("PLScenes", "The product is in activation. Retrying...");
     516           4 :             CPLSleep(nActivationTimeout == 1 ? 0.5 : 1.0);
     517           4 :             poLocation = nullptr;
     518           4 :             json_object_put(poObj);
     519           4 :             goto retry;
     520             :         }
     521           9 :         bActive = EQUAL(pszStatus, "active");
     522             :     }
     523          14 :     if (poLocation == nullptr ||
     524           9 :         json_object_get_type(poLocation) != json_type_string || !bActive)
     525             :     {
     526           4 :         CPLDebug("PLScenes", "The product isn't activated yet. Activating it");
     527             :         json_object *poActivate =
     528           4 :             json_ex_get_object_by_path(poSubObj, "_links.activate");
     529           8 :         if (poActivate == nullptr ||
     530           4 :             json_object_get_type(poActivate) != json_type_string)
     531             :         {
     532           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     533             :                      "Cannot find link to activate scene %s", osScene.c_str());
     534           0 :             json_object_put(poObj);
     535           0 :             return nullptr;
     536             :         }
     537           4 :         CPLString osActivate = json_object_get_string(poActivate);
     538           4 :         poLocation = nullptr;
     539           4 :         json_object_put(poObj);
     540           4 :         poObj = RunRequest(osActivate, FALSE, "GET", false);
     541           4 :         if (poObj != nullptr)
     542           0 :             json_object_put(poObj);
     543           4 :         poObj = nullptr;
     544           4 :         CPLSleep(nActivationTimeout == 1 ? 0.5 : 1.0);
     545           4 :         goto retry;
     546             :     }
     547             : 
     548           5 :     const char *pszLink = json_object_get_string(poLocation);
     549             : 
     550           5 :     osRasterURL = pszLink ? pszLink : "";
     551           5 :     json_object_put(poObj);
     552           5 :     if (osRasterURL.empty())
     553             :     {
     554           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find link to scene %s",
     555             :                  osScene.c_str());
     556           0 :         return nullptr;
     557             :     }
     558             : 
     559           5 :     osRasterURL = InsertAPIKeyInURL(osRasterURL);
     560             : 
     561             :     const bool bUseVSICURL =
     562           5 :         CPLFetchBool(poOpenInfo->papszOpenOptions, "RANDOM_ACCESS", true);
     563           5 :     if (bUseVSICURL && !(STARTS_WITH(m_osBaseURL, "/vsimem/")))
     564             :     {
     565           5 :         char *pszEscapedURL = CPLEscapeString(osRasterURL, -1, CPLES_URL);
     566             :         CPLString osTmpURL("/vsicurl?use_head=no&max_retry=3&empty_dir=yes&use_"
     567          10 :                            "redirect_url_if_no_query_string_params=yes&url=");
     568           5 :         osTmpURL += pszEscapedURL;
     569           5 :         CPLFree(pszEscapedURL);
     570           5 :         CPLDebug("PLSCENES", "URL = %s", osTmpURL.c_str());
     571             : 
     572             :         VSIStatBufL sStat;
     573           5 :         if (VSIStatL(osTmpURL, &sStat) == 0 && sStat.st_size > 0)
     574             :         {
     575           4 :             osRasterURL = std::move(osTmpURL);
     576             :         }
     577             :         else
     578             :         {
     579           1 :             CPLDebug("PLSCENES", "Cannot use random access for that file");
     580             :         }
     581             :     }
     582             : 
     583           5 :     char **papszAllowedDrivers = nullptr;
     584           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "HTTP");
     585           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "GTiff");
     586           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "PNG");
     587           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JPEG");
     588           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "NITF");
     589           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2KAK");
     590           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2ECW");
     591           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2MrSID");
     592           5 :     papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2OpenJPEG");
     593           5 :     GDALDataset *poOutDS = (GDALDataset *)GDALOpenEx(
     594             :         osRasterURL, GDAL_OF_RASTER, papszAllowedDrivers, nullptr, nullptr);
     595           5 :     CSLDestroy(papszAllowedDrivers);
     596           5 :     if (poOutDS)
     597             :     {
     598           3 :         if (CPLFetchBool(
     599             :                 papszOptions, "metadata",
     600           3 :                 CPLFetchBool(poOpenInfo->papszOpenOptions, "METADATA", true)))
     601             :         {
     602           3 :             OGRLayer *poLayer = GetLayerByName(pszCatalog);
     603           3 :             if (poLayer != nullptr)
     604             :             {
     605             :                 // Set a dummy name so that PAM goes here
     606           2 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
     607             : 
     608             :                 std::string osTmpFilename =
     609           4 :                     VSIMemGenerateHiddenFilename("ogrplscenesDataV1");
     610             : 
     611           2 :                 poOutDS->SetDescription(osTmpFilename.c_str());
     612             : 
     613             :                 /* Attach scene metadata. */
     614           2 :                 poLayer->SetAttributeFilter(
     615           2 :                     CPLSPrintf("id = '%s'", osScene.c_str()));
     616           2 :                 OGRFeature *poFeat = poLayer->GetNextFeature();
     617           2 :                 if (poFeat)
     618             :                 {
     619          41 :                     for (int i = 0; i < poFeat->GetFieldCount(); i++)
     620             :                     {
     621          40 :                         if (poFeat->IsFieldSetAndNotNull(i))
     622             :                         {
     623             :                             const char *pszKey =
     624           2 :                                 poFeat->GetFieldDefnRef(i)->GetNameRef();
     625           2 :                             const char *pszVal = poFeat->GetFieldAsString(i);
     626           2 :                             if (strncmp(pszKey, "asset_", strlen("asset_")) ==
     627           2 :                                     0 ||
     628           2 :                                 strstr(pszVal, "https://") != nullptr ||
     629           2 :                                 strcmp(pszKey, "columns") == 0 ||
     630           2 :                                 strcmp(pszKey, "rows") == 0 ||
     631           2 :                                 strcmp(pszKey, "epsg_code") == 0 ||
     632           2 :                                 strcmp(pszKey, "origin_x") == 0 ||
     633           2 :                                 strcmp(pszKey, "origin_y") == 0 ||
     634           2 :                                 strcmp(pszKey, "permissions") == 0 ||
     635           2 :                                 strcmp(pszKey, "acquired") ==
     636             :                                     0  // Redundant with TIFFTAG_DATETIME
     637             :                             )
     638             :                             {
     639           0 :                                 continue;
     640             :                             }
     641           2 :                             poOutDS->SetMetadataItem(pszKey, pszVal);
     642             :                         }
     643             :                     }
     644             :                 }
     645           2 :                 delete poFeat;
     646             : 
     647           2 :                 poOutDS->FlushCache(false);
     648           2 :                 VSIUnlink(osTmpFilename.c_str());
     649           2 :                 VSIUnlink(
     650           4 :                     std::string(osTmpFilename).append(".aux.xml").c_str());
     651           2 :                 CPLPopErrorHandler();
     652           2 :                 CPL_IGNORE_RET_VAL(osTmpFilename);
     653             :             }
     654             :         }
     655             : 
     656           3 :         CPLErrorReset();
     657           3 :         poOutDS->SetDescription(poOpenInfo->pszFilename);
     658             :     }
     659           2 :     else if (CPLGetLastErrorType() == CE_None)
     660             :     {
     661           2 :         poObj = RunRequest(osRasterURL);
     662           2 :         if (poObj == nullptr)
     663             :         {
     664           2 :             CPLError(
     665             :                 CE_Failure, CPLE_AppDefined,
     666             :                 "The generation of the product is in progress. Retry later");
     667             :         }
     668             :         else
     669             :         {
     670           0 :             CPLError(
     671             :                 CE_Failure, CPLE_AppDefined, "%s",
     672             :                 json_object_to_json_string_ext(poObj, JSON_C_TO_STRING_PRETTY));
     673           0 :             json_object_put(poObj);
     674             :         }
     675             :     }
     676             : 
     677           5 :     return poOutDS;
     678             : }
     679             : 
     680             : /************************************************************************/
     681             : /*                                Open()                                */
     682             : /************************************************************************/
     683             : 
     684          28 : GDALDataset *OGRPLScenesDataV1Dataset::Open(GDALOpenInfo *poOpenInfo)
     685             : {
     686          28 :     OGRPLScenesDataV1Dataset *poDS = new OGRPLScenesDataV1Dataset();
     687             : 
     688             :     poDS->m_osBaseURL =
     689          28 :         CPLGetConfigOption("PL_URL", "https://api.planet.com/data/v1/");
     690             : 
     691          56 :     char **papszOptions = CSLTokenizeStringComplex(
     692          28 :         poOpenInfo->pszFilename + strlen("PLScenes:"), ",", TRUE, FALSE);
     693             : 
     694             :     poDS->m_osAPIKey = CSLFetchNameValueDef(
     695             :         papszOptions, "api_key",
     696          28 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "API_KEY",
     697          28 :                              CPLGetConfigOption("PL_API_KEY", "")));
     698          28 :     if (poDS->m_osAPIKey.empty())
     699             :     {
     700           1 :         CPLError(
     701             :             CE_Failure, CPLE_AppDefined,
     702             :             "Missing PL_API_KEY configuration option or API_KEY open option");
     703           1 :         delete poDS;
     704           1 :         CSLDestroy(papszOptions);
     705           1 :         return nullptr;
     706             :     }
     707             : 
     708          27 :     poDS->m_bFollowLinks = CPLTestBool(
     709             :         CSLFetchNameValueDef(papszOptions, "follow_links",
     710          27 :                              CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     711             :                                                   "FOLLOW_LINKS", "FALSE")));
     712             : 
     713             :     poDS->m_osFilter = CSLFetchNameValueDef(
     714             :         papszOptions, "filter",
     715          27 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "FILTER", ""));
     716          27 :     poDS->m_osFilter.Trim();
     717             : 
     718          27 :     const char *pszScene = CSLFetchNameValueDef(
     719             :         papszOptions, "scene",
     720          27 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "SCENE"));
     721          27 :     if (pszScene)
     722             :     {
     723             :         GDALDataset *poRasterDS =
     724          13 :             poDS->OpenRasterScene(poOpenInfo, pszScene, papszOptions);
     725          13 :         delete poDS;
     726          13 :         CSLDestroy(papszOptions);
     727          13 :         return poRasterDS;
     728             :     }
     729          14 :     else if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) &&
     730           0 :              !(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR))
     731             :     {
     732           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Missing scene");
     733           0 :         delete poDS;
     734           0 :         CSLDestroy(papszOptions);
     735           0 :         return nullptr;
     736             :     }
     737             : 
     738          19 :     for (char **papszIter = papszOptions; papszIter && *papszIter; papszIter++)
     739             :     {
     740           6 :         char *pszKey = nullptr;
     741           6 :         const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
     742           6 :         if (pszValue != nullptr)
     743             :         {
     744           6 :             if (!EQUAL(pszKey, "api_key") && !EQUAL(pszKey, "version") &&
     745           2 :                 !EQUAL(pszKey, "catalog") && !EQUAL(pszKey, "itemtypes") &&
     746           2 :                 !EQUAL(pszKey, "follow_links") && !EQUAL(pszKey, "filter"))
     747             :             {
     748           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
     749             :                          "Unsupported option '%s'", pszKey);
     750           1 :                 CPLFree(pszKey);
     751           1 :                 delete poDS;
     752           1 :                 CSLDestroy(papszOptions);
     753           1 :                 return nullptr;
     754             :             }
     755           5 :             CPLFree(pszKey);
     756             :         }
     757             :     }
     758             : 
     759             :     json_object *poObj =
     760          13 :         poDS->RunRequest((poDS->m_osBaseURL + "item-types/").c_str());
     761          13 :     if (poObj == nullptr)
     762             :     {
     763           2 :         delete poDS;
     764           2 :         CSLDestroy(papszOptions);
     765           2 :         return nullptr;
     766             :     }
     767             : 
     768          11 :     const char *pszCatalog = CSLFetchNameValueDef(
     769             :         papszOptions, "itemtypes",
     770             :         CSLFetchNameValueDef(
     771             :             papszOptions, "catalog",
     772             :             CSLFetchNameValueDef(
     773          11 :                 poOpenInfo->papszOpenOptions, "ITEMTYPES",
     774          11 :                 CSLFetchNameValue(poOpenInfo->papszOpenOptions, "CATALOG"))));
     775          11 :     if (pszCatalog == nullptr)
     776             :     {
     777             :         // Establish (partial if there are other pages) layer list.
     778           9 :         if (!poDS->ParseItemTypes(poObj, poDS->m_osNextItemTypesPageURL))
     779             :         {
     780           1 :             delete poDS;
     781           1 :             poDS = nullptr;
     782             :         }
     783             :     }
     784             :     else
     785             :     {
     786           2 :         if (poDS->GetLayerByName(pszCatalog) == nullptr)
     787             :         {
     788           1 :             delete poDS;
     789           1 :             poDS = nullptr;
     790             :         }
     791             :     }
     792             : 
     793          11 :     json_object_put(poObj);
     794             : 
     795          11 :     CSLDestroy(papszOptions);
     796             : 
     797          11 :     if (!(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR))
     798             :     {
     799           0 :         delete poDS;
     800           0 :         return nullptr;
     801             :     }
     802             : 
     803          11 :     return poDS;
     804             : }

Generated by: LCOV version 1.14