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: 2024-11-21 22:18:42 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          51 : OGRFeatureDefn *OGRPLScenesDataV1Layer::GetLayerDefn()
      82             : {
      83          51 :     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             : /*                          SetSpatialFilter()                          */
     492             : /************************************************************************/
     493             : 
     494           2 : void OGRPLScenesDataV1Layer::SetSpatialFilter(OGRGeometry *poGeomIn)
     495             : {
     496           2 :     m_poFeatures = nullptr;
     497             : 
     498           2 :     if (poGeomIn)
     499             :     {
     500           1 :         OGREnvelope sEnvelope;
     501           1 :         poGeomIn->getEnvelope(&sEnvelope);
     502           1 :         if (sEnvelope.MinX == sEnvelope.MaxX &&
     503           1 :             sEnvelope.MinY == sEnvelope.MaxY)
     504             :         {
     505           2 :             OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
     506           2 :             InstallFilter(&p);
     507             :         }
     508             :         else
     509           0 :             InstallFilter(poGeomIn);
     510             :     }
     511             :     else
     512           1 :         InstallFilter(poGeomIn);
     513             : 
     514           2 :     ResetReading();
     515           2 : }
     516             : 
     517             : /************************************************************************/
     518             : /*                      OGRPLScenesDataV1ParseDateTime()                    */
     519             : /************************************************************************/
     520             : 
     521           4 : static bool OGRPLScenesDataV1ParseDateTime(const char *pszValue, int &nYear,
     522             :                                            int &nMonth, int &nDay, int &nHour,
     523             :                                            int &nMinute, int &nSecond)
     524             : {
     525           4 :     return (sscanf(pszValue, "%04d/%02d/%02d %02d:%02d:%02d", &nYear, &nMonth,
     526           4 :                    &nDay, &nHour, &nMinute, &nSecond) >= 3 ||
     527           0 :             sscanf(pszValue, "%04d-%02d-%02dT%02d:%02d:%02d", &nYear, &nMonth,
     528           4 :                    &nDay, &nHour, &nMinute, &nSecond) >= 3);
     529             : }
     530             : 
     531             : /************************************************************************/
     532             : /*                          IsSimpleComparison()                        */
     533             : /************************************************************************/
     534             : 
     535          26 : bool OGRPLScenesDataV1Layer::IsSimpleComparison(const swq_expr_node *poNode)
     536             : {
     537          52 :     return poNode->eNodeType == SNT_OPERATION &&
     538          26 :            (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_NE ||
     539          13 :             poNode->nOperation == SWQ_LT || poNode->nOperation == SWQ_LE ||
     540          10 :             poNode->nOperation == SWQ_GT || poNode->nOperation == SWQ_GE) &&
     541          23 :            poNode->nSubExprCount == 2 &&
     542          23 :            poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     543          75 :            poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
     544           0 :            m_oMapFieldIdxToQueryableJSonFieldName.find(
     545          23 :                poNode->papoSubExpr[0]->field_index) !=
     546          49 :                m_oMapFieldIdxToQueryableJSonFieldName.end();
     547             : }
     548             : 
     549             : /************************************************************************/
     550             : /*                             GetOperatorText()                        */
     551             : /************************************************************************/
     552             : 
     553           6 : static const char *GetOperatorText(swq_op nOp)
     554             : {
     555           6 :     if (nOp == SWQ_LT)
     556           2 :         return "lt";
     557           4 :     if (nOp == SWQ_LE)
     558           1 :         return "lte";
     559           3 :     if (nOp == SWQ_GT)
     560           2 :         return "gt";
     561           1 :     if (nOp == SWQ_GE)
     562           1 :         return "gte";
     563           0 :     CPLAssert(false);
     564             :     return "";
     565             : }
     566             : 
     567             : /************************************************************************/
     568             : /*                             BuildFilter()                            */
     569             : /************************************************************************/
     570             : 
     571          45 : json_object *OGRPLScenesDataV1Layer::BuildFilter(swq_expr_node *poNode)
     572             : {
     573          45 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
     574          15 :         poNode->nSubExprCount == 2)
     575             :     {
     576             :         // For AND, we can deal with a failure in one of the branch
     577             :         // since client-side will do that extra filtering
     578          15 :         json_object *poFilter1 = BuildFilter(poNode->papoSubExpr[0]);
     579          15 :         json_object *poFilter2 = BuildFilter(poNode->papoSubExpr[1]);
     580          15 :         if (poFilter1 && poFilter2)
     581             :         {
     582          13 :             json_object *poFilter = json_object_new_object();
     583          13 :             json_object_object_add(poFilter, "type",
     584             :                                    json_object_new_string("AndFilter"));
     585          13 :             json_object *poConfig = json_object_new_array();
     586          13 :             json_object_object_add(poFilter, "config", poConfig);
     587          13 :             json_object_array_add(poConfig, poFilter1);
     588          13 :             json_object_array_add(poConfig, poFilter2);
     589          13 :             return poFilter;
     590             :         }
     591           2 :         else if (poFilter1)
     592           1 :             return poFilter1;
     593             :         else
     594           1 :             return poFilter2;
     595             :     }
     596          30 :     else if (poNode->eNodeType == SNT_OPERATION &&
     597          30 :              poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
     598             :     {
     599             :         // For OR, we need both members to be valid
     600           2 :         json_object *poFilter1 = BuildFilter(poNode->papoSubExpr[0]);
     601           2 :         json_object *poFilter2 = BuildFilter(poNode->papoSubExpr[1]);
     602           2 :         if (poFilter1 && poFilter2)
     603             :         {
     604           1 :             json_object *poFilter = json_object_new_object();
     605           1 :             json_object_object_add(poFilter, "type",
     606             :                                    json_object_new_string("OrFilter"));
     607           1 :             json_object *poConfig = json_object_new_array();
     608           1 :             json_object_object_add(poFilter, "config", poConfig);
     609           1 :             json_object_array_add(poConfig, poFilter1);
     610           1 :             json_object_array_add(poConfig, poFilter2);
     611           1 :             return poFilter;
     612             :         }
     613             :         else
     614             :         {
     615           1 :             if (poFilter1)
     616           0 :                 json_object_put(poFilter1);
     617           1 :             if (poFilter2)
     618           1 :                 json_object_put(poFilter2);
     619           1 :             return nullptr;
     620             :         }
     621             :     }
     622          28 :     else if (poNode->eNodeType == SNT_OPERATION &&
     623          28 :              poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
     624             :     {
     625           2 :         json_object *poFilter1 = BuildFilter(poNode->papoSubExpr[0]);
     626           2 :         if (poFilter1)
     627             :         {
     628           1 :             json_object *poFilter = json_object_new_object();
     629           1 :             json_object_object_add(poFilter, "type",
     630             :                                    json_object_new_string("NotFilter"));
     631           1 :             json_object_object_add(poFilter, "config", poFilter1);
     632           1 :             return poFilter;
     633             :         }
     634             :         else
     635             :         {
     636           1 :             return nullptr;
     637             :         }
     638             :     }
     639          26 :     else if (IsSimpleComparison(poNode))
     640             :     {
     641          22 :         int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMinute = 0,
     642          22 :             nSecond = 0;
     643          22 :         const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
     644          22 :         if (poNode->nOperation == SWQ_NE)
     645             :         {
     646           1 :             poNode->nOperation = SWQ_EQ;
     647           1 :             json_object *poFilter1 = BuildFilter(poNode);
     648           1 :             poNode->nOperation = SWQ_NE;
     649           1 :             if (poFilter1)
     650             :             {
     651           1 :                 json_object *poFilter = json_object_new_object();
     652           1 :                 json_object_object_add(poFilter, "type",
     653             :                                        json_object_new_string("NotFilter"));
     654           1 :                 json_object_object_add(poFilter, "config", poFilter1);
     655          18 :                 return poFilter;
     656             :             }
     657             :             else
     658             :             {
     659           0 :                 return nullptr;
     660             :             }
     661             :         }
     662          11 :         else if (poNode->nOperation == SWQ_EQ &&
     663          11 :                  (m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     664          10 :                       OFTInteger ||
     665          10 :                   m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     666          32 :                       OFTReal) &&
     667           2 :                  (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER ||
     668           1 :                   poNode->papoSubExpr[1]->field_type == SWQ_FLOAT))
     669             :         {
     670           2 :             json_object *poFilter = json_object_new_object();
     671           2 :             if (m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() == OFTReal)
     672             :             {
     673           1 :                 json_object_object_add(poFilter, "type",
     674             :                                        json_object_new_string("RangeFilter"));
     675           1 :                 json_object_object_add(
     676             :                     poFilter, "field_name",
     677             :                     json_object_new_string(
     678           1 :                         m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     679           1 :                 json_object *poConfig = json_object_new_object();
     680           1 :                 const double EPS = 1e-8;
     681           1 :                 json_object_object_add(
     682             :                     poConfig, "gte",
     683           1 :                     (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER)
     684           0 :                         ? json_object_new_double(
     685           0 :                               poNode->papoSubExpr[1]->int_value - EPS)
     686           1 :                         : json_object_new_double(
     687           1 :                               poNode->papoSubExpr[1]->float_value - EPS));
     688           1 :                 json_object_object_add(
     689             :                     poConfig, "lte",
     690           1 :                     (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER)
     691           0 :                         ? json_object_new_double(
     692           0 :                               poNode->papoSubExpr[1]->int_value + EPS)
     693           1 :                         : json_object_new_double(
     694           1 :                               poNode->papoSubExpr[1]->float_value + EPS));
     695           1 :                 json_object_object_add(poFilter, "config", poConfig);
     696             :             }
     697             :             else
     698             :             {
     699           1 :                 json_object_object_add(
     700             :                     poFilter, "type", json_object_new_string("NumberInFilter"));
     701           1 :                 json_object_object_add(
     702             :                     poFilter, "field_name",
     703             :                     json_object_new_string(
     704           1 :                         m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     705           1 :                 json_object *poConfig = json_object_new_array();
     706           1 :                 json_object_array_add(
     707             :                     poConfig,
     708           1 :                     (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER)
     709           1 :                         ? json_object_new_int64(
     710           1 :                               poNode->papoSubExpr[1]->int_value)
     711           0 :                         : json_object_new_double(
     712           0 :                               poNode->papoSubExpr[1]->float_value));
     713           1 :                 json_object_object_add(poFilter, "config", poConfig);
     714             :             }
     715           2 :             return poFilter;
     716             :         }
     717           9 :         else if (poNode->nOperation == SWQ_EQ &&
     718           9 :                  m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     719          28 :                      OFTString &&
     720           9 :                  poNode->papoSubExpr[1]->field_type == SWQ_STRING)
     721             :         {
     722           9 :             json_object *poFilter = json_object_new_object();
     723           9 :             json_object_object_add(poFilter, "type",
     724             :                                    json_object_new_string("StringInFilter"));
     725           9 :             json_object_object_add(
     726             :                 poFilter, "field_name",
     727             :                 json_object_new_string(
     728           9 :                     m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     729           9 :             json_object *poConfig = json_object_new_array();
     730           9 :             json_object_array_add(
     731             :                 poConfig,
     732           9 :                 json_object_new_string(poNode->papoSubExpr[1]->string_value));
     733           9 :             json_object_object_add(poFilter, "config", poConfig);
     734           9 :             return poFilter;
     735             :         }
     736          28 :         else if ((poNode->nOperation == SWQ_LT ||
     737           8 :                   poNode->nOperation == SWQ_LE ||
     738           7 :                   poNode->nOperation == SWQ_GT ||
     739          11 :                   poNode->nOperation == SWQ_GE) &&
     740          10 :                  (m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     741           8 :                       OFTInteger ||
     742           8 :                   m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     743          20 :                       OFTReal) &&
     744           2 :                  (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER ||
     745           0 :                   poNode->papoSubExpr[1]->field_type == SWQ_FLOAT))
     746             :         {
     747           2 :             json_object *poFilter = json_object_new_object();
     748           2 :             json_object_object_add(poFilter, "type",
     749             :                                    json_object_new_string("RangeFilter"));
     750           2 :             json_object_object_add(
     751             :                 poFilter, "field_name",
     752             :                 json_object_new_string(
     753           2 :                     m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     754           2 :             json_object *poConfig = json_object_new_object();
     755           2 :             json_object_object_add(
     756             :                 poConfig, GetOperatorText(poNode->nOperation),
     757           2 :                 (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER)
     758           2 :                     ? json_object_new_int64(poNode->papoSubExpr[1]->int_value)
     759           0 :                     : json_object_new_double(
     760           0 :                           poNode->papoSubExpr[1]->float_value));
     761           2 :             json_object_object_add(poFilter, "config", poConfig);
     762           2 :             return poFilter;
     763             :         }
     764          23 :         else if ((poNode->nOperation == SWQ_LT ||
     765           7 :                   poNode->nOperation == SWQ_LE ||
     766           6 :                   poNode->nOperation == SWQ_GT ||
     767           9 :                   poNode->nOperation == SWQ_GE) &&
     768           8 :                  m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     769           4 :                      OFTDateTime &&
     770          20 :                  poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP &&
     771           4 :                  OGRPLScenesDataV1ParseDateTime(
     772           4 :                      poNode->papoSubExpr[1]->string_value, nYear, nMonth, nDay,
     773             :                      nHour, nMinute, nSecond))
     774             :         {
     775           4 :             json_object *poFilter = json_object_new_object();
     776           4 :             json_object_object_add(poFilter, "type",
     777             :                                    json_object_new_string("DateRangeFilter"));
     778           4 :             json_object_object_add(
     779             :                 poFilter, "field_name",
     780             :                 json_object_new_string(
     781           4 :                     m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     782           4 :             json_object *poConfig = json_object_new_object();
     783           4 :             json_object_object_add(poConfig,
     784             :                                    GetOperatorText(poNode->nOperation),
     785             :                                    json_object_new_string(CPLSPrintf(
     786             :                                        "%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
     787             :                                        nMonth, nDay, nHour, nMinute, nSecond)));
     788           4 :             json_object_object_add(poFilter, "config", poConfig);
     789           4 :             return poFilter;
     790             :         }
     791             :     }
     792          12 :     else if (poNode->eNodeType == SNT_OPERATION &&
     793           4 :              poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 &&
     794          11 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     795           0 :              m_oMapFieldIdxToQueryableJSonFieldName.find(
     796           3 :                  poNode->papoSubExpr[0]->field_index) !=
     797           7 :                  m_oMapFieldIdxToQueryableJSonFieldName.end())
     798             :     {
     799           2 :         const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
     800           2 :         if (m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() == OFTString)
     801             :         {
     802           1 :             json_object *poFilter = json_object_new_object();
     803           1 :             json_object_object_add(poFilter, "type",
     804             :                                    json_object_new_string("StringInFilter"));
     805           1 :             json_object_object_add(
     806             :                 poFilter, "field_name",
     807             :                 json_object_new_string(
     808           1 :                     m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     809           1 :             json_object *poConfig = json_object_new_array();
     810           1 :             json_object_object_add(poFilter, "config", poConfig);
     811           2 :             for (int i = 1; i < poNode->nSubExprCount; i++)
     812             :             {
     813           1 :                 if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT ||
     814           1 :                     poNode->papoSubExpr[i]->field_type != SWQ_STRING)
     815             :                 {
     816           0 :                     json_object_put(poFilter);
     817           0 :                     m_bFilterMustBeClientSideEvaluated = true;
     818           2 :                     return nullptr;
     819             :                 }
     820           1 :                 json_object_array_add(
     821             :                     poConfig, json_object_new_string(
     822           1 :                                   poNode->papoSubExpr[i]->string_value));
     823             :             }
     824           1 :             return poFilter;
     825             :         }
     826           1 :         else if (m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetType() ==
     827             :                  OFTInteger)
     828             :         {
     829           1 :             json_object *poFilter = json_object_new_object();
     830           1 :             json_object_object_add(poFilter, "type",
     831             :                                    json_object_new_string("NumberInFilter"));
     832           1 :             json_object_object_add(
     833             :                 poFilter, "field_name",
     834             :                 json_object_new_string(
     835           1 :                     m_oMapFieldIdxToQueryableJSonFieldName[nFieldIdx]));
     836           1 :             json_object *poConfig = json_object_new_array();
     837           1 :             json_object_object_add(poFilter, "config", poConfig);
     838           3 :             for (int i = 1; i < poNode->nSubExprCount; i++)
     839             :             {
     840           2 :                 if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT ||
     841           2 :                     poNode->papoSubExpr[i]->field_type != SWQ_INTEGER)
     842             :                 {
     843           0 :                     json_object_put(poFilter);
     844           0 :                     m_bFilterMustBeClientSideEvaluated = true;
     845           0 :                     return nullptr;
     846             :                 }
     847           2 :                 json_object_array_add(
     848             :                     poConfig,
     849           2 :                     json_object_new_int64(poNode->papoSubExpr[i]->int_value));
     850             :             }
     851           1 :             return poFilter;
     852             :         }
     853             :     }
     854           6 :     else if (poNode->eNodeType == SNT_OPERATION &&
     855           2 :              poNode->nOperation == SWQ_EQ && poNode->nSubExprCount == 2 &&
     856           1 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     857           1 :              poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
     858           1 :              poNode->papoSubExpr[0]->field_index ==
     859           5 :                  m_poFeatureDefn->GetFieldIndex("permissions") &&
     860           1 :              poNode->papoSubExpr[1]->field_type == SWQ_STRING)
     861             :     {
     862           1 :         json_object *poFilter = json_object_new_object();
     863           1 :         json_object_object_add(poFilter, "type",
     864             :                                json_object_new_string("PermissionFilter"));
     865           1 :         json_object *poConfig = json_object_new_array();
     866           1 :         json_object_object_add(poFilter, "config", poConfig);
     867           1 :         json_object_array_add(
     868             :             poConfig,
     869           1 :             json_object_new_string(poNode->papoSubExpr[1]->string_value));
     870           1 :         return poFilter;
     871             :     }
     872           3 :     else if (poNode->eNodeType == SNT_OPERATION &&
     873           1 :              poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 &&
     874           3 :              poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     875           1 :              poNode->papoSubExpr[0]->field_index ==
     876           1 :                  m_poFeatureDefn->GetFieldIndex("permissions"))
     877             :     {
     878           1 :         json_object *poFilter = json_object_new_object();
     879           1 :         json_object_object_add(poFilter, "type",
     880             :                                json_object_new_string("PermissionFilter"));
     881           1 :         json_object *poConfig = json_object_new_array();
     882           1 :         json_object_object_add(poFilter, "config", poConfig);
     883           2 :         for (int i = 1; i < poNode->nSubExprCount; i++)
     884             :         {
     885           1 :             if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT ||
     886           1 :                 poNode->papoSubExpr[i]->field_type != SWQ_STRING)
     887             :             {
     888           0 :                 json_object_put(poFilter);
     889           0 :                 m_bFilterMustBeClientSideEvaluated = true;
     890           0 :                 return nullptr;
     891             :             }
     892           1 :             json_object_array_add(
     893             :                 poConfig,
     894           1 :                 json_object_new_string(poNode->papoSubExpr[i]->string_value));
     895             :         }
     896           1 :         return poFilter;
     897             :     }
     898             : 
     899           4 :     m_bFilterMustBeClientSideEvaluated = true;
     900           4 :     return nullptr;
     901             : }
     902             : 
     903             : /************************************************************************/
     904             : /*                         SetAttributeFilter()                         */
     905             : /************************************************************************/
     906             : 
     907           9 : OGRErr OGRPLScenesDataV1Layer::SetAttributeFilter(const char *pszQuery)
     908             : 
     909             : {
     910           9 :     m_poFeatures = nullptr;
     911             : 
     912           9 :     OGRErr eErr = OGRLayer::SetAttributeFilter(pszQuery);
     913             : 
     914           9 :     if (m_poAttributeFilter)
     915           4 :         json_object_put(m_poAttributeFilter);
     916           9 :     m_poAttributeFilter = nullptr;
     917           9 :     m_bFilterMustBeClientSideEvaluated = false;
     918           9 :     if (m_poAttrQuery != nullptr)
     919             :     {
     920           8 :         swq_expr_node *poNode = (swq_expr_node *)m_poAttrQuery->GetSWQExpr();
     921             : 
     922           8 :         poNode->ReplaceBetweenByGEAndLERecurse();
     923             : 
     924           8 :         m_poAttributeFilter = BuildFilter(poNode);
     925           8 :         if (m_poAttributeFilter == nullptr)
     926             :         {
     927           2 :             CPLDebug("PLSCENES",
     928             :                      "Full filter will be evaluated on client side.");
     929             :         }
     930           6 :         else if (m_bFilterMustBeClientSideEvaluated)
     931             :         {
     932           2 :             CPLDebug(
     933             :                 "PLSCENES",
     934             :                 "Only part of the filter will be evaluated on server side.");
     935             :         }
     936             :     }
     937             : 
     938           9 :     ResetReading();
     939             : 
     940           9 :     return eErr;
     941             : }
     942             : 
     943             : /************************************************************************/
     944             : /*                           GetNextFeature()                           */
     945             : /************************************************************************/
     946             : 
     947          23 : OGRFeature *OGRPLScenesDataV1Layer::GetNextFeature()
     948             : {
     949             :     while (true)
     950             :     {
     951          23 :         OGRFeature *poFeature = GetNextRawFeature();
     952          23 :         if (poFeature == nullptr)
     953           7 :             return nullptr;
     954             : 
     955          20 :         if (m_poAttrQuery == nullptr || !m_bFilterMustBeClientSideEvaluated ||
     956           4 :             m_poAttrQuery->Evaluate(poFeature))
     957             :         {
     958          16 :             return poFeature;
     959             :         }
     960             :         else
     961             :         {
     962           0 :             delete poFeature;
     963             :         }
     964           0 :     }
     965             : }
     966             : 
     967             : /************************************************************************/
     968             : /*                            GetNextRawFeature()                       */
     969             : /************************************************************************/
     970             : 
     971          23 : OGRFeature *OGRPLScenesDataV1Layer::GetNextRawFeature()
     972             : {
     973          23 :     EstablishLayerDefn();
     974          23 :     if (m_bEOF)
     975           1 :         return nullptr;
     976             : 
     977          22 :     if (m_poFeatures == nullptr)
     978             :     {
     979          16 :         if (!GetNextPage())
     980           3 :             return nullptr;
     981             :     }
     982             : 
     983          38 :     if (m_nFeatureIdx ==
     984          19 :         static_cast<int>(json_object_array_length(m_poFeatures)))
     985             :     {
     986           4 :         m_osRequestURL = m_osNextURL;
     987           4 :         m_bStillInFirstPage = false;
     988           4 :         if (!GetNextPage())
     989           2 :             return nullptr;
     990             :     }
     991             :     json_object *poJSonFeature =
     992          17 :         json_object_array_get_idx(m_poFeatures, m_nFeatureIdx);
     993          17 :     m_nFeatureIdx++;
     994          33 :     if (poJSonFeature == nullptr ||
     995          16 :         json_object_get_type(poJSonFeature) != json_type_object)
     996             :     {
     997           1 :         m_bEOF = true;
     998           1 :         return nullptr;
     999             :     }
    1000             : 
    1001          16 :     OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
    1002          16 :     poFeature->SetFID(m_nNextFID++);
    1003             : 
    1004             :     json_object *poJSonGeom =
    1005          16 :         CPL_json_object_object_get(poJSonFeature, "geometry");
    1006          26 :     if (poJSonGeom != nullptr &&
    1007          10 :         json_object_get_type(poJSonGeom) == json_type_object)
    1008             :     {
    1009          10 :         OGRGeometry *poGeom = OGRGeoJSONReadGeometry(poJSonGeom);
    1010          10 :         if (poGeom != nullptr)
    1011             :         {
    1012          10 :             if (poGeom->getGeometryType() == wkbPolygon)
    1013             :             {
    1014           8 :                 OGRMultiPolygon *poMP = new OGRMultiPolygon();
    1015           8 :                 poMP->addGeometryDirectly(poGeom);
    1016           8 :                 poGeom = poMP;
    1017             :             }
    1018          10 :             poGeom->assignSpatialReference(m_poSRS);
    1019          10 :             poFeature->SetGeometryDirectly(poGeom);
    1020             :         }
    1021             :     }
    1022             : 
    1023          16 :     json_object *poId = CPL_json_object_object_get(poJSonFeature, "id");
    1024          16 :     if (poId != nullptr && json_object_get_type(poId) == json_type_string)
    1025             :     {
    1026             :         std::map<CPLString, int>::const_iterator oIter =
    1027          16 :             m_oMapPrefixedJSonFieldNameToFieldIdx.find("id");
    1028          16 :         if (oIter != m_oMapPrefixedJSonFieldNameToFieldIdx.end())
    1029             :         {
    1030          16 :             const int iField = oIter->second;
    1031          16 :             poFeature->SetField(iField, json_object_get_string(poId));
    1032             :         }
    1033             :     }
    1034             : 
    1035             :     json_object *poPermissions =
    1036          16 :         CPL_json_object_object_get(poJSonFeature, "_permissions");
    1037          24 :     if (poPermissions != nullptr &&
    1038           8 :         json_object_get_type(poPermissions) == json_type_array)
    1039             :     {
    1040             :         std::map<CPLString, int>::const_iterator oIter =
    1041           8 :             m_oMapPrefixedJSonFieldNameToFieldIdx.find("_permissions");
    1042           8 :         if (oIter != m_oMapPrefixedJSonFieldNameToFieldIdx.end())
    1043             :         {
    1044           8 :             const int iField = oIter->second;
    1045           8 :             const auto nStrings = json_object_array_length(poPermissions);
    1046             :             char **papszPermissions =
    1047           8 :                 static_cast<char **>(CPLCalloc(nStrings + 1, sizeof(char *)));
    1048          16 :             for (auto i = decltype(nStrings){0}, j = decltype(nStrings){0};
    1049          16 :                  i < nStrings; i++)
    1050             :             {
    1051             :                 json_object *poPerm =
    1052           8 :                     json_object_array_get_idx(poPermissions, i);
    1053           8 :                 if (poPerm && json_object_get_type(poPerm) == json_type_string)
    1054             :                 {
    1055           8 :                     papszPermissions[j++] =
    1056           8 :                         CPLStrdup(json_object_get_string(poPerm));
    1057             :                 }
    1058             :             }
    1059           8 :             poFeature->SetField(iField, papszPermissions);
    1060           8 :             CSLDestroy(papszPermissions);
    1061             :         }
    1062             :     }
    1063             : 
    1064          48 :     for (int i = 0; i < 2; i++)
    1065             :     {
    1066          32 :         const char *pszFeaturePart = (i == 0) ? "properties" : "_links";
    1067             :         json_object *poProperties =
    1068          32 :             CPL_json_object_object_get(poJSonFeature, pszFeaturePart);
    1069          52 :         if (poProperties != nullptr &&
    1070          20 :             json_object_get_type(poProperties) == json_type_object)
    1071             :         {
    1072             :             json_object_iter it;
    1073          20 :             it.key = nullptr;
    1074          20 :             it.val = nullptr;
    1075          20 :             it.entry = nullptr;
    1076          76 :             json_object_object_foreachC(poProperties, it)
    1077             :             {
    1078         112 :                 CPLString osPrefixedJSonFieldName(pszFeaturePart);
    1079          56 :                 osPrefixedJSonFieldName += ".";
    1080          56 :                 osPrefixedJSonFieldName += it.key;
    1081          56 :                 if (!SetFieldFromPrefixedJSonFieldName(
    1082             :                         poFeature, osPrefixedJSonFieldName, it.val))
    1083             :                 {
    1084           0 :                     if (i == 0 && m_oSetUnregisteredFields.find(
    1085           0 :                                       osPrefixedJSonFieldName) ==
    1086           0 :                                       m_oSetUnregisteredFields.end())
    1087             :                     {
    1088           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1089             :                                  "Field %s found in data but not "
    1090             :                                  "in configuration",
    1091             :                                  osPrefixedJSonFieldName.c_str());
    1092             :                         m_oSetUnregisteredFields.insert(
    1093           0 :                             osPrefixedJSonFieldName);
    1094             :                     }
    1095             :                 }
    1096             :             }
    1097             :         }
    1098             :     }
    1099             : 
    1100          16 :     json_object *poAssets = nullptr;
    1101          31 :     if (m_poDS->DoesFollowLinks() &&
    1102          15 :         (!m_bInFeatureCountOrGetExtent || m_poAttrQuery != nullptr))
    1103             :     {
    1104             :         std::map<CPLString, int>::const_iterator oIter =
    1105          14 :             m_oMapPrefixedJSonFieldNameToFieldIdx.find("_links.assets");
    1106          14 :         if (oIter != m_oMapPrefixedJSonFieldNameToFieldIdx.end())
    1107             :         {
    1108          14 :             const int iField = oIter->second;
    1109          14 :             if (poFeature->IsFieldSetAndNotNull(iField))
    1110             :             {
    1111           8 :                 const char *pszAssetURL = poFeature->GetFieldAsString(iField);
    1112           8 :                 poAssets = m_poDS->RunRequest(pszAssetURL);
    1113             :             }
    1114             :         }
    1115             :     }
    1116          16 :     if (poAssets != nullptr)
    1117             :     {
    1118             :         json_object_iter itAsset;
    1119           8 :         itAsset.key = nullptr;
    1120           8 :         itAsset.val = nullptr;
    1121           8 :         itAsset.entry = nullptr;
    1122          16 :         json_object_object_foreachC(poAssets, itAsset)
    1123             :         {
    1124           8 :             if (m_oSetAssets.find(itAsset.key) == m_oSetAssets.end())
    1125             :             {
    1126           0 :                 if (m_oSetUnregisteredAssets.find(itAsset.key) ==
    1127           0 :                     m_oSetUnregisteredAssets.end())
    1128             :                 {
    1129           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1130             :                              "Asset %s found in data but "
    1131             :                              "not in configuration",
    1132             :                              itAsset.key);
    1133           0 :                     m_oSetUnregisteredAssets.insert(itAsset.key);
    1134             :                 }
    1135           0 :                 continue;
    1136             :             }
    1137             : 
    1138           8 :             json_object *poAsset = itAsset.val;
    1139          16 :             if (poAsset != nullptr &&
    1140           8 :                 json_object_get_type(poAsset) == json_type_object)
    1141             :             {
    1142             :                 json_object_iter it;
    1143           8 :                 it.key = nullptr;
    1144           8 :                 it.val = nullptr;
    1145           8 :                 it.entry = nullptr;
    1146          48 :                 json_object_object_foreachC(poAsset, it)
    1147             :                 {
    1148          40 :                     if (it.val == nullptr)
    1149           0 :                         continue;
    1150          80 :                     CPLString osPrefixedJSonFieldName("/assets." +
    1151         120 :                                                       CPLString(itAsset.key));
    1152          40 :                     osPrefixedJSonFieldName += "." + CPLString(it.key);
    1153          48 :                     if (strcmp(it.key, "_links") == 0 &&
    1154           8 :                         json_object_get_type(it.val) == json_type_object)
    1155             :                     {
    1156           8 :                         if (CPL_json_object_object_get(it.val, "_self") !=
    1157             :                             nullptr)
    1158             :                         {
    1159             :                             CPLString osPrefixedJSonFieldNameNew(
    1160          16 :                                 osPrefixedJSonFieldName + "._self");
    1161           8 :                             SetFieldFromPrefixedJSonFieldName(
    1162             :                                 poFeature, osPrefixedJSonFieldNameNew,
    1163             :                                 CPL_json_object_object_get(it.val, "_self"));
    1164             :                         }
    1165           8 :                         if (CPL_json_object_object_get(it.val, "activate") !=
    1166             :                             nullptr)
    1167             :                         {
    1168             :                             CPLString osPrefixedJSonFieldNameNew(
    1169          16 :                                 osPrefixedJSonFieldName + ".activate");
    1170           8 :                             SetFieldFromPrefixedJSonFieldName(
    1171             :                                 poFeature, osPrefixedJSonFieldNameNew,
    1172             :                                 CPL_json_object_object_get(it.val, "activate"));
    1173             :                         }
    1174             :                     }
    1175             :                     else
    1176             :                     {
    1177          32 :                         SetFieldFromPrefixedJSonFieldName(
    1178             :                             poFeature, osPrefixedJSonFieldName, it.val);
    1179             :                     }
    1180             :                 }
    1181             :             }
    1182             :         }
    1183           8 :         json_object_put(poAssets);
    1184             :     }
    1185             : 
    1186          16 :     return poFeature;
    1187             : }
    1188             : 
    1189             : /************************************************************************/
    1190             : /*                    SetFieldFromPrefixedJSonFieldName()               */
    1191             : /************************************************************************/
    1192             : 
    1193         104 : bool OGRPLScenesDataV1Layer::SetFieldFromPrefixedJSonFieldName(
    1194             :     OGRFeature *poFeature, const CPLString &osPrefixedJSonFieldName,
    1195             :     json_object *poVal)
    1196             : {
    1197             :     std::map<CPLString, int>::const_iterator oIter =
    1198         104 :         m_oMapPrefixedJSonFieldNameToFieldIdx.find(osPrefixedJSonFieldName);
    1199         208 :     if (poVal != nullptr &&
    1200         208 :         oIter != m_oMapPrefixedJSonFieldNameToFieldIdx.end())
    1201             :     {
    1202         104 :         const int iField = oIter->second;
    1203         104 :         json_type eJSonType = json_object_get_type(poVal);
    1204         104 :         if (eJSonType == json_type_int)
    1205             :         {
    1206           8 :             poFeature->SetField(
    1207           8 :                 iField, static_cast<GIntBig>(json_object_get_int64(poVal)));
    1208             :         }
    1209          96 :         else if (eJSonType == json_type_double)
    1210             :         {
    1211           9 :             poFeature->SetField(iField, json_object_get_double(poVal));
    1212             :         }
    1213          87 :         else if (eJSonType == json_type_string)
    1214             :         {
    1215          72 :             poFeature->SetField(iField, json_object_get_string(poVal));
    1216             :         }
    1217          15 :         else if (eJSonType == json_type_boolean)
    1218             :         {
    1219           7 :             poFeature->SetField(iField, json_object_get_boolean(poVal));
    1220             :         }
    1221             :         else
    1222             :         {
    1223           8 :             poFeature->SetField(iField,
    1224             :                                 json_object_to_json_string_ext(poVal, 0));
    1225             :         }
    1226         104 :         return true;
    1227             :     }
    1228           0 :     return false;
    1229             : }
    1230             : 
    1231             : /************************************************************************/
    1232             : /*                          GetFeatureCount()                           */
    1233             : /************************************************************************/
    1234             : 
    1235           2 : GIntBig OGRPLScenesDataV1Layer::GetFeatureCount(int bForce)
    1236             : {
    1237           2 :     if (m_poDS->GetFilter().empty())
    1238             :     {
    1239           2 :         if (m_nTotalFeatures >= 0 && m_poFilterGeom == nullptr &&
    1240           0 :             m_poAttrQuery == nullptr)
    1241             :         {
    1242           1 :             return m_nTotalFeatures;
    1243             :         }
    1244             : 
    1245           2 :         json_object *poFilterRoot = json_object_new_object();
    1246           2 :         json_object *poItemTypes = json_object_new_array();
    1247           2 :         json_object_array_add(poItemTypes, json_object_new_string(GetName()));
    1248           2 :         json_object_object_add(poFilterRoot, "interval",
    1249             :                                json_object_new_string("year"));
    1250           2 :         json_object_object_add(poFilterRoot, "item_types", poItemTypes);
    1251           2 :         json_object *poFilter = json_object_new_object();
    1252           2 :         json_object_object_add(poFilterRoot, "filter", poFilter);
    1253           2 :         json_object_object_add(poFilter, "type",
    1254             :                                json_object_new_string("AndFilter"));
    1255           2 :         json_object *poConfig = json_object_new_array();
    1256           2 :         json_object_object_add(poFilter, "config", poConfig);
    1257             : 
    1258             :         // We need to put a dummy filter
    1259           2 :         if (m_poFilterGeom == nullptr && m_poAttributeFilter == nullptr)
    1260             :         {
    1261           1 :             json_object *poRangeFilter = json_object_new_object();
    1262           1 :             json_object_array_add(poConfig, poRangeFilter);
    1263           1 :             json_object_object_add(poRangeFilter, "type",
    1264             :                                    json_object_new_string("RangeFilter"));
    1265           1 :             json_object_object_add(poRangeFilter, "field_name",
    1266             :                                    json_object_new_string("cloud_cover"));
    1267           1 :             json_object *poRangeFilterConfig = json_object_new_object();
    1268           1 :             json_object_object_add(poRangeFilterConfig, "gte",
    1269             :                                    json_object_new_double(0.0));
    1270           1 :             json_object_object_add(poRangeFilter, "config",
    1271             :                                    poRangeFilterConfig);
    1272             :         }
    1273             : 
    1274           2 :         if (m_poFilterGeom != nullptr)
    1275             :         {
    1276           1 :             json_object *poGeomFilter = json_object_new_object();
    1277           1 :             json_object_array_add(poConfig, poGeomFilter);
    1278           1 :             json_object_object_add(poGeomFilter, "type",
    1279             :                                    json_object_new_string("GeometryFilter"));
    1280           1 :             json_object_object_add(poGeomFilter, "field_name",
    1281             :                                    json_object_new_string("geometry"));
    1282           2 :             OGRGeoJSONWriteOptions oOptions;
    1283             :             json_object *poGeoJSONGeom =
    1284           1 :                 OGRGeoJSONWriteGeometry(m_poFilterGeom, oOptions);
    1285           1 :             json_object_object_add(poGeomFilter, "config", poGeoJSONGeom);
    1286             :         }
    1287           2 :         if (m_poAttributeFilter != nullptr)
    1288             :         {
    1289           0 :             json_object_get(m_poAttributeFilter);
    1290           0 :             json_object_array_add(poConfig, m_poAttributeFilter);
    1291             :         }
    1292             : 
    1293           2 :         CPLString osFilter = json_object_to_json_string_ext(poFilterRoot, 0);
    1294           2 :         json_object_put(poFilterRoot);
    1295             : 
    1296             :         json_object *poObj =
    1297           2 :             m_poDS->RunRequest((m_poDS->GetBaseURL() + "stats").c_str(), FALSE,
    1298             :                                "POST", true, osFilter);
    1299           2 :         if (poObj != nullptr)
    1300             :         {
    1301             :             json_object *poBuckets =
    1302           1 :                 CPL_json_object_object_get(poObj, "buckets");
    1303           1 :             if (poBuckets && json_object_get_type(poBuckets) == json_type_array)
    1304             :             {
    1305           1 :                 GIntBig nRes = 0;
    1306           1 :                 const auto nBuckets = json_object_array_length(poBuckets);
    1307           3 :                 for (auto i = decltype(nBuckets){0}; i < nBuckets; i++)
    1308             :                 {
    1309             :                     json_object *poBucket =
    1310           2 :                         json_object_array_get_idx(poBuckets, i);
    1311           4 :                     if (poBucket &&
    1312           2 :                         json_object_get_type(poBucket) == json_type_object)
    1313             :                     {
    1314             :                         json_object *poCount =
    1315           2 :                             CPL_json_object_object_get(poBucket, "count");
    1316           4 :                         if (poCount &&
    1317           2 :                             json_object_get_type(poCount) == json_type_int)
    1318             :                         {
    1319           2 :                             nRes += json_object_get_int64(poCount);
    1320             :                         }
    1321             :                     }
    1322             :                 }
    1323           1 :                 if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
    1324           1 :                     m_nTotalFeatures = nRes;
    1325             : 
    1326           1 :                 json_object_put(poObj);
    1327           1 :                 return nRes;
    1328             :             }
    1329           0 :             json_object_put(poObj);
    1330             :         }
    1331             :     }
    1332             : 
    1333           1 :     m_bInFeatureCountOrGetExtent = true;
    1334           1 :     GIntBig nRes = OGRLayer::GetFeatureCount(bForce);
    1335           1 :     m_bInFeatureCountOrGetExtent = false;
    1336           1 :     return nRes;
    1337             : }
    1338             : 
    1339             : /************************************************************************/
    1340             : /*                                GetExtent()                           */
    1341             : /************************************************************************/
    1342             : 
    1343           1 : OGRErr OGRPLScenesDataV1Layer::GetExtent(OGREnvelope *psExtent, int bForce)
    1344             : {
    1345           1 :     if (m_poFilterGeom != nullptr)
    1346             :     {
    1347           0 :         m_bInFeatureCountOrGetExtent = true;
    1348           0 :         OGRErr eErr = OGRLayer::GetExtentInternal(0, psExtent, bForce);
    1349           0 :         m_bInFeatureCountOrGetExtent = false;
    1350           0 :         return eErr;
    1351             :     }
    1352             : 
    1353           1 :     psExtent->MinX = -180;
    1354           1 :     psExtent->MinY = -90;
    1355           1 :     psExtent->MaxX = 180;
    1356           1 :     psExtent->MaxY = 90;
    1357           1 :     return OGRERR_NONE;
    1358             : }
    1359             : 
    1360             : /************************************************************************/
    1361             : /*                              TestCapability()                        */
    1362             : /************************************************************************/
    1363             : 
    1364          11 : int OGRPLScenesDataV1Layer::TestCapability(const char *pszCap)
    1365             : {
    1366          11 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    1367           1 :         return !m_bFilterMustBeClientSideEvaluated;
    1368          10 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
    1369           9 :         return TRUE;
    1370           1 :     return FALSE;
    1371             : }

Generated by: LCOV version 1.14