Coverage Report

Created: 2026-08-11 08:26

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