LCOV - code coverage report
Current view: top level - frmts/eeda - eedadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 509 590 86.3 %
Date: 2025-06-28 21:28:23 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].gt[0];
     526           2 :             double dfULY = aoBands[0].gt[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, std::min(aoBands[i].gt[1],
     534           4 :                                                       fabs(aoBands[i].gt[5])));
     535           2 :                 if (osSRS != aoBands[i].osWKT)
     536             :                 {
     537           0 :                     osSRS.clear();
     538             :                 }
     539           2 :                 if (dfULX != aoBands[i].gt[0] || dfULY != aoBands[i].gt[3])
     540             :                 {
     541           0 :                     bULValid = false;
     542             :                 }
     543             :             }
     544           2 :             poFeature->SetField("band_max_width", nWidth);
     545           2 :             poFeature->SetField("band_max_height", nHeight);
     546           2 :             poFeature->SetField("band_min_pixel_size", dfMinPixelSize);
     547           2 :             if (bULValid)
     548             :             {
     549           2 :                 poFeature->SetField("band_upper_left_x", dfULX);
     550           2 :                 poFeature->SetField("band_upper_left_y", dfULY);
     551             :             }
     552           2 :             if (!osSRS.empty())
     553             :             {
     554           4 :                 OGRSpatialReference oSRS;
     555           2 :                 oSRS.SetFromUserInput(
     556             :                     osSRS,
     557             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
     558           2 :                 const char *pszAuthName = oSRS.GetAuthorityName(nullptr);
     559           2 :                 const char *pszAuthCode = oSRS.GetAuthorityCode(nullptr);
     560           2 :                 if (pszAuthName && pszAuthCode)
     561             :                 {
     562           2 :                     poFeature->SetField(
     563             :                         "band_crs",
     564             :                         CPLSPrintf("%s:%s", pszAuthName, pszAuthCode));
     565             :                 }
     566             :                 else
     567             :                 {
     568           0 :                     poFeature->SetField("band_crs", osSRS.c_str());
     569             :                 }
     570             :             }
     571             :         }
     572             :     }
     573             : 
     574             :     json_object *poProperties =
     575           8 :         CPL_json_object_object_get(poAsset, "properties");
     576          11 :     if (poProperties != nullptr &&
     577           3 :         json_object_get_type(poProperties) == json_type_object)
     578             :     {
     579           3 :         json_object *poOtherProperties = nullptr;
     580             : 
     581             :         json_object_iter it;
     582           3 :         it.key = nullptr;
     583           3 :         it.val = nullptr;
     584           3 :         it.entry = nullptr;
     585          18 :         json_object_object_foreachC(poProperties, it)
     586             :         {
     587          15 :             if (it.val)
     588             :             {
     589          15 :                 int nIdx = m_poFeatureDefn->GetFieldIndex(it.key);
     590          15 :                 if (nIdx >= 0)
     591             :                 {
     592          12 :                     poFeature->SetField(nIdx, json_object_get_string(it.val));
     593             :                 }
     594             :                 else
     595             :                 {
     596           3 :                     if (poOtherProperties == nullptr)
     597           3 :                         poOtherProperties = json_object_new_object();
     598           3 :                     json_object_object_add(poOtherProperties, it.key, it.val);
     599           3 :                     json_object_get(it.val);
     600             :                 }
     601             :             }
     602             :         }
     603             : 
     604           3 :         if (poOtherProperties)
     605             :         {
     606             :             int nIdxOtherProperties =
     607           3 :                 m_poFeatureDefn->GetFieldIndex("other_properties");
     608           3 :             if (nIdxOtherProperties >= 0)
     609             :             {
     610           3 :                 poFeature->SetField(nIdxOtherProperties,
     611             :                                     json_object_get_string(poOtherProperties));
     612             :             }
     613           3 :             json_object_put(poOtherProperties);
     614             :         }
     615             :     }
     616             : 
     617           8 :     m_nFID++;
     618           8 :     m_nIndexInPage++;
     619             : 
     620           8 :     return poFeature;
     621             : }
     622             : 
     623             : /************************************************************************/
     624             : /*                           GetNextFeature()                           */
     625             : /************************************************************************/
     626             : 
     627           9 : OGRFeature *GDALEEDALayer::GetNextFeature()
     628             : {
     629             :     while (true)
     630             :     {
     631           9 :         OGRFeature *poFeature = GetNextRawFeature();
     632           9 :         if (poFeature == nullptr)
     633           1 :             return nullptr;
     634             : 
     635           8 :         if (m_poAttrQuery == nullptr || !m_bFilterMustBeClientSideEvaluated ||
     636           0 :             m_poAttrQuery->Evaluate(poFeature))
     637             :         {
     638           8 :             return poFeature;
     639             :         }
     640             :         else
     641             :         {
     642           0 :             delete poFeature;
     643             :         }
     644           0 :     }
     645             : }
     646             : 
     647             : /************************************************************************/
     648             : /*                      GDALEEDALayerParseDateTime()                    */
     649             : /************************************************************************/
     650             : 
     651           5 : static int GDALEEDALayerParseDateTime(const char *pszValue, int operation,
     652             :                                       int &nYear, int &nMonth, int &nDay,
     653             :                                       int &nHour, int &nMinute, int &nSecond)
     654             : {
     655           5 :     nHour = (operation == SWQ_GE) ? 0 : 23;
     656           5 :     nMinute = (operation == SWQ_GE) ? 0 : 59;
     657           5 :     nSecond = (operation == SWQ_GE) ? 0 : 59;
     658           5 :     int nRet = sscanf(pszValue, "%04d/%02d/%02d %02d:%02d:%02d", &nYear,
     659             :                       &nMonth, &nDay, &nHour, &nMinute, &nSecond);
     660           5 :     if (nRet >= 3)
     661             :     {
     662           0 :         return nRet;
     663             :     }
     664           5 :     nRet = sscanf(pszValue, "%04d-%02d-%02dT%02d:%02d:%02d", &nYear, &nMonth,
     665             :                   &nDay, &nHour, &nMinute, &nSecond);
     666           5 :     if (nRet >= 3)
     667             :     {
     668           5 :         return nRet;
     669             :     }
     670           0 :     return 0;
     671             : }
     672             : 
     673             : /************************************************************************/
     674             : /*                          IsSimpleComparison()                        */
     675             : /************************************************************************/
     676             : 
     677          14 : bool GDALEEDALayer::IsSimpleComparison(const swq_expr_node *poNode)
     678             : {
     679          28 :     return poNode->eNodeType == SNT_OPERATION &&
     680          14 :            (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_NE ||
     681          10 :             poNode->nOperation == SWQ_LT || poNode->nOperation == SWQ_LE ||
     682           7 :             poNode->nOperation == SWQ_GT || poNode->nOperation == SWQ_GE) &&
     683          11 :            poNode->nSubExprCount == 2 &&
     684          11 :            poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     685          39 :            poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
     686          11 :            m_oSetQueryableFields.find(poNode->papoSubExpr[0]->field_index) !=
     687          25 :                m_oSetQueryableFields.end();
     688             : }
     689             : 
     690             : /************************************************************************/
     691             : /*                             GetOperatorText()                        */
     692             : /************************************************************************/
     693             : 
     694           6 : static const char *GetOperatorText(swq_op nOp)
     695             : {
     696           6 :     if (nOp == SWQ_LT)
     697           1 :         return "<";
     698           5 :     if (nOp == SWQ_LE)
     699           1 :         return "<=";
     700           4 :     if (nOp == SWQ_GT)
     701           1 :         return ">";
     702           3 :     if (nOp == SWQ_GE)
     703           1 :         return ">=";
     704           2 :     if (nOp == SWQ_EQ)
     705           1 :         return "=";
     706           1 :     if (nOp == SWQ_NE)
     707           1 :         return "!=";
     708           0 :     CPLAssert(false);
     709             :     return "";
     710             : }
     711             : 
     712             : /************************************************************************/
     713             : /*                             BuildFilter()                            */
     714             : /************************************************************************/
     715             : 
     716          26 : CPLString GDALEEDALayer::BuildFilter(swq_expr_node *poNode, bool bIsAndTopLevel)
     717             : {
     718             :     int nYear, nMonth, nDay, nHour, nMinute, nSecond;
     719             : 
     720          26 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
     721          10 :         poNode->nSubExprCount == 2)
     722             :     {
     723             :         // For AND, we can deal with a failure in one of the branch
     724             :         // since client-side will do that extra filtering
     725          20 :         CPLString osLeft = BuildFilter(poNode->papoSubExpr[0], bIsAndTopLevel);
     726          20 :         CPLString osRight = BuildFilter(poNode->papoSubExpr[1], bIsAndTopLevel);
     727          10 :         if (!osLeft.empty() && !osRight.empty())
     728             :         {
     729          14 :             return "(" + osLeft + " AND " + osRight + ")";
     730             :         }
     731           3 :         else if (!osLeft.empty())
     732           0 :             return osLeft;
     733             :         else
     734           3 :             return osRight;
     735             :     }
     736          16 :     else if (poNode->eNodeType == SNT_OPERATION &&
     737          16 :              poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
     738             :     {
     739             :         // For OR, we need both members to be valid
     740           2 :         CPLString osLeft = BuildFilter(poNode->papoSubExpr[0], false);
     741           2 :         CPLString osRight = BuildFilter(poNode->papoSubExpr[1], false);
     742           1 :         if (!osLeft.empty() && !osRight.empty())
     743             :         {
     744           2 :             return "(" + osLeft + " OR " + osRight + ")";
     745             :         }
     746           0 :         return "";
     747             :     }
     748          15 :     else if (poNode->eNodeType == SNT_OPERATION &&
     749          15 :              poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
     750             :     {
     751           2 :         CPLString osFilter = BuildFilter(poNode->papoSubExpr[0], false);
     752           1 :         if (!osFilter.empty())
     753             :         {
     754           2 :             return "(NOT " + osFilter + ")";
     755             :         }
     756           0 :         return "";
     757             :     }
     758          14 :     else if (IsSimpleComparison(poNode))
     759             :     {
     760           6 :         const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
     761             :         CPLString osFilter(
     762          12 :             m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef());
     763           6 :         osFilter += " ";
     764           6 :         osFilter += GetOperatorText(poNode->nOperation);
     765           6 :         osFilter += " ";
     766           6 :         if (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER ||
     767           4 :             poNode->papoSubExpr[1]->field_type == SWQ_INTEGER64)
     768             :         {
     769           4 :             osFilter +=
     770           4 :                 CPLSPrintf("%" PRId64, poNode->papoSubExpr[1]->int_value);
     771             :         }
     772           2 :         else if (poNode->papoSubExpr[1]->field_type == SWQ_FLOAT)
     773             :         {
     774             :             osFilter +=
     775           1 :                 CPLSPrintf("%.17g", poNode->papoSubExpr[1]->float_value);
     776             :         }
     777             :         else
     778             :         {
     779           1 :             osFilter += "\"";
     780           1 :             osFilter += poNode->papoSubExpr[1]->string_value;
     781           1 :             osFilter += "\"";
     782             :         }
     783           6 :         return osFilter;
     784             :     }
     785           6 :     else if (bIsAndTopLevel && poNode->eNodeType == SNT_OPERATION &&
     786           6 :              (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_GE) &&
     787           4 :              poNode->nSubExprCount == 2 &&
     788           4 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     789           4 :              poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
     790           4 :              poNode->papoSubExpr[0]->field_index ==
     791          17 :                  m_poFeatureDefn->GetFieldIndex("startTime") &&
     792           3 :              poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP)
     793             :     {
     794           3 :         int nTerms = GDALEEDALayerParseDateTime(
     795           3 :             poNode->papoSubExpr[1]->string_value, SWQ_GE, nYear, nMonth, nDay,
     796             :             nHour, nMinute, nSecond);
     797           3 :         if (nTerms >= 3)
     798             :         {
     799             :             m_osStartTime = CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
     800           3 :                                        nMonth, nDay, nHour, nMinute, nSecond);
     801             :         }
     802             :         else
     803             :         {
     804           0 :             m_bFilterMustBeClientSideEvaluated = true;
     805             :         }
     806           3 :         return "";
     807             :     }
     808           3 :     else if (bIsAndTopLevel && poNode->eNodeType == SNT_OPERATION &&
     809           3 :              (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_LE) &&
     810           2 :              poNode->nSubExprCount == 2 &&
     811           2 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     812           2 :              poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
     813           2 :              poNode->papoSubExpr[0]->field_index ==
     814          10 :                  m_poFeatureDefn->GetFieldIndex("endTime") &&
     815           2 :              poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP)
     816             :     {
     817           2 :         int nTerms = GDALEEDALayerParseDateTime(
     818           2 :             poNode->papoSubExpr[1]->string_value, SWQ_LE, nYear, nMonth, nDay,
     819             :             nHour, nMinute, nSecond);
     820           2 :         if (nTerms >= 3)
     821             :         {
     822           2 :             if (poNode->nOperation == SWQ_EQ && nTerms == 6)
     823             :             {
     824           0 :                 if (nSecond < 59)
     825           0 :                     nSecond++;
     826           0 :                 else if (nMinute < 59)
     827           0 :                     nMinute++;
     828           0 :                 else if (nHour < 23)
     829           0 :                     nHour++;
     830             :                 else
     831           0 :                     nDay++;
     832             :             }
     833             :             m_osEndTime = CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
     834           2 :                                      nMonth, nDay, nHour, nMinute, nSecond);
     835             :         }
     836             :         else
     837             :         {
     838           0 :             m_bFilterMustBeClientSideEvaluated = true;
     839             :         }
     840           2 :         return "";
     841             :     }
     842           9 :     else if (poNode->eNodeType == SNT_OPERATION &&
     843           3 :              poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 &&
     844           9 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     845           3 :              m_oSetQueryableFields.find(poNode->papoSubExpr[0]->field_index) !=
     846           6 :                  m_oSetQueryableFields.end())
     847             :     {
     848           3 :         const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
     849           6 :         CPLString osFilter;
     850             : 
     851           7 :         for (int i = 1; i < poNode->nSubExprCount; i++)
     852             :         {
     853           4 :             if (!osFilter.empty())
     854           1 :                 osFilter += " OR ";
     855           4 :             osFilter += m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
     856           4 :             osFilter += " = ";
     857           4 :             if (poNode->papoSubExpr[i]->field_type == SWQ_INTEGER ||
     858           3 :                 poNode->papoSubExpr[i]->field_type == SWQ_INTEGER64)
     859             :             {
     860           1 :                 osFilter +=
     861           1 :                     CPLSPrintf("%" PRId64, poNode->papoSubExpr[i]->int_value);
     862             :             }
     863           3 :             else if (poNode->papoSubExpr[i]->field_type == SWQ_FLOAT)
     864             :             {
     865             :                 osFilter +=
     866           1 :                     CPLSPrintf("%.17g", poNode->papoSubExpr[i]->float_value);
     867             :             }
     868             :             else
     869             :             {
     870           2 :                 osFilter += "\"";
     871           2 :                 osFilter += poNode->papoSubExpr[i]->string_value;
     872           2 :                 osFilter += "\"";
     873             :             }
     874             :         }
     875             : 
     876           3 :         return osFilter;
     877             :     }
     878             : 
     879           0 :     m_bFilterMustBeClientSideEvaluated = true;
     880           0 :     return "";
     881             : }
     882             : 
     883             : /************************************************************************/
     884             : /*                         SetAttributeFilter()                         */
     885             : /************************************************************************/
     886             : 
     887           5 : OGRErr GDALEEDALayer::SetAttributeFilter(const char *pszQuery)
     888             : 
     889             : {
     890           5 :     m_osAttributeFilter.clear();
     891           5 :     m_osStartTime.clear();
     892           5 :     m_osEndTime.clear();
     893           5 :     m_bFilterMustBeClientSideEvaluated = false;
     894             : 
     895           5 :     if (pszQuery && STARTS_WITH_CI(pszQuery, "EEDA:"))
     896             :     {
     897           1 :         m_osAttributeFilter = pszQuery + strlen("EEDA:");
     898           1 :         OGRLayer::SetAttributeFilter(nullptr);
     899           1 :         ResetReading();
     900           1 :         return OGRERR_NONE;
     901             :     }
     902             : 
     903           4 :     OGRErr eErr = OGRLayer::SetAttributeFilter(pszQuery);
     904             : 
     905           4 :     if (m_poAttrQuery != nullptr)
     906             :     {
     907             :         swq_expr_node *poNode =
     908           3 :             static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
     909             : 
     910             : #ifndef PLUGIN
     911           3 :         poNode->ReplaceBetweenByGEAndLERecurse();
     912             : #endif
     913             : 
     914           3 :         m_osAttributeFilter = BuildFilter(poNode, true);
     915           3 :         if (m_osAttributeFilter.empty() && m_osStartTime.empty() &&
     916           0 :             m_osEndTime.empty())
     917             :         {
     918           0 :             CPLDebug("EEDA", "Full filter will be evaluated on client side.");
     919             :         }
     920           3 :         else if (m_bFilterMustBeClientSideEvaluated)
     921             :         {
     922           0 :             CPLDebug(
     923             :                 "EEDA",
     924             :                 "Only part of the filter will be evaluated on server side.");
     925             :         }
     926             :     }
     927             : 
     928           4 :     ResetReading();
     929             : 
     930           4 :     return eErr;
     931             : }
     932             : 
     933             : /************************************************************************/
     934             : /*                          ISetSpatialFilter()                         */
     935             : /************************************************************************/
     936             : 
     937           2 : OGRErr GDALEEDALayer::ISetSpatialFilter(int /* iGeomField */,
     938             :                                         const OGRGeometry *poGeomIn)
     939             : {
     940           2 :     if (poGeomIn)
     941             :     {
     942           1 :         OGREnvelope sEnvelope;
     943           1 :         poGeomIn->getEnvelope(&sEnvelope);
     944           1 :         if (sEnvelope.MinX == sEnvelope.MaxX &&
     945           0 :             sEnvelope.MinY == sEnvelope.MaxY)
     946             :         {
     947           0 :             OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
     948           0 :             InstallFilter(&p);
     949             :         }
     950             :         else
     951           1 :             InstallFilter(poGeomIn);
     952             :     }
     953             :     else
     954           1 :         InstallFilter(poGeomIn);
     955             : 
     956           2 :     ResetReading();
     957           2 :     return OGRERR_NONE;
     958             : }
     959             : 
     960             : /************************************************************************/
     961             : /*                                GetExtent()                           */
     962             : /************************************************************************/
     963             : 
     964           1 : OGRErr GDALEEDALayer::IGetExtent(int /* iGeomField*/, OGREnvelope *psExtent,
     965             :                                  bool /* bForce */)
     966             : {
     967           1 :     psExtent->MinX = -180;
     968           1 :     psExtent->MinY = -90;
     969           1 :     psExtent->MaxX = 180;
     970           1 :     psExtent->MaxY = 90;
     971           1 :     return OGRERR_NONE;
     972             : }
     973             : 
     974             : /************************************************************************/
     975             : /*                              TestCapability()                        */
     976             : /************************************************************************/
     977             : 
     978           5 : int GDALEEDALayer::TestCapability(const char *pszCap)
     979             : {
     980           5 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
     981           4 :         return TRUE;
     982           1 :     return FALSE;
     983             : }
     984             : 
     985             : /************************************************************************/
     986             : /*                         GDALEEDADataset()                           */
     987             : /************************************************************************/
     988             : 
     989           7 : GDALEEDADataset::GDALEEDADataset() : m_poLayer(nullptr)
     990             : {
     991           7 : }
     992             : 
     993             : /************************************************************************/
     994             : /*                        ~GDALEEDADataset()                            */
     995             : /************************************************************************/
     996             : 
     997          14 : GDALEEDADataset::~GDALEEDADataset()
     998             : {
     999           7 :     delete m_poLayer;
    1000          14 : }
    1001             : 
    1002             : /************************************************************************/
    1003             : /*                            GetLayer()                                */
    1004             : /************************************************************************/
    1005             : 
    1006           7 : OGRLayer *GDALEEDADataset::GetLayer(int idx)
    1007             : {
    1008           7 :     if (idx == 0)
    1009           7 :         return m_poLayer;
    1010           0 :     return nullptr;
    1011             : }
    1012             : 
    1013             : /************************************************************************/
    1014             : /*                            RunRequest()                              */
    1015             : /************************************************************************/
    1016             : 
    1017          13 : json_object *GDALEEDADataset::RunRequest(const CPLString &osURL)
    1018             : {
    1019          13 :     char **papszOptions = GetBaseHTTPOptions();
    1020          13 :     if (papszOptions == nullptr)
    1021           0 :         return nullptr;
    1022          13 :     CPLHTTPResult *psResult = EEDAHTTPFetch(osURL, papszOptions);
    1023          13 :     CSLDestroy(papszOptions);
    1024          13 :     if (psResult == nullptr)
    1025           0 :         return nullptr;
    1026          13 :     if (psResult->pszErrBuf != nullptr)
    1027             :     {
    1028           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
    1029           0 :                  psResult->pabyData
    1030             :                      ? reinterpret_cast<const char *>(psResult->pabyData)
    1031             :                      : psResult->pszErrBuf);
    1032           0 :         CPLHTTPDestroyResult(psResult);
    1033           0 :         return nullptr;
    1034             :     }
    1035             : 
    1036          13 :     if (psResult->pabyData == nullptr)
    1037             :     {
    1038           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1039             :                  "Empty content returned by server");
    1040           0 :         CPLHTTPDestroyResult(psResult);
    1041           0 :         return nullptr;
    1042             :     }
    1043             : 
    1044          13 :     const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
    1045             : #ifdef DEBUG_VERBOSE
    1046             :     CPLDebug("EEDA", "%s", pszText);
    1047             : #endif
    1048             : 
    1049          13 :     json_object *poObj = nullptr;
    1050          13 :     if (!OGRJSonParse(pszText, &poObj, true))
    1051             :     {
    1052           0 :         CPLHTTPDestroyResult(psResult);
    1053           0 :         return nullptr;
    1054             :     }
    1055             : 
    1056          13 :     CPLHTTPDestroyResult(psResult);
    1057             : 
    1058          13 :     if (json_object_get_type(poObj) != json_type_object)
    1059             :     {
    1060           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1061             :                  "Return is not a JSON dictionary");
    1062           0 :         json_object_put(poObj);
    1063           0 :         return nullptr;
    1064             :     }
    1065             : 
    1066          13 :     return poObj;
    1067             : }
    1068             : 
    1069             : /************************************************************************/
    1070             : /*                         GDALEEDADatasetGetConf()                     */
    1071             : /************************************************************************/
    1072             : 
    1073           7 : static json_object *GDALEEDADatasetGetConf()
    1074             : {
    1075           7 :     const char *pszConfFile = CPLFindFile("gdal", "eedaconf.json");
    1076           7 :     if (pszConfFile == nullptr)
    1077             :     {
    1078           0 :         CPLDebug("EEDA", "Cannot find eedaconf.json");
    1079           0 :         return nullptr;
    1080             :     }
    1081             : 
    1082           7 :     GByte *pabyRet = nullptr;
    1083           7 :     if (!VSIIngestFile(nullptr, pszConfFile, &pabyRet, nullptr, -1))
    1084             :     {
    1085           0 :         return nullptr;
    1086             :     }
    1087             : 
    1088           7 :     json_object *poRoot = nullptr;
    1089           7 :     const char *pzText = reinterpret_cast<char *>(pabyRet);
    1090           7 :     if (!OGRJSonParse(pzText, &poRoot))
    1091             :     {
    1092           0 :         VSIFree(pabyRet);
    1093           0 :         return nullptr;
    1094             :     }
    1095           7 :     VSIFree(pabyRet);
    1096             : 
    1097           7 :     if (json_object_get_type(poRoot) != json_type_object)
    1098             :     {
    1099           0 :         json_object_put(poRoot);
    1100           0 :         return nullptr;
    1101             :     }
    1102             : 
    1103           7 :     return poRoot;
    1104             : }
    1105             : 
    1106             : /************************************************************************/
    1107             : /*                               Open()                                 */
    1108             : /************************************************************************/
    1109             : 
    1110           7 : bool GDALEEDADataset::Open(GDALOpenInfo *poOpenInfo)
    1111             : {
    1112             :     m_osBaseURL = CPLGetConfigOption(
    1113           7 :         "EEDA_URL", "https://earthengine-highvolume.googleapis.com/v1alpha/");
    1114             : 
    1115             :     CPLString osCollection =
    1116          14 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "COLLECTION", "");
    1117           7 :     if (osCollection.empty())
    1118             :     {
    1119             :         char **papszTokens =
    1120           7 :             CSLTokenizeString2(poOpenInfo->pszFilename, ":", 0);
    1121           7 :         if (CSLCount(papszTokens) < 2)
    1122             :         {
    1123           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1124             :                      "No collection specified in connection string or "
    1125             :                      "COLLECTION open option");
    1126           0 :             CSLDestroy(papszTokens);
    1127           0 :             return false;
    1128             :         }
    1129           7 :         osCollection = papszTokens[1];
    1130           7 :         CSLDestroy(papszTokens);
    1131             :     }
    1132          14 :     CPLString osCollectionName = ConvertPathToName(osCollection);
    1133             : 
    1134           7 :     json_object *poRootConf = GDALEEDADatasetGetConf();
    1135           7 :     if (poRootConf)
    1136             :     {
    1137             :         json_object *poLayerConf =
    1138           7 :             CPL_json_object_object_get(poRootConf, osCollection);
    1139           8 :         if (poLayerConf != nullptr &&
    1140           1 :             json_object_get_type(poLayerConf) == json_type_object)
    1141             :         {
    1142           1 :             m_poLayer = new GDALEEDALayer(this, osCollection, osCollectionName,
    1143           1 :                                           nullptr, poLayerConf);
    1144           1 :             json_object_put(poRootConf);
    1145           1 :             return true;
    1146             :         }
    1147           6 :         json_object_put(poRootConf);
    1148             :     }
    1149             : 
    1150             :     // Issue request to get layer schema
    1151             :     json_object *poRootAsset =
    1152           6 :         RunRequest(m_osBaseURL + osCollectionName + ":listImages?pageSize=1");
    1153           6 :     if (poRootAsset == nullptr)
    1154           0 :         return false;
    1155             : 
    1156           6 :     json_object *poAssets = CPL_json_object_object_get(poRootAsset, "images");
    1157          12 :     if (poAssets == nullptr ||
    1158          12 :         json_object_get_type(poAssets) != json_type_array ||
    1159           6 :         json_object_array_length(poAssets) != 1)
    1160             :     {
    1161           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No assets");
    1162           0 :         json_object_put(poRootAsset);
    1163           0 :         return false;
    1164             :     }
    1165           6 :     json_object *poAsset = json_object_array_get_idx(poAssets, 0);
    1166           6 :     if (poAsset == nullptr || json_object_get_type(poAsset) != json_type_object)
    1167             :     {
    1168           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No assets");
    1169           0 :         json_object_put(poRootAsset);
    1170           0 :         return false;
    1171             :     }
    1172             : 
    1173           6 :     m_poLayer = new GDALEEDALayer(this, osCollection, osCollectionName, poAsset,
    1174           6 :                                   nullptr);
    1175           6 :     json_object_put(poRootAsset);
    1176             : 
    1177           6 :     return true;
    1178             : }
    1179             : 
    1180             : /************************************************************************/
    1181             : /*                          GDALEEDAdentify()                          */
    1182             : /************************************************************************/
    1183             : 
    1184       59581 : static int GDALEEDAdentify(GDALOpenInfo *poOpenInfo)
    1185             : {
    1186       59581 :     return STARTS_WITH_CI(poOpenInfo->pszFilename, "EEDA:");
    1187             : }
    1188             : 
    1189             : /************************************************************************/
    1190             : /*                            GDALEEDAOpen()                            */
    1191             : /************************************************************************/
    1192             : 
    1193           7 : static GDALDataset *GDALEEDAOpen(GDALOpenInfo *poOpenInfo)
    1194             : {
    1195           7 :     if (!GDALEEDAdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
    1196           0 :         return nullptr;
    1197             : 
    1198           7 :     GDALEEDADataset *poDS = new GDALEEDADataset();
    1199           7 :     if (!poDS->Open(poOpenInfo))
    1200             :     {
    1201           0 :         delete poDS;
    1202           0 :         return nullptr;
    1203             :     }
    1204           7 :     return poDS;
    1205             : }
    1206             : 
    1207             : /************************************************************************/
    1208             : /*                         GDALRegister_EEDA()                          */
    1209             : /************************************************************************/
    1210             : 
    1211        1911 : void GDALRegister_EEDA()
    1212             : 
    1213             : {
    1214        1911 :     if (GDALGetDriverByName("EEDA") != nullptr)
    1215         282 :         return;
    1216             : 
    1217        1629 :     GDALDriver *poDriver = new GDALDriver();
    1218             : 
    1219        1629 :     poDriver->SetDescription("EEDA");
    1220        1629 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    1221        1629 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Earth Engine Data API");
    1222        1629 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/eeda.html");
    1223        1629 :     poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "EEDA:");
    1224        1629 :     poDriver->SetMetadataItem(
    1225             :         GDAL_DMD_OPENOPTIONLIST,
    1226             :         "<OpenOptionList>"
    1227             :         "  <Option name='COLLECTION' type='string' "
    1228             :         "description='Collection name'/>"
    1229             :         "  <Option name='VSI_PATH_FOR_AUTH' type='string' "
    1230             :         "description='/vsigs/... path onto which a "
    1231             :         "GOOGLE_APPLICATION_CREDENTIALS path specific "
    1232             :         "option is set'/>"
    1233        1629 :         "</OpenOptionList>");
    1234             : 
    1235        1629 :     poDriver->pfnOpen = GDALEEDAOpen;
    1236        1629 :     poDriver->pfnIdentify = GDALEEDAdentify;
    1237             : 
    1238        1629 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1239             : 
    1240             : #ifdef GDAL_ENABLE_DRIVER_EEDA_PLUGIN
    1241             :     GDALRegister_EEDAI();
    1242             : #endif
    1243             : }

Generated by: LCOV version 1.14