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

Generated by: LCOV version 1.14