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

Generated by: LCOV version 1.14