LCOV - code coverage report
Current view: top level - frmts/eeda - eedadataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 513 597 85.9 %
Date: 2024-11-21 22:18:42 Functions: 29 31 93.5 %

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

Generated by: LCOV version 1.14