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