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

Generated by: LCOV version 1.14