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