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.xorig;
525 2 : double dfULY = aoBands[0].gt.yorig;
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 = std::min(
532 : dfMinPixelSize,
533 2 : std::min(aoBands[i].gt.xscale, fabs(aoBands[i].gt.yscale)));
534 2 : if (osSRS != aoBands[i].osWKT)
535 : {
536 0 : osSRS.clear();
537 : }
538 4 : if (dfULX != aoBands[i].gt.xorig ||
539 2 : dfULY != aoBands[i].gt.yorig)
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) const
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 : const OGRLayer *GDALEEDADataset::GetLayer(int idx) const
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 63951 : static int GDALEEDAdentify(GDALOpenInfo *poOpenInfo)
1185 : {
1186 63951 : 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 2063 : void GDALRegister_EEDA()
1212 :
1213 : {
1214 2063 : if (GDALGetDriverByName("EEDA") != nullptr)
1215 283 : return;
1216 :
1217 1780 : GDALDriver *poDriver = new GDALDriver();
1218 :
1219 1780 : poDriver->SetDescription("EEDA");
1220 1780 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
1221 1780 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Earth Engine Data API");
1222 1780 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/eeda.html");
1223 1780 : poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "EEDA:");
1224 1780 : 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 1780 : "</OpenOptionList>");
1234 :
1235 1780 : poDriver->pfnOpen = GDALEEDAOpen;
1236 1780 : poDriver->pfnIdentify = GDALEEDAdentify;
1237 :
1238 1780 : GetGDALDriverManager()->RegisterDriver(poDriver);
1239 :
1240 : #ifdef GDAL_ENABLE_DRIVER_EEDA_PLUGIN
1241 : GDALRegister_EEDAI();
1242 : #endif
1243 : }
|