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