1 : /******************************************************************************
2 : * $Id: pdsdataset.cpp 18435 2010-01-04 18:40:38Z warmerdam $
3 : *
4 : * Project: PDS Driver; Planetary Data System Format
5 : * Purpose: Implementation of PDSDataset
6 : * Author: Trent Hare (thare@usgs.gov),
7 : * Robert Soricone (rsoricone@usgs.gov)
8 : *
9 : * NOTE: Original code authored by Trent and Robert and placed in the public
10 : * domain as per US government policy. I have (within my rights) appropriated
11 : * it and placed it under the following license. This is not intended to
12 : * diminish Trent and Roberts contribution.
13 : ******************************************************************************
14 : * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
15 : *
16 : * Permission is hereby granted, free of charge, to any person obtaining a
17 : * copy of this software and associated documentation files (the "Software"),
18 : * to deal in the Software without restriction, including without limitation
19 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20 : * and/or sell copies of the Software, and to permit persons to whom the
21 : * Software is furnished to do so, subject to the following conditions:
22 : *
23 : * The above copyright notice and this permission notice shall be included
24 : * in all copies or substantial portions of the Software.
25 : *
26 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
29 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 : * DEALINGS IN THE SOFTWARE.
33 : ****************************************************************************/
34 :
35 : // Set up PDS NULL values
36 : #define NULL1 0
37 : #define NULL2 -32768
38 : //#define NULL3 -0.3402822655089E+39
39 : //Same as ESRI_GRID_FLOAT_NO_DATA
40 : //#define NULL3 -340282346638528859811704183484516925440.0
41 : #define NULL3 -3.4028226550889044521e+38
42 :
43 : #include "rawdataset.h"
44 : #include "gdal_proxy.h"
45 : #include "ogr_spatialref.h"
46 : #include "cpl_string.h"
47 : #include "nasakeywordhandler.h"
48 :
49 : CPL_CVSID("$Id: pdsdataset.cpp 18435 2010-01-04 18:40:38Z warmerdam $");
50 :
51 : CPL_C_START
52 : void GDALRegister_PDS(void);
53 : CPL_C_END
54 :
55 : /************************************************************************/
56 : /* ==================================================================== */
57 : /* PDSDataset */
58 : /* ==================================================================== */
59 : /************************************************************************/
60 :
61 : class PDSDataset : public RawDataset
62 : {
63 : FILE *fpImage; // image data file.
64 : GDALDataset *poCompressedDS;
65 :
66 : NASAKeywordHandler oKeywords;
67 :
68 : int bGotTransform;
69 : double adfGeoTransform[6];
70 :
71 : CPLString osProjection;
72 :
73 : CPLString osTempResult;
74 :
75 : void ParseSRS();
76 : int ParseUncompressedImage();
77 : int ParseCompressedImage();
78 : void CleanString( CPLString &osInput );
79 :
80 : const char *GetKeyword( const char *pszPath,
81 : const char *pszDefault = "");
82 : const char *GetKeywordSub( const char *pszPath,
83 : int iSubscript,
84 : const char *pszDefault = "");
85 : const char *GetKeywordUnit( const char *pszPath,
86 : int iSubscript,
87 : const char *pszDefault = "");
88 :
89 : public:
90 : PDSDataset();
91 : ~PDSDataset();
92 :
93 : virtual CPLErr GetGeoTransform( double * padfTransform );
94 : virtual const char *GetProjectionRef(void);
95 :
96 : virtual char **GetFileList(void);
97 :
98 : virtual CPLErr IBuildOverviews( const char *, int, int *,
99 : int, int *, GDALProgressFunc, void * );
100 :
101 : virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
102 : void *, int, int, GDALDataType,
103 : int, int *, int, int, int );
104 :
105 : static int Identify( GDALOpenInfo * );
106 : static GDALDataset *Open( GDALOpenInfo * );
107 : static GDALDataset *Create( const char * pszFilename,
108 : int nXSize, int nYSize, int nBands,
109 : GDALDataType eType, char ** papszParmList );
110 : };
111 :
112 : /************************************************************************/
113 : /* PDSDataset() */
114 : /************************************************************************/
115 :
116 8 : PDSDataset::PDSDataset()
117 : {
118 8 : fpImage = NULL;
119 8 : bGotTransform = FALSE;
120 8 : adfGeoTransform[0] = 0.0;
121 8 : adfGeoTransform[1] = 1.0;
122 8 : adfGeoTransform[2] = 0.0;
123 8 : adfGeoTransform[3] = 0.0;
124 8 : adfGeoTransform[4] = 0.0;
125 8 : adfGeoTransform[5] = 1.0;
126 8 : poCompressedDS = NULL;
127 8 : }
128 :
129 : /************************************************************************/
130 : /* ~PDSDataset() */
131 : /************************************************************************/
132 :
133 16 : PDSDataset::~PDSDataset()
134 :
135 : {
136 8 : FlushCache();
137 8 : if( fpImage != NULL )
138 6 : VSIFCloseL( fpImage );
139 :
140 8 : if( poCompressedDS )
141 2 : delete poCompressedDS;
142 16 : }
143 :
144 : /************************************************************************/
145 : /* GetFileList() */
146 : /************************************************************************/
147 :
148 1 : char **PDSDataset::GetFileList()
149 :
150 : {
151 1 : char **papszFileList = RawDataset::GetFileList();
152 :
153 1 : if( poCompressedDS != NULL )
154 : {
155 1 : char **papszCFileList = poCompressedDS->GetFileList();
156 :
157 : papszFileList = CSLInsertStrings( papszFileList, -1,
158 1 : papszCFileList );
159 1 : CSLDestroy( papszCFileList );
160 : }
161 :
162 1 : return papszFileList;
163 : }
164 :
165 : /************************************************************************/
166 : /* IBuildOverviews() */
167 : /************************************************************************/
168 :
169 0 : CPLErr PDSDataset::IBuildOverviews( const char *pszResampling,
170 : int nOverviews, int *panOverviewList,
171 : int nListBands, int *panBandList,
172 : GDALProgressFunc pfnProgress,
173 : void * pProgressData )
174 : {
175 0 : if( poCompressedDS != NULL )
176 : return poCompressedDS->BuildOverviews( pszResampling,
177 : nOverviews, panOverviewList,
178 : nListBands, panBandList,
179 0 : pfnProgress, pProgressData );
180 : else
181 : return RawDataset::IBuildOverviews( pszResampling,
182 : nOverviews, panOverviewList,
183 : nListBands, panBandList,
184 0 : pfnProgress, pProgressData );
185 : }
186 :
187 : /************************************************************************/
188 : /* IRasterIO() */
189 : /************************************************************************/
190 :
191 0 : CPLErr PDSDataset::IRasterIO( GDALRWFlag eRWFlag,
192 : int nXOff, int nYOff, int nXSize, int nYSize,
193 : void * pData, int nBufXSize, int nBufYSize,
194 : GDALDataType eBufType,
195 : int nBandCount, int *panBandMap,
196 : int nPixelSpace, int nLineSpace, int nBandSpace)
197 :
198 : {
199 0 : if( poCompressedDS != NULL )
200 : return poCompressedDS->RasterIO( eRWFlag,
201 : nXOff, nYOff, nXSize, nYSize,
202 : pData, nBufXSize, nBufYSize,
203 : eBufType, nBandCount, panBandMap,
204 0 : nPixelSpace, nLineSpace, nBandSpace );
205 : else
206 : return RawDataset::IRasterIO( eRWFlag,
207 : nXOff, nYOff, nXSize, nYSize,
208 : pData, nBufXSize, nBufYSize,
209 : eBufType, nBandCount, panBandMap,
210 0 : nPixelSpace, nLineSpace, nBandSpace );
211 : }
212 :
213 : /************************************************************************/
214 : /* GetProjectionRef() */
215 : /************************************************************************/
216 :
217 3 : const char *PDSDataset::GetProjectionRef()
218 :
219 : {
220 3 : if( strlen(osProjection) > 0 )
221 3 : return osProjection;
222 : else
223 0 : return GDALPamDataset::GetProjectionRef();
224 : }
225 :
226 : /************************************************************************/
227 : /* GetGeoTransform() */
228 : /************************************************************************/
229 :
230 2 : CPLErr PDSDataset::GetGeoTransform( double * padfTransform )
231 :
232 : {
233 2 : if( bGotTransform )
234 : {
235 2 : memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
236 2 : return CE_None;
237 : }
238 : else
239 : {
240 0 : return GDALPamDataset::GetGeoTransform( padfTransform );
241 : }
242 : }
243 :
244 : /************************************************************************/
245 : /* ParseSRS() */
246 : /************************************************************************/
247 :
248 8 : void PDSDataset::ParseSRS()
249 :
250 : {
251 8 : const char *pszFilename = GetDescription();
252 :
253 : /* ==================================================================== */
254 : /* Get the geotransform. */
255 : /* ==================================================================== */
256 : /*********** Grab Cellsize ************/
257 : //example:
258 : //MAP_SCALE = 14.818 <KM/PIXEL>
259 : //added search for unit (only checks for CM, KM - defaults to Meters)
260 : const char *value;
261 : //Georef parameters
262 8 : double dfULXMap=0.5;
263 8 : double dfULYMap = 0.5;
264 8 : double dfXDim = 1.0;
265 8 : double dfYDim = 1.0;
266 8 : double xulcenter = 0.0;
267 8 : double yulcenter = 0.0;
268 :
269 8 : value = GetKeyword("IMAGE_MAP_PROJECTION.MAP_SCALE");
270 8 : if (strlen(value) > 0 ) {
271 6 : dfXDim = (float) atof(value);
272 6 : dfYDim = (float) atof(value) * -1;
273 :
274 6 : CPLString unit = GetKeywordUnit("IMAGE_MAP_PROJECTION.MAP_SCALE",2); //KM
275 : //value = GetKeywordUnit("IMAGE_MAP_PROJECTION.MAP_SCALE",3); //PIXEL
276 6 : if((EQUAL(unit,"M")) || (EQUAL(unit,"METER")) || (EQUAL(unit,"METERS"))) {
277 : // do nothing
278 : }
279 3 : else if (EQUAL(unit,"CM")) {
280 : // convert from cm to m
281 0 : dfXDim = dfXDim / 100.0;
282 0 : dfYDim = dfYDim / 100.0;
283 : } else {
284 : //defaults to convert km to m
285 3 : dfXDim = dfXDim * 1000.0;
286 3 : dfYDim = dfYDim * 1000.0;
287 6 : }
288 : }
289 :
290 : // Calculate upper left corner of pixel in meters from the upper left center pixel which
291 : // should be correct for what is documented in the PDS manual
292 : // It doesn't mean it will work perfectly for every PDS image, as they tend to be released in different ways.
293 : // both dfULYMap, dfULXMap were update October 11, 2007 to correct 0.5 cellsize offset
294 : /*********** Grab LINE_PROJECTION_OFFSET ************/
295 8 : value = GetKeyword("IMAGE_MAP_PROJECTION.LINE_PROJECTION_OFFSET");
296 8 : if (strlen(value) > 0) {
297 6 : yulcenter = (float) atof(value);
298 6 : dfULYMap = ((yulcenter - 0.5) * dfYDim * -1);
299 : //notice dfYDim is negative here which is why it is negated again
300 : }
301 : /*********** Grab SAMPLE_PROJECTION_OFFSET ************/
302 8 : value = GetKeyword("IMAGE_MAP_PROJECTION.SAMPLE_PROJECTION_OFFSET");
303 8 : if( strlen(value) > 0 ) {
304 6 : xulcenter = (float) atof(value);
305 6 : dfULXMap = ((xulcenter - 0.5) * dfXDim * -1);
306 : }
307 :
308 : /* ==================================================================== */
309 : /* Get the coordinate system. */
310 : /* ==================================================================== */
311 8 : int bProjectionSet = TRUE;
312 8 : double semi_major = 0.0;
313 8 : double semi_minor = 0.0;
314 8 : double iflattening = 0.0;
315 8 : float center_lat = 0.0;
316 8 : float center_lon = 0.0;
317 8 : float first_std_parallel = 0.0;
318 8 : float second_std_parallel = 0.0;
319 8 : OGRSpatialReference oSRS;
320 :
321 : /*********** Grab TARGET_NAME ************/
322 : /**** This is the planets name i.e. MARS ***/
323 8 : CPLString target_name = GetKeyword("TARGET_NAME");
324 8 : CleanString( target_name );
325 :
326 : /********** Grab MAP_PROJECTION_TYPE *****/
327 : CPLString map_proj_name =
328 8 : GetKeyword( "IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE");
329 8 : CleanString( map_proj_name );
330 :
331 : /****** Grab semi_major & convert to KM ******/
332 : semi_major =
333 8 : atof(GetKeyword( "IMAGE_MAP_PROJECTION.A_AXIS_RADIUS")) * 1000.0;
334 :
335 : /****** Grab semi-minor & convert to KM ******/
336 : semi_minor =
337 8 : atof(GetKeyword( "IMAGE_MAP_PROJECTION.C_AXIS_RADIUS")) * 1000.0;
338 :
339 : /*********** Grab CENTER_LAT ************/
340 : center_lat =
341 8 : atof(GetKeyword( "IMAGE_MAP_PROJECTION.CENTER_LATITUDE"));
342 :
343 : /*********** Grab CENTER_LON ************/
344 : center_lon =
345 8 : atof(GetKeyword( "IMAGE_MAP_PROJECTION.CENTER_LONGITUDE"));
346 :
347 : /********** Grab 1st std parallel *******/
348 : first_std_parallel =
349 8 : atof(GetKeyword( "IMAGE_MAP_PROJECTION.FIRST_STANDARD_PARALLEL"));
350 :
351 : /********** Grab 2nd std parallel *******/
352 : second_std_parallel =
353 8 : atof(GetKeyword( "IMAGE_MAP_PROJECTION.SECOND_STANDARD_PARALLEL"));
354 :
355 : /*** grab PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/
356 : // Need to further study how ocentric/ographic will effect the gdal library.
357 : // So far we will use this fact to define a sphere or ellipse for some projections
358 : // Frank - may need to talk this over
359 8 : char bIsGeographic = TRUE;
360 8 : value = GetKeyword("IMAGE_MAP_PROJECTION.COORDINATE_SYSTEM_NAME");
361 8 : if (EQUAL( value, "PLANETOCENTRIC" ))
362 3 : bIsGeographic = FALSE;
363 :
364 : /** Set oSRS projection and parameters --- all PDS supported types added if apparently supported in oSRS
365 : "AITOFF", ** Not supported in GDAL??
366 : "ALBERS",
367 : "BONNE",
368 : "BRIESEMEISTER", ** Not supported in GDAL??
369 : "CYLINDRICAL EQUAL AREA",
370 : "EQUIDISTANT",
371 : "EQUIRECTANGULAR",
372 : "GNOMONIC",
373 : "HAMMER", ** Not supported in GDAL??
374 : "HENDU", ** Not supported in GDAL??
375 : "LAMBERT AZIMUTHAL EQUAL AREA",
376 : "LAMBERT CONFORMAL",
377 : "MERCATOR",
378 : "MOLLWEIDE",
379 : "OBLIQUE CYLINDRICAL",
380 : "ORTHOGRAPHIC",
381 : "SIMPLE CYLINDRICAL",
382 : "SINUSOIDAL",
383 : "STEREOGRAPHIC",
384 : "TRANSVERSE MERCATOR",
385 : "VAN DER GRINTEN", ** Not supported in GDAL??
386 : "WERNER" ** Not supported in GDAL??
387 : **/
388 8 : CPLDebug( "PDS","using projection %s\n\n", map_proj_name.c_str());
389 :
390 8 : if ((EQUAL( map_proj_name, "EQUIRECTANGULAR" )) ||
391 : (EQUAL( map_proj_name, "SIMPLE_CYLINDRICAL" )) ||
392 : (EQUAL( map_proj_name, "EQUIDISTANT" )) ) {
393 4 : oSRS.SetEquirectangular2 ( 0.0, center_lon, center_lat, 0, 0 );
394 4 : } else if (EQUAL( map_proj_name, "ORTHOGRAPHIC" )) {
395 0 : oSRS.SetOrthographic ( center_lat, center_lon, 0, 0 );
396 4 : } else if (EQUAL( map_proj_name, "SINUSOIDAL" )) {
397 2 : oSRS.SetSinusoidal ( center_lon, 0, 0 );
398 2 : } else if (EQUAL( map_proj_name, "MERCATOR" )) {
399 0 : oSRS.SetMercator ( center_lat, center_lon, 1, 0, 0 );
400 2 : } else if (EQUAL( map_proj_name, "STEREOGRAPHIC" )) {
401 0 : oSRS.SetStereographic ( center_lat, center_lon, 1, 0, 0 );
402 2 : } else if (EQUAL( map_proj_name, "POLAR_STEREOGRAPHIC")) {
403 0 : oSRS.SetPS ( center_lat, center_lon, 1, 0, 0 );
404 2 : } else if (EQUAL( map_proj_name, "TRANSVERSE_MERCATOR" )) {
405 0 : oSRS.SetTM ( center_lat, center_lon, 1, 0, 0 );
406 2 : } else if (EQUAL( map_proj_name, "LAMBERT_CONFORMAL_CONIC" )) {
407 : oSRS.SetLCC ( first_std_parallel, second_std_parallel,
408 0 : center_lat, center_lon, 0, 0 );
409 2 : } else if (EQUAL( map_proj_name, "LAMBERT_AZIMUTHAL_EQUAL_AREA" )) {
410 0 : oSRS.SetLAEA( center_lat, center_lon, 0, 0 );
411 2 : } else if (EQUAL( map_proj_name, "CYLINDRICAL_EQUAL_AREA" )) {
412 0 : oSRS.SetCEA ( first_std_parallel, center_lon, 0, 0 );
413 2 : } else if (EQUAL( map_proj_name, "MOLLWEIDE" )) {
414 0 : oSRS.SetMollweide ( center_lon, 0, 0 );
415 2 : } else if (EQUAL( map_proj_name, "ALBERS" )) {
416 : oSRS.SetACEA ( first_std_parallel, second_std_parallel,
417 0 : center_lat, center_lon, 0, 0 );
418 2 : } else if (EQUAL( map_proj_name, "BONNE" )) {
419 0 : oSRS.SetBonne ( first_std_parallel, center_lon, 0, 0 );
420 2 : } else if (EQUAL( map_proj_name, "GNOMONIC" )) {
421 0 : oSRS.SetGnomonic ( center_lat, center_lon, 0, 0 );
422 2 : } else if (EQUAL( map_proj_name, "OBLIQUE_CYLINDRICAL" )) {
423 : // hope Swiss Oblique Cylindrical is the same
424 0 : oSRS.SetSOC ( center_lat, center_lon, 0, 0 );
425 : } else {
426 : CPLDebug( "PDS",
427 : "Dataset projection %s is not supported. Continuing...",
428 2 : map_proj_name.c_str() );
429 2 : bProjectionSet = FALSE;
430 : }
431 :
432 8 : if (bProjectionSet) {
433 : //Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
434 6 : CPLString proj_target_name = map_proj_name + " " + target_name;
435 6 : oSRS.SetProjCS(proj_target_name); //set ProjCS keyword
436 :
437 : //The geographic/geocentric name will be the same basic name as the body name
438 : //'GCS' = Geographic/Geocentric Coordinate System
439 6 : CPLString geog_name = "GCS_" + target_name;
440 :
441 : //The datum and sphere names will be the same basic name aas the planet
442 6 : CPLString datum_name = "D_" + target_name;
443 6 : CPLString sphere_name = target_name; // + "_IAU_IAG"); //Might not be IAU defined so don't add
444 :
445 : //calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
446 6 : if ((semi_major - semi_minor) < 0.0000001)
447 5 : iflattening = 0;
448 : else
449 1 : iflattening = semi_major / (semi_major - semi_minor);
450 :
451 : //Set the body size but take into consideration which proj is being used to help w/ compatibility
452 : //Notice that most PDS projections are spherical based on the fact that ISIS/PICS are spherical
453 : //Set the body size but take into consideration which proj is being used to help w/ proj4 compatibility
454 : //The use of a Sphere, polar radius or ellipse here is based on how ISIS does it internally
455 6 : if ( ( (EQUAL( map_proj_name, "STEREOGRAPHIC" ) && (fabs(center_lat) == 90)) ) ||
456 : (EQUAL( map_proj_name, "POLAR_STEREOGRAPHIC" )))
457 : {
458 0 : if (bIsGeographic) {
459 : //Geograpraphic, so set an ellipse
460 : oSRS.SetGeogCS( geog_name, datum_name, sphere_name,
461 : semi_major, iflattening,
462 0 : "Reference_Meridian", 0.0 );
463 : } else {
464 : //Geocentric, so force a sphere using the semi-minor axis. I hope...
465 0 : sphere_name += "_polarRadius";
466 : oSRS.SetGeogCS( geog_name, datum_name, sphere_name,
467 : semi_minor, 0.0,
468 0 : "Reference_Meridian", 0.0 );
469 : }
470 : }
471 6 : else if ( (EQUAL( map_proj_name, "SIMPLE_CYLINDRICAL" )) ||
472 : (EQUAL( map_proj_name, "EQUIDISTANT" )) ||
473 : (EQUAL( map_proj_name, "ORTHOGRAPHIC" )) ||
474 : (EQUAL( map_proj_name, "STEREOGRAPHIC" )) ||
475 : (EQUAL( map_proj_name, "SINUSOIDAL" )) ) {
476 : //isis uses the spherical equation for these projections so force a sphere
477 : oSRS.SetGeogCS( geog_name, datum_name, sphere_name,
478 : semi_major, 0.0,
479 3 : "Reference_Meridian", 0.0 );
480 : }
481 3 : else if (EQUAL( map_proj_name, "EQUIRECTANGULAR" )) {
482 : //isis uses local radius as a sphere, which is pre-calculated in the PDS label as the semi-major
483 3 : sphere_name += "_localRadius";
484 : oSRS.SetGeogCS( geog_name, datum_name, sphere_name,
485 : semi_major, 0.0,
486 3 : "Reference_Meridian", 0.0 );
487 : }
488 : else {
489 : //All other projections: Mercator, Transverse Mercator, Lambert Conformal, etc.
490 : //Geographic, so set an ellipse
491 0 : if (bIsGeographic) {
492 : oSRS.SetGeogCS( geog_name, datum_name, sphere_name,
493 : semi_major, iflattening,
494 0 : "Reference_Meridian", 0.0 );
495 : } else {
496 : //Geocentric, so force a sphere. I hope...
497 : oSRS.SetGeogCS( geog_name, datum_name, sphere_name,
498 : semi_major, 0.0,
499 0 : "Reference_Meridian", 0.0 );
500 : }
501 : }
502 :
503 : // translate back into a projection string.
504 6 : char *pszResult = NULL;
505 6 : oSRS.exportToWkt( &pszResult );
506 6 : osProjection = pszResult;
507 6 : CPLFree( pszResult );
508 : }
509 :
510 : /* ==================================================================== */
511 : /* Check for a .prj and world file to override the georeferencing. */
512 : /* ==================================================================== */
513 : {
514 8 : CPLString osPath, osName;
515 : FILE *fp;
516 :
517 8 : osPath = CPLGetPath( pszFilename );
518 8 : osName = CPLGetBasename(pszFilename);
519 8 : const char *pszPrjFile = CPLFormCIFilename( osPath, osName, "prj" );
520 :
521 8 : fp = VSIFOpen( pszPrjFile, "r" );
522 8 : if( fp != NULL )
523 : {
524 : char **papszLines;
525 0 : OGRSpatialReference oSRS;
526 :
527 0 : VSIFClose( fp );
528 :
529 0 : papszLines = CSLLoad( pszPrjFile );
530 :
531 0 : if( oSRS.importFromESRI( papszLines ) == OGRERR_NONE )
532 : {
533 0 : char *pszResult = NULL;
534 0 : oSRS.exportToWkt( &pszResult );
535 0 : osProjection = pszResult;
536 0 : CPLFree( pszResult );
537 : }
538 :
539 0 : CSLDestroy( papszLines );
540 8 : }
541 : }
542 :
543 8 : if( dfULYMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0 )
544 : {
545 6 : bGotTransform = TRUE;
546 6 : adfGeoTransform[0] = dfULXMap;
547 6 : adfGeoTransform[1] = dfXDim;
548 6 : adfGeoTransform[2] = 0.0;
549 6 : adfGeoTransform[3] = dfULYMap;
550 6 : adfGeoTransform[4] = 0.0;
551 6 : adfGeoTransform[5] = dfYDim;
552 : }
553 :
554 8 : if( !bGotTransform )
555 : bGotTransform =
556 : GDALReadWorldFile( pszFilename, "psw",
557 2 : adfGeoTransform );
558 :
559 8 : if( !bGotTransform )
560 : bGotTransform =
561 : GDALReadWorldFile( pszFilename, "wld",
562 2 : adfGeoTransform );
563 :
564 8 : }
565 :
566 : /************************************************************************/
567 : /* ParseUncompressedImage() */
568 : /************************************************************************/
569 :
570 6 : int PDSDataset::ParseUncompressedImage()
571 :
572 : {
573 : /* ------------------------------------------------------------------- */
574 : /* We assume the user is pointing to the label (ie. .lbl) file. */
575 : /* ------------------------------------------------------------------- */
576 : // IMAGE can be inline or detached and point to an image name
577 : // ^IMAGE = 3
578 : // ^IMAGE = "GLOBAL_ALBEDO_8PPD.IMG"
579 : // ^IMAGE = "MEGT90N000CB.IMG"
580 : // ^SPECTRAL_QUBE = 5 for multi-band images
581 :
582 6 : CPLString osImageKeyword = "^IMAGE";
583 6 : CPLString osQube = GetKeyword( osImageKeyword, "" );
584 6 : CPLString osTargetFile = GetDescription();
585 :
586 6 : if (EQUAL(osQube,"")) {
587 0 : osImageKeyword = "^SPECTRAL_QUBE";
588 0 : osQube = GetKeyword( osImageKeyword );
589 : }
590 :
591 6 : int nQube = atoi(osQube);
592 6 : int nDetachedOffset = 0;
593 :
594 6 : if( osQube[0] == '(' )
595 : {
596 1 : osQube = GetKeywordSub( osImageKeyword, 1 );
597 1 : nDetachedOffset = atoi(GetKeywordSub( osImageKeyword, 2 ));
598 : }
599 :
600 6 : if( osQube[0] == '"' )
601 : {
602 0 : CPLString osTPath = CPLGetPath(GetDescription());
603 0 : CPLString osFilename = osQube;
604 0 : CleanString( osFilename );
605 0 : osTargetFile = CPLFormCIFilename( osTPath, osFilename, NULL );
606 : }
607 :
608 6 : GDALDataType eDataType = GDT_Byte;
609 :
610 :
611 : //image parameters
612 6 : int nRows, nCols, nBands = 1;
613 6 : int nSkipBytes = 0;
614 : int itype;
615 : int record_bytes;
616 6 : int bNoDataSet = FALSE;
617 6 : char chByteOrder = 'M'; //default to MSB
618 6 : double dfNoData = 0.0;
619 :
620 : /* -------------------------------------------------------------------- */
621 : /* Checks to see if this is raw PDS image not compressed image */
622 : /* so ENCODING_TYPE either does not exist or it equals "N/A". */
623 : /* Compressed types will not be supported in this routine */
624 : /* -------------------------------------------------------------------- */
625 : const char *value;
626 :
627 6 : value = GetKeyword( "IMAGE.ENCODING_TYPE", "N/A" );
628 6 : if ( !(EQUAL(value,"N/A") ) )
629 : {
630 : CPLError( CE_Failure, CPLE_OpenFailed,
631 : "*** PDS image file has an ENCODING_TYPE parameter:\n"
632 : "*** gdal pds driver does not support compressed image types\n"
633 0 : "found: (%s)\n\n", value );
634 0 : return FALSE;
635 : }
636 : /**************** end ENCODING_TYPE check ***********************/
637 :
638 :
639 : /*********** Grab layout type (BSQ, BIP, BIL) ************/
640 : // AXIS_NAME = (SAMPLE,LINE,BAND)
641 : /*********** Grab samples lines band **************/
642 : /** if AXIS_NAME = "" then Bands=1 and Sample and Lines **/
643 : /** are there own keywords "LINES" and "LINE_SAMPLES" **/
644 : /** if not NULL then CORE_ITEMS keyword i.e. (234,322,2) **/
645 : /***********************************************************/
646 6 : char szLayout[10] = "BSQ"; //default to band seq.
647 6 : value = GetKeyword( "IMAGE.AXIS_NAME", "" );
648 6 : if (EQUAL(value,"(SAMPLE,LINE,BAND)") ) {
649 0 : strcpy(szLayout,"BSQ");
650 0 : nCols = atoi(GetKeywordSub("IMAGE.CORE_ITEMS",1));
651 0 : nRows = atoi(GetKeywordSub("IMAGE.CORE_ITEMS",2));
652 0 : nBands = atoi(GetKeywordSub("IMAGE.CORE_ITEMS",3));
653 : }
654 6 : else if (EQUAL(value,"(BAND,LINE,SAMPLE)") ) {
655 0 : strcpy(szLayout,"BIP");
656 0 : nBands = atoi(GetKeywordSub("IMAGE.CORE_ITEMS",1));
657 0 : nRows = atoi(GetKeywordSub("IMAGE.CORE_ITEMS",2));
658 0 : nCols = atoi(GetKeywordSub("IMAGE.CORE_ITEMS",3));
659 : }
660 6 : else if (EQUAL(value,"(SAMPLE,BAND,LINE)") ) {
661 0 : strcpy(szLayout,"BIL");
662 0 : nCols = atoi(GetKeywordSub("IMAGE.CORE_ITEMS",1));
663 0 : nBands = atoi(GetKeywordSub("IMAGE.CORE_ITEMS",2));
664 0 : nRows = atoi(GetKeywordSub("IMAGE.CORE_ITEMS",3));
665 : }
666 6 : else if ( EQUAL(value,"") ) {
667 6 : strcpy(szLayout,"BSQ");
668 6 : nCols = atoi(GetKeyword("IMAGE.LINE_SAMPLES",""));
669 6 : nRows = atoi(GetKeyword("IMAGE.LINES",""));
670 6 : nBands = atoi(GetKeyword("IMAGE.BANDS","1"));
671 : }
672 : else {
673 : CPLError( CE_Failure, CPLE_OpenFailed,
674 0 : "%s layout not supported. Abort\n\n", value);
675 0 : return FALSE;
676 : }
677 :
678 : /*********** Grab Qube record bytes **********/
679 6 : record_bytes = atoi(GetKeyword("IMAGE.RECORD_BYTES"));
680 6 : if (record_bytes == 0)
681 6 : record_bytes = atoi(GetKeyword("RECORD_BYTES"));
682 :
683 6 : if (nQube > 0)
684 5 : nSkipBytes = (nQube - 1) * record_bytes;
685 1 : else if( nDetachedOffset > 0 )
686 1 : nSkipBytes = nDetachedOffset;
687 : else
688 0 : nSkipBytes = 0;
689 :
690 : /**** Grab format type - pds supports 1,2,4,8,16,32,64 (in theory) **/
691 : /**** I have only seen 8, 16, 32 (float) in released datasets **/
692 6 : itype = atoi(GetKeyword("IMAGE.SAMPLE_BITS",""));
693 6 : switch(itype) {
694 : case 8 :
695 4 : eDataType = GDT_Byte;
696 4 : dfNoData = NULL1;
697 4 : bNoDataSet = TRUE;
698 4 : break;
699 : case 16 :
700 2 : eDataType = GDT_Int16;
701 2 : dfNoData = NULL2;
702 2 : bNoDataSet = TRUE;
703 2 : break;
704 : case 32 :
705 0 : eDataType = GDT_Float32;
706 0 : dfNoData = NULL3;
707 0 : bNoDataSet = TRUE;
708 0 : break;
709 : case 64 :
710 0 : eDataType = GDT_Float64;
711 0 : dfNoData = NULL3;
712 0 : bNoDataSet = TRUE;
713 0 : break;
714 : default :
715 : CPLError( CE_Failure, CPLE_AppDefined,
716 : "Sample_bits of %d is not supported in this gdal PDS reader.",
717 0 : itype);
718 0 : return FALSE;
719 : }
720 :
721 : /*********** Grab SAMPLE_TYPE *****************/
722 : /** if keyword not found leave as "M" or "MSB" **/
723 6 : value = GetKeyword( "IMAGE.SAMPLE_TYPE" );
724 6 : if( (EQUAL(value,"LSB_INTEGER")) ||
725 : (EQUAL(value,"LSB")) || // just incase
726 : (EQUAL(value,"LSB_UNSIGNED_INTEGER")) ||
727 : (EQUAL(value,"LSB_SIGNED_INTEGER")) ||
728 : (EQUAL(value,"UNSIGNED_INTEGER")) ||
729 : (EQUAL(value,"VAX_REAL")) ||
730 : (EQUAL(value,"VAX_INTEGER")) ||
731 : (EQUAL(value,"PC_INTEGER")) || //just incase
732 : (EQUAL(value,"PC_REAL")) ) {
733 4 : chByteOrder = 'I';
734 : }
735 :
736 : /* -------------------------------------------------------------------- */
737 : /* Did we get the required keywords? If not we return with */
738 : /* this never having been considered to be a match. This isn't */
739 : /* an error! */
740 : /* -------------------------------------------------------------------- */
741 6 : if( nRows < 1 || nCols < 1 || nBands < 1 )
742 : {
743 : CPLError( CE_Failure, CPLE_AppDefined,
744 : "File %s appears to be a PDS file, but failed to find some required keywords.",
745 0 : GetDescription() );
746 0 : return FALSE;
747 : }
748 :
749 : /* -------------------------------------------------------------------- */
750 : /* Capture some information from the file that is of interest. */
751 : /* -------------------------------------------------------------------- */
752 6 : nRasterXSize = nCols;
753 6 : nRasterYSize = nRows;
754 :
755 : /* -------------------------------------------------------------------- */
756 : /* Open target binary file. */
757 : /* -------------------------------------------------------------------- */
758 :
759 6 : if( eAccess == GA_ReadOnly )
760 6 : fpImage = VSIFOpenL( osTargetFile, "rb" );
761 : else
762 0 : fpImage = VSIFOpenL( osTargetFile, "r+b" );
763 :
764 6 : if( fpImage == NULL )
765 : {
766 : CPLError( CE_Failure, CPLE_OpenFailed,
767 : "Failed to open %s with write permission.\n%s",
768 : osTargetFile.c_str(),
769 0 : VSIStrerror( errno ) );
770 0 : return FALSE;
771 : }
772 :
773 : /* -------------------------------------------------------------------- */
774 : /* Compute the line offset. */
775 : /* -------------------------------------------------------------------- */
776 6 : int nItemSize = GDALGetDataTypeSize(eDataType)/8;
777 : int nLineOffset, nPixelOffset, nBandOffset;
778 :
779 6 : if( EQUAL(szLayout,"BIP") )
780 : {
781 0 : nPixelOffset = nItemSize * nBands;
782 0 : nLineOffset = nPixelOffset * nCols;
783 0 : nBandOffset = nItemSize;
784 : }
785 6 : else if( EQUAL(szLayout,"BSQ") )
786 : {
787 6 : nPixelOffset = nItemSize;
788 6 : nLineOffset = nPixelOffset * nCols;
789 6 : nBandOffset = nLineOffset * nRows;
790 : }
791 : else /* assume BIL */
792 : {
793 0 : nPixelOffset = nItemSize;
794 0 : nLineOffset = nItemSize * nBands * nCols;
795 0 : nBandOffset = nItemSize * nCols;
796 : }
797 :
798 : /* -------------------------------------------------------------------- */
799 : /* Create band information objects. */
800 : /* -------------------------------------------------------------------- */
801 : int i;
802 :
803 6 : nBands = nBands;;
804 12 : for( i = 0; i < nBands; i++ )
805 : {
806 : RawRasterBand *poBand;
807 :
808 : poBand =
809 : new RawRasterBand( this, i+1, fpImage,
810 : nSkipBytes + nBandOffset * i,
811 : nPixelOffset, nLineOffset, eDataType,
812 : #ifdef CPL_LSB
813 : chByteOrder == 'I' || chByteOrder == 'L',
814 : #else
815 : chByteOrder == 'M',
816 : #endif
817 6 : TRUE );
818 :
819 6 : if( bNoDataSet )
820 6 : poBand->SetNoDataValue( dfNoData );
821 :
822 6 : SetBand( i+1, poBand );
823 :
824 : // Set offset/scale values at the PAM level.
825 : poBand->SetOffset(
826 6 : CPLAtofM(GetKeyword("IMAGE.OFFSET","0.0")));
827 : poBand->SetScale(
828 6 : CPLAtofM(GetKeyword("IMAGE.SCALING_FACTOR","1.0")));
829 : }
830 :
831 6 : return TRUE;
832 : }
833 :
834 : /************************************************************************/
835 : /* ==================================================================== */
836 : /* PDSWrapperRasterBand */
837 : /* */
838 : /* proxy for the jp2 or other compressed bands. */
839 : /* ==================================================================== */
840 : /************************************************************************/
841 : class PDSWrapperRasterBand : public GDALProxyRasterBand
842 : {
843 : GDALRasterBand* poBaseBand;
844 :
845 : protected:
846 22 : virtual GDALRasterBand* RefUnderlyingRasterBand() { return poBaseBand; }
847 :
848 : public:
849 2 : PDSWrapperRasterBand( GDALRasterBand* poBaseBand )
850 2 : {
851 2 : this->poBaseBand = poBaseBand;
852 2 : eDataType = poBaseBand->GetRasterDataType();
853 2 : poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
854 2 : }
855 4 : ~PDSWrapperRasterBand() {}
856 : };
857 :
858 : /************************************************************************/
859 : /* ParseCompressedImage() */
860 : /************************************************************************/
861 :
862 2 : int PDSDataset::ParseCompressedImage()
863 :
864 : {
865 2 : CPLString osFileName = GetKeyword( "COMPRESSED_FILE.FILE_NAME", "" );
866 2 : CleanString( osFileName );
867 :
868 2 : CPLString osPath = CPLGetPath(GetDescription());
869 2 : CPLString osFullFileName = CPLFormFilename( osPath, osFileName, NULL );
870 : int iBand;
871 :
872 2 : poCompressedDS = (GDALDataset*) GDALOpen( osFullFileName, GA_ReadOnly );
873 :
874 2 : if( poCompressedDS == NULL )
875 0 : return FALSE;
876 :
877 2 : nRasterXSize = poCompressedDS->GetRasterXSize();
878 2 : nRasterYSize = poCompressedDS->GetRasterYSize();
879 :
880 4 : for( iBand = 0; iBand < poCompressedDS->GetRasterCount(); iBand++ )
881 : {
882 2 : SetBand( iBand+1, new PDSWrapperRasterBand( poCompressedDS->GetRasterBand( iBand+1 ) ) );
883 : }
884 :
885 2 : return TRUE;
886 : }
887 :
888 : /************************************************************************/
889 : /* Identify() */
890 : /************************************************************************/
891 :
892 9028 : int PDSDataset::Identify( GDALOpenInfo * poOpenInfo )
893 :
894 : {
895 9028 : if( poOpenInfo->pabyHeader == NULL )
896 8275 : return FALSE;
897 :
898 753 : return strstr((char*)poOpenInfo->pabyHeader,"PDS_VERSION_ID") != NULL;
899 : }
900 :
901 : /************************************************************************/
902 : /* Open() */
903 : /************************************************************************/
904 :
905 1299 : GDALDataset *PDSDataset::Open( GDALOpenInfo * poOpenInfo )
906 : {
907 1299 : if( !Identify( poOpenInfo ) )
908 1291 : return NULL;
909 :
910 8 : if( strstr((const char *)poOpenInfo->pabyHeader,"PDS3") == NULL )
911 : {
912 : CPLError( CE_Failure, CPLE_OpenFailed,
913 0 : "It appears this is an older PDS image type. Only PDS_VERSION_ID = PDS3 are currently supported by this gdal PDS reader.");
914 0 : return NULL;
915 : }
916 :
917 : /* -------------------------------------------------------------------- */
918 : /* Open and parse the keyword header. Sometimes there is stuff */
919 : /* before the PDS_VERSION_ID, which we want to ignore. */
920 : /* -------------------------------------------------------------------- */
921 8 : FILE *fpQube = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
922 :
923 8 : if( fpQube == NULL )
924 0 : return NULL;
925 :
926 : PDSDataset *poDS;
927 :
928 8 : poDS = new PDSDataset();
929 8 : poDS->SetDescription( poOpenInfo->pszFilename );
930 8 : poDS->eAccess = poOpenInfo->eAccess;
931 :
932 8 : const char* pszPDSVersionID = strstr((const char *)poOpenInfo->pabyHeader,"PDS_VERSION_ID");
933 8 : int nOffset = 0;
934 8 : if (pszPDSVersionID)
935 8 : nOffset = pszPDSVersionID - (const char *)poOpenInfo->pabyHeader;
936 :
937 8 : if( ! poDS->oKeywords.Ingest( fpQube, nOffset ) )
938 : {
939 0 : delete poDS;
940 0 : VSIFCloseL( fpQube );
941 0 : return NULL;
942 : }
943 8 : VSIFCloseL( fpQube );
944 :
945 : /* -------------------------------------------------------------------- */
946 : /* Is this a comprssed image with COMPRESSED_FILE subdomain? */
947 : /* */
948 : /* The corresponding parse operations will read keywords, */
949 : /* establish bands and raster size. */
950 : /* -------------------------------------------------------------------- */
951 8 : CPLString osEncodingType = poDS->GetKeyword( "COMPRESSED_FILE.ENCODING_TYPE", "" );
952 :
953 8 : if( osEncodingType.size() != 0 )
954 : {
955 2 : if( !poDS->ParseCompressedImage() )
956 : {
957 0 : delete poDS;
958 0 : return NULL;
959 : }
960 : }
961 : else
962 : {
963 6 : if( !poDS->ParseUncompressedImage() )
964 : {
965 0 : delete poDS;
966 0 : return NULL;
967 : }
968 : }
969 :
970 : /* -------------------------------------------------------------------- */
971 : /* Set the coordinate system and geotransform. */
972 : /* -------------------------------------------------------------------- */
973 8 : poDS->ParseSRS();
974 :
975 : /* -------------------------------------------------------------------- */
976 : /* Transfer a few interesting keywords as metadata. */
977 : /* -------------------------------------------------------------------- */
978 : int i;
979 : static const char *apszKeywords[] =
980 : { "FILTER_NAME", "DATA_SET_ID", "PRODUCT_ID",
981 : "PRODUCER_INSTITUTION_NAME", "PRODUCT_TYPE", "MISSION_NAME",
982 : "SPACECRAFT_NAME", "INSTRUMENT_NAME", "INSTRUMENT_ID",
983 : "TARGET_NAME", "CENTER_FILTER_WAVELENGTH", "BANDWIDTH",
984 : "PRODUCT_CREATION_TIME", "NOTE",
985 : NULL };
986 :
987 120 : for( i = 0; apszKeywords[i] != NULL; i++ )
988 : {
989 112 : const char *pszKeywordValue = poDS->GetKeyword( apszKeywords[i] );
990 :
991 112 : if( pszKeywordValue != NULL )
992 112 : poDS->SetMetadataItem( apszKeywords[i], pszKeywordValue );
993 : }
994 :
995 : /* -------------------------------------------------------------------- */
996 : /* Initialize any PAM information. */
997 : /* -------------------------------------------------------------------- */
998 8 : poDS->TryLoadXML();
999 :
1000 : /* -------------------------------------------------------------------- */
1001 : /* Check for overviews. */
1002 : /* -------------------------------------------------------------------- */
1003 8 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
1004 :
1005 8 : return( poDS );
1006 : }
1007 :
1008 : /************************************************************************/
1009 : /* GetKeyword() */
1010 : /************************************************************************/
1011 :
1012 290 : const char *PDSDataset::GetKeyword( const char *pszPath,
1013 : const char *pszDefault )
1014 :
1015 : {
1016 290 : return oKeywords.GetKeyword( pszPath, pszDefault );
1017 : }
1018 :
1019 : /************************************************************************/
1020 : /* GetKeywordSub() */
1021 : /************************************************************************/
1022 :
1023 2 : const char *PDSDataset::GetKeywordSub( const char *pszPath,
1024 : int iSubscript,
1025 : const char *pszDefault )
1026 :
1027 : {
1028 2 : const char *pszResult = oKeywords.GetKeyword( pszPath, NULL );
1029 :
1030 2 : if( pszResult == NULL )
1031 0 : return pszDefault;
1032 :
1033 2 : if( pszResult[0] != '(' )
1034 0 : return pszDefault;
1035 :
1036 : char **papszTokens = CSLTokenizeString2( pszResult, "(,)",
1037 2 : CSLT_HONOURSTRINGS );
1038 :
1039 2 : if( iSubscript <= CSLCount(papszTokens) )
1040 : {
1041 2 : osTempResult = papszTokens[iSubscript-1];
1042 2 : CSLDestroy( papszTokens );
1043 2 : return osTempResult.c_str();
1044 : }
1045 : else
1046 : {
1047 0 : CSLDestroy( papszTokens );
1048 0 : return pszDefault;
1049 : }
1050 : }
1051 :
1052 : /************************************************************************/
1053 : /* GetKeywordUnit() */
1054 : /************************************************************************/
1055 :
1056 6 : const char *PDSDataset::GetKeywordUnit( const char *pszPath,
1057 : int iSubscript,
1058 : const char *pszDefault )
1059 :
1060 : {
1061 6 : const char *pszResult = oKeywords.GetKeyword( pszPath, NULL );
1062 :
1063 6 : if( pszResult == NULL )
1064 0 : return pszDefault;
1065 :
1066 : char **papszTokens = CSLTokenizeString2( pszResult, "</>",
1067 6 : CSLT_HONOURSTRINGS );
1068 :
1069 6 : if( iSubscript <= CSLCount(papszTokens) )
1070 : {
1071 5 : osTempResult = papszTokens[iSubscript-1];
1072 5 : CSLDestroy( papszTokens );
1073 5 : return osTempResult.c_str();
1074 : }
1075 : else
1076 : {
1077 1 : CSLDestroy( papszTokens );
1078 1 : return pszDefault;
1079 : }
1080 : }
1081 :
1082 : /************************************************************************/
1083 : /* CleanString() */
1084 : /* */
1085 : /* Removes single or double quotes, and converts spaces to underscores. */
1086 : /* The change is made in-place to CPLString. */
1087 : /************************************************************************/
1088 :
1089 18 : void PDSDataset::CleanString( CPLString &osInput )
1090 :
1091 : {
1092 18 : if( ( osInput.size() < 2 ) ||
1093 : ((osInput.at(0) != '"' || osInput.at(osInput.size()-1) != '"' ) &&
1094 : ( osInput.at(0) != '\'' || osInput.at(osInput.size()-1) != '\'')) )
1095 7 : return;
1096 :
1097 11 : char *pszWrk = CPLStrdup(osInput.c_str() + 1);
1098 : int i;
1099 :
1100 11 : pszWrk[strlen(pszWrk)-1] = '\0';
1101 :
1102 104 : for( i = 0; pszWrk[i] != '\0'; i++ )
1103 : {
1104 93 : if( pszWrk[i] == ' ' )
1105 2 : pszWrk[i] = '_';
1106 : }
1107 :
1108 11 : osInput = pszWrk;
1109 11 : CPLFree( pszWrk );
1110 : }
1111 : /************************************************************************/
1112 : /* GDALRegister_PDS() */
1113 : /************************************************************************/
1114 :
1115 338 : void GDALRegister_PDS()
1116 :
1117 : {
1118 : GDALDriver *poDriver;
1119 :
1120 338 : if( GDALGetDriverByName( "PDS" ) == NULL )
1121 : {
1122 336 : poDriver = new GDALDriver();
1123 :
1124 336 : poDriver->SetDescription( "PDS" );
1125 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1126 336 : "NASA Planetary Data System" );
1127 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
1128 336 : "frmt_various.html#PDS" );
1129 :
1130 336 : poDriver->pfnOpen = PDSDataset::Open;
1131 336 : poDriver->pfnIdentify = PDSDataset::Identify;
1132 :
1133 336 : GetGDALDriverManager()->RegisterDriver( poDriver );
1134 : }
1135 338 : }
1136 :
|