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