Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Planetary drivers
5 : * Author: Even Rouault
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdal_frmts.h"
14 : #include "gdalplugindriverproxy.h"
15 :
16 : #include "pdsdrivercore.h"
17 :
18 : #include "nasakeywordhandler.h"
19 :
20 : /************************************************************************/
21 : /* GetVICARLabelOffsetFromPDS3() */
22 : /************************************************************************/
23 :
24 5 : vsi_l_offset GetVICARLabelOffsetFromPDS3(const char *pszHdr, VSILFILE *fp,
25 : std::string &osVICARHeader)
26 : {
27 5 : const char *pszPDSVersionID = strstr(pszHdr, "PDS_VERSION_ID");
28 5 : int nOffset = 0;
29 5 : if (pszPDSVersionID)
30 5 : nOffset = static_cast<int>(pszPDSVersionID - pszHdr);
31 :
32 10 : NASAKeywordHandler oKeywords;
33 5 : if (oKeywords.Ingest(fp, nOffset))
34 : {
35 : const int nRecordBytes =
36 5 : atoi(oKeywords.GetKeyword("RECORD_BYTES", "0"));
37 : const int nImageHeader =
38 5 : atoi(oKeywords.GetKeyword("^IMAGE_HEADER", "0"));
39 5 : if (nRecordBytes > 0 && nImageHeader > 0)
40 : {
41 5 : const auto nImgHeaderOffset =
42 5 : static_cast<vsi_l_offset>(nImageHeader - 1) * nRecordBytes;
43 5 : osVICARHeader.resize(1024);
44 : size_t nMemb;
45 5 : if (VSIFSeekL(fp, nImgHeaderOffset, SEEK_SET) == 0 &&
46 5 : (nMemb = VSIFReadL(&osVICARHeader[0], 1, osVICARHeader.size(),
47 10 : fp)) != 0 &&
48 5 : osVICARHeader.find("LBLSIZE") != std::string::npos)
49 : {
50 5 : osVICARHeader.resize(nMemb);
51 5 : return nImgHeaderOffset;
52 : }
53 : }
54 : }
55 0 : return 0;
56 : }
57 :
58 : /************************************************************************/
59 : /* PDSDriverIdentify() */
60 : /************************************************************************/
61 :
62 65561 : int PDSDriverIdentify(GDALOpenInfo *poOpenInfo)
63 :
64 : {
65 65561 : if (poOpenInfo->pabyHeader == nullptr || poOpenInfo->fpL == nullptr)
66 58405 : return FALSE;
67 :
68 7156 : const char *pszHdr = reinterpret_cast<char *>(poOpenInfo->pabyHeader);
69 7156 : if (strstr(pszHdr, "PDS_VERSION_ID") == nullptr &&
70 7077 : strstr(pszHdr, "ODL_VERSION_ID") == nullptr)
71 : {
72 7077 : return FALSE;
73 : }
74 :
75 : // Some PDS3 images include a VICAR header pointed by ^IMAGE_HEADER.
76 : // If the user sets GDAL_TRY_PDS3_WITH_VICAR=YES, then we will gracefully
77 : // hand over the file to the VICAR dataset.
78 158 : std::string unused;
79 79 : if (CPLTestBool(CPLGetConfigOption("GDAL_TRY_PDS3_WITH_VICAR", "NO")) &&
80 80 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsisubfile/") &&
81 1 : GetVICARLabelOffsetFromPDS3(pszHdr, poOpenInfo->fpL, unused) > 0)
82 : {
83 1 : CPLDebug("PDS3", "File is detected to have a VICAR header. "
84 : "Handing it over to the VICAR driver");
85 1 : return FALSE;
86 : }
87 :
88 78 : return TRUE;
89 : }
90 :
91 : /************************************************************************/
92 : /* PDSDriverSetCommonMetadata() */
93 : /************************************************************************/
94 :
95 1873 : void PDSDriverSetCommonMetadata(GDALDriver *poDriver)
96 : {
97 1873 : poDriver->SetDescription(PDS_DRIVER_NAME);
98 1873 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
99 1873 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "NASA Planetary Data System");
100 1873 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/pds.html");
101 1873 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
102 :
103 1873 : poDriver->pfnIdentify = PDSDriverIdentify;
104 1873 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
105 1873 : }
106 :
107 : /************************************************************************/
108 : /* PDS4DriverIdentify() */
109 : /************************************************************************/
110 :
111 77325 : int PDS4DriverIdentify(GDALOpenInfo *poOpenInfo)
112 : {
113 77325 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "PDS4:"))
114 30 : return TRUE;
115 77295 : if (poOpenInfo->nHeaderBytes == 0)
116 61168 : return FALSE;
117 :
118 16127 : const auto HasProductSomethingRootElement = [](const char *pszStr)
119 : {
120 31768 : return strstr(pszStr, "Product_Observational") != nullptr ||
121 15641 : strstr(pszStr, "Product_Ancillary") != nullptr ||
122 47405 : strstr(pszStr, "Product_Browse") != nullptr ||
123 31764 : strstr(pszStr, "Product_Collection") != nullptr;
124 : };
125 16127 : const auto HasPDS4Schema = [](const char *pszStr)
126 16127 : { return strstr(pszStr, "://pds.nasa.gov/pds4/pds/v1") != nullptr; };
127 :
128 16127 : for (int i = 0; i < 2; ++i)
129 : {
130 16127 : const char *pszHeader =
131 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
132 16127 : int nMatches = 0;
133 16127 : if (HasProductSomethingRootElement(pszHeader))
134 492 : nMatches++;
135 16127 : if (HasPDS4Schema(pszHeader))
136 492 : nMatches++;
137 16127 : if (nMatches == 2)
138 : {
139 492 : return TRUE;
140 : }
141 15635 : if (i == 0)
142 : {
143 15635 : if (nMatches == 0 || poOpenInfo->nHeaderBytes >= 8192)
144 : break;
145 : // If we have found one of the 2 matching elements to identify
146 : // PDS4 products, but have only ingested the default 1024 bytes,
147 : // then try to ingest more.
148 0 : poOpenInfo->TryToIngest(8192);
149 : }
150 : }
151 15635 : return FALSE;
152 : }
153 :
154 : /************************************************************************/
155 : /* PDS4DriverSetCommonMetadata() */
156 : /************************************************************************/
157 :
158 1873 : void PDS4DriverSetCommonMetadata(GDALDriver *poDriver)
159 : {
160 1873 : poDriver->SetDescription(PDS4_DRIVER_NAME);
161 1873 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
162 1873 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
163 1873 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
164 1873 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
165 1873 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
166 1873 : poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
167 1873 : poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS,
168 1873 : "Name Type WidthPrecision");
169 1873 : poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
170 :
171 1873 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
172 1873 : "NASA Planetary Data System 4");
173 1873 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/pds4.html");
174 1873 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "xml");
175 1873 : poDriver->SetMetadataItem(
176 : GDAL_DMD_CREATIONDATATYPES,
177 : "Byte Int8 UInt16 Int16 UInt32 Int32 UInt64 Int64 "
178 1873 : "Float32 Float64 CFloat32 CFloat64");
179 1873 : poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST, "<OpenOptionList/>");
180 1873 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
181 1873 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
182 1873 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS, "YES");
183 1873 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
184 :
185 1873 : poDriver->SetMetadataItem(
186 : GDAL_DMD_OPENOPTIONLIST,
187 : "<OpenOptionList>"
188 : " <Option name='LAT' type='string' scope='vector' description="
189 : "'Name of a field containing a Latitude value' default='Latitude'/>"
190 : " <Option name='LONG' type='string' scope='vector' description="
191 : "'Name of a field containing a Longitude value' default='Longitude'/>"
192 : " <Option name='ALT' type='string' scope='vector' description="
193 : "'Name of a field containing a Altitude value' default='Altitude'/>"
194 : " <Option name='WKT' type='string' scope='vector' description="
195 : "'Name of a field containing a geometry encoded in the WKT format' "
196 : "default='WKT'/>"
197 : " <Option name='KEEP_GEOM_COLUMNS' scope='vector' type='boolean' "
198 : "description="
199 : "'whether to add original x/y/geometry columns as regular fields.' "
200 : "default='NO' />"
201 1873 : "</OpenOptionList>");
202 :
203 1873 : poDriver->SetMetadataItem(
204 : GDAL_DMD_CREATIONOPTIONLIST,
205 : "<CreationOptionList>"
206 : " <Option name='IMAGE_FILENAME' type='string' scope='raster' "
207 : "description="
208 : "'Image filename'/>"
209 : " <Option name='IMAGE_EXTENSION' type='string' scope='raster' "
210 : "description="
211 : "'Extension of the binary raw/geotiff file'/>"
212 : " <Option name='CREATE_LABEL_ONLY' scope='raster' type='boolean' "
213 : "description="
214 : "'whether to create only the XML label when converting from an "
215 : "existing raw format.' default='NO' />"
216 : " <Option name='IMAGE_FORMAT' type='string-select' scope='raster' "
217 : "description='Format of the image file' default='RAW'>"
218 : " <Value>RAW</Value>"
219 : " <Value>GEOTIFF</Value>"
220 : " </Option>"
221 : #ifdef notdef
222 : " <Option name='GEOTIFF_OPTIONS' type='string' scope='raster' "
223 : "description='Comma separated list of KEY=VALUE tuples to forward "
224 : "to the GeoTIFF driver'/>"
225 : #endif
226 : " <Option name='INTERLEAVE' type='string-select' scope='raster' "
227 : "description="
228 : "'Pixel organization' default='BSQ'>"
229 : " <Value>BSQ</Value>"
230 : " <Value>BIP</Value>"
231 : " <Value>BIL</Value>"
232 : " </Option>"
233 : " <Option name='VAR_*' type='string' scope='raster,vector' "
234 : "description="
235 : "'Value to substitute to a variable in the template'/>"
236 : " <Option name='TEMPLATE' type='string' scope='raster,vector' "
237 : "description="
238 : "'.xml template to use'/>"
239 : " <Option name='USE_SRC_LABEL' type='boolean' scope='raster' "
240 : "description='Whether to use source label in PDS4 to PDS4 conversions' "
241 : "default='YES'/>"
242 : " <Option name='LATITUDE_TYPE' type='string-select' "
243 : "scope='raster,vector' "
244 : "description='Value of latitude_type' default='Planetocentric'>"
245 : " <Value>Planetocentric</Value>"
246 : " <Value>Planetographic</Value>"
247 : " </Option>"
248 : " <Option name='LONGITUDE_DIRECTION' type='string-select' "
249 : "scope='raster,vector' "
250 : "description='Value of longitude_direction' "
251 : "default='Positive East'>"
252 : " <Value>Positive East</Value>"
253 : " <Value>Positive West</Value>"
254 : " </Option>"
255 : " <Option name='RADII' type='string' scope='raster,vector' "
256 : "description='Value of form "
257 : "semi_major_radius,semi_minor_radius to override the ones of the SRS'/>"
258 : " <Option name='ARRAY_TYPE' type='string-select' scope='raster' "
259 : "description='Name of the "
260 : "Array XML element' default='Array_3D_Image'>"
261 : " <Value>Array</Value>"
262 : " <Value>Array_2D</Value>"
263 : " <Value>Array_2D_Image</Value>"
264 : " <Value>Array_2D_Map</Value>"
265 : " <Value>Array_2D_Spectrum</Value>"
266 : " <Value>Array_3D</Value>"
267 : " <Value>Array_3D_Image</Value>"
268 : " <Value>Array_3D_Movie</Value>"
269 : " <Value>Array_3D_Spectrum</Value>"
270 : " </Option>"
271 : " <Option name='ARRAY_IDENTIFIER' type='string' scope='raster' "
272 : "description='Identifier to put in the Array element'/>"
273 : " <Option name='UNIT' type='string' scope='raster' "
274 : "description='Name of the unit of the array elements'/>"
275 : " <Option name='BOUNDING_DEGREES' type='string' scope='raster,vector' "
276 : "description='Manually set bounding box with the syntax "
277 : "west_lon,south_lat,east_lon,north_lat'/>"
278 : " <Option name='PROPAGATE_SRC_METADATA' type='boolean' scope='raster' "
279 : "description='Whether to propagate particular metadata domains, such "
280 : "as json:ISIS3' default='YES'/>"
281 1873 : "</CreationOptionList>");
282 :
283 1873 : poDriver->SetMetadataItem(
284 : GDAL_DS_LAYER_CREATIONOPTIONLIST,
285 : "<LayerCreationOptionList>"
286 : " <Option name='TABLE_TYPE' type='string-select' description='Type of "
287 : "table' default='DELIMITED'>"
288 : " <Value>DELIMITED</Value>"
289 : " <Value>CHARACTER</Value>"
290 : " <Value>BINARY</Value>"
291 : " </Option>"
292 : " <Option name='LINE_ENDING' type='string-select' description="
293 : "'end-of-line sequence. Only applies for "
294 : "TABLE_TYPE=DELIMITED/CHARACTER' "
295 : "default='CRLF'>"
296 : " <Value>CRLF</Value>"
297 : " <Value>LF</Value>"
298 : " </Option>"
299 : " <Option name='GEOM_COLUMNS' type='string-select' description='How "
300 : "geometry is encoded' default='AUTO'>"
301 : " <Value>AUTO</Value>"
302 : " <Value>WKT</Value>"
303 : " <Value>LONG_LAT</Value>"
304 : " </Option>"
305 : " <Option name='CREATE_VRT' type='boolean' description='Whether to "
306 : "generate "
307 : "a OGR VRT file. Only applies for TABLE_TYPE=DELIMITED' default='YES'/>"
308 : " <Option name='LAT' type='string' description="
309 : "'Name of a field containing a Latitude value' default='Latitude'/>"
310 : " <Option name='LONG' type='string' description="
311 : "'Name of a field containing a Longitude value' default='Longitude'/>"
312 : " <Option name='ALT' type='string' description="
313 : "'Name of a field containing a Altitude value' default='Altitude'/>"
314 : " <Option name='WKT' type='string' description="
315 : "'Name of a field containing a WKT value' default='WKT'/>"
316 1873 : "</LayerCreationOptionList>");
317 :
318 1873 : poDriver->SetMetadataItem(
319 : GDAL_DMD_CREATIONFIELDDATATYPES,
320 1873 : "Integer Integer64 Real String Date DateTime Time");
321 1873 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
322 :
323 1873 : poDriver->pfnIdentify = PDS4DriverIdentify;
324 1873 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
325 1873 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
326 1873 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
327 :
328 1873 : poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
329 1873 : poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS, "Features");
330 1873 : }
331 :
332 : /************************************************************************/
333 : /* ISIS2DriverIdentify() */
334 : /************************************************************************/
335 :
336 65536 : int ISIS2DriverIdentify(GDALOpenInfo *poOpenInfo)
337 : {
338 65536 : if (poOpenInfo->pabyHeader == nullptr)
339 58357 : return FALSE;
340 :
341 7179 : if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
342 : "^QUBE") == nullptr)
343 7175 : return FALSE;
344 :
345 4 : return TRUE;
346 : }
347 :
348 : /************************************************************************/
349 : /* ISIS2DriverSetCommonMetadata() */
350 : /************************************************************************/
351 :
352 1873 : void ISIS2DriverSetCommonMetadata(GDALDriver *poDriver)
353 : {
354 1873 : poDriver->SetDescription(ISIS2_DRIVER_NAME);
355 1873 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
356 1873 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
357 1873 : "USGS Astrogeology ISIS cube (Version 2)");
358 1873 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/isis2.html");
359 1873 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
360 :
361 1873 : poDriver->pfnIdentify = ISIS2DriverIdentify;
362 1873 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
363 1873 : }
364 :
365 : /************************************************************************/
366 : /* ISIS3DriverIdentify() */
367 : /************************************************************************/
368 :
369 66152 : int ISIS3DriverIdentify(GDALOpenInfo *poOpenInfo)
370 : {
371 66152 : if (poOpenInfo->fpL != nullptr && poOpenInfo->pabyHeader != nullptr &&
372 7670 : strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
373 : "IsisCube") != nullptr)
374 543 : return TRUE;
375 :
376 65609 : return FALSE;
377 : }
378 :
379 : /************************************************************************/
380 : /* ISIS3DriverSetCommonMetadata() */
381 : /************************************************************************/
382 :
383 1873 : void ISIS3DriverSetCommonMetadata(GDALDriver *poDriver)
384 : {
385 1873 : poDriver->SetDescription(ISIS3_DRIVER_NAME);
386 1873 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
387 1873 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
388 1873 : "USGS Astrogeology ISIS cube (Version 3)");
389 1873 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/isis3.html");
390 1873 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
391 1873 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "lbl cub");
392 1873 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
393 1873 : "Byte UInt16 Int16 Float32");
394 1873 : poDriver->SetMetadataItem(GDAL_DMD_OPENOPTIONLIST, "<OpenOptionList/>");
395 :
396 1873 : poDriver->SetMetadataItem(
397 : GDAL_DMD_OPENOPTIONLIST,
398 : "<OpenOptionList>"
399 : " <Option name='INCLUDE_OFFLINE_CONTENT' type='boolean' default='YES' "
400 : "description='Whether to include a _data member in "
401 : "json:ISIS3 metadata with offline content of label objects'/>"
402 : " <Option name='MAX_SIZE_OFFLINE_CONTENT' type='string' "
403 : "default='100000000' description='Maximum size of offline content to "
404 : "include in _data member, in bytes' min='0'/>"
405 1873 : "</OpenOptionList>");
406 :
407 1873 : poDriver->SetMetadataItem(
408 : GDAL_DMD_CREATIONOPTIONLIST,
409 : "<CreationOptionList>"
410 : " <Option name='DATA_LOCATION' type='string-select' "
411 : "description='Location of pixel data' default='LABEL'>"
412 : " <Value>LABEL</Value>"
413 : " <Value>EXTERNAL</Value>"
414 : " <Value>GEOTIFF</Value>"
415 : " </Option>"
416 : " <Option name='GEOTIFF_AS_REGULAR_EXTERNAL' type='boolean' "
417 : "description='Whether the GeoTIFF file, if uncompressed, should be "
418 : "registered as a regular raw file' default='YES'/>"
419 : " <Option name='GEOTIFF_OPTIONS' type='string' "
420 : "description='Comma separated list of KEY=VALUE tuples to forward "
421 : "to the GeoTIFF driver'/>"
422 : " <Option name='EXTERNAL_FILENAME' type='string' "
423 : "description='Override default external filename. "
424 : "Only for DATA_LOCATION=EXTERNAL or GEOTIFF'/>"
425 : " <Option name='TILED' type='boolean' "
426 : "description='Whether the pixel data should be tiled' default='NO'/>"
427 : " <Option name='BLOCKXSIZE' type='int' "
428 : "description='Tile width' default='256'/>"
429 : " <Option name='BLOCKYSIZE' type='int' "
430 : "description='Tile height' default='256'/>"
431 : " <Option name='COMMENT' type='string' "
432 : "description='Comment to add into the label'/>"
433 : " <Option name='LATITUDE_TYPE' type='string-select' "
434 : "description='Value of Mapping.LatitudeType' default='Planetocentric'>"
435 : " <Value>Planetocentric</Value>"
436 : " <Value>Planetographic</Value>"
437 : " </Option>"
438 : " <Option name='LONGITUDE_DIRECTION' type='string-select' "
439 : "description='Value of Mapping.LongitudeDirection' "
440 : "default='PositiveEast'>"
441 : " <Value>PositiveEast</Value>"
442 : " <Value>PositiveWest</Value>"
443 : " </Option>"
444 : " <Option name='TARGET_NAME' type='string' description='Value of "
445 : "Mapping.TargetName'/>"
446 : " <Option name='FORCE_360' type='boolean' "
447 : "description='Whether to force longitudes in [0,360] range' "
448 : "default='NO'/>"
449 : " <Option name='WRITE_BOUNDING_DEGREES' type='boolean' "
450 : "description='Whether to write Min/MaximumLong/Latitude values' "
451 : "default='YES'/>"
452 : " <Option name='BOUNDING_DEGREES' type='string' "
453 : "description='Manually set bounding box with the syntax "
454 : "min_long,min_lat,max_long,max_lat'/>"
455 : " <Option name='USE_SRC_LABEL' type='boolean' "
456 : "description='Whether to use source label in ISIS3 to ISIS3 "
457 : "conversions' "
458 : "default='YES'/>"
459 : " <Option name='USE_SRC_MAPPING' type='boolean' "
460 : "description='Whether to use Mapping group from source label in "
461 : "ISIS3 to ISIS3 conversions' "
462 : "default='NO'/>"
463 : " <Option name='USE_SRC_HISTORY' type='boolean' "
464 : "description='Whether to use content pointed by the History object in "
465 : "ISIS3 to ISIS3 conversions' "
466 : "default='YES'/>"
467 : " <Option name='ADD_GDAL_HISTORY' type='boolean' "
468 : "description='Whether to add GDAL specific history in the content "
469 : "pointed "
470 : "by the History object in "
471 : "ISIS3 to ISIS3 conversions' "
472 : "default='YES'/>"
473 : " <Option name='GDAL_HISTORY' type='string' "
474 : "description='Manually defined GDAL history. Must be formatted as "
475 : "ISIS3 "
476 : "PDL. If not specified, it is automatically composed.'/>"
477 1873 : "</CreationOptionList>");
478 :
479 1873 : poDriver->pfnIdentify = ISIS3DriverIdentify;
480 1873 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
481 1873 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
482 1873 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
483 1873 : }
484 :
485 : /************************************************************************/
486 : /* VICARGetLabelOffset() */
487 : /************************************************************************/
488 :
489 76914 : vsi_l_offset VICARGetLabelOffset(GDALOpenInfo *poOpenInfo)
490 :
491 : {
492 76914 : if (poOpenInfo->pabyHeader == nullptr || poOpenInfo->fpL == nullptr)
493 61193 : return static_cast<vsi_l_offset>(-1);
494 :
495 7054 : const auto HasFoundVICARKeywords = [](const char *pszHeader)
496 : {
497 7284 : return strstr(pszHeader, "LBLSIZE") != nullptr &&
498 230 : strstr(pszHeader, "FORMAT") != nullptr &&
499 230 : strstr(pszHeader, "NL") != nullptr &&
500 7514 : strstr(pszHeader, "NS") != nullptr &&
501 7284 : strstr(pszHeader, "NB") != nullptr;
502 : };
503 :
504 31442 : std::string osHeader;
505 15721 : const char *pszHeader =
506 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
507 : // Some PDS3 images include a VICAR header pointed by ^IMAGE_HEADER.
508 : // If the user sets GDAL_TRY_PDS3_WITH_VICAR=YES, then we will gracefully
509 : // hand over the file to the VICAR dataset.
510 15721 : vsi_l_offset nOffset = 0;
511 : const bool bTryPDS3WithVicar =
512 15724 : CPLTestBool(CPLGetConfigOption("GDAL_TRY_PDS3_WITH_VICAR", "NO")) &&
513 3 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsisubfile/");
514 15721 : if (bTryPDS3WithVicar && (nOffset = GetVICARLabelOffsetFromPDS3(
515 : pszHeader, poOpenInfo->fpL, osHeader)) > 0)
516 : {
517 2 : pszHeader = osHeader.c_str();
518 : }
519 :
520 15721 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
521 8673 : (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0)
522 : {
523 : // If opening in vector-only mode, then check when have NBB != 0
524 8671 : const char *pszNBB = strstr(pszHeader, "NBB");
525 8671 : if (pszNBB == nullptr)
526 8668 : return static_cast<vsi_l_offset>(-1);
527 3 : const char *pszEqualSign = strchr(pszNBB, '=');
528 3 : if (pszEqualSign == nullptr)
529 0 : return static_cast<vsi_l_offset>(-1);
530 3 : if (atoi(pszEqualSign + 1) == 0)
531 1 : return static_cast<vsi_l_offset>(-1);
532 : }
533 :
534 7052 : if (HasFoundVICARKeywords(pszHeader))
535 : {
536 : // If we find VICAR keywords, but the file starts with PDS_VERSION_ID,
537 : // it might be a PDS3 image that includes a VICAR header. Check if
538 : // this is the case.
539 228 : if (nOffset == 0 && STARTS_WITH(pszHeader, "PDS_VERSION_ID"))
540 : {
541 4 : if (!bTryPDS3WithVicar &&
542 2 : (!GDALGetDriverByName("PDS") ||
543 2 : poOpenInfo->IsSingleAllowedDriver("VICAR")))
544 : {
545 2 : const auto nOffset2 = GetVICARLabelOffsetFromPDS3(
546 : pszHeader, poOpenInfo->fpL, osHeader);
547 2 : if (nOffset2 > 0 && HasFoundVICARKeywords(osHeader.c_str()))
548 : {
549 2 : return nOffset2;
550 : }
551 : }
552 : }
553 226 : return nOffset;
554 : }
555 6824 : return static_cast<vsi_l_offset>(-1);
556 : }
557 :
558 : /************************************************************************/
559 : /* VICARDriverIdentify() */
560 : /************************************************************************/
561 :
562 76799 : static int VICARDriverIdentify(GDALOpenInfo *poOpenInfo)
563 : {
564 76799 : return VICARGetLabelOffset(poOpenInfo) != static_cast<vsi_l_offset>(-1);
565 : }
566 :
567 : /************************************************************************/
568 : /* VICARDriverSetCommonMetadata() */
569 : /************************************************************************/
570 :
571 1873 : void VICARDriverSetCommonMetadata(GDALDriver *poDriver)
572 : {
573 1873 : poDriver->SetDescription(VICAR_DRIVER_NAME);
574 1873 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
575 1873 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
576 1873 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "MIPL VICAR file");
577 1873 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/vicar.html");
578 1873 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
579 1873 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
580 1873 : "Byte Int16 Int32 Float32 Float64 CFloat32");
581 1873 : poDriver->SetMetadataItem(
582 : GDAL_DMD_CREATIONOPTIONLIST,
583 : "<CreationOptionList>"
584 : " <Option name='GEOREF_FORMAT' type='string-select' "
585 : "description='How to encode georeferencing information' "
586 : "default='MIPL'>"
587 : " <Value>MIPL</Value>"
588 : " <Value>GEOTIFF</Value>"
589 : " </Option>"
590 : " <Option name='COORDINATE_SYSTEM_NAME' type='string-select' "
591 : "description='Value of MAP.COORDINATE_SYSTEM_NAME' "
592 : "default='PLANETOCENTRIC'>"
593 : " <Value>PLANETOCENTRIC</Value>"
594 : " <Value>PLANETOGRAPHIC</Value>"
595 : " </Option>"
596 : " <Option name='POSITIVE_LONGITUDE_DIRECTION' type='string-select' "
597 : "description='Value of MAP.POSITIVE_LONGITUDE_DIRECTION' "
598 : "default='EAST'>"
599 : " <Value>EAST</Value>"
600 : " <Value>WEST</Value>"
601 : " </Option>"
602 : " <Option name='TARGET_NAME' type='string' description='Value of "
603 : "MAP.TARGET_NAME'/>"
604 : " <Option name='USE_SRC_LABEL' type='boolean' "
605 : "description='Whether to use source label in VICAR to VICAR "
606 : "conversions' "
607 : "default='YES'/>"
608 : " <Option name='USE_SRC_MAP' type='boolean' "
609 : "description='Whether to use MAP property from source label in "
610 : "VICAR to VICAR conversions' "
611 : "default='NO'/>"
612 : " <Option name='LABEL' type='string' "
613 : "description='Label to use, either as a JSON string or a filename "
614 : "containing one'/>"
615 : " <Option name='COMPRESS' type='string-select' "
616 : "description='Compression method' default='NONE'>"
617 : " <Value>NONE</Value>"
618 : " <Value>BASIC</Value>"
619 : " <Value>BASIC2</Value>"
620 : " </Option>"
621 1873 : "</CreationOptionList>");
622 :
623 1873 : poDriver->pfnIdentify = VICARDriverIdentify;
624 1873 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
625 1873 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
626 1873 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
627 1873 : }
628 :
629 : /************************************************************************/
630 : /* DeclareDeferredPDSPlugin() */
631 : /************************************************************************/
632 :
633 : #ifdef PLUGIN_FILENAME
634 : void DeclareDeferredPDSPlugin()
635 : {
636 : if (GDALGetDriverByName(PDS_DRIVER_NAME) != nullptr)
637 : {
638 : return;
639 : }
640 : {
641 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
642 : #ifdef PLUGIN_INSTALLATION_MESSAGE
643 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
644 : PLUGIN_INSTALLATION_MESSAGE);
645 : #endif
646 : PDSDriverSetCommonMetadata(poDriver);
647 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
648 : }
649 : {
650 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
651 : #ifdef PLUGIN_INSTALLATION_MESSAGE
652 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
653 : PLUGIN_INSTALLATION_MESSAGE);
654 : #endif
655 : PDS4DriverSetCommonMetadata(poDriver);
656 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
657 : }
658 : {
659 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
660 : #ifdef PLUGIN_INSTALLATION_MESSAGE
661 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
662 : PLUGIN_INSTALLATION_MESSAGE);
663 : #endif
664 : ISIS2DriverSetCommonMetadata(poDriver);
665 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
666 : }
667 : {
668 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
669 : #ifdef PLUGIN_INSTALLATION_MESSAGE
670 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
671 : PLUGIN_INSTALLATION_MESSAGE);
672 : #endif
673 : ISIS3DriverSetCommonMetadata(poDriver);
674 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
675 : }
676 : {
677 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
678 : #ifdef PLUGIN_INSTALLATION_MESSAGE
679 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
680 : PLUGIN_INSTALLATION_MESSAGE);
681 : #endif
682 : VICARDriverSetCommonMetadata(poDriver);
683 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
684 : }
685 : }
686 : #endif
|