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].adfGeoTransform[0];
526 2 : double dfULY = aoBands[0].adfGeoTransform[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,
534 2 : std::min(aoBands[i].adfGeoTransform[1],
535 4 : fabs(aoBands[i].adfGeoTransform[5])));
536 2 : if (osSRS != aoBands[i].osWKT)
537 : {
538 0 : osSRS.clear();
539 : }
540 4 : if (dfULX != aoBands[i].adfGeoTransform[0] ||
541 2 : dfULY != aoBands[i].adfGeoTransform[3])
542 : {
543 0 : bULValid = false;
544 : }
545 : }
546 2 : poFeature->SetField("band_max_width", nWidth);
547 2 : poFeature->SetField("band_max_height", nHeight);
548 2 : poFeature->SetField("band_min_pixel_size", dfMinPixelSize);
549 2 : if (bULValid)
550 : {
551 2 : poFeature->SetField("band_upper_left_x", dfULX);
552 2 : poFeature->SetField("band_upper_left_y", dfULY);
553 : }
554 2 : if (!osSRS.empty())
555 : {
556 4 : OGRSpatialReference oSRS;
557 2 : oSRS.SetFromUserInput(
558 : osSRS,
559 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
560 2 : const char *pszAuthName = oSRS.GetAuthorityName(nullptr);
561 2 : const char *pszAuthCode = oSRS.GetAuthorityCode(nullptr);
562 2 : if (pszAuthName && pszAuthCode)
563 : {
564 2 : poFeature->SetField(
565 : "band_crs",
566 : CPLSPrintf("%s:%s", pszAuthName, pszAuthCode));
567 : }
568 : else
569 : {
570 0 : poFeature->SetField("band_crs", osSRS.c_str());
571 : }
572 : }
573 : }
574 : }
575 :
576 : json_object *poProperties =
577 8 : CPL_json_object_object_get(poAsset, "properties");
578 11 : if (poProperties != nullptr &&
579 3 : json_object_get_type(poProperties) == json_type_object)
580 : {
581 3 : json_object *poOtherProperties = nullptr;
582 :
583 : json_object_iter it;
584 3 : it.key = nullptr;
585 3 : it.val = nullptr;
586 3 : it.entry = nullptr;
587 18 : json_object_object_foreachC(poProperties, it)
588 : {
589 15 : if (it.val)
590 : {
591 15 : int nIdx = m_poFeatureDefn->GetFieldIndex(it.key);
592 15 : if (nIdx >= 0)
593 : {
594 12 : poFeature->SetField(nIdx, json_object_get_string(it.val));
595 : }
596 : else
597 : {
598 3 : if (poOtherProperties == nullptr)
599 3 : poOtherProperties = json_object_new_object();
600 3 : json_object_object_add(poOtherProperties, it.key, it.val);
601 3 : json_object_get(it.val);
602 : }
603 : }
604 : }
605 :
606 3 : if (poOtherProperties)
607 : {
608 : int nIdxOtherProperties =
609 3 : m_poFeatureDefn->GetFieldIndex("other_properties");
610 3 : if (nIdxOtherProperties >= 0)
611 : {
612 3 : poFeature->SetField(nIdxOtherProperties,
613 : json_object_get_string(poOtherProperties));
614 : }
615 3 : json_object_put(poOtherProperties);
616 : }
617 : }
618 :
619 8 : m_nFID++;
620 8 : m_nIndexInPage++;
621 :
622 8 : return poFeature;
623 : }
624 :
625 : /************************************************************************/
626 : /* GetNextFeature() */
627 : /************************************************************************/
628 :
629 9 : OGRFeature *GDALEEDALayer::GetNextFeature()
630 : {
631 : while (true)
632 : {
633 9 : OGRFeature *poFeature = GetNextRawFeature();
634 9 : if (poFeature == nullptr)
635 1 : return nullptr;
636 :
637 8 : if (m_poAttrQuery == nullptr || !m_bFilterMustBeClientSideEvaluated ||
638 0 : m_poAttrQuery->Evaluate(poFeature))
639 : {
640 8 : return poFeature;
641 : }
642 : else
643 : {
644 0 : delete poFeature;
645 : }
646 0 : }
647 : }
648 :
649 : /************************************************************************/
650 : /* GDALEEDALayerParseDateTime() */
651 : /************************************************************************/
652 :
653 5 : static int GDALEEDALayerParseDateTime(const char *pszValue, int operation,
654 : int &nYear, int &nMonth, int &nDay,
655 : int &nHour, int &nMinute, int &nSecond)
656 : {
657 5 : nHour = (operation == SWQ_GE) ? 0 : 23;
658 5 : nMinute = (operation == SWQ_GE) ? 0 : 59;
659 5 : nSecond = (operation == SWQ_GE) ? 0 : 59;
660 5 : int nRet = sscanf(pszValue, "%04d/%02d/%02d %02d:%02d:%02d", &nYear,
661 : &nMonth, &nDay, &nHour, &nMinute, &nSecond);
662 5 : if (nRet >= 3)
663 : {
664 0 : return nRet;
665 : }
666 5 : nRet = sscanf(pszValue, "%04d-%02d-%02dT%02d:%02d:%02d", &nYear, &nMonth,
667 : &nDay, &nHour, &nMinute, &nSecond);
668 5 : if (nRet >= 3)
669 : {
670 5 : return nRet;
671 : }
672 0 : return 0;
673 : }
674 :
675 : /************************************************************************/
676 : /* IsSimpleComparison() */
677 : /************************************************************************/
678 :
679 14 : bool GDALEEDALayer::IsSimpleComparison(const swq_expr_node *poNode)
680 : {
681 28 : return poNode->eNodeType == SNT_OPERATION &&
682 14 : (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_NE ||
683 10 : poNode->nOperation == SWQ_LT || poNode->nOperation == SWQ_LE ||
684 7 : poNode->nOperation == SWQ_GT || poNode->nOperation == SWQ_GE) &&
685 11 : poNode->nSubExprCount == 2 &&
686 11 : poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
687 39 : poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
688 11 : m_oSetQueryableFields.find(poNode->papoSubExpr[0]->field_index) !=
689 25 : m_oSetQueryableFields.end();
690 : }
691 :
692 : /************************************************************************/
693 : /* GetOperatorText() */
694 : /************************************************************************/
695 :
696 6 : static const char *GetOperatorText(swq_op nOp)
697 : {
698 6 : if (nOp == SWQ_LT)
699 1 : return "<";
700 5 : if (nOp == SWQ_LE)
701 1 : return "<=";
702 4 : if (nOp == SWQ_GT)
703 1 : return ">";
704 3 : if (nOp == SWQ_GE)
705 1 : return ">=";
706 2 : if (nOp == SWQ_EQ)
707 1 : return "=";
708 1 : if (nOp == SWQ_NE)
709 1 : return "!=";
710 0 : CPLAssert(false);
711 : return "";
712 : }
713 :
714 : /************************************************************************/
715 : /* BuildFilter() */
716 : /************************************************************************/
717 :
718 26 : CPLString GDALEEDALayer::BuildFilter(swq_expr_node *poNode, bool bIsAndTopLevel)
719 : {
720 : int nYear, nMonth, nDay, nHour, nMinute, nSecond;
721 :
722 26 : if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
723 10 : poNode->nSubExprCount == 2)
724 : {
725 : // For AND, we can deal with a failure in one of the branch
726 : // since client-side will do that extra filtering
727 20 : CPLString osLeft = BuildFilter(poNode->papoSubExpr[0], bIsAndTopLevel);
728 20 : CPLString osRight = BuildFilter(poNode->papoSubExpr[1], bIsAndTopLevel);
729 10 : if (!osLeft.empty() && !osRight.empty())
730 : {
731 14 : return "(" + osLeft + " AND " + osRight + ")";
732 : }
733 3 : else if (!osLeft.empty())
734 0 : return osLeft;
735 : else
736 3 : return osRight;
737 : }
738 16 : else if (poNode->eNodeType == SNT_OPERATION &&
739 16 : poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
740 : {
741 : // For OR, we need both members to be valid
742 2 : CPLString osLeft = BuildFilter(poNode->papoSubExpr[0], false);
743 2 : CPLString osRight = BuildFilter(poNode->papoSubExpr[1], false);
744 1 : if (!osLeft.empty() && !osRight.empty())
745 : {
746 2 : return "(" + osLeft + " OR " + osRight + ")";
747 : }
748 0 : return "";
749 : }
750 15 : else if (poNode->eNodeType == SNT_OPERATION &&
751 15 : poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
752 : {
753 2 : CPLString osFilter = BuildFilter(poNode->papoSubExpr[0], false);
754 1 : if (!osFilter.empty())
755 : {
756 2 : return "(NOT " + osFilter + ")";
757 : }
758 0 : return "";
759 : }
760 14 : else if (IsSimpleComparison(poNode))
761 : {
762 6 : const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
763 : CPLString osFilter(
764 12 : m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef());
765 6 : osFilter += " ";
766 6 : osFilter += GetOperatorText(poNode->nOperation);
767 6 : osFilter += " ";
768 6 : if (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER ||
769 4 : poNode->papoSubExpr[1]->field_type == SWQ_INTEGER64)
770 : {
771 4 : osFilter +=
772 4 : CPLSPrintf("%" PRId64, poNode->papoSubExpr[1]->int_value);
773 : }
774 2 : else if (poNode->papoSubExpr[1]->field_type == SWQ_FLOAT)
775 : {
776 : osFilter +=
777 1 : CPLSPrintf("%.17g", poNode->papoSubExpr[1]->float_value);
778 : }
779 : else
780 : {
781 1 : osFilter += "\"";
782 1 : osFilter += poNode->papoSubExpr[1]->string_value;
783 1 : osFilter += "\"";
784 : }
785 6 : return osFilter;
786 : }
787 6 : else if (bIsAndTopLevel && poNode->eNodeType == SNT_OPERATION &&
788 6 : (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_GE) &&
789 4 : poNode->nSubExprCount == 2 &&
790 4 : poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
791 4 : poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
792 4 : poNode->papoSubExpr[0]->field_index ==
793 17 : m_poFeatureDefn->GetFieldIndex("startTime") &&
794 3 : poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP)
795 : {
796 3 : int nTerms = GDALEEDALayerParseDateTime(
797 3 : poNode->papoSubExpr[1]->string_value, SWQ_GE, nYear, nMonth, nDay,
798 : nHour, nMinute, nSecond);
799 3 : if (nTerms >= 3)
800 : {
801 : m_osStartTime = CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
802 3 : nMonth, nDay, nHour, nMinute, nSecond);
803 : }
804 : else
805 : {
806 0 : m_bFilterMustBeClientSideEvaluated = true;
807 : }
808 3 : return "";
809 : }
810 3 : else if (bIsAndTopLevel && poNode->eNodeType == SNT_OPERATION &&
811 3 : (poNode->nOperation == SWQ_EQ || poNode->nOperation == SWQ_LE) &&
812 2 : poNode->nSubExprCount == 2 &&
813 2 : poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
814 2 : poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT &&
815 2 : poNode->papoSubExpr[0]->field_index ==
816 10 : m_poFeatureDefn->GetFieldIndex("endTime") &&
817 2 : poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP)
818 : {
819 2 : int nTerms = GDALEEDALayerParseDateTime(
820 2 : poNode->papoSubExpr[1]->string_value, SWQ_LE, nYear, nMonth, nDay,
821 : nHour, nMinute, nSecond);
822 2 : if (nTerms >= 3)
823 : {
824 2 : if (poNode->nOperation == SWQ_EQ && nTerms == 6)
825 : {
826 0 : if (nSecond < 59)
827 0 : nSecond++;
828 0 : else if (nMinute < 59)
829 0 : nMinute++;
830 0 : else if (nHour < 23)
831 0 : nHour++;
832 : else
833 0 : nDay++;
834 : }
835 : m_osEndTime = CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
836 2 : nMonth, nDay, nHour, nMinute, nSecond);
837 : }
838 : else
839 : {
840 0 : m_bFilterMustBeClientSideEvaluated = true;
841 : }
842 2 : return "";
843 : }
844 9 : else if (poNode->eNodeType == SNT_OPERATION &&
845 3 : poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 &&
846 9 : poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
847 3 : m_oSetQueryableFields.find(poNode->papoSubExpr[0]->field_index) !=
848 6 : m_oSetQueryableFields.end())
849 : {
850 3 : const int nFieldIdx = poNode->papoSubExpr[0]->field_index;
851 6 : CPLString osFilter;
852 :
853 7 : for (int i = 1; i < poNode->nSubExprCount; i++)
854 : {
855 4 : if (!osFilter.empty())
856 1 : osFilter += " OR ";
857 4 : osFilter += m_poFeatureDefn->GetFieldDefn(nFieldIdx)->GetNameRef();
858 4 : osFilter += " = ";
859 4 : if (poNode->papoSubExpr[i]->field_type == SWQ_INTEGER ||
860 3 : poNode->papoSubExpr[i]->field_type == SWQ_INTEGER64)
861 : {
862 1 : osFilter +=
863 1 : CPLSPrintf("%" PRId64, poNode->papoSubExpr[i]->int_value);
864 : }
865 3 : else if (poNode->papoSubExpr[i]->field_type == SWQ_FLOAT)
866 : {
867 : osFilter +=
868 1 : CPLSPrintf("%.17g", poNode->papoSubExpr[i]->float_value);
869 : }
870 : else
871 : {
872 2 : osFilter += "\"";
873 2 : osFilter += poNode->papoSubExpr[i]->string_value;
874 2 : osFilter += "\"";
875 : }
876 : }
877 :
878 3 : return osFilter;
879 : }
880 :
881 0 : m_bFilterMustBeClientSideEvaluated = true;
882 0 : return "";
883 : }
884 :
885 : /************************************************************************/
886 : /* SetAttributeFilter() */
887 : /************************************************************************/
888 :
889 5 : OGRErr GDALEEDALayer::SetAttributeFilter(const char *pszQuery)
890 :
891 : {
892 5 : m_osAttributeFilter.clear();
893 5 : m_osStartTime.clear();
894 5 : m_osEndTime.clear();
895 5 : m_bFilterMustBeClientSideEvaluated = false;
896 :
897 5 : if (pszQuery && STARTS_WITH_CI(pszQuery, "EEDA:"))
898 : {
899 1 : m_osAttributeFilter = pszQuery + strlen("EEDA:");
900 1 : OGRLayer::SetAttributeFilter(nullptr);
901 1 : ResetReading();
902 1 : return OGRERR_NONE;
903 : }
904 :
905 4 : OGRErr eErr = OGRLayer::SetAttributeFilter(pszQuery);
906 :
907 4 : if (m_poAttrQuery != nullptr)
908 : {
909 : swq_expr_node *poNode =
910 3 : static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
911 :
912 : #ifndef PLUGIN
913 3 : poNode->ReplaceBetweenByGEAndLERecurse();
914 : #endif
915 :
916 3 : m_osAttributeFilter = BuildFilter(poNode, true);
917 3 : if (m_osAttributeFilter.empty() && m_osStartTime.empty() &&
918 0 : m_osEndTime.empty())
919 : {
920 0 : CPLDebug("EEDA", "Full filter will be evaluated on client side.");
921 : }
922 3 : else if (m_bFilterMustBeClientSideEvaluated)
923 : {
924 0 : CPLDebug(
925 : "EEDA",
926 : "Only part of the filter will be evaluated on server side.");
927 : }
928 : }
929 :
930 4 : ResetReading();
931 :
932 4 : return eErr;
933 : }
934 :
935 : /************************************************************************/
936 : /* ISetSpatialFilter() */
937 : /************************************************************************/
938 :
939 2 : OGRErr GDALEEDALayer::ISetSpatialFilter(int /* iGeomField */,
940 : const OGRGeometry *poGeomIn)
941 : {
942 2 : if (poGeomIn)
943 : {
944 1 : OGREnvelope sEnvelope;
945 1 : poGeomIn->getEnvelope(&sEnvelope);
946 1 : if (sEnvelope.MinX == sEnvelope.MaxX &&
947 0 : sEnvelope.MinY == sEnvelope.MaxY)
948 : {
949 0 : OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
950 0 : InstallFilter(&p);
951 : }
952 : else
953 1 : InstallFilter(poGeomIn);
954 : }
955 : else
956 1 : InstallFilter(poGeomIn);
957 :
958 2 : ResetReading();
959 2 : return OGRERR_NONE;
960 : }
961 :
962 : /************************************************************************/
963 : /* GetExtent() */
964 : /************************************************************************/
965 :
966 1 : OGRErr GDALEEDALayer::IGetExtent(int /* iGeomField*/, OGREnvelope *psExtent,
967 : bool /* bForce */)
968 : {
969 1 : psExtent->MinX = -180;
970 1 : psExtent->MinY = -90;
971 1 : psExtent->MaxX = 180;
972 1 : psExtent->MaxY = 90;
973 1 : return OGRERR_NONE;
974 : }
975 :
976 : /************************************************************************/
977 : /* TestCapability() */
978 : /************************************************************************/
979 :
980 5 : int GDALEEDALayer::TestCapability(const char *pszCap)
981 : {
982 5 : if (EQUAL(pszCap, OLCStringsAsUTF8))
983 4 : return TRUE;
984 1 : return FALSE;
985 : }
986 :
987 : /************************************************************************/
988 : /* GDALEEDADataset() */
989 : /************************************************************************/
990 :
991 7 : GDALEEDADataset::GDALEEDADataset() : m_poLayer(nullptr)
992 : {
993 7 : }
994 :
995 : /************************************************************************/
996 : /* ~GDALEEDADataset() */
997 : /************************************************************************/
998 :
999 14 : GDALEEDADataset::~GDALEEDADataset()
1000 : {
1001 7 : delete m_poLayer;
1002 14 : }
1003 :
1004 : /************************************************************************/
1005 : /* GetLayer() */
1006 : /************************************************************************/
1007 :
1008 7 : OGRLayer *GDALEEDADataset::GetLayer(int idx)
1009 : {
1010 7 : if (idx == 0)
1011 7 : return m_poLayer;
1012 0 : return nullptr;
1013 : }
1014 :
1015 : /************************************************************************/
1016 : /* RunRequest() */
1017 : /************************************************************************/
1018 :
1019 13 : json_object *GDALEEDADataset::RunRequest(const CPLString &osURL)
1020 : {
1021 13 : char **papszOptions = GetBaseHTTPOptions();
1022 13 : if (papszOptions == nullptr)
1023 0 : return nullptr;
1024 13 : CPLHTTPResult *psResult = EEDAHTTPFetch(osURL, papszOptions);
1025 13 : CSLDestroy(papszOptions);
1026 13 : if (psResult == nullptr)
1027 0 : return nullptr;
1028 13 : if (psResult->pszErrBuf != nullptr)
1029 : {
1030 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
1031 0 : psResult->pabyData
1032 : ? reinterpret_cast<const char *>(psResult->pabyData)
1033 : : psResult->pszErrBuf);
1034 0 : CPLHTTPDestroyResult(psResult);
1035 0 : return nullptr;
1036 : }
1037 :
1038 13 : if (psResult->pabyData == nullptr)
1039 : {
1040 0 : CPLError(CE_Failure, CPLE_AppDefined,
1041 : "Empty content returned by server");
1042 0 : CPLHTTPDestroyResult(psResult);
1043 0 : return nullptr;
1044 : }
1045 :
1046 13 : const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
1047 : #ifdef DEBUG_VERBOSE
1048 : CPLDebug("EEDA", "%s", pszText);
1049 : #endif
1050 :
1051 13 : json_object *poObj = nullptr;
1052 13 : if (!OGRJSonParse(pszText, &poObj, true))
1053 : {
1054 0 : CPLHTTPDestroyResult(psResult);
1055 0 : return nullptr;
1056 : }
1057 :
1058 13 : CPLHTTPDestroyResult(psResult);
1059 :
1060 13 : if (json_object_get_type(poObj) != json_type_object)
1061 : {
1062 0 : CPLError(CE_Failure, CPLE_AppDefined,
1063 : "Return is not a JSON dictionary");
1064 0 : json_object_put(poObj);
1065 0 : return nullptr;
1066 : }
1067 :
1068 13 : return poObj;
1069 : }
1070 :
1071 : /************************************************************************/
1072 : /* GDALEEDADatasetGetConf() */
1073 : /************************************************************************/
1074 :
1075 7 : static json_object *GDALEEDADatasetGetConf()
1076 : {
1077 7 : const char *pszConfFile = CPLFindFile("gdal", "eedaconf.json");
1078 7 : if (pszConfFile == nullptr)
1079 : {
1080 0 : CPLDebug("EEDA", "Cannot find eedaconf.json");
1081 0 : return nullptr;
1082 : }
1083 :
1084 7 : GByte *pabyRet = nullptr;
1085 7 : if (!VSIIngestFile(nullptr, pszConfFile, &pabyRet, nullptr, -1))
1086 : {
1087 0 : return nullptr;
1088 : }
1089 :
1090 7 : json_object *poRoot = nullptr;
1091 7 : const char *pzText = reinterpret_cast<char *>(pabyRet);
1092 7 : if (!OGRJSonParse(pzText, &poRoot))
1093 : {
1094 0 : VSIFree(pabyRet);
1095 0 : return nullptr;
1096 : }
1097 7 : VSIFree(pabyRet);
1098 :
1099 7 : if (json_object_get_type(poRoot) != json_type_object)
1100 : {
1101 0 : json_object_put(poRoot);
1102 0 : return nullptr;
1103 : }
1104 :
1105 7 : return poRoot;
1106 : }
1107 :
1108 : /************************************************************************/
1109 : /* Open() */
1110 : /************************************************************************/
1111 :
1112 7 : bool GDALEEDADataset::Open(GDALOpenInfo *poOpenInfo)
1113 : {
1114 : m_osBaseURL = CPLGetConfigOption(
1115 7 : "EEDA_URL", "https://earthengine-highvolume.googleapis.com/v1alpha/");
1116 :
1117 : CPLString osCollection =
1118 14 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "COLLECTION", "");
1119 7 : if (osCollection.empty())
1120 : {
1121 : char **papszTokens =
1122 7 : CSLTokenizeString2(poOpenInfo->pszFilename, ":", 0);
1123 7 : if (CSLCount(papszTokens) < 2)
1124 : {
1125 0 : CPLError(CE_Failure, CPLE_AppDefined,
1126 : "No collection specified in connection string or "
1127 : "COLLECTION open option");
1128 0 : CSLDestroy(papszTokens);
1129 0 : return false;
1130 : }
1131 7 : osCollection = papszTokens[1];
1132 7 : CSLDestroy(papszTokens);
1133 : }
1134 14 : CPLString osCollectionName = ConvertPathToName(osCollection);
1135 :
1136 7 : json_object *poRootConf = GDALEEDADatasetGetConf();
1137 7 : if (poRootConf)
1138 : {
1139 : json_object *poLayerConf =
1140 7 : CPL_json_object_object_get(poRootConf, osCollection);
1141 8 : if (poLayerConf != nullptr &&
1142 1 : json_object_get_type(poLayerConf) == json_type_object)
1143 : {
1144 1 : m_poLayer = new GDALEEDALayer(this, osCollection, osCollectionName,
1145 1 : nullptr, poLayerConf);
1146 1 : json_object_put(poRootConf);
1147 1 : return true;
1148 : }
1149 6 : json_object_put(poRootConf);
1150 : }
1151 :
1152 : // Issue request to get layer schema
1153 : json_object *poRootAsset =
1154 6 : RunRequest(m_osBaseURL + osCollectionName + ":listImages?pageSize=1");
1155 6 : if (poRootAsset == nullptr)
1156 0 : return false;
1157 :
1158 6 : json_object *poAssets = CPL_json_object_object_get(poRootAsset, "images");
1159 12 : if (poAssets == nullptr ||
1160 12 : json_object_get_type(poAssets) != json_type_array ||
1161 6 : json_object_array_length(poAssets) != 1)
1162 : {
1163 0 : CPLError(CE_Failure, CPLE_AppDefined, "No assets");
1164 0 : json_object_put(poRootAsset);
1165 0 : return false;
1166 : }
1167 6 : json_object *poAsset = json_object_array_get_idx(poAssets, 0);
1168 6 : if (poAsset == nullptr || json_object_get_type(poAsset) != json_type_object)
1169 : {
1170 0 : CPLError(CE_Failure, CPLE_AppDefined, "No assets");
1171 0 : json_object_put(poRootAsset);
1172 0 : return false;
1173 : }
1174 :
1175 6 : m_poLayer = new GDALEEDALayer(this, osCollection, osCollectionName, poAsset,
1176 6 : nullptr);
1177 6 : json_object_put(poRootAsset);
1178 :
1179 6 : return true;
1180 : }
1181 :
1182 : /************************************************************************/
1183 : /* GDALEEDAdentify() */
1184 : /************************************************************************/
1185 :
1186 54111 : static int GDALEEDAdentify(GDALOpenInfo *poOpenInfo)
1187 : {
1188 54111 : return STARTS_WITH_CI(poOpenInfo->pszFilename, "EEDA:");
1189 : }
1190 :
1191 : /************************************************************************/
1192 : /* GDALEEDAOpen() */
1193 : /************************************************************************/
1194 :
1195 7 : static GDALDataset *GDALEEDAOpen(GDALOpenInfo *poOpenInfo)
1196 : {
1197 7 : if (!GDALEEDAdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
1198 0 : return nullptr;
1199 :
1200 7 : GDALEEDADataset *poDS = new GDALEEDADataset();
1201 7 : if (!poDS->Open(poOpenInfo))
1202 : {
1203 0 : delete poDS;
1204 0 : return nullptr;
1205 : }
1206 7 : return poDS;
1207 : }
1208 :
1209 : /************************************************************************/
1210 : /* GDALRegister_EEDA() */
1211 : /************************************************************************/
1212 :
1213 1686 : void GDALRegister_EEDA()
1214 :
1215 : {
1216 1686 : if (GDALGetDriverByName("EEDA") != nullptr)
1217 302 : return;
1218 :
1219 1384 : GDALDriver *poDriver = new GDALDriver();
1220 :
1221 1384 : poDriver->SetDescription("EEDA");
1222 1384 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
1223 1384 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Earth Engine Data API");
1224 1384 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/eeda.html");
1225 1384 : poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "EEDA:");
1226 1384 : poDriver->SetMetadataItem(
1227 : GDAL_DMD_OPENOPTIONLIST,
1228 : "<OpenOptionList>"
1229 : " <Option name='COLLECTION' type='string' "
1230 : "description='Collection name'/>"
1231 : " <Option name='VSI_PATH_FOR_AUTH' type='string' "
1232 : "description='/vsigs/... path onto which a "
1233 : "GOOGLE_APPLICATION_CREDENTIALS path specific "
1234 : "option is set'/>"
1235 1384 : "</OpenOptionList>");
1236 :
1237 1384 : poDriver->pfnOpen = GDALEEDAOpen;
1238 1384 : poDriver->pfnIdentify = GDALEEDAdentify;
1239 :
1240 1384 : GetGDALDriverManager()->RegisterDriver(poDriver);
1241 :
1242 : #ifdef GDAL_ENABLE_DRIVER_EEDA_PLUGIN
1243 : GDALRegister_EEDAI();
1244 : #endif
1245 : }
|