LCOV - code coverage report
Current view: top level - frmts/eeda - eedadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 511 592 86.3 %
Date: 2025-02-20 10:14:44 Functions: 28 29 96.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Earth Engine Data API driver
       4             :  * Purpose:  Earth Engine Data API driver
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2017-2018, Planet Labs
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_priv.h"
      14             : #include "ogrsf_frmts.h"
      15             : #include "cpl_http.h"
      16             : #include "cpl_conv.h"
      17             : #include "ogrlibjsonutils.h"
      18             : #include "ogr_swq.h"
      19             : #include "eeda.h"
      20             : 
      21             : #include <algorithm>
      22             : #include <cinttypes>
      23             : #include <vector>
      24             : #include <map>
      25             : #include <set>
      26             : #include <limits>
      27             : 
      28             : extern "C" void GDALRegister_EEDA();
      29             : 
      30             : /************************************************************************/
      31             : /*                     CPLEscapeURLQueryParameter()                     */
      32             : /************************************************************************/
      33             : 
      34           9 : static CPLString CPLEscapeURLQueryParameter(const char *pszInput)
      35             : {
      36           9 :     int nLength = static_cast<int>(strlen(pszInput));
      37             : 
      38           9 :     const size_t nSizeAlloc = nLength * 4 + 1;
      39           9 :     char *pszOutput = static_cast<char *>(CPLMalloc(nSizeAlloc));
      40           9 :     int iOut = 0;
      41             : 
      42         506 :     for (int iIn = 0; iIn < nLength; ++iIn)
      43             :     {
      44         497 :         if ((pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') ||
      45         357 :             (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') ||
      46         317 :             (pszInput[iIn] >= '0' && pszInput[iIn] <= '9'))
      47             :         {
      48         310 :             pszOutput[iOut++] = pszInput[iIn];
      49             :         }
      50             :         else
      51             :         {
      52         187 :             snprintf(pszOutput + iOut, nSizeAlloc - iOut, "%%%02X",
      53         187 :                      static_cast<unsigned char>(pszInput[iIn]));
      54         187 :             iOut += 3;
      55             :         }
      56             :     }
      57           9 :     pszOutput[iOut] = '\0';
      58             : 
      59           9 :     CPLString osRet(pszOutput);
      60           9 :     CPLFree(pszOutput);
      61           9 :     return osRet;
      62             : }
      63             : 
      64             : /************************************************************************/
      65             : /*                          GDALEEDADataset                             */
      66             : /************************************************************************/
      67             : 
      68             : class GDALEEDALayer;
      69             : 
      70             : class GDALEEDADataset final : public GDALEEDABaseDataset
      71             : {
      72             :     CPL_DISALLOW_COPY_ASSIGN(GDALEEDADataset)
      73             : 
      74             :     GDALEEDALayer *m_poLayer;
      75             : 
      76             :   public:
      77             :     GDALEEDADataset();
      78             :     virtual ~GDALEEDADataset();
      79             : 
      80           0 :     virtual int GetLayerCount() CPL_OVERRIDE
      81             :     {
      82           0 :         return m_poLayer ? 1 : 0;
      83             :     }
      84             : 
      85             :     virtual OGRLayer *GetLayer(int idx) CPL_OVERRIDE;
      86             : 
      87             :     bool Open(GDALOpenInfo *poOpenInfo);
      88             :     json_object *RunRequest(const CPLString &osURL);
      89             : 
      90           7 :     const CPLString &GetBaseURL() const
      91             :     {
      92           7 :         return m_osBaseURL;
      93             :     }
      94             : };
      95             : 
      96             : /************************************************************************/
      97             : /*                          GDALEEDALayer                               */
      98             : /************************************************************************/
      99             : 
     100             : class GDALEEDALayer final : public OGRLayer
     101             : {
     102             :     CPL_DISALLOW_COPY_ASSIGN(GDALEEDALayer)
     103             : 
     104             :     GDALEEDADataset *m_poDS;
     105             :     CPLString m_osCollection{};
     106             :     CPLString m_osCollectionName{};
     107             :     OGRFeatureDefn *m_poFeatureDefn;
     108             :     json_object *m_poCurPageObj;
     109             :     json_object *m_poCurPageAssets;
     110             :     int m_nIndexInPage;
     111             :     GIntBig m_nFID;
     112             :     CPLString m_osAttributeFilter{};
     113             :     CPLString m_osStartTime{};
     114             :     CPLString m_osEndTime{};
     115             :     bool m_bFilterMustBeClientSideEvaluated;
     116             :     std::set<int> m_oSetQueryableFields{};
     117             :     std::map<CPLString, CPLString> m_oMapCodeToWKT{};
     118             : 
     119             :     OGRFeature *GetNextRawFeature();
     120             :     bool IsSimpleComparison(const swq_expr_node *poNode);
     121             :     CPLString BuildFilter(swq_expr_node *poNode, bool bIsAndTopLevel);
     122             : 
     123             :   public:
     124             :     GDALEEDALayer(GDALEEDADataset *poDS, const CPLString &osCollection,
     125             :                   const CPLString &osCollectionName, json_object *poAsset,
     126             :                   json_object *poLayerConf);
     127             :     virtual ~GDALEEDALayer();
     128             : 
     129             :     virtual void ResetReading() CPL_OVERRIDE;
     130             :     virtual OGRFeature *GetNextFeature() CPL_OVERRIDE;
     131             :     virtual int TestCapability(const char *) CPL_OVERRIDE;
     132             : 
     133           8 :     virtual OGRFeatureDefn *GetLayerDefn() CPL_OVERRIDE
     134             :     {
     135           8 :         return m_poFeatureDefn;
     136             :     }
     137             : 
     138           1 :     virtual GIntBig GetFeatureCount(int) CPL_OVERRIDE
     139             :     {
     140           1 :         return -1;
     141             :     }
     142             : 
     143             :     virtual OGRErr ISetSpatialFilter(int iGeomField,
     144             :                                      const OGRGeometry *poGeom) override;
     145             : 
     146             :     virtual OGRErr SetAttributeFilter(const char *) CPL_OVERRIDE;
     147             : 
     148             :     virtual OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
     149             :                               bool bForce) override;
     150             : };
     151             : 
     152             : /************************************************************************/
     153             : /*                            GDALEEDALayer()                           */
     154             : /************************************************************************/
     155             : 
     156           7 : GDALEEDALayer::GDALEEDALayer(GDALEEDADataset *poDS,
     157             :                              const CPLString &osCollection,
     158             :                              const CPLString &osCollectionName,
     159           7 :                              json_object *poAsset, json_object *poLayerConf)
     160             :     : m_poDS(poDS), m_osCollection(osCollection),
     161             :       m_osCollectionName(osCollectionName), m_poFeatureDefn(nullptr),
     162             :       m_poCurPageObj(nullptr), m_poCurPageAssets(nullptr), m_nIndexInPage(0),
     163           7 :       m_nFID(1), m_bFilterMustBeClientSideEvaluated(true)
     164             : {
     165           7 :     CPLString osLaundered(osCollection);
     166         126 :     for (size_t i = 0; i < osLaundered.size(); i++)
     167             :     {
     168         119 :         if (!isalnum(static_cast<unsigned char>(osLaundered[i])))
     169             :         {
     170          13 :             osLaundered[i] = '_';
     171             :         }
     172             :     }
     173           7 :     SetDescription(osLaundered);
     174           7 :     m_poFeatureDefn = new OGRFeatureDefn(osLaundered);
     175           7 :     m_poFeatureDefn->Reference();
     176           7 :     m_poFeatureDefn->SetGeomType(wkbMultiPolygon);
     177           7 :     OGRSpatialReference *poSRS = new OGRSpatialReference();
     178           7 :     poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
     179           7 :     m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
     180           7 :     poSRS->Release();
     181             : 
     182             :     {
     183          14 :         OGRFieldDefn oFieldDefn("name", OFTString);
     184           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     185             :     }
     186             :     {
     187          14 :         OGRFieldDefn oFieldDefn("id", OFTString);
     188           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     189             :     }
     190             :     {
     191          14 :         OGRFieldDefn oFieldDefn("gdal_dataset", OFTString);
     192           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     193             :     }
     194             :     {
     195          14 :         OGRFieldDefn oFieldDefn("updateTime", OFTDateTime);
     196           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     197             :     }
     198             :     {
     199          14 :         OGRFieldDefn oFieldDefn("startTime", OFTDateTime);
     200           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     201             :     }
     202             :     {
     203          14 :         OGRFieldDefn oFieldDefn("endTime", OFTDateTime);
     204           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     205             :     }
     206             :     {
     207          14 :         OGRFieldDefn oFieldDefn("sizeBytes", OFTInteger64);
     208           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     209             :     }
     210             :     {
     211          14 :         OGRFieldDefn oFieldDefn("band_count", OFTInteger);
     212           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     213             :     }
     214             :     {
     215          14 :         OGRFieldDefn oFieldDefn("band_max_width", OFTInteger);
     216           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     217             :     }
     218             :     {
     219          14 :         OGRFieldDefn oFieldDefn("band_max_height", OFTInteger);
     220           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     221             :     }
     222             :     {
     223          14 :         OGRFieldDefn oFieldDefn("band_min_pixel_size", OFTReal);
     224           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     225             :     }
     226             :     {
     227          14 :         OGRFieldDefn oFieldDefn("band_upper_left_x", OFTReal);
     228           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     229             :     }
     230             :     {
     231          14 :         OGRFieldDefn oFieldDefn("band_upper_left_y", OFTReal);
     232           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     233             :     }
     234             :     {
     235          14 :         OGRFieldDefn oFieldDefn("band_crs", OFTString);
     236           7 :         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     237             :     }
     238             : 
     239           7 :     if (poLayerConf)
     240             :     {
     241             :         json_object *poFields =
     242           1 :             CPL_json_object_object_get(poLayerConf, "fields");
     243           2 :         if (poFields == nullptr ||
     244           1 :             json_object_get_type(poFields) != json_type_array)
     245             :         {
     246           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     247             :                      "Cannot find %s.fields object in eedaconf.json",
     248             :                      GetDescription());
     249           0 :             return;
     250             :         }
     251             : 
     252           1 :         const auto nFields = json_object_array_length(poFields);
     253           5 :         for (auto i = decltype(nFields){0}; i < nFields; i++)
     254             :         {
     255           4 :             json_object *poField = json_object_array_get_idx(poFields, i);
     256           4 :             if (poField && json_object_get_type(poField) == json_type_object)
     257             :             {
     258             :                 json_object *poName =
     259           4 :                     CPL_json_object_object_get(poField, "name");
     260             :                 json_object *poType =
     261           4 :                     CPL_json_object_object_get(poField, "type");
     262           8 :                 if (poName &&
     263           4 :                     json_object_get_type(poName) == json_type_string &&
     264           8 :                     poType && json_object_get_type(poType) == json_type_string)
     265             :                 {
     266           4 :                     const char *pszName = json_object_get_string(poName);
     267           4 :                     const char *pszType = json_object_get_string(poType);
     268           4 :                     OGRFieldType eType(OFTString);
     269           4 :                     if (EQUAL(pszType, "datetime"))
     270           0 :                         eType = OFTDateTime;
     271           4 :                     else if (EQUAL(pszType, "double"))
     272           1 :                         eType = OFTReal;
     273           3 :                     else if (EQUAL(pszType, "int"))
     274           1 :                         eType = OFTInteger;
     275           2 :                     else if (EQUAL(pszType, "int64"))
     276           2 :                         eType = OFTInteger64;
     277           0 :                     else if (EQUAL(pszType, "string"))
     278           0 :                         eType = OFTString;
     279             :                     else
     280             :                     {
     281           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     282             :                                  "Unrecognized field type %s for field %s",
     283             :                                  pszType, pszName);
     284             :                     }
     285           4 :                     OGRFieldDefn oFieldDefn(pszName, eType);
     286           4 :                     m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     287             :                     m_oSetQueryableFields.insert(
     288           4 :                         m_poFeatureDefn->GetFieldCount() - 1);
     289             :                 }
     290             :             }
     291             :         }
     292             : 
     293           1 :         json_object *poAddOtherProp = CPL_json_object_object_get(
     294             :             poLayerConf, "add_other_properties_field");
     295           1 :         if (json_object_get_boolean(poAddOtherProp))
     296             :         {
     297           2 :             OGRFieldDefn oFieldDefn("other_properties", OFTString);
     298           1 :             m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     299             :         }
     300             :     }
     301             :     else
     302             :     {
     303             :         json_object *poProperties =
     304           6 :             CPL_json_object_object_get(poAsset, "properties");
     305           7 :         if (poProperties != nullptr &&
     306           1 :             json_object_get_type(poProperties) == json_type_object)
     307             :         {
     308             :             json_object_iter it;
     309           1 :             it.key = nullptr;
     310           1 :             it.val = nullptr;
     311           1 :             it.entry = nullptr;
     312           5 :             json_object_object_foreachC(poProperties, it)
     313             :             {
     314           4 :                 OGRFieldType eType(OFTString);
     315           4 :                 if (it.val)
     316             :                 {
     317           4 :                     if (json_object_get_type(it.val) == json_type_int)
     318             :                     {
     319           2 :                         if (strstr(it.key, "PERCENTAGE"))
     320           0 :                             eType = OFTReal;
     321           2 :                         else if (CPLAtoGIntBig(json_object_get_string(it.val)) >
     322             :                                  INT_MAX)
     323           1 :                             eType = OFTInteger64;
     324             :                         else
     325           1 :                             eType = OFTInteger;
     326             :                     }
     327           2 :                     else if (json_object_get_type(it.val) == json_type_double)
     328             :                     {
     329           1 :                         eType = OFTReal;
     330             :                     }
     331             :                 }
     332           4 :                 OGRFieldDefn oFieldDefn(it.key, eType);
     333           4 :                 m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     334           4 :                 m_oSetQueryableFields.insert(m_poFeatureDefn->GetFieldCount() -
     335           4 :                                              1);
     336             :             }
     337             :         }
     338             :         {
     339          12 :             OGRFieldDefn oFieldDefn("other_properties", OFTString);
     340           6 :             m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     341             :         }
     342             :     }
     343             : }
     344             : 
     345             : /************************************************************************/
     346             : /*                           ~GDALEEDALayer()                           */
     347             : /************************************************************************/
     348             : 
     349          14 : GDALEEDALayer::~GDALEEDALayer()
     350             : {
     351           7 :     m_poFeatureDefn->Release();
     352           7 :     if (m_poCurPageObj)
     353           1 :         json_object_put(m_poCurPageObj);
     354          14 : }
     355             : 
     356             : /************************************************************************/
     357             : /*                            ResetReading()                            */
     358             : /************************************************************************/
     359             : 
     360          11 : void GDALEEDALayer::ResetReading()
     361             : {
     362          11 :     if (m_poCurPageObj)
     363           5 :         json_object_put(m_poCurPageObj);
     364          11 :     m_poCurPageObj = nullptr;
     365          11 :     m_poCurPageAssets = nullptr;
     366          11 :     m_nIndexInPage = 0;
     367          11 :     m_nFID = 1;
     368          11 : }
     369             : 
     370             : /************************************************************************/
     371             : /*                        GetNextRawFeature()                           */
     372             : /************************************************************************/
     373             : 
     374           9 : OGRFeature *GDALEEDALayer::GetNextRawFeature()
     375             : {
     376          18 :     CPLString osNextPageToken;
     377          12 :     if (m_poCurPageAssets != nullptr &&
     378           6 :         m_nIndexInPage >=
     379           3 :             static_cast<int>(json_object_array_length(m_poCurPageAssets)))
     380             :     {
     381             :         json_object *poToken =
     382           2 :             CPL_json_object_object_get(m_poCurPageObj, "nextPageToken");
     383           2 :         const char *pszToken = json_object_get_string(poToken);
     384           2 :         if (pszToken == nullptr)
     385           1 :             return nullptr;
     386           1 :         osNextPageToken = pszToken;
     387           1 :         json_object_put(m_poCurPageObj);
     388           1 :         m_poCurPageObj = nullptr;
     389           1 :         m_poCurPageAssets = nullptr;
     390           1 :         m_nIndexInPage = 0;
     391             :     }
     392             : 
     393           8 :     if (m_poCurPageObj == nullptr)
     394             :     {
     395          14 :         CPLString osURL(m_poDS->GetBaseURL() + m_osCollectionName +
     396           7 :                         ":listImages");
     397           7 :         CPLString query = "";
     398           7 :         if (!osNextPageToken.empty())
     399             :         {
     400             :             query +=
     401           1 :                 "&pageToken=" + CPLEscapeURLQueryParameter(osNextPageToken);
     402             :         }
     403           7 :         const char *pszPageSize = CPLGetConfigOption("EEDA_PAGE_SIZE", nullptr);
     404           7 :         if (pszPageSize)
     405             :         {
     406           0 :             query += "&pageSize=";
     407           0 :             query += pszPageSize;
     408             :         }
     409           7 :         if (m_poFilterGeom != nullptr)
     410             :         {
     411             :             char *pszGeoJSON =
     412           1 :                 OGR_G_ExportToJson(OGRGeometry::ToHandle(m_poFilterGeom));
     413           1 :             query += "&region=";
     414           1 :             query += CPLEscapeURLQueryParameter(pszGeoJSON);
     415           1 :             CPLFree(pszGeoJSON);
     416             :         }
     417           7 :         if (!m_osAttributeFilter.empty())
     418             :         {
     419           2 :             query += "&filter=";
     420           2 :             query += CPLEscapeURLQueryParameter(m_osAttributeFilter);
     421             :         }
     422           7 :         if (!m_osStartTime.empty())
     423             :         {
     424           3 :             query += "&startTime=";
     425           3 :             query += CPLEscapeURLQueryParameter(m_osStartTime);
     426             :         }
     427           7 :         if (!m_osEndTime.empty())
     428             :         {
     429           2 :             query += "&endTime=";
     430           2 :             query += CPLEscapeURLQueryParameter(m_osEndTime);
     431             :         }
     432           7 :         if (query.size() > 0)
     433             :         {
     434           5 :             osURL = osURL + "?" + query.substr(1);
     435             :         }
     436           7 :         m_poCurPageObj = m_poDS->RunRequest(osURL);
     437           7 :         if (m_poCurPageObj == nullptr)
     438           0 :             return nullptr;
     439             : 
     440           7 :         m_poCurPageAssets =
     441           7 :             CPL_json_object_object_get(m_poCurPageObj, "images");
     442             :     }
     443             : 
     444          16 :     if (m_poCurPageAssets == nullptr ||
     445           8 :         json_object_get_type(m_poCurPageAssets) != json_type_array)
     446             :     {
     447           0 :         json_object_put(m_poCurPageObj);
     448           0 :         m_poCurPageObj = nullptr;
     449           0 :         return nullptr;
     450             :     }
     451             :     json_object *poAsset =
     452           8 :         json_object_array_get_idx(m_poCurPageAssets, m_nIndexInPage);
     453           8 :     if (poAsset == nullptr || json_object_get_type(poAsset) != json_type_object)
     454             :     {
     455           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid asset");
     456           0 :         return nullptr;
     457             :     }
     458             : 
     459           8 :     OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
     460           8 :     poFeature->SetFID(m_nFID);
     461             : 
     462           8 :     json_object *poJSonGeom = CPL_json_object_object_get(poAsset, "geometry");
     463          11 :     if (poJSonGeom != nullptr &&
     464           3 :         json_object_get_type(poJSonGeom) == json_type_object)
     465             :     {
     466           3 :         const char *pszGeoJSON = json_object_get_string(poJSonGeom);
     467           3 :         if (strstr(pszGeoJSON, "Infinity") == nullptr)
     468             :         {
     469           3 :             OGRGeometry *poGeom = OGRGeometry::FromHandle(
     470             :                 OGR_G_CreateGeometryFromJson(pszGeoJSON));
     471           3 :             if (poGeom != nullptr)
     472             :             {
     473           3 :                 if (poGeom->getGeometryType() == wkbPolygon)
     474             :                 {
     475           3 :                     OGRMultiPolygon *poMP = new OGRMultiPolygon();
     476           3 :                     poMP->addGeometryDirectly(poGeom);
     477           3 :                     poGeom = poMP;
     478             :                 }
     479           3 :                 poGeom->assignSpatialReference(
     480           3 :                     m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef());
     481           3 :                 poFeature->SetGeometryDirectly(poGeom);
     482             :             }
     483             :         }
     484             :     }
     485             : 
     486             :     const char *pszName =
     487           8 :         json_object_get_string(CPL_json_object_object_get(poAsset, "name"));
     488           8 :     if (pszName)
     489             :     {
     490           8 :         poFeature->SetField("name", pszName);
     491           8 :         poFeature->SetField("gdal_dataset",
     492          16 :                             ("EEDAI:" + CPLString(pszName)).c_str());
     493             :     }
     494             : 
     495             :     const char *pszId =
     496           8 :         json_object_get_string(CPL_json_object_object_get(poAsset, "id"));
     497           8 :     if (pszId)
     498             :     {
     499           2 :         poFeature->SetField("id", pszId);
     500             :     }
     501             : 
     502           8 :     const char *const apszBaseProps[] = {"updateTime", "startTime", "endTime",
     503             :                                          "sizeBytes"};
     504          40 :     for (size_t i = 0; i < CPL_ARRAYSIZE(apszBaseProps); i++)
     505             :     {
     506          32 :         const char *pszVal = json_object_get_string(
     507          32 :             CPL_json_object_object_get(poAsset, apszBaseProps[i]));
     508          32 :         if (pszVal)
     509             :         {
     510          15 :             poFeature->SetField(apszBaseProps[i], pszVal);
     511             :         }
     512             :     }
     513             : 
     514           8 :     json_object *poBands = CPL_json_object_object_get(poAsset, "bands");
     515           8 :     if (poBands != nullptr && json_object_get_type(poBands) == json_type_array)
     516             :     {
     517             :         std::vector<EEDAIBandDesc> aoBands =
     518           4 :             BuildBandDescArray(poBands, m_oMapCodeToWKT);
     519           2 :         poFeature->SetField("band_count", static_cast<int>(aoBands.size()));
     520           2 :         if (!aoBands.empty())
     521             :         {
     522           2 :             int nWidth = 0, nHeight = 0;
     523           2 :             double dfMinPixelSize = std::numeric_limits<double>::max();
     524           4 :             CPLString osSRS(aoBands[0].osWKT);
     525           2 :             double dfULX = aoBands[0].adfGeoTransform[0];
     526           2 :             double dfULY = aoBands[0].adfGeoTransform[3];
     527           2 :             bool bULValid = true;
     528           4 :             for (size_t i = 0; i < aoBands.size(); i++)
     529             :             {
     530           2 :                 nWidth = std::max(nWidth, aoBands[i].nWidth);
     531           2 :                 nHeight = std::max(nHeight, aoBands[i].nHeight);
     532           2 :                 dfMinPixelSize =
     533           2 :                     std::min(dfMinPixelSize,
     534           2 :                              std::min(aoBands[i].adfGeoTransform[1],
     535           4 :                                       fabs(aoBands[i].adfGeoTransform[5])));
     536           2 :                 if (osSRS != aoBands[i].osWKT)
     537             :                 {
     538           0 :                     osSRS.clear();
     539             :                 }
     540           4 :                 if (dfULX != aoBands[i].adfGeoTransform[0] ||
     541           2 :                     dfULY != aoBands[i].adfGeoTransform[3])
     542             :                 {
     543           0 :                     bULValid = false;
     544             :                 }
     545             :             }
     546           2 :             poFeature->SetField("band_max_width", nWidth);
     547           2 :             poFeature->SetField("band_max_height", nHeight);
     548           2 :             poFeature->SetField("band_min_pixel_size", dfMinPixelSize);
     549           2 :             if (bULValid)
     550             :             {
     551           2 :                 poFeature->SetField("band_upper_left_x", dfULX);
     552           2 :                 poFeature->SetField("band_upper_left_y", dfULY);
     553             :             }
     554           2 :             if (!osSRS.empty())
     555             :             {
     556           4 :                 OGRSpatialReference oSRS;
     557           2 :                 oSRS.SetFromUserInput(
     558             :                     osSRS,
     559             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
     560           2 :                 const char *pszAuthName = oSRS.GetAuthorityName(nullptr);
     561           2 :                 const char *pszAuthCode = oSRS.GetAuthorityCode(nullptr);
     562           2 :                 if (pszAuthName && pszAuthCode)
     563             :                 {
     564           2 :                     poFeature->SetField(
     565             :                         "band_crs",
     566             :                         CPLSPrintf("%s:%s", pszAuthName, pszAuthCode));
     567             :                 }
     568             :                 else
     569             :                 {
     570           0 :                     poFeature->SetField("band_crs", osSRS.c_str());
     571             :                 }
     572             :             }
     573             :         }
     574             :     }
     575             : 
     576             :     json_object *poProperties =
     577           8 :         CPL_json_object_object_get(poAsset, "properties");
     578          11 :     if (poProperties != nullptr &&
     579           3 :         json_object_get_type(poProperties) == json_type_object)
     580             :     {
     581           3 :         json_object *poOtherProperties = nullptr;
     582             : 
     583             :         json_object_iter it;
     584           3 :         it.key = nullptr;
     585           3 :         it.val = nullptr;
     586           3 :         it.entry = nullptr;
     587          18 :         json_object_object_foreachC(poProperties, it)
     588             :         {
     589          15 :             if (it.val)
     590             :             {
     591          15 :                 int nIdx = m_poFeatureDefn->GetFieldIndex(it.key);
     592          15 :                 if (nIdx >= 0)
     593             :                 {
     594          12 :                     poFeature->SetField(nIdx, json_object_get_string(it.val));
     595             :                 }
     596             :                 else
     597             :                 {
     598           3 :                     if (poOtherProperties == nullptr)
     599           3 :                         poOtherProperties = json_object_new_object();
     600           3 :                     json_object_object_add(poOtherProperties, it.key, it.val);
     601           3 :                     json_object_get(it.val);
     602             :                 }
     603             :             }
     604             :         }
     605             : 
     606           3 :         if (poOtherProperties)
     607             :         {
     608             :             int nIdxOtherProperties =
     609           3 :                 m_poFeatureDefn->GetFieldIndex("other_properties");
     610           3 :             if (nIdxOtherProperties >= 0)
     611             :             {
     612           3 :                 poFeature->SetField(nIdxOtherProperties,
     613             :                                     json_object_get_string(poOtherProperties));
     614             :             }
     615           3 :             json_object_put(poOtherProperties);
     616             :         }
     617             :     }
     618             : 
     619           8 :     m_nFID++;
     620           8 :     m_nIndexInPage++;
     621             : 
     622           8 :     return poFeature;
     623             : }
     624             : 
     625             : /************************************************************************/
     626             : /*                           GetNextFeature()                           */
     627             : /************************************************************************/
     628             : 
     629           9 : OGRFeature *GDALEEDALayer::GetNextFeature()
     630             : {
     631             :     while (true)
     632             :     {
     633           9 :         OGRFeature *poFeature = GetNextRawFeature();
     634           9 :         if (poFeature == nullptr)
     635           1 :             return nullptr;
     636             : 
     637           8 :         if (m_poAttrQuery == nullptr || !m_bFilterMustBeClientSideEvaluated ||
     638           0 :             m_poAttrQuery->Evaluate(poFeature))
     639             :         {
     640           8 :             return poFeature;
     641             :         }
     642             :         else
     643             :         {
     644           0 :             delete poFeature;
     645             :         }
     646           0 :     }
     647             : }
     648             : 
     649             : /************************************************************************/
     650             : /*                      GDALEEDALayerParseDateTime()                    */
     651             : /************************************************************************/
     652             : 
     653           5 : static int GDALEEDALayerParseDateTime(const char *pszValue, int operation,
     654             :                                       int &nYear, int &nMonth, int &nDay,
     655             :                                       int &nHour, int &nMinute, int &nSecond)
     656             : {
     657           5 :     nHour = (operation == SWQ_GE) ? 0 : 23;
     658           5 :     nMinute = (operation == SWQ_GE) ? 0 : 59;
     659           5 :     nSecond = (operation == SWQ_GE) ? 0 : 59;
     660           5 :     int nRet = sscanf(pszValue, "%04d/%02d/%02d %02d:%02d:%02d", &nYear,
     661             :                       &nMonth, &nDay, &nHour, &nMinute, &nSecond);
     662           5 :     if (nRet >= 3)
     663             :     {
     664           0 :         return nRet;
     665             :     }
     666           5 :     nRet = sscanf(pszValue, "%04d-%02d-%02dT%02d:%02d:%02d", &nYear, &nMonth,
     667             :                   &nDay, &nHour, &nMinute, &nSecond);
     668           5 :     if (nRet >= 3)
     669             :     {
     670           5 :         return nRet;
     671             :     }
     672           0 :     return 0;
     673             : }
     674             : 
     675             : /************************************************************************/
     676             : /*                          IsSimpleComparison()                        */
     677             : /************************************************************************/
     678             : 
     679          14 : bool GDALEEDALayer::IsSimpleComparison(const swq_expr_node *poNode)
     680             : {
     681          28 :     return poNode->eNodeType == SNT_OPERATION &&
     682          14 :            (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_NE ||
     683          10 :             poNode->nOperation == SWQ_LT || poNode->nOperation == SWQ_LE ||
     684           7 :             poNode->nOperation == SWQ_GT || poNode->nOperation == SWQ_GE) &&
     685          11 :            poNode->nSubExprCount == 2 &&
     686          11 :            poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     687          39 :            poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
     688          11 :            m_oSetQueryableFields.find(poNode->papoSubExpr[0]->field_index) !=
     689          25 :                m_oSetQueryableFields.end();
     690             : }
     691             : 
     692             : /************************************************************************/
     693             : /*                             GetOperatorText()                        */
     694             : /************************************************************************/
     695             : 
     696           6 : static const char *GetOperatorText(swq_op nOp)
     697             : {
     698           6 :     if (nOp == SWQ_LT)
     699           1 :         return "<";
     700           5 :     if (nOp == SWQ_LE)
     701           1 :         return "<=";
     702           4 :     if (nOp == SWQ_GT)
     703           1 :         return ">";
     704           3 :     if (nOp == SWQ_GE)
     705           1 :         return ">=";
     706           2 :     if (nOp == SWQ_EQ)
     707           1 :         return "=";
     708           1 :     if (nOp == SWQ_NE)
     709           1 :         return "!=";
     710           0 :     CPLAssert(false);
     711             :     return "";
     712             : }
     713             : 
     714             : /************************************************************************/
     715             : /*                             BuildFilter()                            */
     716             : /************************************************************************/
     717             : 
     718          26 : CPLString GDALEEDALayer::BuildFilter(swq_expr_node *poNode, bool bIsAndTopLevel)
     719             : {
     720             :     int nYear, nMonth, nDay, nHour, nMinute, nSecond;
     721             : 
     722          26 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
     723          10 :         poNode->nSubExprCount == 2)
     724             :     {
     725             :         // For AND, we can deal with a failure in one of the branch
     726             :         // since client-side will do that extra filtering
     727          20 :         CPLString osLeft = BuildFilter(poNode->papoSubExpr[0], bIsAndTopLevel);
     728          20 :         CPLString osRight = BuildFilter(poNode->papoSubExpr[1], bIsAndTopLevel);
     729          10 :         if (!osLeft.empty() && !osRight.empty())
     730             :         {
     731          14 :             return "(" + osLeft + " AND " + osRight + ")";
     732             :         }
     733           3 :         else if (!osLeft.empty())
     734           0 :             return osLeft;
     735             :         else
     736           3 :             return osRight;
     737             :     }
     738          16 :     else if (poNode->eNodeType == SNT_OPERATION &&
     739          16 :              poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
     740             :     {
     741             :         // For OR, we need both members to be valid
     742           2 :         CPLString osLeft = BuildFilter(poNode->papoSubExpr[0], false);
     743           2 :         CPLString osRight = BuildFilter(poNode->papoSubExpr[1], false);
     744           1 :         if (!osLeft.empty() && !osRight.empty())
     745             :         {
     746           2 :             return "(" + osLeft + " OR " + osRight + ")";
     747             :         }
     748           0 :         return "";
     749             :     }
     750          15 :     else if (poNode->eNodeType == SNT_OPERATION &&
     751          15 :              poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
     752             :     {
     753           2 :         CPLString osFilter = BuildFilter(poNode->papoSubExpr[0], false);
     754           1 :         if (!osFilter.empty())
     755             :         {
     756           2 :             return "(NOT " + osFilter + ")";
     757             :         }
     758           0 :         return "";
     759             :     }
     760          14 :     else if (IsSimpleComparison(poNode))
     761             :     {
     762           6 :         const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
     763             :         CPLString osFilter(
     764          12 :             m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef());
     765           6 :         osFilter += " ";
     766           6 :         osFilter += GetOperatorText(poNode->nOperation);
     767           6 :         osFilter += " ";
     768           6 :         if (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER ||
     769           4 :             poNode->papoSubExpr[1]->field_type == SWQ_INTEGER64)
     770             :         {
     771           4 :             osFilter +=
     772           4 :                 CPLSPrintf("%" PRId64, poNode->papoSubExpr[1]->int_value);
     773             :         }
     774           2 :         else if (poNode->papoSubExpr[1]->field_type == SWQ_FLOAT)
     775             :         {
     776             :             osFilter +=
     777           1 :                 CPLSPrintf("%.17g", poNode->papoSubExpr[1]->float_value);
     778             :         }
     779             :         else
     780             :         {
     781           1 :             osFilter += "\"";
     782           1 :             osFilter += poNode->papoSubExpr[1]->string_value;
     783           1 :             osFilter += "\"";
     784             :         }
     785           6 :         return osFilter;
     786             :     }
     787           6 :     else if (bIsAndTopLevel && poNode->eNodeType == SNT_OPERATION &&
     788           6 :              (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_GE) &&
     789           4 :              poNode->nSubExprCount == 2 &&
     790           4 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     791           4 :              poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
     792           4 :              poNode->papoSubExpr[0]->field_index ==
     793          17 :                  m_poFeatureDefn->GetFieldIndex("startTime") &&
     794           3 :              poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP)
     795             :     {
     796           3 :         int nTerms = GDALEEDALayerParseDateTime(
     797           3 :             poNode->papoSubExpr[1]->string_value, SWQ_GE, nYear, nMonth, nDay,
     798             :             nHour, nMinute, nSecond);
     799           3 :         if (nTerms >= 3)
     800             :         {
     801             :             m_osStartTime = CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
     802           3 :                                        nMonth, nDay, nHour, nMinute, nSecond);
     803             :         }
     804             :         else
     805             :         {
     806           0 :             m_bFilterMustBeClientSideEvaluated = true;
     807             :         }
     808           3 :         return "";
     809             :     }
     810           3 :     else if (bIsAndTopLevel && poNode->eNodeType == SNT_OPERATION &&
     811           3 :              (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_LE) &&
     812           2 :              poNode->nSubExprCount == 2 &&
     813           2 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     814           2 :              poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
     815           2 :              poNode->papoSubExpr[0]->field_index ==
     816          10 :                  m_poFeatureDefn->GetFieldIndex("endTime") &&
     817           2 :              poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP)
     818             :     {
     819           2 :         int nTerms = GDALEEDALayerParseDateTime(
     820           2 :             poNode->papoSubExpr[1]->string_value, SWQ_LE, nYear, nMonth, nDay,
     821             :             nHour, nMinute, nSecond);
     822           2 :         if (nTerms >= 3)
     823             :         {
     824           2 :             if (poNode->nOperation == SWQ_EQ && nTerms == 6)
     825             :             {
     826           0 :                 if (nSecond < 59)
     827           0 :                     nSecond++;
     828           0 :                 else if (nMinute < 59)
     829           0 :                     nMinute++;
     830           0 :                 else if (nHour < 23)
     831           0 :                     nHour++;
     832             :                 else
     833           0 :                     nDay++;
     834             :             }
     835             :             m_osEndTime = CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
     836           2 :                                      nMonth, nDay, nHour, nMinute, nSecond);
     837             :         }
     838             :         else
     839             :         {
     840           0 :             m_bFilterMustBeClientSideEvaluated = true;
     841             :         }
     842           2 :         return "";
     843             :     }
     844           9 :     else if (poNode->eNodeType == SNT_OPERATION &&
     845           3 :              poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 &&
     846           9 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     847           3 :              m_oSetQueryableFields.find(poNode->papoSubExpr[0]->field_index) !=
     848           6 :                  m_oSetQueryableFields.end())
     849             :     {
     850           3 :         const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
     851           6 :         CPLString osFilter;
     852             : 
     853           7 :         for (int i = 1; i < poNode->nSubExprCount; i++)
     854             :         {
     855           4 :             if (!osFilter.empty())
     856           1 :                 osFilter += " OR ";
     857           4 :             osFilter += m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
     858           4 :             osFilter += " = ";
     859           4 :             if (poNode->papoSubExpr[i]->field_type == SWQ_INTEGER ||
     860           3 :                 poNode->papoSubExpr[i]->field_type == SWQ_INTEGER64)
     861             :             {
     862           1 :                 osFilter +=
     863           1 :                     CPLSPrintf("%" PRId64, poNode->papoSubExpr[i]->int_value);
     864             :             }
     865           3 :             else if (poNode->papoSubExpr[i]->field_type == SWQ_FLOAT)
     866             :             {
     867             :                 osFilter +=
     868           1 :                     CPLSPrintf("%.17g", poNode->papoSubExpr[i]->float_value);
     869             :             }
     870             :             else
     871             :             {
     872           2 :                 osFilter += "\"";
     873           2 :                 osFilter += poNode->papoSubExpr[i]->string_value;
     874           2 :                 osFilter += "\"";
     875             :             }
     876             :         }
     877             : 
     878           3 :         return osFilter;
     879             :     }
     880             : 
     881           0 :     m_bFilterMustBeClientSideEvaluated = true;
     882           0 :     return "";
     883             : }
     884             : 
     885             : /************************************************************************/
     886             : /*                         SetAttributeFilter()                         */
     887             : /************************************************************************/
     888             : 
     889           5 : OGRErr GDALEEDALayer::SetAttributeFilter(const char *pszQuery)
     890             : 
     891             : {
     892           5 :     m_osAttributeFilter.clear();
     893           5 :     m_osStartTime.clear();
     894           5 :     m_osEndTime.clear();
     895           5 :     m_bFilterMustBeClientSideEvaluated = false;
     896             : 
     897           5 :     if (pszQuery && STARTS_WITH_CI(pszQuery, "EEDA:"))
     898             :     {
     899           1 :         m_osAttributeFilter = pszQuery + strlen("EEDA:");
     900           1 :         OGRLayer::SetAttributeFilter(nullptr);
     901           1 :         ResetReading();
     902           1 :         return OGRERR_NONE;
     903             :     }
     904             : 
     905           4 :     OGRErr eErr = OGRLayer::SetAttributeFilter(pszQuery);
     906             : 
     907           4 :     if (m_poAttrQuery != nullptr)
     908             :     {
     909             :         swq_expr_node *poNode =
     910           3 :             static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
     911             : 
     912             : #ifndef PLUGIN
     913           3 :         poNode->ReplaceBetweenByGEAndLERecurse();
     914             : #endif
     915             : 
     916           3 :         m_osAttributeFilter = BuildFilter(poNode, true);
     917           3 :         if (m_osAttributeFilter.empty() && m_osStartTime.empty() &&
     918           0 :             m_osEndTime.empty())
     919             :         {
     920           0 :             CPLDebug("EEDA", "Full filter will be evaluated on client side.");
     921             :         }
     922           3 :         else if (m_bFilterMustBeClientSideEvaluated)
     923             :         {
     924           0 :             CPLDebug(
     925             :                 "EEDA",
     926             :                 "Only part of the filter will be evaluated on server side.");
     927             :         }
     928             :     }
     929             : 
     930           4 :     ResetReading();
     931             : 
     932           4 :     return eErr;
     933             : }
     934             : 
     935             : /************************************************************************/
     936             : /*                          ISetSpatialFilter()                         */
     937             : /************************************************************************/
     938             : 
     939           2 : OGRErr GDALEEDALayer::ISetSpatialFilter(int /* iGeomField */,
     940             :                                         const OGRGeometry *poGeomIn)
     941             : {
     942           2 :     if (poGeomIn)
     943             :     {
     944           1 :         OGREnvelope sEnvelope;
     945           1 :         poGeomIn->getEnvelope(&sEnvelope);
     946           1 :         if (sEnvelope.MinX == sEnvelope.MaxX &&
     947           0 :             sEnvelope.MinY == sEnvelope.MaxY)
     948             :         {
     949           0 :             OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
     950           0 :             InstallFilter(&p);
     951             :         }
     952             :         else
     953           1 :             InstallFilter(poGeomIn);
     954             :     }
     955             :     else
     956           1 :         InstallFilter(poGeomIn);
     957             : 
     958           2 :     ResetReading();
     959           2 :     return OGRERR_NONE;
     960             : }
     961             : 
     962             : /************************************************************************/
     963             : /*                                GetExtent()                           */
     964             : /************************************************************************/
     965             : 
     966           1 : OGRErr GDALEEDALayer::IGetExtent(int /* iGeomField*/, OGREnvelope *psExtent,
     967             :                                  bool /* bForce */)
     968             : {
     969           1 :     psExtent->MinX = -180;
     970           1 :     psExtent->MinY = -90;
     971           1 :     psExtent->MaxX = 180;
     972           1 :     psExtent->MaxY = 90;
     973           1 :     return OGRERR_NONE;
     974             : }
     975             : 
     976             : /************************************************************************/
     977             : /*                              TestCapability()                        */
     978             : /************************************************************************/
     979             : 
     980           5 : int GDALEEDALayer::TestCapability(const char *pszCap)
     981             : {
     982           5 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
     983           4 :         return TRUE;
     984           1 :     return FALSE;
     985             : }
     986             : 
     987             : /************************************************************************/
     988             : /*                         GDALEEDADataset()                           */
     989             : /************************************************************************/
     990             : 
     991           7 : GDALEEDADataset::GDALEEDADataset() : m_poLayer(nullptr)
     992             : {
     993           7 : }
     994             : 
     995             : /************************************************************************/
     996             : /*                        ~GDALEEDADataset()                            */
     997             : /************************************************************************/
     998             : 
     999          14 : GDALEEDADataset::~GDALEEDADataset()
    1000             : {
    1001           7 :     delete m_poLayer;
    1002          14 : }
    1003             : 
    1004             : /************************************************************************/
    1005             : /*                            GetLayer()                                */
    1006             : /************************************************************************/
    1007             : 
    1008           7 : OGRLayer *GDALEEDADataset::GetLayer(int idx)
    1009             : {
    1010           7 :     if (idx == 0)
    1011           7 :         return m_poLayer;
    1012           0 :     return nullptr;
    1013             : }
    1014             : 
    1015             : /************************************************************************/
    1016             : /*                            RunRequest()                              */
    1017             : /************************************************************************/
    1018             : 
    1019          13 : json_object *GDALEEDADataset::RunRequest(const CPLString &osURL)
    1020             : {
    1021          13 :     char **papszOptions = GetBaseHTTPOptions();
    1022          13 :     if (papszOptions == nullptr)
    1023           0 :         return nullptr;
    1024          13 :     CPLHTTPResult *psResult = EEDAHTTPFetch(osURL, papszOptions);
    1025          13 :     CSLDestroy(papszOptions);
    1026          13 :     if (psResult == nullptr)
    1027           0 :         return nullptr;
    1028          13 :     if (psResult->pszErrBuf != nullptr)
    1029             :     {
    1030           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
    1031           0 :                  psResult->pabyData
    1032             :                      ? reinterpret_cast<const char *>(psResult->pabyData)
    1033             :                      : psResult->pszErrBuf);
    1034           0 :         CPLHTTPDestroyResult(psResult);
    1035           0 :         return nullptr;
    1036             :     }
    1037             : 
    1038          13 :     if (psResult->pabyData == nullptr)
    1039             :     {
    1040           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1041             :                  "Empty content returned by server");
    1042           0 :         CPLHTTPDestroyResult(psResult);
    1043           0 :         return nullptr;
    1044             :     }
    1045             : 
    1046          13 :     const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
    1047             : #ifdef DEBUG_VERBOSE
    1048             :     CPLDebug("EEDA", "%s", pszText);
    1049             : #endif
    1050             : 
    1051          13 :     json_object *poObj = nullptr;
    1052          13 :     if (!OGRJSonParse(pszText, &poObj, true))
    1053             :     {
    1054           0 :         CPLHTTPDestroyResult(psResult);
    1055           0 :         return nullptr;
    1056             :     }
    1057             : 
    1058          13 :     CPLHTTPDestroyResult(psResult);
    1059             : 
    1060          13 :     if (json_object_get_type(poObj) != json_type_object)
    1061             :     {
    1062           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1063             :                  "Return is not a JSON dictionary");
    1064           0 :         json_object_put(poObj);
    1065           0 :         return nullptr;
    1066             :     }
    1067             : 
    1068          13 :     return poObj;
    1069             : }
    1070             : 
    1071             : /************************************************************************/
    1072             : /*                         GDALEEDADatasetGetConf()                     */
    1073             : /************************************************************************/
    1074             : 
    1075           7 : static json_object *GDALEEDADatasetGetConf()
    1076             : {
    1077           7 :     const char *pszConfFile = CPLFindFile("gdal", "eedaconf.json");
    1078           7 :     if (pszConfFile == nullptr)
    1079             :     {
    1080           0 :         CPLDebug("EEDA", "Cannot find eedaconf.json");
    1081           0 :         return nullptr;
    1082             :     }
    1083             : 
    1084           7 :     GByte *pabyRet = nullptr;
    1085           7 :     if (!VSIIngestFile(nullptr, pszConfFile, &pabyRet, nullptr, -1))
    1086             :     {
    1087           0 :         return nullptr;
    1088             :     }
    1089             : 
    1090           7 :     json_object *poRoot = nullptr;
    1091           7 :     const char *pzText = reinterpret_cast<char *>(pabyRet);
    1092           7 :     if (!OGRJSonParse(pzText, &poRoot))
    1093             :     {
    1094           0 :         VSIFree(pabyRet);
    1095           0 :         return nullptr;
    1096             :     }
    1097           7 :     VSIFree(pabyRet);
    1098             : 
    1099           7 :     if (json_object_get_type(poRoot) != json_type_object)
    1100             :     {
    1101           0 :         json_object_put(poRoot);
    1102           0 :         return nullptr;
    1103             :     }
    1104             : 
    1105           7 :     return poRoot;
    1106             : }
    1107             : 
    1108             : /************************************************************************/
    1109             : /*                               Open()                                 */
    1110             : /************************************************************************/
    1111             : 
    1112           7 : bool GDALEEDADataset::Open(GDALOpenInfo *poOpenInfo)
    1113             : {
    1114             :     m_osBaseURL = CPLGetConfigOption(
    1115           7 :         "EEDA_URL", "https://earthengine-highvolume.googleapis.com/v1alpha/");
    1116             : 
    1117             :     CPLString osCollection =
    1118          14 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "COLLECTION", "");
    1119           7 :     if (osCollection.empty())
    1120             :     {
    1121             :         char **papszTokens =
    1122           7 :             CSLTokenizeString2(poOpenInfo->pszFilename, ":", 0);
    1123           7 :         if (CSLCount(papszTokens) < 2)
    1124             :         {
    1125           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1126             :                      "No collection specified in connection string or "
    1127             :                      "COLLECTION open option");
    1128           0 :             CSLDestroy(papszTokens);
    1129           0 :             return false;
    1130             :         }
    1131           7 :         osCollection = papszTokens[1];
    1132           7 :         CSLDestroy(papszTokens);
    1133             :     }
    1134          14 :     CPLString osCollectionName = ConvertPathToName(osCollection);
    1135             : 
    1136           7 :     json_object *poRootConf = GDALEEDADatasetGetConf();
    1137           7 :     if (poRootConf)
    1138             :     {
    1139             :         json_object *poLayerConf =
    1140           7 :             CPL_json_object_object_get(poRootConf, osCollection);
    1141           8 :         if (poLayerConf != nullptr &&
    1142           1 :             json_object_get_type(poLayerConf) == json_type_object)
    1143             :         {
    1144           1 :             m_poLayer = new GDALEEDALayer(this, osCollection, osCollectionName,
    1145           1 :                                           nullptr, poLayerConf);
    1146           1 :             json_object_put(poRootConf);
    1147           1 :             return true;
    1148             :         }
    1149           6 :         json_object_put(poRootConf);
    1150             :     }
    1151             : 
    1152             :     // Issue request to get layer schema
    1153             :     json_object *poRootAsset =
    1154           6 :         RunRequest(m_osBaseURL + osCollectionName + ":listImages?pageSize=1");
    1155           6 :     if (poRootAsset == nullptr)
    1156           0 :         return false;
    1157             : 
    1158           6 :     json_object *poAssets = CPL_json_object_object_get(poRootAsset, "images");
    1159          12 :     if (poAssets == nullptr ||
    1160          12 :         json_object_get_type(poAssets) != json_type_array ||
    1161           6 :         json_object_array_length(poAssets) != 1)
    1162             :     {
    1163           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No assets");
    1164           0 :         json_object_put(poRootAsset);
    1165           0 :         return false;
    1166             :     }
    1167           6 :     json_object *poAsset = json_object_array_get_idx(poAssets, 0);
    1168           6 :     if (poAsset == nullptr || json_object_get_type(poAsset) != json_type_object)
    1169             :     {
    1170           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No assets");
    1171           0 :         json_object_put(poRootAsset);
    1172           0 :         return false;
    1173             :     }
    1174             : 
    1175           6 :     m_poLayer = new GDALEEDALayer(this, osCollection, osCollectionName, poAsset,
    1176           6 :                                   nullptr);
    1177           6 :     json_object_put(poRootAsset);
    1178             : 
    1179           6 :     return true;
    1180             : }
    1181             : 
    1182             : /************************************************************************/
    1183             : /*                          GDALEEDAdentify()                          */
    1184             : /************************************************************************/
    1185             : 
    1186       54111 : static int GDALEEDAdentify(GDALOpenInfo *poOpenInfo)
    1187             : {
    1188       54111 :     return STARTS_WITH_CI(poOpenInfo->pszFilename, "EEDA:");
    1189             : }
    1190             : 
    1191             : /************************************************************************/
    1192             : /*                            GDALEEDAOpen()                            */
    1193             : /************************************************************************/
    1194             : 
    1195           7 : static GDALDataset *GDALEEDAOpen(GDALOpenInfo *poOpenInfo)
    1196             : {
    1197           7 :     if (!GDALEEDAdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
    1198           0 :         return nullptr;
    1199             : 
    1200           7 :     GDALEEDADataset *poDS = new GDALEEDADataset();
    1201           7 :     if (!poDS->Open(poOpenInfo))
    1202             :     {
    1203           0 :         delete poDS;
    1204           0 :         return nullptr;
    1205             :     }
    1206           7 :     return poDS;
    1207             : }
    1208             : 
    1209             : /************************************************************************/
    1210             : /*                         GDALRegister_EEDA()                          */
    1211             : /************************************************************************/
    1212             : 
    1213        1686 : void GDALRegister_EEDA()
    1214             : 
    1215             : {
    1216        1686 :     if (GDALGetDriverByName("EEDA") != nullptr)
    1217         302 :         return;
    1218             : 
    1219        1384 :     GDALDriver *poDriver = new GDALDriver();
    1220             : 
    1221        1384 :     poDriver->SetDescription("EEDA");
    1222        1384 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    1223        1384 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Earth Engine Data API");
    1224        1384 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/eeda.html");
    1225        1384 :     poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "EEDA:");
    1226        1384 :     poDriver->SetMetadataItem(
    1227             :         GDAL_DMD_OPENOPTIONLIST,
    1228             :         "<OpenOptionList>"
    1229             :         "  <Option name='COLLECTION' type='string' "
    1230             :         "description='Collection name'/>"
    1231             :         "  <Option name='VSI_PATH_FOR_AUTH' type='string' "
    1232             :         "description='/vsigs/... path onto which a "
    1233             :         "GOOGLE_APPLICATION_CREDENTIALS path specific "
    1234             :         "option is set'/>"
    1235        1384 :         "</OpenOptionList>");
    1236             : 
    1237        1384 :     poDriver->pfnOpen = GDALEEDAOpen;
    1238        1384 :     poDriver->pfnIdentify = GDALEEDAdentify;
    1239             : 
    1240        1384 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1241             : 
    1242             : #ifdef GDAL_ENABLE_DRIVER_EEDA_PLUGIN
    1243             :     GDALRegister_EEDAI();
    1244             : #endif
    1245             : }

Generated by: LCOV version 1.14