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