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