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

Generated by: LCOV version 1.14