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