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

Generated by: LCOV version 1.14