Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: PlanetLabs scene driver
4 : * Purpose: Implements OGRPLScenesDataV1Dataset
5 : * Author: Even Rouault, even dot rouault at spatialys.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2016, Planet Labs
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_plscenes.h"
14 : #include "ogrlibjsonutils.h"
15 : #include <time.h>
16 :
17 : /************************************************************************/
18 : /* OGRPLScenesDataV1Dataset() */
19 : /************************************************************************/
20 :
21 29 : OGRPLScenesDataV1Dataset::OGRPLScenesDataV1Dataset()
22 : : m_bLayerListInitialized(false), m_bMustCleanPersistent(false),
23 29 : m_nLayers(0), m_papoLayers(nullptr), m_bFollowLinks(false)
24 : {
25 29 : }
26 :
27 : /************************************************************************/
28 : /* ~OGRPLScenesDataV1Dataset() */
29 : /************************************************************************/
30 :
31 58 : OGRPLScenesDataV1Dataset::~OGRPLScenesDataV1Dataset()
32 : {
33 41 : for (int i = 0; i < m_nLayers; i++)
34 12 : delete m_papoLayers[i];
35 29 : CPLFree(m_papoLayers);
36 :
37 29 : if (m_bMustCleanPersistent)
38 : {
39 20 : char **papszOptions = CSLSetNameValue(nullptr, "CLOSE_PERSISTENT",
40 : CPLSPrintf("PLSCENES:%p", this));
41 20 : CPLHTTPDestroyResult(CPLHTTPFetch(m_osBaseURL, papszOptions));
42 20 : CSLDestroy(papszOptions);
43 : }
44 58 : }
45 :
46 : /************************************************************************/
47 : /* GetLayer() */
48 : /************************************************************************/
49 :
50 27 : OGRLayer *OGRPLScenesDataV1Dataset::GetLayer(int idx)
51 : {
52 27 : if (idx < 0 || idx >= GetLayerCount())
53 2 : return nullptr;
54 25 : return m_papoLayers[idx];
55 : }
56 :
57 : /************************************************************************/
58 : /* GetLayerCount() */
59 : /************************************************************************/
60 :
61 99 : int OGRPLScenesDataV1Dataset::GetLayerCount()
62 : {
63 99 : if (!m_bLayerListInitialized)
64 : {
65 8 : m_bLayerListInitialized = true;
66 8 : EstablishLayerList();
67 : }
68 99 : return m_nLayers;
69 : }
70 :
71 : /************************************************************************/
72 : /* ParseItemType() */
73 : /************************************************************************/
74 :
75 17 : OGRLayer *OGRPLScenesDataV1Dataset::ParseItemType(json_object *poItemType)
76 : {
77 33 : if (poItemType == nullptr ||
78 16 : json_object_get_type(poItemType) != json_type_object)
79 2 : return nullptr;
80 15 : json_object *poId = CPL_json_object_object_get(poItemType, "id");
81 15 : if (poId == nullptr || json_object_get_type(poId) != json_type_string)
82 2 : return nullptr;
83 :
84 26 : CPLString osDisplayDescription;
85 : json_object *poDisplayDescription =
86 13 : CPL_json_object_object_get(poItemType, "display_description");
87 16 : if (poDisplayDescription != nullptr &&
88 3 : json_object_get_type(poDisplayDescription) == json_type_string)
89 3 : osDisplayDescription = json_object_get_string(poDisplayDescription);
90 26 : CPLString osDisplayName;
91 : json_object *poDisplayName =
92 13 : CPL_json_object_object_get(poItemType, "display_name");
93 16 : if (poDisplayName != nullptr &&
94 3 : json_object_get_type(poDisplayName) == json_type_string)
95 3 : osDisplayName = json_object_get_string(poDisplayName);
96 :
97 13 : const char *pszId = json_object_get_string(poId);
98 :
99 : // The layer might already exist if GetLayerByName() is called before
100 : // GetLayer()/GetLayerCount() is
101 :
102 : // Prevent GetLayerCount() from calling EstablishLayerList()
103 13 : bool bLayerListInitializedBackup = m_bLayerListInitialized;
104 13 : m_bLayerListInitialized = true;
105 13 : OGRLayer *poExistingLayer = GDALDataset::GetLayerByName(pszId);
106 13 : m_bLayerListInitialized = bLayerListInitializedBackup;
107 13 : if (poExistingLayer != nullptr)
108 1 : return poExistingLayer;
109 :
110 12 : OGRPLScenesDataV1Layer *poPLLayer = new OGRPLScenesDataV1Layer(this, pszId);
111 12 : if (!osDisplayName.empty())
112 3 : poPLLayer->SetMetadataItem("SHORT_DESCRIPTION", osDisplayName.c_str());
113 12 : if (!osDisplayDescription.empty())
114 3 : poPLLayer->SetMetadataItem("DESCRIPTION", osDisplayDescription.c_str());
115 24 : m_papoLayers = (OGRPLScenesDataV1Layer **)CPLRealloc(
116 12 : m_papoLayers, sizeof(OGRPLScenesDataV1Layer *) * (m_nLayers + 1));
117 12 : m_papoLayers[m_nLayers++] = poPLLayer;
118 12 : return poPLLayer;
119 : }
120 :
121 : /************************************************************************/
122 : /* ParseItemTypes() */
123 : /************************************************************************/
124 :
125 10 : bool OGRPLScenesDataV1Dataset::ParseItemTypes(json_object *poObj,
126 : CPLString &osNext)
127 : {
128 10 : json_object *poItemTypes = CPL_json_object_object_get(poObj, "item_types");
129 19 : if (poItemTypes == nullptr ||
130 9 : json_object_get_type(poItemTypes) != json_type_array)
131 : {
132 1 : CPLError(CE_Failure, CPLE_AppDefined,
133 : "Missing item_types object, or not of type array");
134 1 : return false;
135 : }
136 9 : const auto nCatalogsLength = json_object_array_length(poItemTypes);
137 22 : for (auto i = decltype(nCatalogsLength){0}; i < nCatalogsLength; i++)
138 : {
139 13 : json_object *poItemType = json_object_array_get_idx(poItemTypes, i);
140 13 : ParseItemType(poItemType);
141 : }
142 :
143 : // Is there a next page ?
144 9 : osNext = "";
145 9 : json_object *poLinks = CPL_json_object_object_get(poObj, "_links");
146 9 : if (poLinks && json_object_get_type(poLinks) == json_type_object)
147 : {
148 2 : json_object *poNext = CPL_json_object_object_get(poLinks, "_next");
149 2 : if (poNext && json_object_get_type(poNext) == json_type_string)
150 : {
151 2 : osNext = json_object_get_string(poNext);
152 : }
153 : }
154 :
155 9 : return true;
156 : }
157 :
158 : /************************************************************************/
159 : /* EstablishLayerList() */
160 : /************************************************************************/
161 :
162 8 : void OGRPLScenesDataV1Dataset::EstablishLayerList()
163 : {
164 16 : CPLString osURL(m_osNextItemTypesPageURL);
165 8 : m_osNextItemTypesPageURL = "";
166 :
167 9 : while (!osURL.empty())
168 : {
169 2 : json_object *poObj = RunRequest(osURL);
170 2 : if (poObj == nullptr)
171 1 : break;
172 1 : if (!ParseItemTypes(poObj, osURL))
173 : {
174 0 : json_object_put(poObj);
175 0 : break;
176 : }
177 1 : json_object_put(poObj);
178 : }
179 8 : }
180 :
181 : /************************************************************************/
182 : /* GetLayerByName() */
183 : /************************************************************************/
184 :
185 14 : OGRLayer *OGRPLScenesDataV1Dataset::GetLayerByName(const char *pszName)
186 : {
187 : // Prevent GetLayerCount() from calling EstablishLayerList()
188 14 : bool bLayerListInitializedBackup = m_bLayerListInitialized;
189 14 : m_bLayerListInitialized = true;
190 14 : OGRLayer *poRet = GDALDataset::GetLayerByName(pszName);
191 14 : m_bLayerListInitialized = bLayerListInitializedBackup;
192 14 : if (poRet != nullptr)
193 3 : return poRet;
194 :
195 33 : CPLString osURL(m_osBaseURL + "item-types/" + pszName);
196 11 : json_object *poObj = RunRequest(osURL);
197 11 : if (poObj == nullptr)
198 7 : return nullptr;
199 4 : poRet = ParseItemType(poObj);
200 4 : json_object_put(poObj);
201 4 : return poRet;
202 : }
203 :
204 : /************************************************************************/
205 : /* GetBaseHTTPOptions() */
206 : /************************************************************************/
207 :
208 75 : char **OGRPLScenesDataV1Dataset::GetBaseHTTPOptions()
209 : {
210 75 : m_bMustCleanPersistent = true;
211 :
212 75 : char **papszOptions = nullptr;
213 : papszOptions =
214 75 : CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=PLSCENES:%p", this));
215 75 : papszOptions = CSLAddString(
216 : papszOptions,
217 : CPLSPrintf("HEADERS=Authorization: api-key %s", m_osAPIKey.c_str()));
218 :
219 75 : return papszOptions;
220 : }
221 :
222 : /************************************************************************/
223 : /* RunRequest() */
224 : /************************************************************************/
225 :
226 75 : json_object *OGRPLScenesDataV1Dataset::RunRequest(const char *pszURL,
227 : int bQuiet404Error,
228 : const char *pszHTTPVerb,
229 : bool bExpectJSonReturn,
230 : const char *pszPostContent)
231 : {
232 75 : char **papszOptions = CSLAddString(GetBaseHTTPOptions(), nullptr);
233 : // We need to set it each time as CURL would reuse the previous value
234 : // if reusing the same connection
235 75 : papszOptions = CSLSetNameValue(papszOptions, "CUSTOMREQUEST", pszHTTPVerb);
236 75 : if (pszPostContent != nullptr)
237 : {
238 18 : CPLString osHeaders = CSLFetchNameValueDef(papszOptions, "HEADERS", "");
239 18 : if (!osHeaders.empty())
240 18 : osHeaders += "\r\n";
241 18 : osHeaders += "Content-Type: application/json";
242 18 : papszOptions = CSLSetNameValue(papszOptions, "HEADERS", osHeaders);
243 : papszOptions =
244 18 : CSLSetNameValue(papszOptions, "POSTFIELDS", pszPostContent);
245 : }
246 75 : papszOptions = CSLSetNameValue(papszOptions, "MAX_RETRY", "3");
247 75 : CPLHTTPResult *psResult = nullptr;
248 75 : if (STARTS_WITH(m_osBaseURL, "/vsimem/") && STARTS_WITH(pszURL, "/vsimem/"))
249 : {
250 11 : psResult = (CPLHTTPResult *)CPLCalloc(1, sizeof(CPLHTTPResult));
251 11 : vsi_l_offset nDataLengthLarge = 0;
252 22 : CPLString osURL(pszURL);
253 11 : if (osURL.back() == '/')
254 6 : osURL.pop_back();
255 11 : if (pszPostContent != nullptr)
256 : {
257 3 : osURL += "&POSTFIELDS=";
258 3 : osURL += pszPostContent;
259 : }
260 11 : CPLDebug("PLSCENES", "Fetching %s", osURL.c_str());
261 11 : GByte *pabyBuf = VSIGetMemFileBuffer(osURL, &nDataLengthLarge, FALSE);
262 11 : size_t nDataLength = static_cast<size_t>(nDataLengthLarge);
263 11 : if (pabyBuf)
264 : {
265 8 : psResult->pabyData = (GByte *)VSI_MALLOC_VERBOSE(1 + nDataLength);
266 8 : if (psResult->pabyData)
267 : {
268 8 : memcpy(psResult->pabyData, pabyBuf, nDataLength);
269 8 : psResult->pabyData[nDataLength] = 0;
270 : }
271 : }
272 : else
273 : {
274 3 : psResult->pszErrBuf = CPLStrdup(
275 : CPLSPrintf("Error 404. Cannot find %s", osURL.c_str()));
276 : }
277 : }
278 : else
279 : {
280 64 : if (bQuiet404Error)
281 0 : CPLPushErrorHandler(CPLQuietErrorHandler);
282 64 : psResult = CPLHTTPFetch(pszURL, papszOptions);
283 64 : if (bQuiet404Error)
284 0 : CPLPopErrorHandler();
285 : }
286 75 : CSLDestroy(papszOptions);
287 :
288 75 : if (pszPostContent != nullptr && m_bMustCleanPersistent)
289 : {
290 18 : papszOptions = CSLSetNameValue(nullptr, "CLOSE_PERSISTENT",
291 : CPLSPrintf("PLSCENES:%p", this));
292 18 : CPLHTTPDestroyResult(CPLHTTPFetch(m_osBaseURL, papszOptions));
293 18 : CSLDestroy(papszOptions);
294 18 : m_bMustCleanPersistent = false;
295 : }
296 :
297 75 : if (psResult->pszErrBuf != nullptr)
298 : {
299 15 : if (!(bQuiet404Error && strstr(psResult->pszErrBuf, "404")))
300 : {
301 15 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
302 15 : psResult->pabyData ? (const char *)psResult->pabyData
303 : : psResult->pszErrBuf);
304 : }
305 15 : CPLHTTPDestroyResult(psResult);
306 15 : return nullptr;
307 : }
308 :
309 60 : if (!bExpectJSonReturn &&
310 0 : (psResult->pabyData == nullptr || psResult->nDataLen == 0))
311 : {
312 0 : CPLHTTPDestroyResult(psResult);
313 0 : return nullptr;
314 : }
315 :
316 60 : if (psResult->pabyData == nullptr)
317 : {
318 2 : CPLError(CE_Failure, CPLE_AppDefined,
319 : "Empty content returned by server");
320 2 : CPLHTTPDestroyResult(psResult);
321 2 : return nullptr;
322 : }
323 :
324 58 : const char *pszText = reinterpret_cast<const char *>(psResult->pabyData);
325 : #ifdef DEBUG_VERBOSE
326 : CPLDebug("PLScenes", "%s", pszText);
327 : #endif
328 :
329 58 : json_object *poObj = nullptr;
330 58 : if (!OGRJSonParse(pszText, &poObj, true))
331 : {
332 2 : CPLHTTPDestroyResult(psResult);
333 2 : return nullptr;
334 : }
335 :
336 56 : CPLHTTPDestroyResult(psResult);
337 :
338 56 : if (json_object_get_type(poObj) != json_type_object)
339 : {
340 0 : CPLError(CE_Failure, CPLE_AppDefined,
341 : "Return is not a JSON dictionary");
342 0 : json_object_put(poObj);
343 0 : poObj = nullptr;
344 : }
345 :
346 56 : return poObj;
347 : }
348 :
349 : /************************************************************************/
350 : /* InsertAPIKeyInURL() */
351 : /************************************************************************/
352 :
353 5 : CPLString OGRPLScenesDataV1Dataset::InsertAPIKeyInURL(CPLString osURL)
354 : {
355 5 : if (STARTS_WITH(osURL, "http://"))
356 : {
357 4 : osURL = "http://" + m_osAPIKey + ":@" + osURL.substr(strlen("http://"));
358 : }
359 1 : else if (STARTS_WITH(osURL, "https://"))
360 : {
361 : osURL =
362 0 : "https://" + m_osAPIKey + ":@" + osURL.substr(strlen("https://"));
363 : }
364 5 : return osURL;
365 : }
366 :
367 : /************************************************************************/
368 : /* OpenRasterScene() */
369 : /************************************************************************/
370 :
371 13 : GDALDataset *OGRPLScenesDataV1Dataset::OpenRasterScene(GDALOpenInfo *poOpenInfo,
372 : CPLString osScene,
373 : char **papszOptions)
374 : {
375 13 : if (!(poOpenInfo->nOpenFlags & GDAL_OF_RASTER))
376 : {
377 0 : CPLError(CE_Failure, CPLE_AppDefined,
378 : "The scene option must only be used with vector access");
379 0 : return nullptr;
380 : }
381 :
382 26 : int nActivationTimeout = atoi(CSLFetchNameValueDef(
383 13 : poOpenInfo->papszOpenOptions, "ACTIVATION_TIMEOUT", "3600"));
384 :
385 13 : for (char **papszIter = papszOptions; papszIter && *papszIter; papszIter++)
386 : {
387 1 : char *pszKey = nullptr;
388 1 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
389 1 : if (pszValue != nullptr)
390 : {
391 1 : if (!EQUAL(pszKey, "api_key") && !EQUAL(pszKey, "scene") &&
392 1 : !EQUAL(pszKey, "product_type") && !EQUAL(pszKey, "asset") &&
393 1 : !EQUAL(pszKey, "catalog") && !EQUAL(pszKey, "itemtypes") &&
394 1 : !EQUAL(pszKey, "version") && !EQUAL(pszKey, "follow_links") &&
395 1 : !EQUAL(pszKey, "metadata"))
396 : {
397 1 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported option %s",
398 : pszKey);
399 1 : CPLFree(pszKey);
400 1 : return nullptr;
401 : }
402 0 : CPLFree(pszKey);
403 : }
404 : }
405 :
406 12 : const char *pszCatalog = CSLFetchNameValueDef(
407 : papszOptions, "itemtypes",
408 : CSLFetchNameValueDef(
409 : papszOptions, "catalog",
410 : CSLFetchNameValueDef(
411 12 : poOpenInfo->papszOpenOptions, "ITEMTYPES",
412 12 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "CATALOG"))));
413 12 : if (pszCatalog == nullptr)
414 : {
415 1 : CPLError(CE_Failure, CPLE_AppDefined, "Missing catalog");
416 1 : return nullptr;
417 : }
418 :
419 11 : const char *pszProductType = CSLFetchNameValueDef(
420 : papszOptions, "asset",
421 : CSLFetchNameValueDef(
422 : papszOptions, "product_type",
423 11 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "ASSET",
424 11 : CSLFetchNameValue(poOpenInfo->papszOpenOptions,
425 : "PRODUCT_TYPE"))));
426 :
427 22 : CPLString osRasterURL;
428 11 : osRasterURL = m_osBaseURL;
429 11 : osRasterURL += "item-types/";
430 11 : osRasterURL += pszCatalog;
431 11 : osRasterURL += "/items/";
432 11 : osRasterURL += osScene;
433 11 : osRasterURL += "/assets/";
434 :
435 11 : time_t nStartTime = time(nullptr);
436 18 : retry:
437 18 : time_t nCurrentTime = time(nullptr);
438 18 : if (nCurrentTime - nStartTime > nActivationTimeout)
439 : {
440 2 : CPLError(CE_Failure, CPLE_AppDefined, "Activation timeout reached");
441 2 : return nullptr;
442 : }
443 16 : json_object *poObj = RunRequest(osRasterURL);
444 16 : if (poObj == nullptr)
445 1 : return nullptr;
446 :
447 15 : json_object *poSubObj = CPL_json_object_object_get(
448 : poObj, pszProductType ? pszProductType : "visual");
449 15 : if (poSubObj == nullptr)
450 : {
451 2 : if (pszProductType != nullptr && !EQUAL(pszProductType, "LIST"))
452 : {
453 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find asset %s",
454 : pszProductType);
455 1 : json_object_put(poObj);
456 : }
457 : else
458 : {
459 : json_object_iter it;
460 1 : it.key = nullptr;
461 1 : it.val = nullptr;
462 1 : it.entry = nullptr;
463 1 : char **papszSubdatasets = nullptr;
464 1 : int nSubDataset = 0;
465 2 : json_object_object_foreachC(poObj, it)
466 : {
467 1 : ++nSubDataset;
468 1 : papszSubdatasets = CSLSetNameValue(
469 : papszSubdatasets,
470 : CPLSPrintf("SUBDATASET_%d_NAME", nSubDataset),
471 : CPLSPrintf("Scene=%s of item types %s, asset %s",
472 : osScene.c_str(), pszCatalog, it.key));
473 1 : papszSubdatasets = CSLSetNameValue(
474 : papszSubdatasets,
475 : CPLSPrintf("SUBDATASET_%d_DESC", nSubDataset),
476 : CPLSPrintf("PLScenes:version=Data_V1,itemtypes=%s,scene=%s,"
477 : "asset=%s",
478 : pszCatalog, osScene.c_str(), it.key));
479 : }
480 1 : json_object_put(poObj);
481 1 : if (nSubDataset != 0)
482 : {
483 1 : GDALDataset *poDS = new OGRPLScenesDataV1Dataset();
484 1 : poDS->SetMetadata(papszSubdatasets, "SUBDATASETS");
485 1 : CSLDestroy(papszSubdatasets);
486 1 : return poDS;
487 : }
488 : }
489 1 : return nullptr;
490 : }
491 :
492 13 : if (json_object_get_type(poSubObj) != json_type_object)
493 : {
494 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find link");
495 1 : json_object_put(poObj);
496 1 : return nullptr;
497 : }
498 :
499 : json_object *poPermissions =
500 12 : CPL_json_object_object_get(poSubObj, "_permissions");
501 12 : if (poPermissions != nullptr)
502 : {
503 : const char *pszPermissions =
504 12 : json_object_to_json_string_ext(poPermissions, 0);
505 12 : if (pszPermissions && strstr(pszPermissions, "download") == nullptr)
506 : {
507 0 : CPLError(CE_Warning, CPLE_AppDefined,
508 : "You don't have download permissions for this product");
509 : }
510 : }
511 :
512 12 : json_object *poLocation = CPL_json_object_object_get(poSubObj, "location");
513 12 : json_object *poStatus = CPL_json_object_object_get(poSubObj, "status");
514 12 : bool bActive = false;
515 24 : if (poStatus != nullptr &&
516 12 : json_object_get_type(poStatus) == json_type_string)
517 : {
518 12 : const char *pszStatus = json_object_get_string(poStatus);
519 12 : if (EQUAL(pszStatus, "activating"))
520 : {
521 4 : CPLDebug("PLScenes", "The product is in activation. Retrying...");
522 4 : CPLSleep(nActivationTimeout == 1 ? 0.5 : 1.0);
523 4 : poLocation = nullptr;
524 4 : json_object_put(poObj);
525 4 : goto retry;
526 : }
527 8 : bActive = EQUAL(pszStatus, "active");
528 : }
529 13 : if (poLocation == nullptr ||
530 8 : json_object_get_type(poLocation) != json_type_string || !bActive)
531 : {
532 3 : CPLDebug("PLScenes", "The product isn't activated yet. Activating it");
533 : json_object *poActivate =
534 3 : json_ex_get_object_by_path(poSubObj, "_links.activate");
535 6 : if (poActivate == nullptr ||
536 3 : json_object_get_type(poActivate) != json_type_string)
537 : {
538 0 : CPLError(CE_Failure, CPLE_AppDefined,
539 : "Cannot find link to activate scene %s", osScene.c_str());
540 0 : json_object_put(poObj);
541 0 : return nullptr;
542 : }
543 3 : CPLString osActivate = json_object_get_string(poActivate);
544 3 : poLocation = nullptr;
545 3 : json_object_put(poObj);
546 3 : poObj = RunRequest(osActivate, FALSE, "GET", false);
547 3 : if (poObj != nullptr)
548 0 : json_object_put(poObj);
549 3 : poObj = nullptr;
550 3 : CPLSleep(nActivationTimeout == 1 ? 0.5 : 1.0);
551 3 : goto retry;
552 : }
553 :
554 5 : const char *pszLink = json_object_get_string(poLocation);
555 :
556 5 : osRasterURL = pszLink ? pszLink : "";
557 5 : json_object_put(poObj);
558 5 : if (osRasterURL.empty())
559 : {
560 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find link to scene %s",
561 : osScene.c_str());
562 0 : return nullptr;
563 : }
564 :
565 5 : osRasterURL = InsertAPIKeyInURL(osRasterURL);
566 :
567 : const bool bUseVSICURL =
568 5 : CPLFetchBool(poOpenInfo->papszOpenOptions, "RANDOM_ACCESS", true);
569 5 : if (bUseVSICURL && !(STARTS_WITH(m_osBaseURL, "/vsimem/")))
570 : {
571 5 : char *pszEscapedURL = CPLEscapeString(osRasterURL, -1, CPLES_URL);
572 : CPLString osTmpURL("/vsicurl?use_head=no&max_retry=3&empty_dir=yes&use_"
573 10 : "redirect_url_if_no_query_string_params=yes&url=");
574 5 : osTmpURL += pszEscapedURL;
575 5 : CPLFree(pszEscapedURL);
576 5 : CPLDebug("PLSCENES", "URL = %s", osTmpURL.c_str());
577 :
578 : VSIStatBufL sStat;
579 5 : if (VSIStatL(osTmpURL, &sStat) == 0 && sStat.st_size > 0)
580 : {
581 4 : osRasterURL = std::move(osTmpURL);
582 : }
583 : else
584 : {
585 1 : CPLDebug("PLSCENES", "Cannot use random access for that file");
586 : }
587 : }
588 :
589 5 : char **papszAllowedDrivers = nullptr;
590 5 : papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "HTTP");
591 5 : papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "GTiff");
592 5 : papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "PNG");
593 5 : papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JPEG");
594 5 : papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "NITF");
595 5 : papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2KAK");
596 5 : papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2ECW");
597 5 : papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2MrSID");
598 5 : papszAllowedDrivers = CSLAddString(papszAllowedDrivers, "JP2OpenJPEG");
599 5 : GDALDataset *poOutDS = (GDALDataset *)GDALOpenEx(
600 : osRasterURL, GDAL_OF_RASTER, papszAllowedDrivers, nullptr, nullptr);
601 5 : CSLDestroy(papszAllowedDrivers);
602 5 : if (poOutDS)
603 : {
604 3 : if (CPLFetchBool(
605 : papszOptions, "metadata",
606 3 : CPLFetchBool(poOpenInfo->papszOpenOptions, "METADATA", true)))
607 : {
608 3 : OGRLayer *poLayer = GetLayerByName(pszCatalog);
609 3 : if (poLayer != nullptr)
610 : {
611 : // Set a dummy name so that PAM goes here
612 2 : CPLPushErrorHandler(CPLQuietErrorHandler);
613 :
614 : const std::string osTmpFilename =
615 4 : VSIMemGenerateHiddenFilename("ogrplscenesDataV1");
616 :
617 2 : poOutDS->SetDescription(osTmpFilename.c_str());
618 :
619 : /* Attach scene metadata. */
620 2 : poLayer->SetAttributeFilter(
621 2 : CPLSPrintf("id = '%s'", osScene.c_str()));
622 2 : OGRFeature *poFeat = poLayer->GetNextFeature();
623 2 : if (poFeat)
624 : {
625 41 : for (int i = 0; i < poFeat->GetFieldCount(); i++)
626 : {
627 40 : if (poFeat->IsFieldSetAndNotNull(i))
628 : {
629 : const char *pszKey =
630 2 : poFeat->GetFieldDefnRef(i)->GetNameRef();
631 2 : const char *pszVal = poFeat->GetFieldAsString(i);
632 2 : if (strncmp(pszKey, "asset_", strlen("asset_")) ==
633 2 : 0 ||
634 2 : strstr(pszVal, "https://") != nullptr ||
635 2 : strcmp(pszKey, "columns") == 0 ||
636 2 : strcmp(pszKey, "rows") == 0 ||
637 2 : strcmp(pszKey, "epsg_code") == 0 ||
638 2 : strcmp(pszKey, "origin_x") == 0 ||
639 2 : strcmp(pszKey, "origin_y") == 0 ||
640 2 : strcmp(pszKey, "permissions") == 0 ||
641 2 : strcmp(pszKey, "acquired") ==
642 : 0 // Redundant with TIFFTAG_DATETIME
643 : )
644 : {
645 0 : continue;
646 : }
647 2 : poOutDS->SetMetadataItem(pszKey, pszVal);
648 : }
649 : }
650 : }
651 2 : delete poFeat;
652 :
653 2 : poOutDS->FlushCache(false);
654 2 : VSIUnlink(osTmpFilename.c_str());
655 2 : VSIUnlink(
656 4 : std::string(osTmpFilename).append(".aux.xml").c_str());
657 2 : CPLPopErrorHandler();
658 : }
659 : }
660 :
661 3 : CPLErrorReset();
662 3 : poOutDS->SetDescription(poOpenInfo->pszFilename);
663 : }
664 2 : else if (CPLGetLastErrorType() == CE_None)
665 : {
666 2 : poObj = RunRequest(osRasterURL);
667 2 : if (poObj == nullptr)
668 : {
669 2 : CPLError(
670 : CE_Failure, CPLE_AppDefined,
671 : "The generation of the product is in progress. Retry later");
672 : }
673 : else
674 : {
675 0 : CPLError(
676 : CE_Failure, CPLE_AppDefined, "%s",
677 : json_object_to_json_string_ext(poObj, JSON_C_TO_STRING_PRETTY));
678 0 : json_object_put(poObj);
679 : }
680 : }
681 :
682 5 : return poOutDS;
683 : }
684 :
685 : /************************************************************************/
686 : /* Open() */
687 : /************************************************************************/
688 :
689 28 : GDALDataset *OGRPLScenesDataV1Dataset::Open(GDALOpenInfo *poOpenInfo)
690 : {
691 28 : OGRPLScenesDataV1Dataset *poDS = new OGRPLScenesDataV1Dataset();
692 :
693 : poDS->m_osBaseURL =
694 28 : CPLGetConfigOption("PL_URL", "https://api.planet.com/data/v1/");
695 :
696 56 : char **papszOptions = CSLTokenizeStringComplex(
697 28 : poOpenInfo->pszFilename + strlen("PLScenes:"), ",", TRUE, FALSE);
698 :
699 : poDS->m_osAPIKey = CSLFetchNameValueDef(
700 : papszOptions, "api_key",
701 28 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "API_KEY",
702 28 : CPLGetConfigOption("PL_API_KEY", "")));
703 28 : if (poDS->m_osAPIKey.empty())
704 : {
705 1 : CPLError(
706 : CE_Failure, CPLE_AppDefined,
707 : "Missing PL_API_KEY configuration option or API_KEY open option");
708 1 : delete poDS;
709 1 : CSLDestroy(papszOptions);
710 1 : return nullptr;
711 : }
712 :
713 27 : poDS->m_bFollowLinks = CPLTestBool(
714 : CSLFetchNameValueDef(papszOptions, "follow_links",
715 27 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
716 : "FOLLOW_LINKS", "FALSE")));
717 :
718 : poDS->m_osFilter = CSLFetchNameValueDef(
719 : papszOptions, "filter",
720 27 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "FILTER", ""));
721 27 : poDS->m_osFilter.Trim();
722 :
723 27 : const char *pszScene = CSLFetchNameValueDef(
724 : papszOptions, "scene",
725 27 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "SCENE"));
726 27 : if (pszScene)
727 : {
728 : GDALDataset *poRasterDS =
729 13 : poDS->OpenRasterScene(poOpenInfo, pszScene, papszOptions);
730 13 : delete poDS;
731 13 : CSLDestroy(papszOptions);
732 13 : return poRasterDS;
733 : }
734 14 : else if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) &&
735 0 : !(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR))
736 : {
737 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing scene");
738 0 : delete poDS;
739 0 : CSLDestroy(papszOptions);
740 0 : return nullptr;
741 : }
742 :
743 19 : for (char **papszIter = papszOptions; papszIter && *papszIter; papszIter++)
744 : {
745 6 : char *pszKey = nullptr;
746 6 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
747 6 : if (pszValue != nullptr)
748 : {
749 6 : if (!EQUAL(pszKey, "api_key") && !EQUAL(pszKey, "version") &&
750 2 : !EQUAL(pszKey, "catalog") && !EQUAL(pszKey, "itemtypes") &&
751 2 : !EQUAL(pszKey, "follow_links") && !EQUAL(pszKey, "filter"))
752 : {
753 1 : CPLError(CE_Failure, CPLE_NotSupported,
754 : "Unsupported option '%s'", pszKey);
755 1 : CPLFree(pszKey);
756 1 : delete poDS;
757 1 : CSLDestroy(papszOptions);
758 1 : return nullptr;
759 : }
760 5 : CPLFree(pszKey);
761 : }
762 : }
763 :
764 : json_object *poObj =
765 13 : poDS->RunRequest((poDS->m_osBaseURL + "item-types/").c_str());
766 13 : if (poObj == nullptr)
767 : {
768 2 : delete poDS;
769 2 : CSLDestroy(papszOptions);
770 2 : return nullptr;
771 : }
772 :
773 11 : const char *pszCatalog = CSLFetchNameValueDef(
774 : papszOptions, "itemtypes",
775 : CSLFetchNameValueDef(
776 : papszOptions, "catalog",
777 : CSLFetchNameValueDef(
778 11 : poOpenInfo->papszOpenOptions, "ITEMTYPES",
779 11 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "CATALOG"))));
780 11 : if (pszCatalog == nullptr)
781 : {
782 : // Establish (partial if there are other pages) layer list.
783 9 : if (!poDS->ParseItemTypes(poObj, poDS->m_osNextItemTypesPageURL))
784 : {
785 1 : delete poDS;
786 1 : poDS = nullptr;
787 : }
788 : }
789 : else
790 : {
791 2 : if (poDS->GetLayerByName(pszCatalog) == nullptr)
792 : {
793 1 : delete poDS;
794 1 : poDS = nullptr;
795 : }
796 : }
797 :
798 11 : json_object_put(poObj);
799 :
800 11 : CSLDestroy(papszOptions);
801 :
802 11 : if (!(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR))
803 : {
804 0 : delete poDS;
805 0 : return nullptr;
806 : }
807 :
808 11 : return poDS;
809 : }
|