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