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

Generated by: LCOV version 1.14