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