Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Earth Engine Data API driver
4 : * Purpose: Earth Engine Data API driver
5 : * Author: Even Rouault, even dot rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2017-2018, Planet Labs
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "gdal_priv.h"
30 : #include "cpl_http.h"
31 : #include "cpl_conv.h"
32 : #include "ogrgeojsonreader.h"
33 : #include "ogrgeojsonwriter.h"
34 : #include "ogr_swq.h"
35 : #include "eeda.h"
36 :
37 : #include <algorithm>
38 : #include <vector>
39 : #include <map>
40 : #include <set>
41 : #include <limits>
42 :
43 : extern "C" void GDALRegister_EEDA();
44 :
45 : /************************************************************************/
46 : /* CPLEscapeURLQueryParameter() */
47 : /************************************************************************/
48 :
49 9 : static CPLString CPLEscapeURLQueryParameter(const char *pszInput)
50 : {
51 9 : int nLength = static_cast<int>(strlen(pszInput));
52 :
53 9 : const size_t nSizeAlloc = nLength * 4 + 1;
54 9 : char *pszOutput = static_cast<char *>(CPLMalloc(nSizeAlloc));
55 9 : int iOut = 0;
56 :
57 506 : for (int iIn = 0; iIn < nLength; ++iIn)
58 : {
59 497 : if ((pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') ||
60 357 : (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') ||
61 317 : (pszInput[iIn] >= '0' && pszInput[iIn] <= '9'))
62 : {
63 310 : pszOutput[iOut++] = pszInput[iIn];
64 : }
65 : else
66 : {
67 187 : snprintf(pszOutput + iOut, nSizeAlloc - iOut, "%%%02X",
68 187 : static_cast<unsigned char>(pszInput[iIn]));
69 187 : iOut += 3;
70 : }
71 : }
72 9 : pszOutput[iOut] = '\0';
73 :
74 9 : CPLString osRet(pszOutput);
75 9 : CPLFree(pszOutput);
76 9 : return osRet;
77 : }
78 :
79 : /************************************************************************/
80 : /* GDALEEDADataset */
81 : /************************************************************************/
82 :
83 : class GDALEEDALayer;
84 :
85 : class GDALEEDADataset final : public GDALEEDABaseDataset
86 : {
87 : CPL_DISALLOW_COPY_ASSIGN(GDALEEDADataset)
88 :
89 : GDALEEDALayer *m_poLayer;
90 :
91 : public:
92 : GDALEEDADataset();
93 : virtual ~GDALEEDADataset();
94 :
95 0 : virtual int GetLayerCount() CPL_OVERRIDE
96 : {
97 0 : return m_poLayer ? 1 : 0;
98 : }
99 :
100 : virtual OGRLayer *GetLayer(int idx) CPL_OVERRIDE;
101 :
102 : bool Open(GDALOpenInfo *poOpenInfo);
103 : json_object *RunRequest(const CPLString &osURL);
104 :
105 7 : const CPLString &GetBaseURL() const
106 : {
107 7 : return m_osBaseURL;
108 : }
109 : };
110 :
111 : /************************************************************************/
112 : /* GDALEEDALayer */
113 : /************************************************************************/
114 :
115 : class GDALEEDALayer final : public OGRLayer
116 : {
117 : CPL_DISALLOW_COPY_ASSIGN(GDALEEDALayer)
118 :
119 : GDALEEDADataset *m_poDS;
120 : CPLString m_osCollection{};
121 : CPLString m_osCollectionName{};
122 : OGRFeatureDefn *m_poFeatureDefn;
123 : json_object *m_poCurPageObj;
124 : json_object *m_poCurPageAssets;
125 : int m_nIndexInPage;
126 : GIntBig m_nFID;
127 : CPLString m_osAttributeFilter{};
128 : CPLString m_osStartTime{};
129 : CPLString m_osEndTime{};
130 : bool m_bFilterMustBeClientSideEvaluated;
131 : std::set<int> m_oSetQueryableFields{};
132 : std::map<CPLString, CPLString> m_oMapCodeToWKT{};
133 :
134 : OGRFeature *GetNextRawFeature();
135 : bool IsSimpleComparison(const swq_expr_node *poNode);
136 : CPLString BuildFilter(swq_expr_node *poNode, bool bIsAndTopLevel);
137 :
138 : public:
139 : GDALEEDALayer(GDALEEDADataset *poDS, const CPLString &osCollection,
140 : const CPLString &osCollectionName, json_object *poAsset,
141 : json_object *poLayerConf);
142 : virtual ~GDALEEDALayer();
143 :
144 : virtual void ResetReading() CPL_OVERRIDE;
145 : virtual OGRFeature *GetNextFeature() CPL_OVERRIDE;
146 : virtual int TestCapability(const char *) CPL_OVERRIDE;
147 :
148 5 : virtual OGRFeatureDefn *GetLayerDefn() CPL_OVERRIDE
149 : {
150 5 : return m_poFeatureDefn;
151 : }
152 :
153 1 : virtual GIntBig GetFeatureCount(int) CPL_OVERRIDE
154 : {
155 1 : return -1;
156 : }
157 :
158 : virtual void SetSpatialFilter(OGRGeometry *poGeom) CPL_OVERRIDE;
159 :
160 0 : virtual void SetSpatialFilter(int iGeomField,
161 : OGRGeometry *poGeom) CPL_OVERRIDE
162 : {
163 0 : OGRLayer::SetSpatialFilter(iGeomField, poGeom);
164 0 : }
165 :
166 : virtual OGRErr SetAttributeFilter(const char *) CPL_OVERRIDE;
167 :
168 : virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce) CPL_OVERRIDE;
169 :
170 1 : virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent,
171 : int bForce) CPL_OVERRIDE
172 : {
173 1 : return OGRLayer::GetExtent(iGeomField, psExtent, bForce);
174 : }
175 : };
176 :
177 : /************************************************************************/
178 : /* GDALEEDALayer() */
179 : /************************************************************************/
180 :
181 7 : GDALEEDALayer::GDALEEDALayer(GDALEEDADataset *poDS,
182 : const CPLString &osCollection,
183 : const CPLString &osCollectionName,
184 7 : json_object *poAsset, json_object *poLayerConf)
185 : : m_poDS(poDS), m_osCollection(osCollection),
186 : m_osCollectionName(osCollectionName), m_poFeatureDefn(nullptr),
187 : m_poCurPageObj(nullptr), m_poCurPageAssets(nullptr), m_nIndexInPage(0),
188 7 : m_nFID(1), m_bFilterMustBeClientSideEvaluated(true)
189 : {
190 7 : CPLString osLaundered(osCollection);
191 126 : for (size_t i = 0; i < osLaundered.size(); i++)
192 : {
193 119 : if (!isalnum(static_cast<unsigned char>(osLaundered[i])))
194 : {
195 13 : osLaundered[i] = '_';
196 : }
197 : }
198 7 : SetDescription(osLaundered);
199 7 : m_poFeatureDefn = new OGRFeatureDefn(osLaundered);
200 7 : m_poFeatureDefn->Reference();
201 7 : m_poFeatureDefn->SetGeomType(wkbMultiPolygon);
202 7 : OGRSpatialReference *poSRS = new OGRSpatialReference();
203 7 : poSRS->SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
204 7 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
205 7 : poSRS->Release();
206 :
207 : {
208 14 : OGRFieldDefn oFieldDefn("name", OFTString);
209 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
210 : }
211 : {
212 14 : OGRFieldDefn oFieldDefn("id", OFTString);
213 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
214 : }
215 : {
216 14 : OGRFieldDefn oFieldDefn("gdal_dataset", OFTString);
217 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
218 : }
219 : {
220 14 : OGRFieldDefn oFieldDefn("updateTime", OFTDateTime);
221 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
222 : }
223 : {
224 14 : OGRFieldDefn oFieldDefn("startTime", OFTDateTime);
225 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
226 : }
227 : {
228 14 : OGRFieldDefn oFieldDefn("endTime", OFTDateTime);
229 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
230 : }
231 : {
232 14 : OGRFieldDefn oFieldDefn("sizeBytes", OFTInteger64);
233 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
234 : }
235 : {
236 14 : OGRFieldDefn oFieldDefn("band_count", OFTInteger);
237 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
238 : }
239 : {
240 14 : OGRFieldDefn oFieldDefn("band_max_width", OFTInteger);
241 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
242 : }
243 : {
244 14 : OGRFieldDefn oFieldDefn("band_max_height", OFTInteger);
245 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
246 : }
247 : {
248 14 : OGRFieldDefn oFieldDefn("band_min_pixel_size", OFTReal);
249 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
250 : }
251 : {
252 14 : OGRFieldDefn oFieldDefn("band_upper_left_x", OFTReal);
253 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
254 : }
255 : {
256 14 : OGRFieldDefn oFieldDefn("band_upper_left_y", OFTReal);
257 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
258 : }
259 : {
260 14 : OGRFieldDefn oFieldDefn("band_crs", OFTString);
261 7 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
262 : }
263 :
264 7 : if (poLayerConf)
265 : {
266 : json_object *poFields =
267 1 : CPL_json_object_object_get(poLayerConf, "fields");
268 2 : if (poFields == nullptr ||
269 1 : json_object_get_type(poFields) != json_type_array)
270 : {
271 0 : CPLError(CE_Failure, CPLE_AppDefined,
272 : "Cannot find %s.fields object in eedaconf.json",
273 : GetDescription());
274 0 : return;
275 : }
276 :
277 1 : const auto nFields = json_object_array_length(poFields);
278 5 : for (auto i = decltype(nFields){0}; i < nFields; i++)
279 : {
280 4 : json_object *poField = json_object_array_get_idx(poFields, i);
281 4 : if (poField && json_object_get_type(poField) == json_type_object)
282 : {
283 : json_object *poName =
284 4 : CPL_json_object_object_get(poField, "name");
285 : json_object *poType =
286 4 : CPL_json_object_object_get(poField, "type");
287 8 : if (poName &&
288 4 : json_object_get_type(poName) == json_type_string &&
289 8 : poType && json_object_get_type(poType) == json_type_string)
290 : {
291 4 : const char *pszName = json_object_get_string(poName);
292 4 : const char *pszType = json_object_get_string(poType);
293 4 : OGRFieldType eType(OFTString);
294 4 : if (EQUAL(pszType, "datetime"))
295 0 : eType = OFTDateTime;
296 4 : else if (EQUAL(pszType, "double"))
297 1 : eType = OFTReal;
298 3 : else if (EQUAL(pszType, "int"))
299 1 : eType = OFTInteger;
300 2 : else if (EQUAL(pszType, "int64"))
301 2 : eType = OFTInteger64;
302 0 : else if (EQUAL(pszType, "string"))
303 0 : eType = OFTString;
304 : else
305 : {
306 0 : CPLError(CE_Warning, CPLE_AppDefined,
307 : "Unrecognized field type %s for field %s",
308 : pszType, pszName);
309 : }
310 4 : OGRFieldDefn oFieldDefn(pszName, eType);
311 4 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
312 : m_oSetQueryableFields.insert(
313 4 : m_poFeatureDefn->GetFieldCount() - 1);
314 : }
315 : }
316 : }
317 :
318 1 : json_object *poAddOtherProp = CPL_json_object_object_get(
319 : poLayerConf, "add_other_properties_field");
320 1 : if (json_object_get_boolean(poAddOtherProp))
321 : {
322 2 : OGRFieldDefn oFieldDefn("other_properties", OFTString);
323 1 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
324 : }
325 : }
326 : else
327 : {
328 : json_object *poProperties =
329 6 : CPL_json_object_object_get(poAsset, "properties");
330 7 : if (poProperties != nullptr &&
331 1 : json_object_get_type(poProperties) == json_type_object)
332 : {
333 : json_object_iter it;
334 1 : it.key = nullptr;
335 1 : it.val = nullptr;
336 1 : it.entry = nullptr;
337 5 : json_object_object_foreachC(poProperties, it)
338 : {
339 4 : OGRFieldType eType(OFTString);
340 4 : if (it.val)
341 : {
342 4 : if (json_object_get_type(it.val) == json_type_int)
343 : {
344 2 : if (strstr(it.key, "PERCENTAGE"))
345 0 : eType = OFTReal;
346 2 : else if (CPLAtoGIntBig(json_object_get_string(it.val)) >
347 : INT_MAX)
348 1 : eType = OFTInteger64;
349 : else
350 1 : eType = OFTInteger;
351 : }
352 2 : else if (json_object_get_type(it.val) == json_type_double)
353 : {
354 1 : eType = OFTReal;
355 : }
356 : }
357 4 : OGRFieldDefn oFieldDefn(it.key, eType);
358 4 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
359 4 : m_oSetQueryableFields.insert(m_poFeatureDefn->GetFieldCount() -
360 4 : 1);
361 : }
362 : }
363 : {
364 12 : OGRFieldDefn oFieldDefn("other_properties", OFTString);
365 6 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
366 : }
367 : }
368 : }
369 :
370 : /************************************************************************/
371 : /* ~GDALEEDALayer() */
372 : /************************************************************************/
373 :
374 14 : GDALEEDALayer::~GDALEEDALayer()
375 : {
376 7 : m_poFeatureDefn->Release();
377 7 : if (m_poCurPageObj)
378 1 : json_object_put(m_poCurPageObj);
379 14 : }
380 :
381 : /************************************************************************/
382 : /* ResetReading() */
383 : /************************************************************************/
384 :
385 11 : void GDALEEDALayer::ResetReading()
386 : {
387 11 : if (m_poCurPageObj)
388 5 : json_object_put(m_poCurPageObj);
389 11 : m_poCurPageObj = nullptr;
390 11 : m_poCurPageAssets = nullptr;
391 11 : m_nIndexInPage = 0;
392 11 : m_nFID = 1;
393 11 : }
394 :
395 : /************************************************************************/
396 : /* GetNextRawFeature() */
397 : /************************************************************************/
398 :
399 9 : OGRFeature *GDALEEDALayer::GetNextRawFeature()
400 : {
401 18 : CPLString osNextPageToken;
402 12 : if (m_poCurPageAssets != nullptr &&
403 6 : m_nIndexInPage >=
404 3 : static_cast<int>(json_object_array_length(m_poCurPageAssets)))
405 : {
406 : json_object *poToken =
407 2 : CPL_json_object_object_get(m_poCurPageObj, "nextPageToken");
408 2 : const char *pszToken = json_object_get_string(poToken);
409 2 : if (pszToken == nullptr)
410 1 : return nullptr;
411 1 : osNextPageToken = pszToken;
412 1 : json_object_put(m_poCurPageObj);
413 1 : m_poCurPageObj = nullptr;
414 1 : m_poCurPageAssets = nullptr;
415 1 : m_nIndexInPage = 0;
416 : }
417 :
418 8 : if (m_poCurPageObj == nullptr)
419 : {
420 14 : CPLString osURL(m_poDS->GetBaseURL() + m_osCollectionName +
421 7 : ":listImages");
422 7 : CPLString query = "";
423 7 : if (!osNextPageToken.empty())
424 : {
425 : query +=
426 1 : "&pageToken=" + CPLEscapeURLQueryParameter(osNextPageToken);
427 : }
428 7 : const char *pszPageSize = CPLGetConfigOption("EEDA_PAGE_SIZE", nullptr);
429 7 : if (pszPageSize)
430 : {
431 0 : query += "&pageSize=";
432 0 : query += pszPageSize;
433 : }
434 7 : if (m_poFilterGeom != nullptr)
435 : {
436 : char *pszGeoJSON =
437 1 : OGR_G_ExportToJson(OGRGeometry::ToHandle(m_poFilterGeom));
438 1 : query += "®ion=";
439 1 : query += CPLEscapeURLQueryParameter(pszGeoJSON);
440 1 : CPLFree(pszGeoJSON);
441 : }
442 7 : if (!m_osAttributeFilter.empty())
443 : {
444 2 : query += "&filter=";
445 2 : query += CPLEscapeURLQueryParameter(m_osAttributeFilter);
446 : }
447 7 : if (!m_osStartTime.empty())
448 : {
449 3 : query += "&startTime=";
450 3 : query += CPLEscapeURLQueryParameter(m_osStartTime);
451 : }
452 7 : if (!m_osEndTime.empty())
453 : {
454 2 : query += "&endTime=";
455 2 : query += CPLEscapeURLQueryParameter(m_osEndTime);
456 : }
457 7 : if (query.size() > 0)
458 : {
459 5 : osURL = osURL + "?" + query.substr(1);
460 : }
461 7 : m_poCurPageObj = m_poDS->RunRequest(osURL);
462 7 : if (m_poCurPageObj == nullptr)
463 0 : return nullptr;
464 :
465 7 : m_poCurPageAssets =
466 7 : CPL_json_object_object_get(m_poCurPageObj, "images");
467 : }
468 :
469 16 : if (m_poCurPageAssets == nullptr ||
470 8 : json_object_get_type(m_poCurPageAssets) != json_type_array)
471 : {
472 0 : json_object_put(m_poCurPageObj);
473 0 : m_poCurPageObj = nullptr;
474 0 : return nullptr;
475 : }
476 : json_object *poAsset =
477 8 : json_object_array_get_idx(m_poCurPageAssets, m_nIndexInPage);
478 8 : if (poAsset == nullptr || json_object_get_type(poAsset) != json_type_object)
479 : {
480 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid asset");
481 0 : return nullptr;
482 : }
483 :
484 8 : OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
485 8 : poFeature->SetFID(m_nFID);
486 :
487 8 : json_object *poJSonGeom = CPL_json_object_object_get(poAsset, "geometry");
488 11 : if (poJSonGeom != nullptr &&
489 3 : json_object_get_type(poJSonGeom) == json_type_object)
490 : {
491 3 : const char *pszGeoJSON = json_object_get_string(poJSonGeom);
492 3 : if (strstr(pszGeoJSON, "Infinity") == nullptr)
493 : {
494 3 : OGRGeometry *poGeom = OGRGeometry::FromHandle(
495 : OGR_G_CreateGeometryFromJson(pszGeoJSON));
496 3 : if (poGeom != nullptr)
497 : {
498 3 : if (poGeom->getGeometryType() == wkbPolygon)
499 : {
500 3 : OGRMultiPolygon *poMP = new OGRMultiPolygon();
501 3 : poMP->addGeometryDirectly(poGeom);
502 3 : poGeom = poMP;
503 : }
504 3 : poGeom->assignSpatialReference(
505 3 : m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef());
506 3 : poFeature->SetGeometryDirectly(poGeom);
507 : }
508 : }
509 : }
510 :
511 : const char *pszName =
512 8 : json_object_get_string(CPL_json_object_object_get(poAsset, "name"));
513 8 : if (pszName)
514 : {
515 8 : poFeature->SetField("name", pszName);
516 8 : poFeature->SetField("gdal_dataset",
517 16 : ("EEDAI:" + CPLString(pszName)).c_str());
518 : }
519 :
520 : const char *pszId =
521 8 : json_object_get_string(CPL_json_object_object_get(poAsset, "id"));
522 8 : if (pszId)
523 : {
524 2 : poFeature->SetField("id", pszId);
525 : }
526 :
527 8 : const char *const apszBaseProps[] = {"updateTime", "startTime", "endTime",
528 : "sizeBytes"};
529 40 : for (size_t i = 0; i < CPL_ARRAYSIZE(apszBaseProps); i++)
530 : {
531 32 : const char *pszVal = json_object_get_string(
532 32 : CPL_json_object_object_get(poAsset, apszBaseProps[i]));
533 32 : if (pszVal)
534 : {
535 15 : poFeature->SetField(apszBaseProps[i], pszVal);
536 : }
537 : }
538 :
539 8 : json_object *poBands = CPL_json_object_object_get(poAsset, "bands");
540 8 : if (poBands != nullptr && json_object_get_type(poBands) == json_type_array)
541 : {
542 : std::vector<EEDAIBandDesc> aoBands =
543 4 : BuildBandDescArray(poBands, m_oMapCodeToWKT);
544 2 : poFeature->SetField("band_count", static_cast<int>(aoBands.size()));
545 2 : if (!aoBands.empty())
546 : {
547 2 : int nWidth = 0, nHeight = 0;
548 2 : double dfMinPixelSize = std::numeric_limits<double>::max();
549 4 : CPLString osSRS(aoBands[0].osWKT);
550 2 : double dfULX = aoBands[0].adfGeoTransform[0];
551 2 : double dfULY = aoBands[0].adfGeoTransform[3];
552 2 : bool bULValid = true;
553 4 : for (size_t i = 0; i < aoBands.size(); i++)
554 : {
555 2 : nWidth = std::max(nWidth, aoBands[i].nWidth);
556 2 : nHeight = std::max(nHeight, aoBands[i].nHeight);
557 2 : dfMinPixelSize =
558 2 : std::min(dfMinPixelSize,
559 2 : std::min(aoBands[i].adfGeoTransform[1],
560 4 : fabs(aoBands[i].adfGeoTransform[5])));
561 2 : if (osSRS != aoBands[i].osWKT)
562 : {
563 0 : osSRS.clear();
564 : }
565 4 : if (dfULX != aoBands[i].adfGeoTransform[0] ||
566 2 : dfULY != aoBands[i].adfGeoTransform[3])
567 : {
568 0 : bULValid = false;
569 : }
570 : }
571 2 : poFeature->SetField("band_max_width", nWidth);
572 2 : poFeature->SetField("band_max_height", nHeight);
573 2 : poFeature->SetField("band_min_pixel_size", dfMinPixelSize);
574 2 : if (bULValid)
575 : {
576 2 : poFeature->SetField("band_upper_left_x", dfULX);
577 2 : poFeature->SetField("band_upper_left_y", dfULY);
578 : }
579 2 : if (!osSRS.empty())
580 : {
581 4 : OGRSpatialReference oSRS;
582 2 : oSRS.SetFromUserInput(
583 : osSRS,
584 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
585 2 : const char *pszAuthName = oSRS.GetAuthorityName(nullptr);
586 2 : const char *pszAuthCode = oSRS.GetAuthorityCode(nullptr);
587 2 : if (pszAuthName && pszAuthCode)
588 : {
589 2 : poFeature->SetField(
590 : "band_crs",
591 : CPLSPrintf("%s:%s", pszAuthName, pszAuthCode));
592 : }
593 : else
594 : {
595 0 : poFeature->SetField("band_crs", osSRS.c_str());
596 : }
597 : }
598 : }
599 : }
600 :
601 : json_object *poProperties =
602 8 : CPL_json_object_object_get(poAsset, "properties");
603 11 : if (poProperties != nullptr &&
604 3 : json_object_get_type(poProperties) == json_type_object)
605 : {
606 3 : json_object *poOtherProperties = nullptr;
607 :
608 : json_object_iter it;
609 3 : it.key = nullptr;
610 3 : it.val = nullptr;
611 3 : it.entry = nullptr;
612 18 : json_object_object_foreachC(poProperties, it)
613 : {
614 15 : if (it.val)
615 : {
616 15 : int nIdx = m_poFeatureDefn->GetFieldIndex(it.key);
617 15 : if (nIdx >= 0)
618 : {
619 12 : poFeature->SetField(nIdx, json_object_get_string(it.val));
620 : }
621 : else
622 : {
623 3 : if (poOtherProperties == nullptr)
624 3 : poOtherProperties = json_object_new_object();
625 3 : json_object_object_add(poOtherProperties, it.key, it.val);
626 3 : json_object_get(it.val);
627 : }
628 : }
629 : }
630 :
631 3 : if (poOtherProperties)
632 : {
633 : int nIdxOtherProperties =
634 3 : m_poFeatureDefn->GetFieldIndex("other_properties");
635 3 : if (nIdxOtherProperties >= 0)
636 : {
637 3 : poFeature->SetField(nIdxOtherProperties,
638 : json_object_get_string(poOtherProperties));
639 : }
640 3 : json_object_put(poOtherProperties);
641 : }
642 : }
643 :
644 8 : m_nFID++;
645 8 : m_nIndexInPage++;
646 :
647 8 : return poFeature;
648 : }
649 :
650 : /************************************************************************/
651 : /* GetNextFeature() */
652 : /************************************************************************/
653 :
654 9 : OGRFeature *GDALEEDALayer::GetNextFeature()
655 : {
656 : while (true)
657 : {
658 9 : OGRFeature *poFeature = GetNextRawFeature();
659 9 : if (poFeature == nullptr)
660 1 : return nullptr;
661 :
662 8 : if (m_poAttrQuery == nullptr || !m_bFilterMustBeClientSideEvaluated ||
663 0 : m_poAttrQuery->Evaluate(poFeature))
664 : {
665 8 : return poFeature;
666 : }
667 : else
668 : {
669 0 : delete poFeature;
670 : }
671 0 : }
672 : }
673 :
674 : /************************************************************************/
675 : /* GDALEEDALayerParseDateTime() */
676 : /************************************************************************/
677 :
678 5 : static int GDALEEDALayerParseDateTime(const char *pszValue, int operation,
679 : int &nYear, int &nMonth, int &nDay,
680 : int &nHour, int &nMinute, int &nSecond)
681 : {
682 5 : nHour = (operation == SWQ_GE) ? 0 : 23;
683 5 : nMinute = (operation == SWQ_GE) ? 0 : 59;
684 5 : nSecond = (operation == SWQ_GE) ? 0 : 59;
685 5 : int nRet = sscanf(pszValue, "%04d/%02d/%02d %02d:%02d:%02d", &nYear,
686 : &nMonth, &nDay, &nHour, &nMinute, &nSecond);
687 5 : if (nRet >= 3)
688 : {
689 0 : return nRet;
690 : }
691 5 : nRet = sscanf(pszValue, "%04d-%02d-%02dT%02d:%02d:%02d", &nYear, &nMonth,
692 : &nDay, &nHour, &nMinute, &nSecond);
693 5 : if (nRet >= 3)
694 : {
695 5 : return nRet;
696 : }
697 0 : return 0;
698 : }
699 :
700 : /************************************************************************/
701 : /* IsSimpleComparison() */
702 : /************************************************************************/
703 :
704 14 : bool GDALEEDALayer::IsSimpleComparison(const swq_expr_node *poNode)
705 : {
706 28 : return poNode->eNodeType == SNT_OPERATION &&
707 14 : (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_NE ||
708 10 : poNode->nOperation == SWQ_LT || poNode->nOperation == SWQ_LE ||
709 7 : poNode->nOperation == SWQ_GT || poNode->nOperation == SWQ_GE) &&
710 11 : poNode->nSubExprCount == 2 &&
711 11 : poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
712 39 : poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
713 11 : m_oSetQueryableFields.find(poNode->papoSubExpr[0]->field_index) !=
714 25 : m_oSetQueryableFields.end();
715 : }
716 :
717 : /************************************************************************/
718 : /* GetOperatorText() */
719 : /************************************************************************/
720 :
721 6 : static const char *GetOperatorText(swq_op nOp)
722 : {
723 6 : if (nOp == SWQ_LT)
724 1 : return "<";
725 5 : if (nOp == SWQ_LE)
726 1 : return "<=";
727 4 : if (nOp == SWQ_GT)
728 1 : return ">";
729 3 : if (nOp == SWQ_GE)
730 1 : return ">=";
731 2 : if (nOp == SWQ_EQ)
732 1 : return "=";
733 1 : if (nOp == SWQ_NE)
734 1 : return "!=";
735 0 : CPLAssert(false);
736 : return "";
737 : }
738 :
739 : /************************************************************************/
740 : /* BuildFilter() */
741 : /************************************************************************/
742 :
743 26 : CPLString GDALEEDALayer::BuildFilter(swq_expr_node *poNode, bool bIsAndTopLevel)
744 : {
745 : int nYear, nMonth, nDay, nHour, nMinute, nSecond;
746 :
747 26 : if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
748 10 : poNode->nSubExprCount == 2)
749 : {
750 : // For AND, we can deal with a failure in one of the branch
751 : // since client-side will do that extra filtering
752 20 : CPLString osLeft = BuildFilter(poNode->papoSubExpr[0], bIsAndTopLevel);
753 20 : CPLString osRight = BuildFilter(poNode->papoSubExpr[1], bIsAndTopLevel);
754 10 : if (!osLeft.empty() && !osRight.empty())
755 : {
756 14 : return "(" + osLeft + " AND " + osRight + ")";
757 : }
758 3 : else if (!osLeft.empty())
759 0 : return osLeft;
760 : else
761 3 : return osRight;
762 : }
763 16 : else if (poNode->eNodeType == SNT_OPERATION &&
764 16 : poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
765 : {
766 : // For OR, we need both members to be valid
767 2 : CPLString osLeft = BuildFilter(poNode->papoSubExpr[0], false);
768 2 : CPLString osRight = BuildFilter(poNode->papoSubExpr[1], false);
769 1 : if (!osLeft.empty() && !osRight.empty())
770 : {
771 2 : return "(" + osLeft + " OR " + osRight + ")";
772 : }
773 0 : return "";
774 : }
775 15 : else if (poNode->eNodeType == SNT_OPERATION &&
776 15 : poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
777 : {
778 2 : CPLString osFilter = BuildFilter(poNode->papoSubExpr[0], false);
779 1 : if (!osFilter.empty())
780 : {
781 2 : return "(NOT " + osFilter + ")";
782 : }
783 0 : return "";
784 : }
785 14 : else if (IsSimpleComparison(poNode))
786 : {
787 6 : const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
788 : CPLString osFilter(
789 12 : m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef());
790 6 : osFilter += " ";
791 6 : osFilter += GetOperatorText(poNode->nOperation);
792 6 : osFilter += " ";
793 6 : if (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER ||
794 4 : poNode->papoSubExpr[1]->field_type == SWQ_INTEGER64)
795 : {
796 4 : osFilter +=
797 4 : CPLSPrintf(CPL_FRMT_GIB, poNode->papoSubExpr[1]->int_value);
798 : }
799 2 : else if (poNode->papoSubExpr[1]->field_type == SWQ_FLOAT)
800 : {
801 : osFilter +=
802 1 : CPLSPrintf("%.18g", poNode->papoSubExpr[1]->float_value);
803 : }
804 : else
805 : {
806 1 : osFilter += "\"";
807 1 : osFilter += poNode->papoSubExpr[1]->string_value;
808 1 : osFilter += "\"";
809 : }
810 6 : return osFilter;
811 : }
812 6 : else if (bIsAndTopLevel && poNode->eNodeType == SNT_OPERATION &&
813 6 : (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_GE) &&
814 4 : poNode->nSubExprCount == 2 &&
815 4 : poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
816 4 : poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
817 4 : poNode->papoSubExpr[0]->field_index ==
818 17 : m_poFeatureDefn->GetFieldIndex("startTime") &&
819 3 : poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP)
820 : {
821 3 : int nTerms = GDALEEDALayerParseDateTime(
822 3 : poNode->papoSubExpr[1]->string_value, SWQ_GE, nYear, nMonth, nDay,
823 : nHour, nMinute, nSecond);
824 3 : if (nTerms >= 3)
825 : {
826 : m_osStartTime = CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
827 3 : nMonth, nDay, nHour, nMinute, nSecond);
828 : }
829 : else
830 : {
831 0 : m_bFilterMustBeClientSideEvaluated = true;
832 : }
833 3 : return "";
834 : }
835 3 : else if (bIsAndTopLevel && poNode->eNodeType == SNT_OPERATION &&
836 3 : (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_LE) &&
837 2 : poNode->nSubExprCount == 2 &&
838 2 : poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
839 2 : poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
840 2 : poNode->papoSubExpr[0]->field_index ==
841 10 : m_poFeatureDefn->GetFieldIndex("endTime") &&
842 2 : poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP)
843 : {
844 2 : int nTerms = GDALEEDALayerParseDateTime(
845 2 : poNode->papoSubExpr[1]->string_value, SWQ_LE, nYear, nMonth, nDay,
846 : nHour, nMinute, nSecond);
847 2 : if (nTerms >= 3)
848 : {
849 2 : if (poNode->nOperation == SWQ_EQ && nTerms == 6)
850 : {
851 0 : if (nSecond < 59)
852 0 : nSecond++;
853 0 : else if (nMinute < 59)
854 0 : nMinute++;
855 0 : else if (nHour < 23)
856 0 : nHour++;
857 : else
858 0 : nDay++;
859 : }
860 : m_osEndTime = CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
861 2 : nMonth, nDay, nHour, nMinute, nSecond);
862 : }
863 : else
864 : {
865 0 : m_bFilterMustBeClientSideEvaluated = true;
866 : }
867 2 : return "";
868 : }
869 9 : else if (poNode->eNodeType == SNT_OPERATION &&
870 3 : poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 &&
871 9 : poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
872 3 : m_oSetQueryableFields.find(poNode->papoSubExpr[0]->field_index) !=
873 6 : m_oSetQueryableFields.end())
874 : {
875 3 : const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
876 6 : CPLString osFilter;
877 :
878 7 : for (int i = 1; i < poNode->nSubExprCount; i++)
879 : {
880 4 : if (!osFilter.empty())
881 1 : osFilter += " OR ";
882 4 : osFilter += m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
883 4 : osFilter += " = ";
884 4 : if (poNode->papoSubExpr[i]->field_type == SWQ_INTEGER ||
885 3 : poNode->papoSubExpr[i]->field_type == SWQ_INTEGER64)
886 : {
887 1 : osFilter +=
888 1 : CPLSPrintf(CPL_FRMT_GIB, poNode->papoSubExpr[i]->int_value);
889 : }
890 3 : else if (poNode->papoSubExpr[i]->field_type == SWQ_FLOAT)
891 : {
892 : osFilter +=
893 1 : CPLSPrintf("%.18g", poNode->papoSubExpr[i]->float_value);
894 : }
895 : else
896 : {
897 2 : osFilter += "\"";
898 2 : osFilter += poNode->papoSubExpr[i]->string_value;
899 2 : osFilter += "\"";
900 : }
901 : }
902 :
903 3 : return osFilter;
904 : }
905 :
906 0 : m_bFilterMustBeClientSideEvaluated = true;
907 0 : return "";
908 : }
909 :
910 : /************************************************************************/
911 : /* SetAttributeFilter() */
912 : /************************************************************************/
913 :
914 5 : OGRErr GDALEEDALayer::SetAttributeFilter(const char *pszQuery)
915 :
916 : {
917 5 : m_osAttributeFilter.clear();
918 5 : m_osStartTime.clear();
919 5 : m_osEndTime.clear();
920 5 : m_bFilterMustBeClientSideEvaluated = false;
921 :
922 5 : if (pszQuery && STARTS_WITH_CI(pszQuery, "EEDA:"))
923 : {
924 1 : m_osAttributeFilter = pszQuery + strlen("EEDA:");
925 1 : OGRLayer::SetAttributeFilter(nullptr);
926 1 : ResetReading();
927 1 : return OGRERR_NONE;
928 : }
929 :
930 4 : OGRErr eErr = OGRLayer::SetAttributeFilter(pszQuery);
931 :
932 4 : if (m_poAttrQuery != nullptr)
933 : {
934 : swq_expr_node *poNode =
935 3 : static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
936 :
937 : #ifndef PLUGIN
938 3 : poNode->ReplaceBetweenByGEAndLERecurse();
939 : #endif
940 :
941 3 : m_osAttributeFilter = BuildFilter(poNode, true);
942 3 : if (m_osAttributeFilter.empty() && m_osStartTime.empty() &&
943 0 : m_osEndTime.empty())
944 : {
945 0 : CPLDebug("EEDA", "Full filter will be evaluated on client side.");
946 : }
947 3 : else if (m_bFilterMustBeClientSideEvaluated)
948 : {
949 0 : CPLDebug(
950 : "EEDA",
951 : "Only part of the filter will be evaluated on server side.");
952 : }
953 : }
954 :
955 4 : ResetReading();
956 :
957 4 : return eErr;
958 : }
959 :
960 : /************************************************************************/
961 : /* SetSpatialFilter() */
962 : /************************************************************************/
963 :
964 2 : void GDALEEDALayer::SetSpatialFilter(OGRGeometry *poGeomIn)
965 : {
966 2 : if (poGeomIn)
967 : {
968 1 : OGREnvelope sEnvelope;
969 1 : poGeomIn->getEnvelope(&sEnvelope);
970 1 : if (sEnvelope.MinX == sEnvelope.MaxX &&
971 0 : sEnvelope.MinY == sEnvelope.MaxY)
972 : {
973 0 : OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
974 0 : InstallFilter(&p);
975 : }
976 : else
977 1 : InstallFilter(poGeomIn);
978 : }
979 : else
980 1 : InstallFilter(poGeomIn);
981 :
982 2 : ResetReading();
983 2 : }
984 :
985 : /************************************************************************/
986 : /* GetExtent() */
987 : /************************************************************************/
988 :
989 1 : OGRErr GDALEEDALayer::GetExtent(OGREnvelope *psExtent, int /* bForce */)
990 : {
991 1 : psExtent->MinX = -180;
992 1 : psExtent->MinY = -90;
993 1 : psExtent->MaxX = 180;
994 1 : psExtent->MaxY = 90;
995 1 : return OGRERR_NONE;
996 : }
997 :
998 : /************************************************************************/
999 : /* TestCapability() */
1000 : /************************************************************************/
1001 :
1002 5 : int GDALEEDALayer::TestCapability(const char *pszCap)
1003 : {
1004 5 : if (EQUAL(pszCap, OLCStringsAsUTF8))
1005 4 : return TRUE;
1006 1 : return FALSE;
1007 : }
1008 :
1009 : /************************************************************************/
1010 : /* GDALEEDADataset() */
1011 : /************************************************************************/
1012 :
1013 7 : GDALEEDADataset::GDALEEDADataset() : m_poLayer(nullptr)
1014 : {
1015 7 : }
1016 :
1017 : /************************************************************************/
1018 : /* ~GDALEEDADataset() */
1019 : /************************************************************************/
1020 :
1021 14 : GDALEEDADataset::~GDALEEDADataset()
1022 : {
1023 7 : delete m_poLayer;
1024 14 : }
1025 :
1026 : /************************************************************************/
1027 : /* GetLayer() */
1028 : /************************************************************************/
1029 :
1030 7 : OGRLayer *GDALEEDADataset::GetLayer(int idx)
1031 : {
1032 7 : if (idx == 0)
1033 7 : return m_poLayer;
1034 0 : return nullptr;
1035 : }
1036 :
1037 : /************************************************************************/
1038 : /* RunRequest() */
1039 : /************************************************************************/
1040 :
1041 13 : json_object *GDALEEDADataset::RunRequest(const CPLString &osURL)
1042 : {
1043 13 : char **papszOptions = GetBaseHTTPOptions();
1044 13 : if (papszOptions == nullptr)
1045 0 : return nullptr;
1046 13 : CPLHTTPResult *psResult = EEDAHTTPFetch(osURL, papszOptions);
1047 13 : CSLDestroy(papszOptions);
1048 13 : if (psResult == nullptr)
1049 0 : return nullptr;
1050 13 : if (psResult->pszErrBuf != nullptr)
1051 : {
1052 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
1053 0 : psResult->pabyData
1054 : ? reinterpret_cast<const char *>(psResult->pabyData)
1055 : : psResult->pszErrBuf);
1056 0 : CPLHTTPDestroyResult(psResult);
1057 0 : return nullptr;
1058 : }
1059 :
1060 13 : if (psResult->pabyData == nullptr)
1061 : {
1062 0 : CPLError(CE_Failure, CPLE_AppDefined,
1063 : "Empty content returned by server");
1064 0 : CPLHTTPDestroyResult(psResult);
1065 0 : return nullptr;
1066 : }
1067 :
1068 13 : const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
1069 : #ifdef DEBUG_VERBOSE
1070 : CPLDebug("EEDA", "%s", pszText);
1071 : #endif
1072 :
1073 13 : json_object *poObj = nullptr;
1074 13 : if (!OGRJSonParse(pszText, &poObj, true))
1075 : {
1076 0 : CPLHTTPDestroyResult(psResult);
1077 0 : return nullptr;
1078 : }
1079 :
1080 13 : CPLHTTPDestroyResult(psResult);
1081 :
1082 13 : if (json_object_get_type(poObj) != json_type_object)
1083 : {
1084 0 : CPLError(CE_Failure, CPLE_AppDefined,
1085 : "Return is not a JSON dictionary");
1086 0 : json_object_put(poObj);
1087 0 : return nullptr;
1088 : }
1089 :
1090 13 : return poObj;
1091 : }
1092 :
1093 : /************************************************************************/
1094 : /* GDALEEDADatasetGetConf() */
1095 : /************************************************************************/
1096 :
1097 7 : static json_object *GDALEEDADatasetGetConf()
1098 : {
1099 7 : const char *pszConfFile = CPLFindFile("gdal", "eedaconf.json");
1100 7 : if (pszConfFile == nullptr)
1101 : {
1102 0 : CPLDebug("EEDA", "Cannot find eedaconf.json");
1103 0 : return nullptr;
1104 : }
1105 :
1106 7 : GByte *pabyRet = nullptr;
1107 7 : if (!VSIIngestFile(nullptr, pszConfFile, &pabyRet, nullptr, -1))
1108 : {
1109 0 : return nullptr;
1110 : }
1111 :
1112 7 : json_object *poRoot = nullptr;
1113 7 : const char *pzText = reinterpret_cast<char *>(pabyRet);
1114 7 : if (!OGRJSonParse(pzText, &poRoot))
1115 : {
1116 0 : VSIFree(pabyRet);
1117 0 : return nullptr;
1118 : }
1119 7 : VSIFree(pabyRet);
1120 :
1121 7 : if (json_object_get_type(poRoot) != json_type_object)
1122 : {
1123 0 : json_object_put(poRoot);
1124 0 : return nullptr;
1125 : }
1126 :
1127 7 : return poRoot;
1128 : }
1129 :
1130 : /************************************************************************/
1131 : /* Open() */
1132 : /************************************************************************/
1133 :
1134 7 : bool GDALEEDADataset::Open(GDALOpenInfo *poOpenInfo)
1135 : {
1136 : m_osBaseURL = CPLGetConfigOption(
1137 7 : "EEDA_URL", "https://earthengine-highvolume.googleapis.com/v1alpha/");
1138 :
1139 : CPLString osCollection =
1140 14 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "COLLECTION", "");
1141 7 : if (osCollection.empty())
1142 : {
1143 : char **papszTokens =
1144 7 : CSLTokenizeString2(poOpenInfo->pszFilename, ":", 0);
1145 7 : if (CSLCount(papszTokens) < 2)
1146 : {
1147 0 : CPLError(CE_Failure, CPLE_AppDefined,
1148 : "No collection specified in connection string or "
1149 : "COLLECTION open option");
1150 0 : CSLDestroy(papszTokens);
1151 0 : return false;
1152 : }
1153 7 : osCollection = papszTokens[1];
1154 7 : CSLDestroy(papszTokens);
1155 : }
1156 14 : CPLString osCollectionName = ConvertPathToName(osCollection);
1157 :
1158 7 : json_object *poRootConf = GDALEEDADatasetGetConf();
1159 7 : if (poRootConf)
1160 : {
1161 : json_object *poLayerConf =
1162 7 : CPL_json_object_object_get(poRootConf, osCollection);
1163 8 : if (poLayerConf != nullptr &&
1164 1 : json_object_get_type(poLayerConf) == json_type_object)
1165 : {
1166 1 : m_poLayer = new GDALEEDALayer(this, osCollection, osCollectionName,
1167 1 : nullptr, poLayerConf);
1168 1 : json_object_put(poRootConf);
1169 1 : return true;
1170 : }
1171 6 : json_object_put(poRootConf);
1172 : }
1173 :
1174 : // Issue request to get layer schema
1175 : json_object *poRootAsset =
1176 6 : RunRequest(m_osBaseURL + osCollectionName + ":listImages?pageSize=1");
1177 6 : if (poRootAsset == nullptr)
1178 0 : return false;
1179 :
1180 6 : json_object *poAssets = CPL_json_object_object_get(poRootAsset, "images");
1181 12 : if (poAssets == nullptr ||
1182 12 : json_object_get_type(poAssets) != json_type_array ||
1183 6 : json_object_array_length(poAssets) != 1)
1184 : {
1185 0 : CPLError(CE_Failure, CPLE_AppDefined, "No assets");
1186 0 : json_object_put(poRootAsset);
1187 0 : return false;
1188 : }
1189 6 : json_object *poAsset = json_object_array_get_idx(poAssets, 0);
1190 6 : if (poAsset == nullptr || json_object_get_type(poAsset) != json_type_object)
1191 : {
1192 0 : CPLError(CE_Failure, CPLE_AppDefined, "No assets");
1193 0 : json_object_put(poRootAsset);
1194 0 : return false;
1195 : }
1196 :
1197 6 : m_poLayer = new GDALEEDALayer(this, osCollection, osCollectionName, poAsset,
1198 6 : nullptr);
1199 6 : json_object_put(poRootAsset);
1200 :
1201 6 : return true;
1202 : }
1203 :
1204 : /************************************************************************/
1205 : /* GDALEEDAdentify() */
1206 : /************************************************************************/
1207 :
1208 49811 : static int GDALEEDAdentify(GDALOpenInfo *poOpenInfo)
1209 : {
1210 49811 : return STARTS_WITH_CI(poOpenInfo->pszFilename, "EEDA:");
1211 : }
1212 :
1213 : /************************************************************************/
1214 : /* GDALEEDAOpen() */
1215 : /************************************************************************/
1216 :
1217 7 : static GDALDataset *GDALEEDAOpen(GDALOpenInfo *poOpenInfo)
1218 : {
1219 7 : if (!GDALEEDAdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
1220 0 : return nullptr;
1221 :
1222 7 : GDALEEDADataset *poDS = new GDALEEDADataset();
1223 7 : if (!poDS->Open(poOpenInfo))
1224 : {
1225 0 : delete poDS;
1226 0 : return nullptr;
1227 : }
1228 7 : return poDS;
1229 : }
1230 :
1231 : /************************************************************************/
1232 : /* GDALRegister_EEDA() */
1233 : /************************************************************************/
1234 :
1235 1520 : void GDALRegister_EEDA()
1236 :
1237 : {
1238 1520 : if (GDALGetDriverByName("EEDA") != nullptr)
1239 301 : return;
1240 :
1241 1219 : GDALDriver *poDriver = new GDALDriver();
1242 :
1243 1219 : poDriver->SetDescription("EEDA");
1244 1219 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
1245 1219 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Earth Engine Data API");
1246 1219 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/eeda.html");
1247 1219 : poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "EEDA:");
1248 1219 : poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST,
1249 : "<OpenOptionList>"
1250 : " <Option name='COLLECTION' type='string' "
1251 : "description='Collection name'/>"
1252 1219 : "</OpenOptionList>");
1253 :
1254 1219 : poDriver->pfnOpen = GDALEEDAOpen;
1255 1219 : poDriver->pfnIdentify = GDALEEDAdentify;
1256 :
1257 1219 : GetGDALDriverManager()->RegisterDriver(poDriver);
1258 :
1259 : #ifdef GDAL_ENABLE_DRIVER_EEDA_PLUGIN
1260 : GDALRegister_EEDAI();
1261 : #endif
1262 : }
|