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