LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/plscenes - ogrplscenesdatav1layer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 655 730 89.7 %
Date: 2025-02-20 10:14:44 Functions: 21 23 91.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PlanetLabs scene driver
       4             :  * Purpose:  Implements OGRPLScenesDataV1Layer
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2017, Planet Labs
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_plscenes.h"
      14             : #include "ogrlibjsonutils.h"
      15             : #include "ogrgeojsongeometry.h"
      16             : #include "ogrgeojsonwriter.h"
      17             : #include <algorithm>
      18             : 
      19             : #ifdef EMBED_RESOURCE_FILES
      20             : #include "embedded_resources.h"
      21             : #endif
      22             : 
      23             : /************************************************************************/
      24             : /*                           GetFieldCount()                            */
      25             : /************************************************************************/
      26             : 
      27        2781 : int OGRPLScenesDataV1FeatureDefn::GetFieldCount() const
      28             : {
      29        2781 :     if (OGRFeatureDefn::GetFieldCount() == 0 && m_poLayer != nullptr)
      30           8 :         m_poLayer->EstablishLayerDefn();
      31        2781 :     return OGRFeatureDefn::GetFieldCount();
      32             : }
      33             : 
      34             : /************************************************************************/
      35             : /*                        OGRPLScenesDataV1Layer()                      */
      36             : /************************************************************************/
      37             : 
      38          12 : OGRPLScenesDataV1Layer::OGRPLScenesDataV1Layer(OGRPLScenesDataV1Dataset *poDS,
      39          12 :                                                const char *pszName)
      40             :     : m_poDS(poDS), m_bFeatureDefnEstablished(false),
      41          12 :       m_poSRS(new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG)),
      42             :       m_nTotalFeatures(-1), m_nNextFID(1), m_bEOF(false),
      43             :       m_bStillInFirstPage(true),
      44          12 :       m_nPageSize(atoi(CPLGetConfigOption("PLSCENES_PAGE_SIZE", "250"))),
      45             :       m_bInFeatureCountOrGetExtent(false), m_poPageObj(nullptr),
      46             :       m_poFeatures(nullptr), m_nFeatureIdx(0), m_poAttributeFilter(nullptr),
      47          36 :       m_bFilterMustBeClientSideEvaluated(false)
      48             : {
      49          12 :     m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      50             : 
      51             :     // Cannot be moved to initializer list because of use of this, which MSVC
      52             :     // 2008 doesn't like
      53          12 :     m_poFeatureDefn = new OGRPLScenesDataV1FeatureDefn(this, pszName);
      54             : 
      55          12 :     SetDescription(pszName);
      56          12 :     m_poFeatureDefn->SetGeomType(wkbMultiPolygon);
      57          12 :     m_poFeatureDefn->Reference();
      58          12 :     m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
      59          12 :     OGRPLScenesDataV1Layer::ResetReading();
      60          12 : }
      61             : 
      62             : /************************************************************************/
      63             : /*                      ~OGRPLScenesDataV1Layer()                       */
      64             : /************************************************************************/
      65             : 
      66          24 : OGRPLScenesDataV1Layer::~OGRPLScenesDataV1Layer()
      67             : {
      68          12 :     m_poFeatureDefn->DropRefToLayer();
      69          12 :     m_poFeatureDefn->Release();
      70          12 :     m_poSRS->Release();
      71          12 :     if (m_poPageObj != nullptr)
      72           3 :         json_object_put(m_poPageObj);
      73          12 :     if (m_poAttributeFilter != nullptr)
      74           2 :         json_object_put(m_poAttributeFilter);
      75          24 : }
      76             : 
      77             : /************************************************************************/
      78             : /*                             GetLayerDefn()                           */
      79             : /************************************************************************/
      80             : 
      81          54 : OGRFeatureDefn *OGRPLScenesDataV1Layer::GetLayerDefn()
      82             : {
      83          54 :     return m_poFeatureDefn;
      84             : }
      85             : 
      86             : /************************************************************************/
      87             : /*                          RegisterField()                             */
      88             : /************************************************************************/
      89             : 
      90         321 : void OGRPLScenesDataV1Layer::RegisterField(OGRFieldDefn *poFieldDefn,
      91             :                                            const char *pszQueryableJSonName,
      92             :                                            const char *pszPrefixedJSonName)
      93             : {
      94         321 :     const int nIdx = m_poFeatureDefn->GetFieldCount();
      95         321 :     m_oMapPrefixedJSonFieldNameToFieldIdx[pszPrefixedJSonName] = nIdx;
      96         321 :     if (pszQueryableJSonName)
      97             :     {
      98         174 :         m_oMapFieldIdxToQueryableJSonFieldName[nIdx] = pszQueryableJSonName;
      99             :     }
     100         321 :     m_poFeatureDefn->AddFieldDefn(poFieldDefn);
     101         321 : }
     102             : 
     103             : /************************************************************************/
     104             : /*                         EstablishLayerDefn()                         */
     105             : /************************************************************************/
     106             : 
     107          31 : void OGRPLScenesDataV1Layer::EstablishLayerDefn()
     108             : {
     109          31 :     if (m_bFeatureDefnEstablished)
     110          26 :         return;
     111           5 :     m_bFeatureDefnEstablished = true;
     112             : 
     113           5 :     const char *pzText = nullptr;
     114           5 :     const char *pszConfFile = nullptr;
     115             : #if !defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
     116           5 :     pszConfFile = CPLFindFile("gdal", "plscenesconf.json");
     117           5 :     if (pszConfFile == nullptr)
     118             : #endif
     119             :     {
     120             : #ifdef EMBED_RESOURCE_FILES
     121             :         static const bool bOnce [[maybe_unused]] = []()
     122             :         {
     123             :             CPLDebug("PLScenes", "Using embedded plscenes.conf");
     124             :             return true;
     125             :         }();
     126             :         pzText = PLScenesGetConfJson();
     127             : #else
     128           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find plscenesconf.json");
     129           0 :         return;
     130             : #endif
     131             :     }
     132             : 
     133           5 :     GByte *pabyRet = nullptr;
     134             : #ifdef EMBED_RESOURCE_FILES
     135             :     if (!pzText)
     136             : #endif
     137             :     {
     138           5 :         if (!VSIIngestFile(nullptr, pszConfFile, &pabyRet, nullptr, -1))
     139             :         {
     140           0 :             return;
     141             :         }
     142           5 :         pzText = reinterpret_cast<char *>(pabyRet);
     143             :     }
     144             : 
     145           5 :     json_object *poRoot = nullptr;
     146           5 :     if (!OGRJSonParse(pzText, &poRoot))
     147             :     {
     148           0 :         VSIFree(pabyRet);
     149           0 :         return;
     150             :     }
     151           5 :     VSIFree(pabyRet);
     152             : 
     153           5 :     json_object *poV1Data = CPL_json_object_object_get(poRoot, "v1_data");
     154          10 :     if (poV1Data == nullptr ||
     155           5 :         json_object_get_type(poV1Data) != json_type_object)
     156             :     {
     157           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     158             :                  "Cannot find v1_data object in plscenesconf.json");
     159           0 :         json_object_put(poRoot);
     160           0 :         return;
     161             :     }
     162             : 
     163             :     json_object *poItemType =
     164           5 :         CPL_json_object_object_get(poV1Data, GetDescription());
     165          10 :     if (poItemType == nullptr ||
     166           5 :         json_object_get_type(poItemType) != json_type_object)
     167             :     {
     168           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     169             :                  "Cannot find v1_data.%s object in plscenesconf.json",
     170           0 :                  GetDescription());
     171           0 :         json_object_put(poRoot);
     172           0 :         return;
     173             :     }
     174             : 
     175           5 :     json_object *poFields = CPL_json_object_object_get(poItemType, "fields");
     176          10 :     if (poFields == nullptr ||
     177           5 :         json_object_get_type(poFields) != json_type_array)
     178             :     {
     179           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     180             :                  "Cannot find v1_data.%s.fields object in plscenesconf.json",
     181           0 :                  GetDescription());
     182           0 :         json_object_put(poRoot);
     183           0 :         return;
     184             :     }
     185             : 
     186             :     {
     187          10 :         OGRFieldDefn oFieldDefn("id", OFTString);
     188           5 :         RegisterField(&oFieldDefn, "id", "id");
     189             :     }
     190           5 :     const auto nFields = json_object_array_length(poFields);
     191         174 :     for (auto i = decltype(nFields){0}; i < nFields; i++)
     192             :     {
     193         169 :         json_object *poField = json_object_array_get_idx(poFields, i);
     194         169 :         if (poField && json_object_get_type(poField) == json_type_object)
     195             :         {
     196         169 :             json_object *poName = CPL_json_object_object_get(poField, "name");
     197         169 :             json_object *poType = CPL_json_object_object_get(poField, "type");
     198         169 :             if (poName && json_object_get_type(poName) == json_type_string &&
     199         338 :                 poType && json_object_get_type(poType) == json_type_string)
     200             :             {
     201         169 :                 const char *pszName = json_object_get_string(poName);
     202         169 :                 const char *pszType = json_object_get_string(poType);
     203         169 :                 OGRFieldType eType(OFTString);
     204         169 :                 OGRFieldSubType eSubType(OFSTNone);
     205         169 :                 if (EQUAL(pszType, "datetime"))
     206          15 :                     eType = OFTDateTime;
     207         154 :                 else if (EQUAL(pszType, "double"))
     208          95 :                     eType = OFTReal;
     209          59 :                 else if (EQUAL(pszType, "int"))
     210          15 :                     eType = OFTInteger;
     211          44 :                 else if (EQUAL(pszType, "string"))
     212          39 :                     eType = OFTString;
     213           5 :                 else if (EQUAL(pszType, "boolean"))
     214             :                 {
     215           5 :                     eType = OFTInteger;
     216           5 :                     eSubType = OFSTBoolean;
     217             :                 }
     218             :                 else
     219             :                 {
     220           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     221             :                              "Unrecognized field type %s for field %s", pszType,
     222             :                              pszName);
     223             :                 }
     224         169 :                 OGRFieldDefn oFieldDefn(pszName, eType);
     225         169 :                 oFieldDefn.SetSubType(eSubType);
     226         169 :                 RegisterField(&oFieldDefn, pszName,
     227         338 :                               (CPLString("properties.") + pszName).c_str());
     228             :             }
     229             :         }
     230             :     }
     231             : 
     232             :     {
     233          10 :         OGRFieldDefn oFieldDefn("self_link", OFTString);
     234           5 :         RegisterField(&oFieldDefn, nullptr, "_links._self");
     235             :     }
     236             : 
     237             :     {
     238          10 :         OGRFieldDefn oFieldDefn("assets_link", OFTString);
     239           5 :         RegisterField(&oFieldDefn, nullptr, "_links.assets");
     240             :     }
     241             : 
     242             :     {
     243          10 :         OGRFieldDefn oFieldDefn("permissions", OFTStringList);
     244           5 :         RegisterField(&oFieldDefn, nullptr, "_permissions");
     245             :     }
     246             : 
     247           5 :     if (m_poDS->DoesFollowLinks())
     248             :     {
     249             :         json_object *poAssets =
     250           2 :             CPL_json_object_object_get(poItemType, "assets");
     251           4 :         if (poAssets == nullptr ||
     252           2 :             json_object_get_type(poAssets) != json_type_array)
     253             :         {
     254           0 :             CPLError(
     255             :                 CE_Failure, CPLE_AppDefined,
     256             :                 "Cannot find v1_data.%s.assets object in plscenesconf.json",
     257           0 :                 GetDescription());
     258           0 :             json_object_put(poRoot);
     259           0 :             return;
     260             :         }
     261             : 
     262           2 :         const auto nAssets = json_object_array_length(poAssets);
     263          24 :         for (auto i = decltype(nAssets){0}; i < nAssets; i++)
     264             :         {
     265          22 :             json_object *poAsset = json_object_array_get_idx(poAssets, i);
     266          22 :             if (poAsset && json_object_get_type(poAsset) == json_type_string)
     267             :             {
     268          22 :                 const char *pszAsset = json_object_get_string(poAsset);
     269          22 :                 m_oSetAssets.insert(pszAsset);
     270             : 
     271             :                 {
     272          44 :                     CPLString osName("asset_");
     273          22 :                     osName += pszAsset;
     274          22 :                     osName += "_self_link";
     275          44 :                     OGRFieldDefn oFieldDefn(osName, OFTString);
     276          22 :                     RegisterField(
     277             :                         &oFieldDefn, nullptr,
     278             :                         CPLSPrintf("/assets.%s._links._self", pszAsset));
     279             :                 }
     280             :                 {
     281          44 :                     CPLString osName("asset_");
     282          22 :                     osName += pszAsset;
     283          22 :                     osName += "_activate_link";
     284          44 :                     OGRFieldDefn oFieldDefn(osName, OFTString);
     285          22 :                     RegisterField(
     286             :                         &oFieldDefn, nullptr,
     287             :                         CPLSPrintf("/assets.%s._links.activate", pszAsset));
     288             :                 }
     289             :                 {
     290          44 :                     CPLString osName("asset_");
     291          22 :                     osName += pszAsset;
     292          22 :                     osName += "_permissions";
     293          44 :                     OGRFieldDefn oFieldDefn(osName, OFTStringList);
     294          22 :                     RegisterField(
     295             :                         &oFieldDefn, nullptr,
     296             :                         CPLSPrintf("/assets.%s._permissions", pszAsset));
     297             :                 }
     298             :                 {
     299          44 :                     CPLString osName("asset_");
     300          22 :                     osName += pszAsset;
     301          22 :                     osName += "_expires_at";
     302          44 :                     OGRFieldDefn oFieldDefn(osName, OFTDateTime);
     303          22 :                     RegisterField(
     304             :                         &oFieldDefn, nullptr,
     305             :                         CPLSPrintf("/assets.%s.expires_at", pszAsset));
     306             :                 }
     307             :                 {
     308          44 :                     CPLString osName("asset_");
     309          22 :                     osName += pszAsset;
     310          22 :                     osName += "_location";
     311          44 :                     OGRFieldDefn oFieldDefn(osName, OFTString);
     312          22 :                     RegisterField(&oFieldDefn, nullptr,
     313             :                                   CPLSPrintf("/assets.%s.location", pszAsset));
     314             :                 }
     315             :                 {
     316          44 :                     CPLString osName("asset_");
     317          22 :                     osName += pszAsset;
     318          22 :                     osName += "_status";
     319          44 :                     OGRFieldDefn oFieldDefn(osName, OFTString);
     320          22 :                     RegisterField(&oFieldDefn, nullptr,
     321             :                                   CPLSPrintf("/assets.%s.status", pszAsset));
     322             :                 }
     323             :             }
     324             :         }
     325             :     }
     326             : 
     327           5 :     json_object_put(poRoot);
     328             : }
     329             : 
     330             : /************************************************************************/
     331             : /*                             GetMetadata()                            */
     332             : /************************************************************************/
     333             : 
     334           0 : char **OGRPLScenesDataV1Layer::GetMetadata(const char *pszDomain)
     335             : {
     336           0 :     if (pszDomain == nullptr || EQUAL(pszDomain, ""))
     337             :     {
     338           0 :         EstablishLayerDefn();
     339             :     }
     340           0 :     return OGRLayer::GetMetadata(pszDomain);
     341             : }
     342             : 
     343             : /************************************************************************/
     344             : /*                           GetMetadataItem()                          */
     345             : /************************************************************************/
     346             : 
     347           0 : const char *OGRPLScenesDataV1Layer::GetMetadataItem(const char *pszName,
     348             :                                                     const char *pszDomain)
     349             : {
     350           0 :     if (pszDomain == nullptr || EQUAL(pszDomain, ""))
     351             :     {
     352           0 :         EstablishLayerDefn();
     353             :     }
     354           0 :     return OGRLayer::GetMetadataItem(pszName, pszDomain);
     355             : }
     356             : 
     357             : /************************************************************************/
     358             : /*                              GetNextPage()                           */
     359             : /************************************************************************/
     360             : 
     361          20 : bool OGRPLScenesDataV1Layer::GetNextPage()
     362             : {
     363          20 :     if (m_poPageObj != nullptr)
     364          12 :         json_object_put(m_poPageObj);
     365          20 :     m_poPageObj = nullptr;
     366          20 :     m_poFeatures = nullptr;
     367          20 :     m_nFeatureIdx = 0;
     368             : 
     369          20 :     if (m_osRequestURL.empty())
     370             :     {
     371           2 :         m_bEOF = true;
     372           2 :         return false;
     373             :     }
     374             : 
     375             :     json_object *poObj;
     376          18 :     if (m_osRequestURL.find(m_poDS->GetBaseURL() + "quick-search?_page_size") ==
     377             :         0)
     378             :     {
     379          16 :         CPLString osFilter(m_poDS->GetFilter());
     380          16 :         if (osFilter.empty())
     381             :         {
     382          16 :             json_object *poFilterRoot = json_object_new_object();
     383          16 :             json_object *poItemTypes = json_object_new_array();
     384          16 :             json_object_array_add(poItemTypes,
     385          16 :                                   json_object_new_string(GetName()));
     386          16 :             json_object_object_add(poFilterRoot, "item_types", poItemTypes);
     387          16 :             json_object *poFilter = json_object_new_object();
     388          16 :             json_object_object_add(poFilterRoot, "filter", poFilter);
     389          16 :             json_object_object_add(poFilter, "type",
     390             :                                    json_object_new_string("AndFilter"));
     391          16 :             json_object *poConfig = json_object_new_array();
     392          16 :             json_object_object_add(poFilter, "config", poConfig);
     393             : 
     394          16 :             if (m_poFilterGeom != nullptr)
     395             :             {
     396           1 :                 json_object *poGeomFilter = json_object_new_object();
     397           1 :                 json_object_array_add(poConfig, poGeomFilter);
     398           1 :                 json_object_object_add(
     399             :                     poGeomFilter, "type",
     400             :                     json_object_new_string("GeometryFilter"));
     401           1 :                 json_object_object_add(poGeomFilter, "field_name",
     402             :                                        json_object_new_string("geometry"));
     403           2 :                 OGRGeoJSONWriteOptions oOptions;
     404             :                 json_object *poGeoJSONGeom =
     405           1 :                     OGRGeoJSONWriteGeometry(m_poFilterGeom, oOptions);
     406           1 :                 json_object_object_add(poGeomFilter, "config", poGeoJSONGeom);
     407             :             }
     408          16 :             if (m_poAttributeFilter != nullptr)
     409             :             {
     410           6 :                 json_object_get(m_poAttributeFilter);
     411           6 :                 json_object_array_add(poConfig, m_poAttributeFilter);
     412             :             }
     413             : 
     414          16 :             osFilter = json_object_to_json_string_ext(poFilterRoot, 0);
     415          16 :             json_object_put(poFilterRoot);
     416             :         }
     417             :         poObj =
     418          16 :             m_poDS->RunRequest(m_osRequestURL, FALSE, "POST", true, osFilter);
     419             :     }
     420             :     else
     421             :     {
     422           2 :         poObj = m_poDS->RunRequest(m_osRequestURL);
     423             :     }
     424          18 :     if (poObj == nullptr)
     425             :     {
     426           2 :         m_bEOF = true;
     427           2 :         return false;
     428             :     }
     429             : 
     430          16 :     json_object *poFeatures = CPL_json_object_object_get(poObj, "features");
     431          29 :     if (poFeatures == nullptr ||
     432          29 :         json_object_get_type(poFeatures) != json_type_array ||
     433          13 :         json_object_array_length(poFeatures) == 0)
     434             :     {
     435             :         // If this is a single item, then wrap it in a features array
     436             :         json_object *poProperties =
     437           3 :             CPL_json_object_object_get(poObj, "properties");
     438           3 :         if (poProperties != nullptr)
     439             :         {
     440           2 :             m_poPageObj = json_object_new_object();
     441           2 :             poFeatures = json_object_new_array();
     442           2 :             json_object_array_add(poFeatures, poObj);
     443           2 :             json_object_object_add(m_poPageObj, "features", poFeatures);
     444           2 :             poObj = m_poPageObj;
     445             :         }
     446             :         else
     447             :         {
     448           1 :             json_object_put(poObj);
     449           1 :             m_bEOF = true;
     450           1 :             return false;
     451             :         }
     452             :     }
     453             : 
     454          15 :     m_poPageObj = poObj;
     455          15 :     m_poFeatures = poFeatures;
     456             : 
     457             :     // Get URL of next page
     458          15 :     m_osNextURL = "";
     459          15 :     json_object *poLinks = CPL_json_object_object_get(poObj, "_links");
     460          15 :     if (poLinks && json_object_get_type(poLinks) == json_type_object)
     461             :     {
     462           6 :         json_object *poNext = CPL_json_object_object_get(poLinks, "_next");
     463           6 :         if (poNext && json_object_get_type(poNext) == json_type_string)
     464             :         {
     465           6 :             m_osNextURL = json_object_get_string(poNext);
     466             :         }
     467             :     }
     468             : 
     469          15 :     return true;
     470             : }
     471             : 
     472             : /************************************************************************/
     473             : /*                             ResetReading()                           */
     474             : /************************************************************************/
     475             : 
     476          38 : void OGRPLScenesDataV1Layer::ResetReading()
     477             : {
     478          38 :     m_bEOF = false;
     479             : 
     480          38 :     if (m_poFeatures != nullptr && m_bStillInFirstPage)
     481           2 :         m_nFeatureIdx = 0;
     482             :     else
     483          36 :         m_poFeatures = nullptr;
     484          38 :     m_nNextFID = 1;
     485          38 :     m_bStillInFirstPage = true;
     486          38 :     m_osRequestURL = m_poDS->GetBaseURL() +
     487          38 :                      CPLSPrintf("quick-search?_page_size=%d", m_nPageSize);
     488          38 : }
     489             : 
     490             : /************************************************************************/
     491             : /*                          ISetSpatialFilter()                         */
     492             : /************************************************************************/
     493             : 
     494           2 : OGRErr OGRPLScenesDataV1Layer::ISetSpatialFilter(int /*iGeomField*/,
     495             :                                                  const OGRGeometry *poGeomIn)
     496             : {
     497           2 :     m_poFeatures = nullptr;
     498             : 
     499           2 :     if (poGeomIn)
     500             :     {
     501           1 :         OGREnvelope sEnvelope;
     502           1 :         poGeomIn->getEnvelope(&sEnvelope);
     503           1 :         if (sEnvelope.MinX == sEnvelope.MaxX &&
     504           1 :             sEnvelope.MinY == sEnvelope.MaxY)
     505             :         {
     506           2 :             OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
     507           2 :             InstallFilter(&p);
     508             :         }
     509             :         else
     510           0 :             InstallFilter(poGeomIn);
     511             :     }
     512             :     else
     513           1 :         InstallFilter(poGeomIn);
     514             : 
     515           2 :     ResetReading();
     516             : 
     517           2 :     return OGRERR_NONE;
     518             : }
     519             : 
     520             : /************************************************************************/
     521             : /*                      OGRPLScenesDataV1ParseDateTime()                    */
     522             : /************************************************************************/
     523             : 
     524           4 : static bool OGRPLScenesDataV1ParseDateTime(const char *pszValue, int &nYear,
     525             :                                            int &nMonth, int &nDay, int &nHour,
     526             :                                            int &nMinute, int &nSecond)
     527             : {
     528           4 :     return (sscanf(pszValue, "%04d/%02d/%02d %02d:%02d:%02d", &nYear, &nMonth,
     529           4 :                    &nDay, &nHour, &nMinute, &nSecond) >= 3 ||
     530           0 :             sscanf(pszValue, "%04d-%02d-%02dT%02d:%02d:%02d", &nYear, &nMonth,
     531           4 :                    &nDay, &nHour, &nMinute, &nSecond) >= 3);
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                          IsSimpleComparison()                        */
     536             : /************************************************************************/
     537             : 
     538          26 : bool OGRPLScenesDataV1Layer::IsSimpleComparison(const swq_expr_node *poNode)
     539             : {
     540          52 :     return poNode->eNodeType == SNT_OPERATION &&
     541          26 :            (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_NE ||
     542          13 :             poNode->nOperation == SWQ_LT || poNode->nOperation == SWQ_LE ||
     543          10 :             poNode->nOperation == SWQ_GT || poNode->nOperation == SWQ_GE) &&
     544          23 :            poNode->nSubExprCount == 2 &&
     545          23 :            poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     546          75 :            poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
     547           0 :            m_oMapFieldIdxToQueryableJSonFieldName.find(
     548          23 :                poNode->papoSubExpr[0]->field_index) !=
     549          49 :                m_oMapFieldIdxToQueryableJSonFieldName.end();
     550             : }
     551             : 
     552             : /************************************************************************/
     553             : /*                             GetOperatorText()                        */
     554             : /************************************************************************/
     555             : 
     556           6 : static const char *GetOperatorText(swq_op nOp)
     557             : {
     558           6 :     if (nOp == SWQ_LT)
     559           2 :         return "lt";
     560           4 :     if (nOp == SWQ_LE)
     561           1 :         return "lte";
     562           3 :     if (nOp == SWQ_GT)
     563           2 :         return "gt";
     564           1 :     if (nOp == SWQ_GE)
     565           1 :         return "gte";
     566           0 :     CPLAssert(false);
     567             :     return "";
     568             : }
     569             : 
     570             : /************************************************************************/
     571             : /*                             BuildFilter()                            */
     572             : /************************************************************************/
     573             : 
     574          45 : json_object *OGRPLScenesDataV1Layer::BuildFilter(swq_expr_node *poNode)
     575             : {
     576          45 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
     577          15 :         poNode->nSubExprCount == 2)
     578             :     {
     579             :         // For AND, we can deal with a failure in one of the branch
     580             :         // since client-side will do that extra filtering
     581          15 :         json_object *poFilter1 = BuildFilter(poNode->papoSubExpr[0]);
     582          15 :         json_object *poFilter2 = BuildFilter(poNode->papoSubExpr[1]);
     583          15 :         if (poFilter1 && poFilter2)
     584             :         {
     585          13 :             json_object *poFilter = json_object_new_object();
     586          13 :             json_object_object_add(poFilter, "type",
     587             :                                    json_object_new_string("AndFilter"));
     588          13 :             json_object *poConfig = json_object_new_array();
     589          13 :             json_object_object_add(poFilter, "config", poConfig);
     590          13 :             json_object_array_add(poConfig, poFilter1);
     591          13 :             json_object_array_add(poConfig, poFilter2);
     592          13 :             return poFilter;
     593             :         }
     594           2 :         else if (poFilter1)
     595           1 :             return poFilter1;
     596             :         else
     597           1 :             return poFilter2;
     598             :     }
     599          30 :     else if (poNode->eNodeType == SNT_OPERATION &&
     600          30 :              poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
     601             :     {
     602             :         // For OR, we need both members to be valid
     603           2 :         json_object *poFilter1 = BuildFilter(poNode->papoSubExpr[0]);
     604           2 :         json_object *poFilter2 = BuildFilter(poNode->papoSubExpr[1]);
     605           2 :         if (poFilter1 && poFilter2)
     606             :         {
     607           1 :             json_object *poFilter = json_object_new_object();
     608           1 :             json_object_object_add(poFilter, "type",
     609             :                                    json_object_new_string("OrFilter"));
     610           1 :             json_object *poConfig = json_object_new_array();
     611           1 :             json_object_object_add(poFilter, "config", poConfig);
     612           1 :             json_object_array_add(poConfig, poFilter1);
     613           1 :             json_object_array_add(poConfig, poFilter2);
     614           1 :             return poFilter;
     615             :         }
     616             :         else
     617             :         {
     618           1 :             if (poFilter1)
     619           0 :                 json_object_put(poFilter1);
     620           1 :             if (poFilter2)
     621           1 :                 json_object_put(poFilter2);
     622           1 :             return nullptr;
     623             :         }
     624             :     }
     625          28 :     else if (poNode->eNodeType == SNT_OPERATION &&
     626          28 :              poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
     627             :     {
     628           2 :         json_object *poFilter1 = BuildFilter(poNode->papoSubExpr[0]);
     629           2 :         if (poFilter1)
     630             :         {
     631           1 :             json_object *poFilter = json_object_new_object();
     632           1 :             json_object_object_add(poFilter, "type",
     633             :                                    json_object_new_string("NotFilter"));
     634           1 :             json_object_object_add(poFilter, "config", poFilter1);
     635           1 :             return poFilter;
     636             :         }
     637             :         else
     638             :         {
     639           1 :             return nullptr;
     640             :         }
     641             :     }
     642          26 :     else if (IsSimpleComparison(poNode))
     643             :     {
     644          22 :         int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMinute = 0,
     645          22 :             nSecond = 0;
     646          22 :         const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
     647          22 :         if (poNode->nOperation == SWQ_NE)
     648             :         {
     649           1 :             poNode->nOperation = SWQ_EQ;
     650           1 :             json_object *poFilter1 = BuildFilter(poNode);
     651           1 :             poNode->nOperation = SWQ_NE;
     652           1 :             if (poFilter1)
     653             :             {
     654           1 :                 json_object *poFilter = json_object_new_object();
     655           1 :                 json_object_object_add(poFilter, "type",
     656             :                                        json_object_new_string("NotFilter"));
     657           1 :                 json_object_object_add(poFilter, "config", poFilter1);
     658          18 :                 return poFilter;
     659             :             }
     660             :             else
     661             :             {
     662           0 :                 return nullptr;
     663             :             }
     664             :         }
     665          11 :         else if (poNode->nOperation == SWQ_EQ &&
     666          11 :                  (m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     667          10 :                       OFTInteger ||
     668          10 :                   m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     669          32 :                       OFTReal) &&
     670           2 :                  (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER ||
     671           1 :                   poNode->papoSubExpr[1]->field_type == SWQ_FLOAT))
     672             :         {
     673           2 :             json_object *poFilter = json_object_new_object();
     674           2 :             if (m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() == OFTReal)
     675             :             {
     676           1 :                 json_object_object_add(poFilter, "type",
     677             :                                        json_object_new_string("RangeFilter"));
     678           1 :                 json_object_object_add(
     679             :                     poFilter, "field_name",
     680             :                     json_object_new_string(
     681           1 :                         m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     682           1 :                 json_object *poConfig = json_object_new_object();
     683           1 :                 const double EPS = 1e-8;
     684           1 :                 json_object_object_add(
     685             :                     poConfig, "gte",
     686           1 :                     (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER)
     687           0 :                         ? json_object_new_double(
     688           0 :                               poNode->papoSubExpr[1]->int_value - EPS)
     689           1 :                         : json_object_new_double(
     690           1 :                               poNode->papoSubExpr[1]->float_value - EPS));
     691           1 :                 json_object_object_add(
     692             :                     poConfig, "lte",
     693           1 :                     (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER)
     694           0 :                         ? json_object_new_double(
     695           0 :                               poNode->papoSubExpr[1]->int_value + EPS)
     696           1 :                         : json_object_new_double(
     697           1 :                               poNode->papoSubExpr[1]->float_value + EPS));
     698           1 :                 json_object_object_add(poFilter, "config", poConfig);
     699             :             }
     700             :             else
     701             :             {
     702           1 :                 json_object_object_add(
     703             :                     poFilter, "type", json_object_new_string("NumberInFilter"));
     704           1 :                 json_object_object_add(
     705             :                     poFilter, "field_name",
     706             :                     json_object_new_string(
     707           1 :                         m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     708           1 :                 json_object *poConfig = json_object_new_array();
     709           1 :                 json_object_array_add(
     710             :                     poConfig,
     711           1 :                     (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER)
     712           1 :                         ? json_object_new_int64(
     713           1 :                               poNode->papoSubExpr[1]->int_value)
     714           0 :                         : json_object_new_double(
     715           0 :                               poNode->papoSubExpr[1]->float_value));
     716           1 :                 json_object_object_add(poFilter, "config", poConfig);
     717             :             }
     718           2 :             return poFilter;
     719             :         }
     720           9 :         else if (poNode->nOperation == SWQ_EQ &&
     721           9 :                  m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     722          28 :                      OFTString &&
     723           9 :                  poNode->papoSubExpr[1]->field_type == SWQ_STRING)
     724             :         {
     725           9 :             json_object *poFilter = json_object_new_object();
     726           9 :             json_object_object_add(poFilter, "type",
     727             :                                    json_object_new_string("StringInFilter"));
     728           9 :             json_object_object_add(
     729             :                 poFilter, "field_name",
     730             :                 json_object_new_string(
     731           9 :                     m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     732           9 :             json_object *poConfig = json_object_new_array();
     733           9 :             json_object_array_add(
     734             :                 poConfig,
     735           9 :                 json_object_new_string(poNode->papoSubExpr[1]->string_value));
     736           9 :             json_object_object_add(poFilter, "config", poConfig);
     737           9 :             return poFilter;
     738             :         }
     739          28 :         else if ((poNode->nOperation == SWQ_LT ||
     740           8 :                   poNode->nOperation == SWQ_LE ||
     741           7 :                   poNode->nOperation == SWQ_GT ||
     742          11 :                   poNode->nOperation == SWQ_GE) &&
     743          10 :                  (m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     744           8 :                       OFTInteger ||
     745           8 :                   m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     746          20 :                       OFTReal) &&
     747           2 :                  (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER ||
     748           0 :                   poNode->papoSubExpr[1]->field_type == SWQ_FLOAT))
     749             :         {
     750           2 :             json_object *poFilter = json_object_new_object();
     751           2 :             json_object_object_add(poFilter, "type",
     752             :                                    json_object_new_string("RangeFilter"));
     753           2 :             json_object_object_add(
     754             :                 poFilter, "field_name",
     755             :                 json_object_new_string(
     756           2 :                     m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     757           2 :             json_object *poConfig = json_object_new_object();
     758           2 :             json_object_object_add(
     759             :                 poConfig, GetOperatorText(poNode->nOperation),
     760           2 :                 (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER)
     761           2 :                     ? json_object_new_int64(poNode->papoSubExpr[1]->int_value)
     762           0 :                     : json_object_new_double(
     763           0 :                           poNode->papoSubExpr[1]->float_value));
     764           2 :             json_object_object_add(poFilter, "config", poConfig);
     765           2 :             return poFilter;
     766             :         }
     767          23 :         else if ((poNode->nOperation == SWQ_LT ||
     768           7 :                   poNode->nOperation == SWQ_LE ||
     769           6 :                   poNode->nOperation == SWQ_GT ||
     770           9 :                   poNode->nOperation == SWQ_GE) &&
     771           8 :                  m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     772           4 :                      OFTDateTime &&
     773          20 :                  poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP &&
     774           4 :                  OGRPLScenesDataV1ParseDateTime(
     775           4 :                      poNode->papoSubExpr[1]->string_value, nYear, nMonth, nDay,
     776             :                      nHour, nMinute, nSecond))
     777             :         {
     778           4 :             json_object *poFilter = json_object_new_object();
     779           4 :             json_object_object_add(poFilter, "type",
     780             :                                    json_object_new_string("DateRangeFilter"));
     781           4 :             json_object_object_add(
     782             :                 poFilter, "field_name",
     783             :                 json_object_new_string(
     784           4 :                     m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     785           4 :             json_object *poConfig = json_object_new_object();
     786           4 :             json_object_object_add(poConfig,
     787             :                                    GetOperatorText(poNode->nOperation),
     788             :                                    json_object_new_string(CPLSPrintf(
     789             :                                        "%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
     790             :                                        nMonth, nDay, nHour, nMinute, nSecond)));
     791           4 :             json_object_object_add(poFilter, "config", poConfig);
     792           4 :             return poFilter;
     793             :         }
     794             :     }
     795          12 :     else if (poNode->eNodeType == SNT_OPERATION &&
     796           4 :              poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 &&
     797          11 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     798           0 :              m_oMapFieldIdxToQueryableJSonFieldName.find(
     799           3 :                  poNode->papoSubExpr[0]->field_index) !=
     800           7 :                  m_oMapFieldIdxToQueryableJSonFieldName.end())
     801             :     {
     802           2 :         const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
     803           2 :         if (m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() == OFTString)
     804             :         {
     805           1 :             json_object *poFilter = json_object_new_object();
     806           1 :             json_object_object_add(poFilter, "type",
     807             :                                    json_object_new_string("StringInFilter"));
     808           1 :             json_object_object_add(
     809             :                 poFilter, "field_name",
     810             :                 json_object_new_string(
     811           1 :                     m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     812           1 :             json_object *poConfig = json_object_new_array();
     813           1 :             json_object_object_add(poFilter, "config", poConfig);
     814           2 :             for (int i = 1; i < poNode->nSubExprCount; i++)
     815             :             {
     816           1 :                 if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT ||
     817           1 :                     poNode->papoSubExpr[i]->field_type != SWQ_STRING)
     818             :                 {
     819           0 :                     json_object_put(poFilter);
     820           0 :                     m_bFilterMustBeClientSideEvaluated = true;
     821           2 :                     return nullptr;
     822             :                 }
     823           1 :                 json_object_array_add(
     824             :                     poConfig, json_object_new_string(
     825           1 :                                   poNode->papoSubExpr[i]->string_value));
     826             :             }
     827           1 :             return poFilter;
     828             :         }
     829           1 :         else if (m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     830             :                  OFTInteger)
     831             :         {
     832           1 :             json_object *poFilter = json_object_new_object();
     833           1 :             json_object_object_add(poFilter, "type",
     834             :                                    json_object_new_string("NumberInFilter"));
     835           1 :             json_object_object_add(
     836             :                 poFilter, "field_name",
     837             :                 json_object_new_string(
     838           1 :                     m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     839           1 :             json_object *poConfig = json_object_new_array();
     840           1 :             json_object_object_add(poFilter, "config", poConfig);
     841           3 :             for (int i = 1; i < poNode->nSubExprCount; i++)
     842             :             {
     843           2 :                 if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT ||
     844           2 :                     poNode->papoSubExpr[i]->field_type != SWQ_INTEGER)
     845             :                 {
     846           0 :                     json_object_put(poFilter);
     847           0 :                     m_bFilterMustBeClientSideEvaluated = true;
     848           0 :                     return nullptr;
     849             :                 }
     850           2 :                 json_object_array_add(
     851             :                     poConfig,
     852           2 :                     json_object_new_int64(poNode->papoSubExpr[i]->int_value));
     853             :             }
     854           1 :             return poFilter;
     855             :         }
     856             :     }
     857           6 :     else if (poNode->eNodeType == SNT_OPERATION &&
     858           2 :              poNode->nOperation == SWQ_EQ && poNode->nSubExprCount == 2 &&
     859           1 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     860           1 :              poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
     861           1 :              poNode->papoSubExpr[0]->field_index ==
     862           5 :                  m_poFeatureDefn->GetFieldIndex("permissions") &&
     863           1 :              poNode->papoSubExpr[1]->field_type == SWQ_STRING)
     864             :     {
     865           1 :         json_object *poFilter = json_object_new_object();
     866           1 :         json_object_object_add(poFilter, "type",
     867             :                                json_object_new_string("PermissionFilter"));
     868           1 :         json_object *poConfig = json_object_new_array();
     869           1 :         json_object_object_add(poFilter, "config", poConfig);
     870           1 :         json_object_array_add(
     871             :             poConfig,
     872           1 :             json_object_new_string(poNode->papoSubExpr[1]->string_value));
     873           1 :         return poFilter;
     874             :     }
     875           3 :     else if (poNode->eNodeType == SNT_OPERATION &&
     876           1 :              poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 &&
     877           3 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     878           1 :              poNode->papoSubExpr[0]->field_index ==
     879           1 :                  m_poFeatureDefn->GetFieldIndex("permissions"))
     880             :     {
     881           1 :         json_object *poFilter = json_object_new_object();
     882           1 :         json_object_object_add(poFilter, "type",
     883             :                                json_object_new_string("PermissionFilter"));
     884           1 :         json_object *poConfig = json_object_new_array();
     885           1 :         json_object_object_add(poFilter, "config", poConfig);
     886           2 :         for (int i = 1; i < poNode->nSubExprCount; i++)
     887             :         {
     888           1 :             if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT ||
     889           1 :                 poNode->papoSubExpr[i]->field_type != SWQ_STRING)
     890             :             {
     891           0 :                 json_object_put(poFilter);
     892           0 :                 m_bFilterMustBeClientSideEvaluated = true;
     893           0 :                 return nullptr;
     894             :             }
     895           1 :             json_object_array_add(
     896             :                 poConfig,
     897           1 :                 json_object_new_string(poNode->papoSubExpr[i]->string_value));
     898             :         }
     899           1 :         return poFilter;
     900             :     }
     901             : 
     902           4 :     m_bFilterMustBeClientSideEvaluated = true;
     903           4 :     return nullptr;
     904             : }
     905             : 
     906             : /************************************************************************/
     907             : /*                         SetAttributeFilter()                         */
     908             : /************************************************************************/
     909             : 
     910           9 : OGRErr OGRPLScenesDataV1Layer::SetAttributeFilter(const char *pszQuery)
     911             : 
     912             : {
     913           9 :     m_poFeatures = nullptr;
     914             : 
     915           9 :     OGRErr eErr = OGRLayer::SetAttributeFilter(pszQuery);
     916             : 
     917           9 :     if (m_poAttributeFilter)
     918           4 :         json_object_put(m_poAttributeFilter);
     919           9 :     m_poAttributeFilter = nullptr;
     920           9 :     m_bFilterMustBeClientSideEvaluated = false;
     921           9 :     if (m_poAttrQuery != nullptr)
     922             :     {
     923           8 :         swq_expr_node *poNode = (swq_expr_node *)m_poAttrQuery->GetSWQExpr();
     924             : 
     925           8 :         poNode->ReplaceBetweenByGEAndLERecurse();
     926             : 
     927           8 :         m_poAttributeFilter = BuildFilter(poNode);
     928           8 :         if (m_poAttributeFilter == nullptr)
     929             :         {
     930           2 :             CPLDebug("PLSCENES",
     931             :                      "Full filter will be evaluated on client side.");
     932             :         }
     933           6 :         else if (m_bFilterMustBeClientSideEvaluated)
     934             :         {
     935           2 :             CPLDebug(
     936             :                 "PLSCENES",
     937             :                 "Only part of the filter will be evaluated on server side.");
     938             :         }
     939             :     }
     940             : 
     941           9 :     ResetReading();
     942             : 
     943           9 :     return eErr;
     944             : }
     945             : 
     946             : /************************************************************************/
     947             : /*                           GetNextFeature()                           */
     948             : /************************************************************************/
     949             : 
     950          23 : OGRFeature *OGRPLScenesDataV1Layer::GetNextFeature()
     951             : {
     952             :     while (true)
     953             :     {
     954          23 :         OGRFeature *poFeature = GetNextRawFeature();
     955          23 :         if (poFeature == nullptr)
     956           7 :             return nullptr;
     957             : 
     958          20 :         if (m_poAttrQuery == nullptr || !m_bFilterMustBeClientSideEvaluated ||
     959           4 :             m_poAttrQuery->Evaluate(poFeature))
     960             :         {
     961          16 :             return poFeature;
     962             :         }
     963             :         else
     964             :         {
     965           0 :             delete poFeature;
     966             :         }
     967           0 :     }
     968             : }
     969             : 
     970             : /************************************************************************/
     971             : /*                            GetNextRawFeature()                       */
     972             : /************************************************************************/
     973             : 
     974          23 : OGRFeature *OGRPLScenesDataV1Layer::GetNextRawFeature()
     975             : {
     976          23 :     EstablishLayerDefn();
     977          23 :     if (m_bEOF)
     978           1 :         return nullptr;
     979             : 
     980          22 :     if (m_poFeatures == nullptr)
     981             :     {
     982          16 :         if (!GetNextPage())
     983           3 :             return nullptr;
     984             :     }
     985             : 
     986          38 :     if (m_nFeatureIdx ==
     987          19 :         static_cast<int>(json_object_array_length(m_poFeatures)))
     988             :     {
     989           4 :         m_osRequestURL = m_osNextURL;
     990           4 :         m_bStillInFirstPage = false;
     991           4 :         if (!GetNextPage())
     992           2 :             return nullptr;
     993             :     }
     994             :     json_object *poJSonFeature =
     995          17 :         json_object_array_get_idx(m_poFeatures, m_nFeatureIdx);
     996          17 :     m_nFeatureIdx++;
     997          33 :     if (poJSonFeature == nullptr ||
     998          16 :         json_object_get_type(poJSonFeature) != json_type_object)
     999             :     {
    1000           1 :         m_bEOF = true;
    1001           1 :         return nullptr;
    1002             :     }
    1003             : 
    1004          16 :     OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
    1005          16 :     poFeature->SetFID(m_nNextFID++);
    1006             : 
    1007             :     json_object *poJSonGeom =
    1008          16 :         CPL_json_object_object_get(poJSonFeature, "geometry");
    1009          26 :     if (poJSonGeom != nullptr &&
    1010          10 :         json_object_get_type(poJSonGeom) == json_type_object)
    1011             :     {
    1012          10 :         OGRGeometry *poGeom = OGRGeoJSONReadGeometry(poJSonGeom);
    1013          10 :         if (poGeom != nullptr)
    1014             :         {
    1015          10 :             if (poGeom->getGeometryType() == wkbPolygon)
    1016             :             {
    1017           8 :                 OGRMultiPolygon *poMP = new OGRMultiPolygon();
    1018           8 :                 poMP->addGeometryDirectly(poGeom);
    1019           8 :                 poGeom = poMP;
    1020             :             }
    1021          10 :             poGeom->assignSpatialReference(m_poSRS);
    1022          10 :             poFeature->SetGeometryDirectly(poGeom);
    1023             :         }
    1024             :     }
    1025             : 
    1026          16 :     json_object *poId = CPL_json_object_object_get(poJSonFeature, "id");
    1027          16 :     if (poId != nullptr && json_object_get_type(poId) == json_type_string)
    1028             :     {
    1029             :         std::map<CPLString, int>::const_iterator oIter =
    1030          16 :             m_oMapPrefixedJSonFieldNameToFieldIdx.find("id");
    1031          16 :         if (oIter != m_oMapPrefixedJSonFieldNameToFieldIdx.end())
    1032             :         {
    1033          16 :             const int iField = oIter->second;
    1034          16 :             poFeature->SetField(iField, json_object_get_string(poId));
    1035             :         }
    1036             :     }
    1037             : 
    1038             :     json_object *poPermissions =
    1039          16 :         CPL_json_object_object_get(poJSonFeature, "_permissions");
    1040          24 :     if (poPermissions != nullptr &&
    1041           8 :         json_object_get_type(poPermissions) == json_type_array)
    1042             :     {
    1043             :         std::map<CPLString, int>::const_iterator oIter =
    1044           8 :             m_oMapPrefixedJSonFieldNameToFieldIdx.find("_permissions");
    1045           8 :         if (oIter != m_oMapPrefixedJSonFieldNameToFieldIdx.end())
    1046             :         {
    1047           8 :             const int iField = oIter->second;
    1048           8 :             const auto nStrings = json_object_array_length(poPermissions);
    1049             :             char **papszPermissions =
    1050           8 :                 static_cast<char **>(CPLCalloc(nStrings + 1, sizeof(char *)));
    1051          16 :             for (auto i = decltype(nStrings){0}, j = decltype(nStrings){0};
    1052          16 :                  i < nStrings; i++)
    1053             :             {
    1054             :                 json_object *poPerm =
    1055           8 :                     json_object_array_get_idx(poPermissions, i);
    1056           8 :                 if (poPerm && json_object_get_type(poPerm) == json_type_string)
    1057             :                 {
    1058           8 :                     papszPermissions[j++] =
    1059           8 :                         CPLStrdup(json_object_get_string(poPerm));
    1060             :                 }
    1061             :             }
    1062           8 :             poFeature->SetField(iField, papszPermissions);
    1063           8 :             CSLDestroy(papszPermissions);
    1064             :         }
    1065             :     }
    1066             : 
    1067          48 :     for (int i = 0; i < 2; i++)
    1068             :     {
    1069          32 :         const char *pszFeaturePart = (i == 0) ? "properties" : "_links";
    1070             :         json_object *poProperties =
    1071          32 :             CPL_json_object_object_get(poJSonFeature, pszFeaturePart);
    1072          52 :         if (poProperties != nullptr &&
    1073          20 :             json_object_get_type(poProperties) == json_type_object)
    1074             :         {
    1075             :             json_object_iter it;
    1076          20 :             it.key = nullptr;
    1077          20 :             it.val = nullptr;
    1078          20 :             it.entry = nullptr;
    1079          76 :             json_object_object_foreachC(poProperties, it)
    1080             :             {
    1081         112 :                 CPLString osPrefixedJSonFieldName(pszFeaturePart);
    1082          56 :                 osPrefixedJSonFieldName += ".";
    1083          56 :                 osPrefixedJSonFieldName += it.key;
    1084          56 :                 if (!SetFieldFromPrefixedJSonFieldName(
    1085             :                         poFeature, osPrefixedJSonFieldName, it.val))
    1086             :                 {
    1087           0 :                     if (i == 0 && m_oSetUnregisteredFields.find(
    1088           0 :                                       osPrefixedJSonFieldName) ==
    1089           0 :                                       m_oSetUnregisteredFields.end())
    1090             :                     {
    1091           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1092             :                                  "Field %s found in data but not "
    1093             :                                  "in configuration",
    1094             :                                  osPrefixedJSonFieldName.c_str());
    1095             :                         m_oSetUnregisteredFields.insert(
    1096           0 :                             osPrefixedJSonFieldName);
    1097             :                     }
    1098             :                 }
    1099             :             }
    1100             :         }
    1101             :     }
    1102             : 
    1103          16 :     json_object *poAssets = nullptr;
    1104          31 :     if (m_poDS->DoesFollowLinks() &&
    1105          15 :         (!m_bInFeatureCountOrGetExtent || m_poAttrQuery != nullptr))
    1106             :     {
    1107             :         std::map<CPLString, int>::const_iterator oIter =
    1108          14 :             m_oMapPrefixedJSonFieldNameToFieldIdx.find("_links.assets");
    1109          14 :         if (oIter != m_oMapPrefixedJSonFieldNameToFieldIdx.end())
    1110             :         {
    1111          14 :             const int iField = oIter->second;
    1112          14 :             if (poFeature->IsFieldSetAndNotNull(iField))
    1113             :             {
    1114           8 :                 const char *pszAssetURL = poFeature->GetFieldAsString(iField);
    1115           8 :                 poAssets = m_poDS->RunRequest(pszAssetURL);
    1116             :             }
    1117             :         }
    1118             :     }
    1119          16 :     if (poAssets != nullptr)
    1120             :     {
    1121             :         json_object_iter itAsset;
    1122           8 :         itAsset.key = nullptr;
    1123           8 :         itAsset.val = nullptr;
    1124           8 :         itAsset.entry = nullptr;
    1125          16 :         json_object_object_foreachC(poAssets, itAsset)
    1126             :         {
    1127           8 :             if (m_oSetAssets.find(itAsset.key) == m_oSetAssets.end())
    1128             :             {
    1129           0 :                 if (m_oSetUnregisteredAssets.find(itAsset.key) ==
    1130           0 :                     m_oSetUnregisteredAssets.end())
    1131             :                 {
    1132           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1133             :                              "Asset %s found in data but "
    1134             :                              "not in configuration",
    1135             :                              itAsset.key);
    1136           0 :                     m_oSetUnregisteredAssets.insert(itAsset.key);
    1137             :                 }
    1138           0 :                 continue;
    1139             :             }
    1140             : 
    1141           8 :             json_object *poAsset = itAsset.val;
    1142          16 :             if (poAsset != nullptr &&
    1143           8 :                 json_object_get_type(poAsset) == json_type_object)
    1144             :             {
    1145             :                 json_object_iter it;
    1146           8 :                 it.key = nullptr;
    1147           8 :                 it.val = nullptr;
    1148           8 :                 it.entry = nullptr;
    1149          48 :                 json_object_object_foreachC(poAsset, it)
    1150             :                 {
    1151          40 :                     if (it.val == nullptr)
    1152           0 :                         continue;
    1153          80 :                     CPLString osPrefixedJSonFieldName("/assets." +
    1154         120 :                                                       CPLString(itAsset.key));
    1155          40 :                     osPrefixedJSonFieldName += "." + CPLString(it.key);
    1156          48 :                     if (strcmp(it.key, "_links") == 0 &&
    1157           8 :                         json_object_get_type(it.val) == json_type_object)
    1158             :                     {
    1159           8 :                         if (CPL_json_object_object_get(it.val, "_self") !=
    1160             :                             nullptr)
    1161             :                         {
    1162             :                             CPLString osPrefixedJSonFieldNameNew(
    1163          16 :                                 osPrefixedJSonFieldName + "._self");
    1164           8 :                             SetFieldFromPrefixedJSonFieldName(
    1165             :                                 poFeature, osPrefixedJSonFieldNameNew,
    1166             :                                 CPL_json_object_object_get(it.val, "_self"));
    1167             :                         }
    1168           8 :                         if (CPL_json_object_object_get(it.val, "activate") !=
    1169             :                             nullptr)
    1170             :                         {
    1171             :                             CPLString osPrefixedJSonFieldNameNew(
    1172          16 :                                 osPrefixedJSonFieldName + ".activate");
    1173           8 :                             SetFieldFromPrefixedJSonFieldName(
    1174             :                                 poFeature, osPrefixedJSonFieldNameNew,
    1175             :                                 CPL_json_object_object_get(it.val, "activate"));
    1176             :                         }
    1177             :                     }
    1178             :                     else
    1179             :                     {
    1180          32 :                         SetFieldFromPrefixedJSonFieldName(
    1181             :                             poFeature, osPrefixedJSonFieldName, it.val);
    1182             :                     }
    1183             :                 }
    1184             :             }
    1185             :         }
    1186           8 :         json_object_put(poAssets);
    1187             :     }
    1188             : 
    1189          16 :     return poFeature;
    1190             : }
    1191             : 
    1192             : /************************************************************************/
    1193             : /*                    SetFieldFromPrefixedJSonFieldName()               */
    1194             : /************************************************************************/
    1195             : 
    1196         104 : bool OGRPLScenesDataV1Layer::SetFieldFromPrefixedJSonFieldName(
    1197             :     OGRFeature *poFeature, const CPLString &osPrefixedJSonFieldName,
    1198             :     json_object *poVal)
    1199             : {
    1200             :     std::map<CPLString, int>::const_iterator oIter =
    1201         104 :         m_oMapPrefixedJSonFieldNameToFieldIdx.find(osPrefixedJSonFieldName);
    1202         208 :     if (poVal != nullptr &&
    1203         208 :         oIter != m_oMapPrefixedJSonFieldNameToFieldIdx.end())
    1204             :     {
    1205         104 :         const int iField = oIter->second;
    1206         104 :         json_type eJSonType = json_object_get_type(poVal);
    1207         104 :         if (eJSonType == json_type_int)
    1208             :         {
    1209           8 :             poFeature->SetField(
    1210           8 :                 iField, static_cast<GIntBig>(json_object_get_int64(poVal)));
    1211             :         }
    1212          96 :         else if (eJSonType == json_type_double)
    1213             :         {
    1214           9 :             poFeature->SetField(iField, json_object_get_double(poVal));
    1215             :         }
    1216          87 :         else if (eJSonType == json_type_string)
    1217             :         {
    1218          72 :             poFeature->SetField(iField, json_object_get_string(poVal));
    1219             :         }
    1220          15 :         else if (eJSonType == json_type_boolean)
    1221             :         {
    1222           7 :             poFeature->SetField(iField, json_object_get_boolean(poVal));
    1223             :         }
    1224             :         else
    1225             :         {
    1226           8 :             poFeature->SetField(iField,
    1227             :                                 json_object_to_json_string_ext(poVal, 0));
    1228             :         }
    1229         104 :         return true;
    1230             :     }
    1231           0 :     return false;
    1232             : }
    1233             : 
    1234             : /************************************************************************/
    1235             : /*                          GetFeatureCount()                           */
    1236             : /************************************************************************/
    1237             : 
    1238           2 : GIntBig OGRPLScenesDataV1Layer::GetFeatureCount(int bForce)
    1239             : {
    1240           2 :     if (m_poDS->GetFilter().empty())
    1241             :     {
    1242           2 :         if (m_nTotalFeatures >= 0 && m_poFilterGeom == nullptr &&
    1243           0 :             m_poAttrQuery == nullptr)
    1244             :         {
    1245           1 :             return m_nTotalFeatures;
    1246             :         }
    1247             : 
    1248           2 :         json_object *poFilterRoot = json_object_new_object();
    1249           2 :         json_object *poItemTypes = json_object_new_array();
    1250           2 :         json_object_array_add(poItemTypes, json_object_new_string(GetName()));
    1251           2 :         json_object_object_add(poFilterRoot, "interval",
    1252             :                                json_object_new_string("year"));
    1253           2 :         json_object_object_add(poFilterRoot, "item_types", poItemTypes);
    1254           2 :         json_object *poFilter = json_object_new_object();
    1255           2 :         json_object_object_add(poFilterRoot, "filter", poFilter);
    1256           2 :         json_object_object_add(poFilter, "type",
    1257             :                                json_object_new_string("AndFilter"));
    1258           2 :         json_object *poConfig = json_object_new_array();
    1259           2 :         json_object_object_add(poFilter, "config", poConfig);
    1260             : 
    1261             :         // We need to put a dummy filter
    1262           2 :         if (m_poFilterGeom == nullptr && m_poAttributeFilter == nullptr)
    1263             :         {
    1264           1 :             json_object *poRangeFilter = json_object_new_object();
    1265           1 :             json_object_array_add(poConfig, poRangeFilter);
    1266           1 :             json_object_object_add(poRangeFilter, "type",
    1267             :                                    json_object_new_string("RangeFilter"));
    1268           1 :             json_object_object_add(poRangeFilter, "field_name",
    1269             :                                    json_object_new_string("cloud_cover"));
    1270           1 :             json_object *poRangeFilterConfig = json_object_new_object();
    1271           1 :             json_object_object_add(poRangeFilterConfig, "gte",
    1272             :                                    json_object_new_double(0.0));
    1273           1 :             json_object_object_add(poRangeFilter, "config",
    1274             :                                    poRangeFilterConfig);
    1275             :         }
    1276             : 
    1277           2 :         if (m_poFilterGeom != nullptr)
    1278             :         {
    1279           1 :             json_object *poGeomFilter = json_object_new_object();
    1280           1 :             json_object_array_add(poConfig, poGeomFilter);
    1281           1 :             json_object_object_add(poGeomFilter, "type",
    1282             :                                    json_object_new_string("GeometryFilter"));
    1283           1 :             json_object_object_add(poGeomFilter, "field_name",
    1284             :                                    json_object_new_string("geometry"));
    1285           2 :             OGRGeoJSONWriteOptions oOptions;
    1286             :             json_object *poGeoJSONGeom =
    1287           1 :                 OGRGeoJSONWriteGeometry(m_poFilterGeom, oOptions);
    1288           1 :             json_object_object_add(poGeomFilter, "config", poGeoJSONGeom);
    1289             :         }
    1290           2 :         if (m_poAttributeFilter != nullptr)
    1291             :         {
    1292           0 :             json_object_get(m_poAttributeFilter);
    1293           0 :             json_object_array_add(poConfig, m_poAttributeFilter);
    1294             :         }
    1295             : 
    1296           2 :         CPLString osFilter = json_object_to_json_string_ext(poFilterRoot, 0);
    1297           2 :         json_object_put(poFilterRoot);
    1298             : 
    1299             :         json_object *poObj =
    1300           2 :             m_poDS->RunRequest((m_poDS->GetBaseURL() + "stats").c_str(), FALSE,
    1301             :                                "POST", true, osFilter);
    1302           2 :         if (poObj != nullptr)
    1303             :         {
    1304             :             json_object *poBuckets =
    1305           1 :                 CPL_json_object_object_get(poObj, "buckets");
    1306           1 :             if (poBuckets && json_object_get_type(poBuckets) == json_type_array)
    1307             :             {
    1308           1 :                 GIntBig nRes = 0;
    1309           1 :                 const auto nBuckets = json_object_array_length(poBuckets);
    1310           3 :                 for (auto i = decltype(nBuckets){0}; i < nBuckets; i++)
    1311             :                 {
    1312             :                     json_object *poBucket =
    1313           2 :                         json_object_array_get_idx(poBuckets, i);
    1314           4 :                     if (poBucket &&
    1315           2 :                         json_object_get_type(poBucket) == json_type_object)
    1316             :                     {
    1317             :                         json_object *poCount =
    1318           2 :                             CPL_json_object_object_get(poBucket, "count");
    1319           4 :                         if (poCount &&
    1320           2 :                             json_object_get_type(poCount) == json_type_int)
    1321             :                         {
    1322           2 :                             nRes += json_object_get_int64(poCount);
    1323             :                         }
    1324             :                     }
    1325             :                 }
    1326           1 :                 if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
    1327           1 :                     m_nTotalFeatures = nRes;
    1328             : 
    1329           1 :                 json_object_put(poObj);
    1330           1 :                 return nRes;
    1331             :             }
    1332           0 :             json_object_put(poObj);
    1333             :         }
    1334             :     }
    1335             : 
    1336           1 :     m_bInFeatureCountOrGetExtent = true;
    1337           1 :     GIntBig nRes = OGRLayer::GetFeatureCount(bForce);
    1338           1 :     m_bInFeatureCountOrGetExtent = false;
    1339           1 :     return nRes;
    1340             : }
    1341             : 
    1342             : /************************************************************************/
    1343             : /*                             IGetExtent()                             */
    1344             : /************************************************************************/
    1345             : 
    1346           1 : OGRErr OGRPLScenesDataV1Layer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
    1347             :                                           bool bForce)
    1348             : {
    1349           1 :     if (m_poFilterGeom != nullptr)
    1350             :     {
    1351           0 :         m_bInFeatureCountOrGetExtent = true;
    1352           0 :         OGRErr eErr = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
    1353           0 :         m_bInFeatureCountOrGetExtent = false;
    1354           0 :         return eErr;
    1355             :     }
    1356             : 
    1357           1 :     psExtent->MinX = -180;
    1358           1 :     psExtent->MinY = -90;
    1359           1 :     psExtent->MaxX = 180;
    1360           1 :     psExtent->MaxY = 90;
    1361           1 :     return OGRERR_NONE;
    1362             : }
    1363             : 
    1364             : /************************************************************************/
    1365             : /*                              TestCapability()                        */
    1366             : /************************************************************************/
    1367             : 
    1368          11 : int OGRPLScenesDataV1Layer::TestCapability(const char *pszCap)
    1369             : {
    1370          11 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    1371           1 :         return !m_bFilterMustBeClientSideEvaluated;
    1372          10 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
    1373           9 :         return TRUE;
    1374           1 :     return FALSE;
    1375             : }

Generated by: LCOV version 1.14