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