1 : /******************************************************************************
2 : * $Id: envidataset.cpp 25660 2013-02-21 20:56:07Z rouault $
3 : *
4 : * Project: ENVI .hdr Driver
5 : * Purpose: Implementation of ENVI .hdr labelled raw raster support.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : * Maintainer: Chris Padwick (cpadwick at ittvis.com)
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2002, Frank Warmerdam
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "rawdataset.h"
32 : #include "ogr_spatialref.h"
33 : #include "cpl_string.h"
34 : #include <algorithm>
35 :
36 : CPL_CVSID("$Id: envidataset.cpp 25660 2013-02-21 20:56:07Z rouault $");
37 :
38 : CPL_C_START
39 : void GDALRegister_ENVI(void);
40 : CPL_C_END
41 :
42 : static const int anUsgsEsriZones[] =
43 : {
44 : 101, 3101,
45 : 102, 3126,
46 : 201, 3151,
47 : 202, 3176,
48 : 203, 3201,
49 : 301, 3226,
50 : 302, 3251,
51 : 401, 3276,
52 : 402, 3301,
53 : 403, 3326,
54 : 404, 3351,
55 : 405, 3376,
56 : 406, 3401,
57 : 407, 3426,
58 : 501, 3451,
59 : 502, 3476,
60 : 503, 3501,
61 : 600, 3526,
62 : 700, 3551,
63 : 901, 3601,
64 : 902, 3626,
65 : 903, 3576,
66 : 1001, 3651,
67 : 1002, 3676,
68 : 1101, 3701,
69 : 1102, 3726,
70 : 1103, 3751,
71 : 1201, 3776,
72 : 1202, 3801,
73 : 1301, 3826,
74 : 1302, 3851,
75 : 1401, 3876,
76 : 1402, 3901,
77 : 1501, 3926,
78 : 1502, 3951,
79 : 1601, 3976,
80 : 1602, 4001,
81 : 1701, 4026,
82 : 1702, 4051,
83 : 1703, 6426,
84 : 1801, 4076,
85 : 1802, 4101,
86 : 1900, 4126,
87 : 2001, 4151,
88 : 2002, 4176,
89 : 2101, 4201,
90 : 2102, 4226,
91 : 2103, 4251,
92 : 2111, 6351,
93 : 2112, 6376,
94 : 2113, 6401,
95 : 2201, 4276,
96 : 2202, 4301,
97 : 2203, 4326,
98 : 2301, 4351,
99 : 2302, 4376,
100 : 2401, 4401,
101 : 2402, 4426,
102 : 2403, 4451,
103 : 2500, 0,
104 : 2501, 4476,
105 : 2502, 4501,
106 : 2503, 4526,
107 : 2600, 0,
108 : 2601, 4551,
109 : 2602, 4576,
110 : 2701, 4601,
111 : 2702, 4626,
112 : 2703, 4651,
113 : 2800, 4676,
114 : 2900, 4701,
115 : 3001, 4726,
116 : 3002, 4751,
117 : 3003, 4776,
118 : 3101, 4801,
119 : 3102, 4826,
120 : 3103, 4851,
121 : 3104, 4876,
122 : 3200, 4901,
123 : 3301, 4926,
124 : 3302, 4951,
125 : 3401, 4976,
126 : 3402, 5001,
127 : 3501, 5026,
128 : 3502, 5051,
129 : 3601, 5076,
130 : 3602, 5101,
131 : 3701, 5126,
132 : 3702, 5151,
133 : 3800, 5176,
134 : 3900, 0,
135 : 3901, 5201,
136 : 3902, 5226,
137 : 4001, 5251,
138 : 4002, 5276,
139 : 4100, 5301,
140 : 4201, 5326,
141 : 4202, 5351,
142 : 4203, 5376,
143 : 4204, 5401,
144 : 4205, 5426,
145 : 4301, 5451,
146 : 4302, 5476,
147 : 4303, 5501,
148 : 4400, 5526,
149 : 4501, 5551,
150 : 4502, 5576,
151 : 4601, 5601,
152 : 4602, 5626,
153 : 4701, 5651,
154 : 4702, 5676,
155 : 4801, 5701,
156 : 4802, 5726,
157 : 4803, 5751,
158 : 4901, 5776,
159 : 4902, 5801,
160 : 4903, 5826,
161 : 4904, 5851,
162 : 5001, 6101,
163 : 5002, 6126,
164 : 5003, 6151,
165 : 5004, 6176,
166 : 5005, 6201,
167 : 5006, 6226,
168 : 5007, 6251,
169 : 5008, 6276,
170 : 5009, 6301,
171 : 5010, 6326,
172 : 5101, 5876,
173 : 5102, 5901,
174 : 5103, 5926,
175 : 5104, 5951,
176 : 5105, 5976,
177 : 5201, 6001,
178 : 5200, 6026,
179 : 5200, 6076,
180 : 5201, 6051,
181 : 5202, 6051,
182 : 5300, 0,
183 : 5400, 0
184 : };
185 :
186 : /************************************************************************/
187 : /* ITTVISToUSGSZone() */
188 : /* */
189 : /* Convert ITTVIS style state plane zones to NOS style state */
190 : /* plane zones. The ENVI default is to use the new NOS zones, */
191 : /* but the old state plane zones can be used. Handle this. */
192 : /************************************************************************/
193 :
194 0 : static int ITTVISToUSGSZone( int nITTVISZone )
195 :
196 : {
197 0 : int nPairs = sizeof(anUsgsEsriZones) / (2*sizeof(int));
198 : int i;
199 :
200 : // Default is to use the zone as-is, as long as it is in the
201 : // available list
202 0 : for( i = 0; i < nPairs; i++ )
203 : {
204 0 : if( anUsgsEsriZones[i*2] == nITTVISZone )
205 0 : return anUsgsEsriZones[i*2];
206 : }
207 :
208 : // If not found in the new style, see if it is present in the
209 : // old style list and convert it. We don't expect to see this
210 : // often, but older files allowed it and may still exist.
211 0 : for( i = 0; i < nPairs; i++ )
212 : {
213 0 : if( anUsgsEsriZones[i*2+1] == nITTVISZone )
214 0 : return anUsgsEsriZones[i*2];
215 : }
216 :
217 0 : return nITTVISZone; // perhaps it *is* the USGS zone?
218 : }
219 :
220 : /************************************************************************/
221 : /* ==================================================================== */
222 : /* ENVIDataset */
223 : /* ==================================================================== */
224 : /************************************************************************/
225 :
226 : class ENVIRasterBand;
227 :
228 : class ENVIDataset : public RawDataset
229 : {
230 : friend class ENVIRasterBand;
231 :
232 : VSILFILE *fpImage; // image data file.
233 : VSILFILE *fp; // header file
234 : char *pszHDRFilename;
235 :
236 : int bFoundMapinfo;
237 :
238 : int bHeaderDirty;
239 :
240 : double adfGeoTransform[6];
241 :
242 : char *pszProjection;
243 :
244 : char **papszHeader;
245 :
246 : CPLString osStaFilename;
247 :
248 : int ReadHeader( VSILFILE * );
249 : int ProcessMapinfo( const char * );
250 : void ProcessRPCinfo( const char * ,int ,int);
251 : void ProcessStatsFile();
252 : int byteSwapInt(int);
253 : float byteSwapFloat(float);
254 : double byteSwapDouble(double);
255 : void SetENVIDatum( OGRSpatialReference *, const char * );
256 : void SetENVIEllipse( OGRSpatialReference *, char ** );
257 : void WriteProjectionInfo();
258 : int ParseRpcCoeffsMetaDataString(const char *psName, char *papszVal[], int& idx);
259 : int WriteRpcInfo();
260 : int WritePseudoGcpInfo();
261 :
262 : char **SplitList( const char * );
263 :
264 : enum Interleave { BSQ, BIL, BIP } interleave;
265 : static int GetEnviType(GDALDataType eType);
266 :
267 : public:
268 : ENVIDataset();
269 : ~ENVIDataset();
270 :
271 : virtual void FlushCache( void );
272 : virtual CPLErr GetGeoTransform( double * padfTransform );
273 : virtual CPLErr SetGeoTransform( double * );
274 : virtual const char *GetProjectionRef(void);
275 : virtual CPLErr SetProjection( const char * );
276 : virtual char **GetFileList(void);
277 :
278 : virtual void SetDescription( const char * );
279 :
280 : virtual CPLErr SetMetadata( char ** papszMetadata,
281 : const char * pszDomain = "" );
282 : virtual CPLErr SetMetadataItem( const char * pszName,
283 : const char * pszValue,
284 : const char * pszDomain = "" );
285 : virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
286 : const char *pszGCPProjection );
287 :
288 : static GDALDataset *Open( GDALOpenInfo * );
289 : static GDALDataset *Create( const char * pszFilename,
290 : int nXSize, int nYSize, int nBands,
291 : GDALDataType eType, char ** papszOptions );
292 : };
293 :
294 : /************************************************************************/
295 : /* ==================================================================== */
296 : /* ENVIRasterBand */
297 : /* ==================================================================== */
298 : /************************************************************************/
299 :
300 : class ENVIRasterBand : public RawRasterBand
301 189 : {
302 : public:
303 : ENVIRasterBand( GDALDataset *poDS, int nBand, void * fpRaw,
304 : vsi_l_offset nImgOffset, int nPixelOffset,
305 : int nLineOffset,
306 : GDALDataType eDataType, int bNativeOrder,
307 : int bIsVSIL = FALSE, int bOwnsFP = FALSE );
308 :
309 : virtual void SetDescription( const char * );
310 :
311 : virtual CPLErr SetCategoryNames( char ** );
312 : };
313 :
314 : /************************************************************************/
315 : /* ENVIDataset() */
316 : /************************************************************************/
317 :
318 116 : ENVIDataset::ENVIDataset()
319 : {
320 116 : fpImage = NULL;
321 116 : fp = NULL;
322 116 : pszHDRFilename = NULL;
323 116 : pszProjection = CPLStrdup("");
324 :
325 116 : papszHeader = NULL;
326 :
327 116 : bFoundMapinfo = FALSE;
328 :
329 116 : bHeaderDirty = FALSE;
330 :
331 116 : adfGeoTransform[0] = 0.0;
332 116 : adfGeoTransform[1] = 1.0;
333 116 : adfGeoTransform[2] = 0.0;
334 116 : adfGeoTransform[3] = 0.0;
335 116 : adfGeoTransform[4] = 0.0;
336 116 : adfGeoTransform[5] = 1.0;
337 116 : }
338 :
339 : /************************************************************************/
340 : /* ~ENVIDataset() */
341 : /************************************************************************/
342 :
343 116 : ENVIDataset::~ENVIDataset()
344 :
345 : {
346 116 : FlushCache();
347 116 : if( fpImage )
348 114 : VSIFCloseL( fpImage );
349 116 : if( fp )
350 116 : VSIFCloseL( fp );
351 116 : CPLFree( pszProjection );
352 116 : CSLDestroy( papszHeader );
353 116 : CPLFree(pszHDRFilename);
354 116 : }
355 :
356 : /************************************************************************/
357 : /* FlushCache() */
358 : /************************************************************************/
359 :
360 116 : void ENVIDataset::FlushCache()
361 :
362 : {
363 116 : RawDataset::FlushCache();
364 :
365 116 : GDALRasterBand* band = (GetRasterCount() > 0) ? GetRasterBand(1) : NULL;
366 :
367 116 : if ( band == NULL || !bHeaderDirty )
368 2 : return;
369 :
370 114 : CPLLocaleC oLocaleEnforcer;
371 :
372 114 : VSIFSeekL( fp, 0, SEEK_SET );
373 : /* -------------------------------------------------------------------- */
374 : /* Rewrite out the header. */
375 : /* -------------------------------------------------------------------- */
376 : int iBigEndian;
377 :
378 : const char *pszInterleaving;
379 : char** catNames;
380 :
381 : #ifdef CPL_LSB
382 114 : iBigEndian = 0;
383 : #else
384 : iBigEndian = 1;
385 : #endif
386 :
387 114 : VSIFPrintfL( fp, "ENVI\n" );
388 114 : if ("" != sDescription)
389 114 : VSIFPrintfL( fp, "description = {\n%s}\n", sDescription.c_str());
390 : VSIFPrintfL( fp, "samples = %d\nlines = %d\nbands = %d\n",
391 114 : nRasterXSize, nRasterYSize, nBands );
392 :
393 114 : catNames = band->GetCategoryNames();
394 :
395 114 : VSIFPrintfL( fp, "header offset = 0\n");
396 114 : if (0 == catNames)
397 110 : VSIFPrintfL( fp, "file type = ENVI Standard\n" );
398 : else
399 4 : VSIFPrintfL( fp, "file type = ENVI Classification\n" );
400 :
401 114 : int iENVIType = GetEnviType(band->GetRasterDataType());
402 114 : VSIFPrintfL( fp, "data type = %d\n", iENVIType );
403 114 : switch (interleave)
404 : {
405 : case BIP:
406 0 : pszInterleaving = "bip"; // interleaved by pixel
407 0 : break;
408 : case BIL:
409 0 : pszInterleaving = "bil"; // interleaved by line
410 0 : break;
411 : case BSQ:
412 114 : pszInterleaving = "bsq"; // band sequental by default
413 114 : break;
414 : default:
415 0 : pszInterleaving = "bsq";
416 : break;
417 : }
418 114 : VSIFPrintfL( fp, "interleave = %s\n", pszInterleaving);
419 114 : VSIFPrintfL( fp, "byte order = %d\n", iBigEndian );
420 :
421 : /* -------------------------------------------------------------------- */
422 : /* Write class and color information */
423 : /* -------------------------------------------------------------------- */
424 114 : catNames = band->GetCategoryNames();
425 114 : if (0 != catNames)
426 : {
427 4 : int nrClasses = 0;
428 16 : while (*catNames++)
429 8 : ++nrClasses;
430 :
431 4 : if (nrClasses > 0)
432 : {
433 4 : VSIFPrintfL( fp, "classes = %d\n", nrClasses );
434 :
435 4 : GDALColorTable* colorTable = band->GetColorTable();
436 4 : if (0 != colorTable)
437 : {
438 4 : int nrColors = colorTable->GetColorEntryCount();
439 4 : if (nrColors > nrClasses)
440 0 : nrColors = nrClasses;
441 4 : VSIFPrintfL( fp, "class lookup = {\n");
442 12 : for (int i = 0; i < nrColors; ++i)
443 : {
444 8 : const GDALColorEntry* color = colorTable->GetColorEntry(i);
445 8 : VSIFPrintfL(fp, "%d, %d, %d", color->c1, color->c2, color->c3);
446 8 : if (i < nrColors - 1)
447 : {
448 4 : VSIFPrintfL(fp, ", ");
449 4 : if (0 == (i+1) % 5)
450 0 : VSIFPrintfL(fp, "\n");
451 : }
452 : }
453 4 : VSIFPrintfL(fp, "}\n");
454 : }
455 :
456 4 : catNames = band->GetCategoryNames();
457 4 : if (0 != *catNames)
458 : {
459 4 : VSIFPrintfL( fp, "class names = {\n%s", *catNames++);
460 4 : int i = 0;
461 12 : while (*catNames) {
462 4 : VSIFPrintfL( fp, ",");
463 4 : if (0 == (++i) % 5)
464 0 : VSIFPrintfL(fp, "\n");
465 4 : VSIFPrintfL( fp, " %s", *catNames++);
466 : }
467 4 : VSIFPrintfL( fp, "}\n");
468 : }
469 : }
470 : }
471 :
472 : /* -------------------------------------------------------------------- */
473 : /* Write the rest of header. */
474 : /* -------------------------------------------------------------------- */
475 :
476 : // only one map info type should be set
477 : // - rpc
478 : // - pseudo/gcp
479 : // - standard
480 114 : if ( !WriteRpcInfo() ) // are rpcs in the metadata
481 : {
482 110 : if ( !WritePseudoGcpInfo() ) // are gcps in the metadata
483 : {
484 110 : WriteProjectionInfo(); // standard - affine xform/coord sys str
485 : }
486 : }
487 :
488 :
489 114 : VSIFPrintfL( fp, "band names = {\n" );
490 303 : for ( int i = 1; i <= nBands; i++ )
491 : {
492 189 : CPLString sBandDesc = GetRasterBand( i )->GetDescription();
493 :
494 189 : if ( sBandDesc == "" )
495 92 : sBandDesc = CPLSPrintf( "Band %d", i );
496 189 : VSIFPrintfL( fp, "%s", sBandDesc.c_str() );
497 189 : if ( i != nBands )
498 75 : VSIFPrintfL( fp, ",\n" );
499 : }
500 114 : VSIFPrintfL( fp, "}\n" );
501 :
502 : /* -------------------------------------------------------------------- */
503 : /* Write the metadata that was read into the ENVI domain */
504 : /* -------------------------------------------------------------------- */
505 114 : char** papszENVIMetadata = GetMetadata("ENVI");
506 :
507 : int i;
508 114 : int count = CSLCount(papszENVIMetadata);
509 : char **papszTokens;
510 :
511 : // For every item of metadata in the ENVI domain
512 114 : for (i = 0; i < count; i++)
513 : {
514 : // Split the entry into two parts at the = character
515 1154 : char *pszEntry = papszENVIMetadata[i];
516 1154 : papszTokens = CSLTokenizeString2( pszEntry, "=", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
517 :
518 1154 : if (CSLCount(papszTokens) != 2)
519 : {
520 1 : CPLDebug("ENVI", "Line of header file could not be split at = into two elements: %s", papszENVIMetadata[i]);
521 1 : CSLDestroy( papszTokens );
522 1 : continue;
523 : }
524 : // Replace _'s in the string with spaces
525 1153 : std::string poKey(papszTokens[0]);
526 2306 : std::replace(poKey.begin(), poKey.end(), '_', ' ');
527 :
528 : // Don't write it out if it is one of the bits of metadata that is written out elsewhere in this routine
529 1153 : if (poKey == "description" || poKey == "samples" || poKey == "lines" ||
530 : poKey == "bands" || poKey == "header offset" || poKey == "file type" ||
531 : poKey == "data type" || poKey == "interleave" || poKey == "byte order" ||
532 : poKey == "class names" || poKey == "band names" || poKey == "map info" ||
533 : poKey == "projection info")
534 : {
535 1096 : CSLDestroy( papszTokens );
536 1096 : continue;
537 : }
538 57 : VSIFPrintfL( fp, "%s = %s\n", poKey.c_str(), papszTokens[1]);
539 57 : CSLDestroy( papszTokens );
540 : }
541 :
542 : /* Clean dirty flag */
543 114 : bHeaderDirty = FALSE;
544 : }
545 :
546 : /************************************************************************/
547 : /* GetFileList() */
548 : /************************************************************************/
549 :
550 11 : char **ENVIDataset::GetFileList()
551 :
552 : {
553 11 : char **papszFileList = NULL;
554 :
555 : // Main data file, etc.
556 11 : papszFileList = RawDataset::GetFileList();
557 :
558 : // Header file.
559 11 : papszFileList = CSLAddString( papszFileList, pszHDRFilename );
560 :
561 : // Statistics file
562 11 : if (osStaFilename.size() != 0)
563 0 : papszFileList = CSLAddString( papszFileList, osStaFilename );
564 :
565 11 : return papszFileList;
566 : }
567 :
568 : /************************************************************************/
569 : /* GetEPSGGeogCS() */
570 : /* */
571 : /* Try to establish what the EPSG code for this coordinate */
572 : /* systems GEOGCS might be. Returns -1 if no reasonable guess */
573 : /* can be made. */
574 : /* */
575 : /* TODO: We really need to do some name lookups. */
576 : /************************************************************************/
577 :
578 98 : static int ENVIGetEPSGGeogCS( OGRSpatialReference *poThis )
579 :
580 : {
581 98 : const char *pszAuthName = poThis->GetAuthorityName( "GEOGCS" );
582 :
583 : /* -------------------------------------------------------------------- */
584 : /* Do we already have it? */
585 : /* -------------------------------------------------------------------- */
586 98 : if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") )
587 23 : return atoi(poThis->GetAuthorityCode( "GEOGCS" ));
588 :
589 : /* -------------------------------------------------------------------- */
590 : /* Get the datum and geogcs names. */
591 : /* -------------------------------------------------------------------- */
592 75 : const char *pszGEOGCS = poThis->GetAttrValue( "GEOGCS" );
593 75 : const char *pszDatum = poThis->GetAttrValue( "DATUM" );
594 :
595 : // We can only operate on coordinate systems with a geogcs.
596 75 : if( pszGEOGCS == NULL || pszDatum == NULL )
597 1 : return -1;
598 :
599 : /* -------------------------------------------------------------------- */
600 : /* Is this a "well known" geographic coordinate system? */
601 : /* -------------------------------------------------------------------- */
602 : int bWGS, bNAD;
603 :
604 : bWGS = strstr(pszGEOGCS,"WGS") != NULL
605 : || strstr(pszDatum, "WGS")
606 : || strstr(pszGEOGCS,"World Geodetic System")
607 : || strstr(pszGEOGCS,"World_Geodetic_System")
608 : || strstr(pszDatum, "World Geodetic System")
609 74 : || strstr(pszDatum, "World_Geodetic_System");
610 :
611 : bNAD = strstr(pszGEOGCS,"NAD") != NULL
612 : || strstr(pszDatum, "NAD")
613 : || strstr(pszGEOGCS,"North American")
614 : || strstr(pszGEOGCS,"North_American")
615 : || strstr(pszDatum, "North American")
616 74 : || strstr(pszDatum, "North_American");
617 :
618 74 : if( bWGS && (strstr(pszGEOGCS,"84") || strstr(pszDatum,"84")) )
619 50 : return 4326;
620 :
621 24 : if( bWGS && (strstr(pszGEOGCS,"72") || strstr(pszDatum,"72")) )
622 0 : return 4322;
623 :
624 24 : if( bNAD && (strstr(pszGEOGCS,"83") || strstr(pszDatum,"83")) )
625 3 : return 4269;
626 :
627 21 : if( bNAD && (strstr(pszGEOGCS,"27") || strstr(pszDatum,"27")) )
628 0 : return 4267;
629 :
630 : /* -------------------------------------------------------------------- */
631 : /* If we know the datum, associate the most likely GCS with */
632 : /* it. */
633 : /* -------------------------------------------------------------------- */
634 21 : pszAuthName = poThis->GetAuthorityName( "GEOGCS|DATUM" );
635 :
636 21 : if( pszAuthName != NULL
637 : && EQUAL(pszAuthName,"epsg")
638 : && poThis->GetPrimeMeridian() == 0.0 )
639 : {
640 0 : int nDatum = atoi(poThis->GetAuthorityCode("GEOGCS|DATUM"));
641 :
642 0 : if( nDatum >= 6000 && nDatum <= 6999 )
643 0 : return nDatum - 2000;
644 : }
645 :
646 21 : return -1;
647 : }
648 :
649 : /************************************************************************/
650 : /* WriteProjectionInfo() */
651 : /************************************************************************/
652 :
653 110 : void ENVIDataset::WriteProjectionInfo()
654 :
655 : {
656 : /* -------------------------------------------------------------------- */
657 : /* Format the location (geotransform) portion of the map info */
658 : /* line. */
659 : /* -------------------------------------------------------------------- */
660 110 : CPLString osLocation;
661 :
662 : osLocation.Printf( "1, 1, %.15g, %.15g, %.15g, %.15g",
663 : adfGeoTransform[0], adfGeoTransform[3],
664 110 : adfGeoTransform[1], fabs(adfGeoTransform[5]) );
665 :
666 : /* -------------------------------------------------------------------- */
667 : /* Minimal case - write out simple geotransform if we have a */
668 : /* non-default geotransform. */
669 : /* -------------------------------------------------------------------- */
670 110 : if( pszProjection == NULL || strlen(pszProjection) == 0 )
671 : {
672 56 : if( adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0
673 22 : || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0
674 22 : || adfGeoTransform[4] != 0.0 || adfGeoTransform[5] != 1.0 )
675 : {
676 1 : const char* pszHemisphere = "North";
677 : VSIFPrintfL( fp, "map info = {Unknown, %s, %d, %s}\n",
678 1 : osLocation.c_str(), 0, pszHemisphere);
679 : }
680 : return;
681 : }
682 :
683 : /* -------------------------------------------------------------------- */
684 : /* Ingest WKT. */
685 : /* -------------------------------------------------------------------- */
686 98 : OGRSpatialReference oSRS;
687 :
688 98 : char *pszProj = pszProjection;
689 :
690 98 : if( oSRS.importFromWkt( &pszProj ) != OGRERR_NONE )
691 : return;
692 :
693 : /* -------------------------------------------------------------------- */
694 : /* Try to translate the datum and get major/minor ellipsoid */
695 : /* values. */
696 : /* -------------------------------------------------------------------- */
697 98 : int nEPSG_GCS = ENVIGetEPSGGeogCS( &oSRS );
698 98 : CPLString osDatum, osCommaDatum;
699 : double dfA, dfB;
700 :
701 98 : if( nEPSG_GCS == 4326 )
702 65 : osDatum = "WGS-84";
703 33 : else if( nEPSG_GCS == 4322 )
704 0 : osDatum = "WGS-72";
705 33 : else if( nEPSG_GCS == 4269 )
706 3 : osDatum = "North America 1983";
707 30 : else if( nEPSG_GCS == 4267 )
708 7 : osDatum = "North America 1927";
709 23 : else if( nEPSG_GCS == 4230 )
710 0 : osDatum = "European 1950";
711 23 : else if( nEPSG_GCS == 4277 )
712 1 : osDatum = "Ordnance Survey of Great Britain '36";
713 22 : else if( nEPSG_GCS == 4291 )
714 0 : osDatum = "SAD-69/Brazil";
715 22 : else if( nEPSG_GCS == 4283 )
716 0 : osDatum = "Geocentric Datum of Australia 1994";
717 22 : else if( nEPSG_GCS == 4275 )
718 0 : osDatum = "Nouvelle Triangulation Francaise IGN";
719 :
720 98 : if( osDatum != "" )
721 76 : osCommaDatum.Printf( ",%s", osDatum.c_str() );
722 :
723 98 : dfA = oSRS.GetSemiMajor();
724 98 : dfB = oSRS.GetSemiMinor();
725 :
726 : /* -------------------------------------------------------------------- */
727 : /* Do we have unusual linear units? */
728 : /* -------------------------------------------------------------------- */
729 98 : CPLString osOptionalUnits;
730 98 : if( fabs(oSRS.GetLinearUnits()-0.3048) < 0.0001 )
731 0 : osOptionalUnits = ", units=Feet";
732 :
733 : /* -------------------------------------------------------------------- */
734 : /* Handle UTM case. */
735 : /* -------------------------------------------------------------------- */
736 : const char *pszHemisphere;
737 98 : const char *pszProjName = oSRS.GetAttrValue("PROJECTION");
738 : int bNorth;
739 : int iUTMZone;
740 :
741 98 : iUTMZone = oSRS.GetUTMZone( &bNorth );
742 98 : if ( iUTMZone )
743 : {
744 7 : if ( bNorth )
745 7 : pszHemisphere = "North";
746 : else
747 0 : pszHemisphere = "South";
748 :
749 : VSIFPrintfL( fp, "map info = {UTM, %s, %d, %s%s%s}\n",
750 : osLocation.c_str(), iUTMZone, pszHemisphere,
751 7 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
752 : }
753 91 : else if( oSRS.IsGeographic() )
754 : {
755 : VSIFPrintfL( fp, "map info = {Geographic Lat/Lon, %s%s}\n",
756 65 : osLocation.c_str(), osCommaDatum.c_str());
757 : }
758 26 : else if( pszProjName == NULL )
759 : {
760 : // what to do?
761 : }
762 25 : else if( EQUAL(pszProjName,SRS_PT_NEW_ZEALAND_MAP_GRID) )
763 : {
764 : VSIFPrintfL( fp, "map info = {New Zealand Map Grid, %s%s%s}\n",
765 : osLocation.c_str(),
766 0 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
767 :
768 : VSIFPrintfL( fp, "projection info = {39, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, New Zealand Map Grid}\n",
769 : dfA, dfB,
770 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0),
771 : oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
772 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
773 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
774 0 : osCommaDatum.c_str() );
775 : }
776 25 : else if( EQUAL(pszProjName,SRS_PT_TRANSVERSE_MERCATOR) )
777 : {
778 : VSIFPrintfL( fp, "map info = {Transverse Mercator, %s%s%s}\n",
779 : osLocation.c_str(),
780 3 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
781 :
782 : VSIFPrintfL( fp, "projection info = {3, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Transverse Mercator}\n",
783 : dfA, dfB,
784 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0),
785 : oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
786 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
787 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
788 : oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0),
789 3 : osCommaDatum.c_str() );
790 : }
791 25 : else if( EQUAL(pszProjName,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP)
792 : || EQUAL(pszProjName,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM) )
793 : {
794 : VSIFPrintfL( fp, "map info = {Lambert Conformal Conic, %s%s%s}\n",
795 : osLocation.c_str(),
796 3 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
797 :
798 : VSIFPrintfL( fp, "projection info = {4, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Lambert Conformal Conic}\n",
799 : dfA, dfB,
800 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0),
801 : oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
802 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
803 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
804 : oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0),
805 : oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0),
806 3 : osCommaDatum.c_str() );
807 : }
808 19 : else if( EQUAL(pszProjName,
809 : SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN) )
810 : {
811 : VSIFPrintfL( fp, "map info = {Hotine Oblique Mercator A, %s%s%s}\n",
812 : osLocation.c_str(),
813 0 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
814 :
815 : VSIFPrintfL( fp, "projection info = {5, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Hotine Oblique Mercator A}\n",
816 : dfA, dfB,
817 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0),
818 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_1,0.0),
819 : oSRS.GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_1,0.0),
820 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_2,0.0),
821 : oSRS.GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_2,0.0),
822 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
823 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
824 : oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0),
825 0 : osCommaDatum.c_str() );
826 : }
827 19 : else if( EQUAL(pszProjName,SRS_PT_HOTINE_OBLIQUE_MERCATOR) )
828 : {
829 : VSIFPrintfL( fp, "map info = {Hotine Oblique Mercator B, %s%s%s}\n",
830 : osLocation.c_str(),
831 0 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
832 :
833 : VSIFPrintfL( fp, "projection info = {6, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Hotine Oblique Mercator B}\n",
834 : dfA, dfB,
835 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0),
836 : oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
837 : oSRS.GetNormProjParm(SRS_PP_AZIMUTH,0.0),
838 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
839 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
840 : oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0),
841 0 : osCommaDatum.c_str() );
842 : }
843 19 : else if( EQUAL(pszProjName,SRS_PT_STEREOGRAPHIC)
844 : || EQUAL(pszProjName,SRS_PT_OBLIQUE_STEREOGRAPHIC) )
845 : {
846 : VSIFPrintfL( fp, "map info = {Stereographic (ellipsoid), %s%s%s}\n",
847 : osLocation.c_str(),
848 0 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
849 :
850 : VSIFPrintfL( fp, "projection info = {7, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %s, Stereographic (ellipsoid)}\n",
851 : dfA, dfB,
852 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0),
853 : oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
854 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
855 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
856 : oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0),
857 0 : osCommaDatum.c_str() );
858 : }
859 19 : else if( EQUAL(pszProjName,SRS_PT_ALBERS_CONIC_EQUAL_AREA) )
860 : {
861 : VSIFPrintfL( fp, "map info = {Albers Conical Equal Area, %s%s%s}\n",
862 : osLocation.c_str(),
863 16 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
864 :
865 : VSIFPrintfL( fp, "projection info = {9, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Albers Conical Equal Area}\n",
866 : dfA, dfB,
867 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0),
868 : oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
869 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
870 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
871 : oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0),
872 : oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0),
873 16 : osCommaDatum.c_str() );
874 : }
875 3 : else if( EQUAL(pszProjName,SRS_PT_POLYCONIC) )
876 : {
877 : VSIFPrintfL( fp, "map info = {Polyconic, %s%s%s}\n",
878 : osLocation.c_str(),
879 0 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
880 :
881 : VSIFPrintfL( fp, "projection info = {10, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Polyconic}\n",
882 : dfA, dfB,
883 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0),
884 : oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
885 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
886 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
887 0 : osCommaDatum.c_str() );
888 : }
889 3 : else if( EQUAL(pszProjName,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) )
890 : {
891 : VSIFPrintfL( fp, "map info = {Lambert Azimuthal Equal Area, %s%s%s}\n",
892 : osLocation.c_str(),
893 3 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
894 :
895 : VSIFPrintfL( fp, "projection info = {11, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Lambert Azimuthal Equal Area}\n",
896 : dfA, dfB,
897 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0),
898 : oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
899 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
900 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
901 3 : osCommaDatum.c_str() );
902 : }
903 0 : else if( EQUAL(pszProjName,SRS_PT_AZIMUTHAL_EQUIDISTANT) )
904 : {
905 : VSIFPrintfL( fp, "map info = {Azimuthal Equadistant, %s%s%s}\n",
906 : osLocation.c_str(),
907 0 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
908 :
909 : VSIFPrintfL( fp, "projection info = {12, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Azimuthal Equadistant}\n",
910 : dfA, dfB,
911 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0),
912 : oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
913 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
914 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
915 0 : osCommaDatum.c_str() );
916 : }
917 0 : else if( EQUAL(pszProjName,SRS_PT_POLAR_STEREOGRAPHIC) )
918 : {
919 : VSIFPrintfL( fp, "map info = {Polar Stereographic, %s%s%s}\n",
920 : osLocation.c_str(),
921 0 : osCommaDatum.c_str(), osOptionalUnits.c_str() );
922 :
923 : VSIFPrintfL( fp, "projection info = {31, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Polar Stereographic}\n",
924 : dfA, dfB,
925 : oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,90.0),
926 : oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
927 : oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0),
928 : oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0),
929 0 : osCommaDatum.c_str() );
930 : }
931 : else
932 : {
933 : VSIFPrintfL( fp, "map info = {%s, %s}\n",
934 0 : pszProjName, osLocation.c_str());
935 : }
936 :
937 : // write out coordinate system string
938 98 : if ( oSRS.morphToESRI() == OGRERR_NONE )
939 : {
940 98 : char *pszProjESRI = NULL;
941 98 : if ( oSRS.exportToWkt(&pszProjESRI) == OGRERR_NONE )
942 : {
943 98 : if ( strlen(pszProjESRI) )
944 97 : VSIFPrintfL( fp, "coordinate system string = {%s}\n", pszProjESRI);
945 : }
946 98 : CPLFree(pszProjESRI);
947 98 : pszProjESRI = NULL;
948 98 : }
949 : }
950 :
951 : /************************************************************************/
952 : /* ParseRpcCoeffsMetaDataString() */
953 : /************************************************************************/
954 :
955 16 : int ENVIDataset::ParseRpcCoeffsMetaDataString(const char *psName, char **papszVal,
956 : int& idx)
957 : {
958 : // separate one string with 20 coefficients into an array of 20 strings.
959 16 : const char *psz20Vals = GetMetadataItem(psName, "RPC");
960 16 : if (!psz20Vals)
961 0 : return FALSE;
962 :
963 16 : char** papszArr = CSLTokenizeString2(psz20Vals, " ", 0);
964 16 : if (!papszArr)
965 0 : return FALSE;
966 :
967 16 : int x = 0;
968 352 : while ((papszArr[x] != NULL) && (x < 20))
969 : {
970 320 : papszVal[idx++] = CPLStrdup(papszArr[x]);
971 320 : x++;
972 : }
973 :
974 16 : CSLDestroy(papszArr);
975 :
976 16 : return (x == 20);
977 : }
978 :
979 : #define CPLStrdupIfNotNull(x) ((x) ? CPLStrdup(x) : NULL)
980 :
981 : /************************************************************************/
982 : /* WriteRpcInfo() */
983 : /************************************************************************/
984 :
985 114 : int ENVIDataset::WriteRpcInfo()
986 : {
987 : // write out 90 rpc coeffs into the envi header plus 3 envi specific rpc values
988 : // returns 0 if the coeffs are not present or not valid
989 114 : int bRet = FALSE;
990 114 : int x, idx = 0;
991 : char* papszVal[93];
992 :
993 114 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LINE_OFF", "RPC"));
994 114 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("SAMP_OFF", "RPC"));
995 114 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LAT_OFF", "RPC"));
996 114 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LONG_OFF", "RPC"));
997 114 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("HEIGHT_OFF", "RPC"));
998 114 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LINE_SCALE", "RPC"));
999 114 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("SAMP_SCALE", "RPC"));
1000 114 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LAT_SCALE", "RPC"));
1001 114 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LONG_SCALE", "RPC"));
1002 114 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("HEIGHT_SCALE", "RPC"));
1003 :
1004 154 : for (x=0; x<10; x++) // if we do not have 10 values we return 0
1005 : {
1006 150 : if (!papszVal[x])
1007 110 : goto end;
1008 : }
1009 :
1010 4 : if (!ParseRpcCoeffsMetaDataString("LINE_NUM_COEFF", papszVal, idx))
1011 0 : goto end;
1012 :
1013 4 : if (!ParseRpcCoeffsMetaDataString("LINE_DEN_COEFF", papszVal, idx))
1014 0 : goto end;
1015 :
1016 4 : if (!ParseRpcCoeffsMetaDataString("SAMP_NUM_COEFF", papszVal, idx))
1017 0 : goto end;
1018 :
1019 4 : if (!ParseRpcCoeffsMetaDataString("SAMP_DEN_COEFF", papszVal, idx))
1020 0 : goto end;
1021 :
1022 4 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("TILE_ROW_OFFSET", "RPC"));
1023 4 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("TILE_COL_OFFSET", "RPC"));
1024 4 : papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("ENVI_RPC_EMULATION", "RPC"));
1025 4 : CPLAssert(idx == 93);
1026 16 : for (x=90; x<93; x++)
1027 : {
1028 12 : if (!papszVal[x])
1029 0 : goto end;
1030 : }
1031 :
1032 : // ok all the needed 93 values are present so write the rpcs into the envi header
1033 4 : x = 1;
1034 4 : VSIFPrintfL(fp, "rpc info = {\n");
1035 376 : for (int iR=0; iR<93; iR++)
1036 : {
1037 372 : if (papszVal[iR][0] == '-')
1038 32 : VSIFPrintfL(fp, " %s", papszVal[iR]);
1039 : else
1040 340 : VSIFPrintfL(fp, " %s", papszVal[iR]);
1041 :
1042 372 : if (iR<92)
1043 368 : VSIFPrintfL(fp, ",");
1044 :
1045 372 : if ((x % 4) == 0)
1046 92 : VSIFPrintfL(fp, "\n");
1047 :
1048 372 : x++;
1049 372 : if (x > 4)
1050 92 : x = 1;
1051 : }
1052 :
1053 4 : VSIFPrintfL(fp, "}\n" );
1054 :
1055 4 : bRet = TRUE;
1056 :
1057 : end:
1058 1586 : for (x=0;x<idx;x++)
1059 1472 : CPLFree(papszVal[x]);
1060 :
1061 114 : return bRet;
1062 : }
1063 :
1064 : /************************************************************************/
1065 : /* WritePseudoGcpInfo() */
1066 : /************************************************************************/
1067 :
1068 110 : int ENVIDataset::WritePseudoGcpInfo()
1069 : {
1070 : // write out gcps into the envi header
1071 : // returns 0 if the gcps are not present
1072 :
1073 110 : int iNum = GetGCPCount();
1074 110 : if (iNum == 0)
1075 110 : return FALSE;
1076 :
1077 0 : const GDAL_GCP *pGcpStructs = GetGCPs();
1078 :
1079 : // double dfGCPPixel; /** Pixel (x) location of GCP on raster */
1080 : // double dfGCPLine; /** Line (y) location of GCP on raster */
1081 : // double dfGCPX; /** X position of GCP in georeferenced space */
1082 : // double dfGCPY; /** Y position of GCP in georeferenced space */
1083 :
1084 0 : VSIFPrintfL(fp, "geo points = {\n");
1085 0 : for (int iR=0; iR<iNum; iR++)
1086 : {
1087 : VSIFPrintfL(fp, " %#0.4f, %#0.4f, %#0.8f, %#0.8f",
1088 0 : pGcpStructs[iR].dfGCPPixel, pGcpStructs[iR].dfGCPLine,
1089 0 : pGcpStructs[iR].dfGCPY, pGcpStructs[iR].dfGCPX);
1090 0 : if (iR<iNum-1)
1091 0 : VSIFPrintfL(fp, ",\n");
1092 : }
1093 :
1094 0 : VSIFPrintfL(fp, "}\n" );
1095 :
1096 0 : return TRUE;
1097 : }
1098 :
1099 : /************************************************************************/
1100 : /* GetProjectionRef() */
1101 : /************************************************************************/
1102 :
1103 20 : const char *ENVIDataset::GetProjectionRef()
1104 :
1105 : {
1106 20 : return pszProjection;
1107 : }
1108 :
1109 : /************************************************************************/
1110 : /* SetProjection() */
1111 : /************************************************************************/
1112 :
1113 46 : CPLErr ENVIDataset::SetProjection( const char *pszNewProjection )
1114 :
1115 : {
1116 46 : CPLFree( pszProjection );
1117 46 : pszProjection = CPLStrdup( pszNewProjection );
1118 :
1119 46 : bHeaderDirty = TRUE;
1120 :
1121 46 : return CE_None;
1122 : }
1123 :
1124 : /************************************************************************/
1125 : /* GetGeoTransform() */
1126 : /************************************************************************/
1127 :
1128 38 : CPLErr ENVIDataset::GetGeoTransform( double * padfTransform )
1129 :
1130 : {
1131 38 : memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
1132 :
1133 38 : if( bFoundMapinfo )
1134 36 : return CE_None;
1135 : else
1136 2 : return CE_Failure;
1137 : }
1138 :
1139 : /************************************************************************/
1140 : /* SetGeoTransform() */
1141 : /************************************************************************/
1142 :
1143 47 : CPLErr ENVIDataset::SetGeoTransform( double * padfTransform )
1144 : {
1145 47 : memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 );
1146 :
1147 47 : bHeaderDirty = TRUE;
1148 47 : bFoundMapinfo = TRUE;
1149 :
1150 47 : return CE_None;
1151 : }
1152 :
1153 : /************************************************************************/
1154 : /* SetDescription() */
1155 : /************************************************************************/
1156 :
1157 114 : void ENVIDataset::SetDescription( const char * pszDescription )
1158 : {
1159 114 : bHeaderDirty = TRUE;
1160 114 : RawDataset::SetDescription(pszDescription);
1161 114 : }
1162 :
1163 : /************************************************************************/
1164 : /* SetMetadata() */
1165 : /************************************************************************/
1166 :
1167 22 : CPLErr ENVIDataset::SetMetadata( char ** papszMetadata,
1168 : const char * pszDomain )
1169 : {
1170 22 : if( pszDomain && (EQUAL(pszDomain, "RPC") || EQUAL(pszDomain, "ENVI")) )
1171 : {
1172 2 : bHeaderDirty = TRUE;
1173 : }
1174 22 : return RawDataset::SetMetadata(papszMetadata, pszDomain);
1175 : }
1176 :
1177 : /************************************************************************/
1178 : /* SetMetadataItem() */
1179 : /************************************************************************/
1180 :
1181 1428 : CPLErr ENVIDataset::SetMetadataItem( const char * pszName,
1182 : const char * pszValue,
1183 : const char * pszDomain )
1184 : {
1185 1428 : if( pszDomain && (EQUAL(pszDomain, "RPC") || EQUAL(pszDomain, "ENVI")) )
1186 : {
1187 1222 : bHeaderDirty = TRUE;
1188 : }
1189 1428 : return RawDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1190 : }
1191 :
1192 : /************************************************************************/
1193 : /* SetGCPs() */
1194 : /************************************************************************/
1195 :
1196 0 : CPLErr ENVIDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
1197 : const char *pszGCPProjection )
1198 : {
1199 0 : bHeaderDirty = TRUE;
1200 :
1201 0 : return RawDataset::SetGCPs(nGCPCount, pasGCPList, pszGCPProjection);
1202 : }
1203 :
1204 : /************************************************************************/
1205 : /* SplitList() */
1206 : /* */
1207 : /* Split an ENVI value list into component fields, and strip */
1208 : /* white space. */
1209 : /************************************************************************/
1210 :
1211 204 : char **ENVIDataset::SplitList( const char *pszCleanInput )
1212 :
1213 : {
1214 204 : char **papszReturn = NULL;
1215 204 : char *pszInput = CPLStrdup(pszCleanInput);
1216 :
1217 204 : if( pszInput[0] != '{' )
1218 : {
1219 62 : CPLFree(pszInput);
1220 62 : return NULL;
1221 : }
1222 :
1223 142 : int iChar=1;
1224 :
1225 :
1226 1276 : while( pszInput[iChar] != '}' && pszInput[iChar] != '\0' )
1227 : {
1228 992 : int iFStart=-1, iFEnd=-1;
1229 :
1230 : // Find start of token.
1231 992 : iFStart = iChar;
1232 3036 : while( pszInput[iFStart] == ' ' )
1233 1052 : iFStart++;
1234 :
1235 992 : iFEnd = iFStart;
1236 19718 : while( pszInput[iFEnd] != ','
1237 6006 : && pszInput[iFEnd] != '}'
1238 5864 : && pszInput[iFEnd] != '\0' )
1239 5864 : iFEnd++;
1240 :
1241 992 : if( pszInput[iFEnd] == '\0' )
1242 0 : break;
1243 :
1244 992 : iChar = iFEnd + 1;
1245 992 : iFEnd = iFEnd - 1;
1246 :
1247 1984 : while( iFEnd > iFStart && pszInput[iFEnd] == ' ' )
1248 0 : iFEnd--;
1249 :
1250 992 : pszInput[iFEnd + 1] = '\0';
1251 992 : papszReturn = CSLAddString( papszReturn, pszInput + iFStart );
1252 : }
1253 :
1254 142 : CPLFree( pszInput );
1255 :
1256 142 : return papszReturn;
1257 : }
1258 :
1259 : /************************************************************************/
1260 : /* SetENVIDatum() */
1261 : /************************************************************************/
1262 :
1263 0 : void ENVIDataset::SetENVIDatum( OGRSpatialReference *poSRS,
1264 : const char *pszENVIDatumName )
1265 :
1266 : {
1267 : // datums
1268 0 : if( EQUAL(pszENVIDatumName, "WGS-84") )
1269 0 : poSRS->SetWellKnownGeogCS( "WGS84" );
1270 0 : else if( EQUAL(pszENVIDatumName, "WGS-72") )
1271 0 : poSRS->SetWellKnownGeogCS( "WGS72" );
1272 0 : else if( EQUAL(pszENVIDatumName, "North America 1983") )
1273 0 : poSRS->SetWellKnownGeogCS( "NAD83" );
1274 0 : else if( EQUAL(pszENVIDatumName, "North America 1927")
1275 : || strstr(pszENVIDatumName,"NAD27")
1276 : || strstr(pszENVIDatumName,"NAD-27") )
1277 0 : poSRS->SetWellKnownGeogCS( "NAD27" );
1278 0 : else if( EQUALN(pszENVIDatumName, "European 1950",13) )
1279 0 : poSRS->SetWellKnownGeogCS( "EPSG:4230" );
1280 0 : else if( EQUAL(pszENVIDatumName, "Ordnance Survey of Great Britain '36") )
1281 0 : poSRS->SetWellKnownGeogCS( "EPSG:4277" );
1282 0 : else if( EQUAL(pszENVIDatumName, "SAD-69/Brazil") )
1283 0 : poSRS->SetWellKnownGeogCS( "EPSG:4291" );
1284 0 : else if( EQUAL(pszENVIDatumName, "Geocentric Datum of Australia 1994") )
1285 0 : poSRS->SetWellKnownGeogCS( "EPSG:4283" );
1286 0 : else if( EQUAL(pszENVIDatumName, "Australian Geodetic 1984") )
1287 0 : poSRS->SetWellKnownGeogCS( "EPSG:4203" );
1288 0 : else if( EQUAL(pszENVIDatumName, "Nouvelle Triangulation Francaise IGN") )
1289 0 : poSRS->SetWellKnownGeogCS( "EPSG:4275" );
1290 :
1291 : // Ellipsoids
1292 0 : else if( EQUAL(pszENVIDatumName, "GRS 80") )
1293 0 : poSRS->SetWellKnownGeogCS( "NAD83" );
1294 0 : else if( EQUAL(pszENVIDatumName, "Airy") )
1295 0 : poSRS->SetWellKnownGeogCS( "EPSG:4001" );
1296 0 : else if( EQUAL(pszENVIDatumName, "Australian National") )
1297 0 : poSRS->SetWellKnownGeogCS( "EPSG:4003" );
1298 0 : else if( EQUAL(pszENVIDatumName, "Bessel 1841") )
1299 0 : poSRS->SetWellKnownGeogCS( "EPSG:4004" );
1300 0 : else if( EQUAL(pszENVIDatumName, "Clark 1866") )
1301 0 : poSRS->SetWellKnownGeogCS( "EPSG:4008" );
1302 : else
1303 : {
1304 : CPLError( CE_Warning, CPLE_AppDefined,
1305 0 : "Unrecognised datum '%s', defaulting to WGS84.", pszENVIDatumName);
1306 0 : poSRS->SetWellKnownGeogCS( "WGS84" );
1307 : }
1308 0 : }
1309 :
1310 : /************************************************************************/
1311 : /* SetENVIEllipse() */
1312 : /************************************************************************/
1313 :
1314 7 : void ENVIDataset::SetENVIEllipse( OGRSpatialReference *poSRS,
1315 : char **papszPI_EI )
1316 :
1317 : {
1318 7 : double dfA = CPLAtofM(papszPI_EI[0]);
1319 7 : double dfB = CPLAtofM(papszPI_EI[1]);
1320 : double dfInvF;
1321 :
1322 7 : if( fabs(dfA-dfB) < 0.1 )
1323 0 : dfInvF = 0.0; // sphere
1324 : else
1325 7 : dfInvF = dfA / (dfA - dfB);
1326 :
1327 :
1328 : poSRS->SetGeogCS( "Ellipse Based", "Ellipse Based", "Unnamed",
1329 7 : dfA, dfInvF );
1330 7 : }
1331 :
1332 : /************************************************************************/
1333 : /* ProcessMapinfo() */
1334 : /* */
1335 : /* Extract projection, and geotransform from a mapinfo value in */
1336 : /* the header. */
1337 : /************************************************************************/
1338 :
1339 52 : int ENVIDataset::ProcessMapinfo( const char *pszMapinfo )
1340 :
1341 : {
1342 : char **papszFields;
1343 : int nCount;
1344 52 : OGRSpatialReference oSRS;
1345 :
1346 52 : papszFields = SplitList( pszMapinfo );
1347 52 : nCount = CSLCount(papszFields);
1348 :
1349 52 : if( nCount < 7 )
1350 : {
1351 0 : CSLDestroy( papszFields );
1352 0 : return FALSE;
1353 : }
1354 :
1355 : /* -------------------------------------------------------------------- */
1356 : /* Check if we have coordinate system string, and if so parse it. */
1357 : /* -------------------------------------------------------------------- */
1358 52 : char **papszCSS = NULL;
1359 52 : if( CSLFetchNameValue( papszHeader, "coordinate_system_string" ) != NULL )
1360 : {
1361 : papszCSS = CSLTokenizeString2(
1362 : CSLFetchNameValue( papszHeader, "coordinate_system_string" ),
1363 37 : "{}", CSLT_PRESERVEQUOTES );
1364 : }
1365 :
1366 : /* -------------------------------------------------------------------- */
1367 : /* Check if we have projection info, and if so parse it. */
1368 : /* -------------------------------------------------------------------- */
1369 52 : char **papszPI = NULL;
1370 52 : int nPICount = 0;
1371 52 : if( CSLFetchNameValue( papszHeader, "projection_info" ) != NULL )
1372 : {
1373 : papszPI = SplitList(
1374 19 : CSLFetchNameValue( papszHeader, "projection_info" ) );
1375 19 : nPICount = CSLCount(papszPI);
1376 : }
1377 :
1378 : /* -------------------------------------------------------------------- */
1379 : /* Capture geotransform. */
1380 : /* -------------------------------------------------------------------- */
1381 52 : adfGeoTransform[1] = atof(papszFields[5]); // Pixel width
1382 52 : adfGeoTransform[5] = -atof(papszFields[6]); // Pixel height
1383 : adfGeoTransform[0] = // Upper left X coordinate
1384 52 : atof(papszFields[3]) - (atof(papszFields[1]) - 1) * adfGeoTransform[1];
1385 : adfGeoTransform[3] = // Upper left Y coordinate
1386 52 : atof(papszFields[4]) - (atof(papszFields[2]) - 1) * adfGeoTransform[5];
1387 52 : adfGeoTransform[2] = 0.0;
1388 52 : adfGeoTransform[4] = 0.0;
1389 :
1390 : /* -------------------------------------------------------------------- */
1391 : /* Capture projection. */
1392 : /* -------------------------------------------------------------------- */
1393 52 : if ( oSRS.importFromESRI( papszCSS ) != OGRERR_NONE )
1394 : {
1395 15 : oSRS.Clear();
1396 :
1397 22 : if( EQUALN(papszFields[0],"UTM",3) && nCount >= 9 )
1398 : {
1399 7 : oSRS.SetUTM( atoi(papszFields[7]),
1400 14 : !EQUAL(papszFields[8],"South") );
1401 7 : if( nCount >= 10 && strstr(papszFields[9],"=") == NULL )
1402 0 : SetENVIDatum( &oSRS, papszFields[9] );
1403 : else
1404 7 : oSRS.SetWellKnownGeogCS( "NAD27" );
1405 : }
1406 8 : else if( EQUALN(papszFields[0],"State Plane (NAD 27)",19)
1407 : && nCount >= 7 )
1408 : {
1409 0 : oSRS.SetStatePlane( ITTVISToUSGSZone(atoi(papszFields[7])), FALSE );
1410 : }
1411 8 : else if( EQUALN(papszFields[0],"State Plane (NAD 83)",19)
1412 : && nCount >= 7 )
1413 : {
1414 0 : oSRS.SetStatePlane( ITTVISToUSGSZone(atoi(papszFields[7])), TRUE );
1415 : }
1416 8 : else if( EQUALN(papszFields[0],"Geographic Lat",14)
1417 : && nCount >= 8 )
1418 : {
1419 0 : if( nCount >= 8 && strstr(papszFields[7],"=") == NULL )
1420 0 : SetENVIDatum( &oSRS, papszFields[7] );
1421 : else
1422 0 : oSRS.SetWellKnownGeogCS( "WGS84" );
1423 : }
1424 8 : else if( nPICount > 8 && atoi(papszPI[0]) == 3 ) // TM
1425 : {
1426 0 : oSRS.SetTM( CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]),
1427 0 : CPLAtofM(papszPI[7]),
1428 0 : CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) );
1429 : }
1430 8 : else if( nPICount > 8 && atoi(papszPI[0]) == 4 ) // Lambert Conformal Conic
1431 : {
1432 0 : oSRS.SetLCC( CPLAtofM(papszPI[7]), CPLAtofM(papszPI[8]),
1433 0 : CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]),
1434 0 : CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) );
1435 : }
1436 8 : else if( nPICount > 10 && atoi(papszPI[0]) == 5 ) // Oblique Merc (2 point)
1437 : {
1438 0 : oSRS.SetHOM2PNO( CPLAtofM(papszPI[3]),
1439 0 : CPLAtofM(papszPI[4]), CPLAtofM(papszPI[5]),
1440 0 : CPLAtofM(papszPI[6]), CPLAtofM(papszPI[7]),
1441 0 : CPLAtofM(papszPI[10]),
1442 0 : CPLAtofM(papszPI[8]), CPLAtofM(papszPI[9]) );
1443 : }
1444 8 : else if( nPICount > 8 && atoi(papszPI[0]) == 6 ) // Oblique Merc
1445 : {
1446 0 : oSRS.SetHOM(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]),
1447 0 : CPLAtofM(papszPI[5]), 0.0,
1448 0 : CPLAtofM(papszPI[8]),
1449 0 : CPLAtofM(papszPI[6]), CPLAtofM(papszPI[7]) );
1450 : }
1451 8 : else if( nPICount > 8 && atoi(papszPI[0]) == 7 ) // Stereographic
1452 : {
1453 0 : oSRS.SetStereographic( CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]),
1454 0 : CPLAtofM(papszPI[7]),
1455 0 : CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) );
1456 : }
1457 15 : else if( nPICount > 8 && atoi(papszPI[0]) == 9 ) // Albers Equal Area
1458 : {
1459 14 : oSRS.SetACEA( CPLAtofM(papszPI[7]), CPLAtofM(papszPI[8]),
1460 14 : CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]),
1461 35 : CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) );
1462 : }
1463 1 : else if( nPICount > 6 && atoi(papszPI[0]) == 10 ) // Polyconic
1464 : {
1465 0 : oSRS.SetPolyconic(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]),
1466 0 : CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) );
1467 : }
1468 1 : else if( nPICount > 6 && atoi(papszPI[0]) == 11 ) // LAEA
1469 : {
1470 0 : oSRS.SetLAEA(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]),
1471 0 : CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) );
1472 : }
1473 1 : else if( nPICount > 6 && atoi(papszPI[0]) == 12 ) // Azimuthal Equid.
1474 : {
1475 0 : oSRS.SetAE(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]),
1476 0 : CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) );
1477 : }
1478 1 : else if( nPICount > 6 && atoi(papszPI[0]) == 31 ) // Polar Stereographic
1479 : {
1480 0 : oSRS.SetPS(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]),
1481 : 1.0,
1482 0 : CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) );
1483 : }
1484 : }
1485 :
1486 52 : CSLDestroy( papszCSS );
1487 :
1488 : // Still lots more that could be added for someone with the patience.
1489 :
1490 : /* -------------------------------------------------------------------- */
1491 : /* fallback to localcs if we don't recognise things. */
1492 : /* -------------------------------------------------------------------- */
1493 52 : if( oSRS.GetRoot() == NULL )
1494 1 : oSRS.SetLocalCS( papszFields[0] );
1495 :
1496 : /* -------------------------------------------------------------------- */
1497 : /* Try to set datum from projection info line if we have a */
1498 : /* projected coordinate system without a GEOGCS. */
1499 : /* -------------------------------------------------------------------- */
1500 52 : if( oSRS.IsProjected() && oSRS.GetAttrNode("GEOGCS") == NULL
1501 : && nPICount > 3 )
1502 : {
1503 : // Do we have a datum on the projection info line?
1504 7 : int iDatum = nPICount-1;
1505 :
1506 : // Ignore units= items.
1507 7 : if( strstr(papszPI[iDatum],"=") != NULL )
1508 0 : iDatum--;
1509 :
1510 : // Skip past the name.
1511 7 : iDatum--;
1512 :
1513 7 : CPLString osDatumName = papszPI[iDatum];
1514 7 : if( osDatumName.find_first_of("abcdefghijklmnopqrstuvwxyz"
1515 : "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
1516 : != CPLString::npos )
1517 : {
1518 0 : SetENVIDatum( &oSRS, osDatumName );
1519 : }
1520 : else
1521 : {
1522 7 : SetENVIEllipse( &oSRS, papszPI + 1 );
1523 7 : }
1524 : }
1525 :
1526 :
1527 : /* -------------------------------------------------------------------- */
1528 : /* Try to process specialized units. */
1529 : /* -------------------------------------------------------------------- */
1530 52 : if( EQUALN( papszFields[nCount-1],"units",5))
1531 : {
1532 : /* Handle linear units first. */
1533 0 : if (EQUAL(papszFields[nCount-1],"units=Feet") )
1534 0 : oSRS.SetLinearUnitsAndUpdateParameters( SRS_UL_FOOT, atof(SRS_UL_FOOT_CONV) );
1535 0 : else if (EQUAL(papszFields[nCount-1],"units=Meters") )
1536 0 : oSRS.SetLinearUnitsAndUpdateParameters( SRS_UL_METER, 1. );
1537 0 : else if (EQUAL(papszFields[nCount-1],"units=Km") )
1538 0 : oSRS.SetLinearUnitsAndUpdateParameters( "Kilometer", 1000. );
1539 0 : else if (EQUAL(papszFields[nCount-1],"units=Yards") )
1540 0 : oSRS.SetLinearUnitsAndUpdateParameters( "Yard", .9144 );
1541 0 : else if (EQUAL(papszFields[nCount-1],"units=Miles") )
1542 0 : oSRS.SetLinearUnitsAndUpdateParameters( "Mile", 1609.344 );
1543 0 : else if (EQUAL(papszFields[nCount-1],"units=Nautical Miles") )
1544 0 : oSRS.SetLinearUnitsAndUpdateParameters( SRS_UL_NAUTICAL_MILE, atof(SRS_UL_NAUTICAL_MILE_CONV) );
1545 :
1546 : /* Only handle angular units if we know the projection is geographic. */
1547 0 : if (oSRS.IsGeographic())
1548 : {
1549 0 : if (EQUAL(papszFields[nCount-1],"units=Radians") )
1550 0 : oSRS.SetAngularUnits( SRS_UA_RADIAN, 1. );
1551 : else
1552 : {
1553 : /* Degrees, minutes and seconds will all be represented as degrees. */
1554 0 : oSRS.SetAngularUnits( SRS_UA_DEGREE, atof(SRS_UA_DEGREE_CONV));
1555 :
1556 0 : double conversionFactor = 1.;
1557 0 : if (EQUAL(papszFields[nCount-1],"units=Minutes") )
1558 0 : conversionFactor = 60.;
1559 0 : else if( EQUAL(papszFields[nCount-1],"units=Seconds") )
1560 0 : conversionFactor = 3600.;
1561 0 : adfGeoTransform[0] /= conversionFactor;
1562 0 : adfGeoTransform[1] /= conversionFactor;
1563 0 : adfGeoTransform[2] /= conversionFactor;
1564 0 : adfGeoTransform[3] /= conversionFactor;
1565 0 : adfGeoTransform[4] /= conversionFactor;
1566 0 : adfGeoTransform[5] /= conversionFactor;
1567 : }
1568 : }
1569 : }
1570 : /* -------------------------------------------------------------------- */
1571 : /* Turn back into WKT. */
1572 : /* -------------------------------------------------------------------- */
1573 52 : if( oSRS.GetRoot() != NULL )
1574 : {
1575 52 : oSRS.Fixup();
1576 52 : if ( pszProjection )
1577 : {
1578 52 : CPLFree( pszProjection );
1579 52 : pszProjection = NULL;
1580 : }
1581 52 : oSRS.exportToWkt( &pszProjection );
1582 : }
1583 :
1584 52 : CSLDestroy( papszFields );
1585 52 : CSLDestroy( papszPI );
1586 52 : return TRUE;
1587 : }
1588 :
1589 : /************************************************************************/
1590 : /* ProcessRPCinfo() */
1591 : /* */
1592 : /* Extract RPC transformation coefficients if they are present */
1593 : /* and sets into the standard metadata fields for RPC. */
1594 : /************************************************************************/
1595 :
1596 3 : void ENVIDataset::ProcessRPCinfo( const char *pszRPCinfo,
1597 : int numCols, int numRows)
1598 : {
1599 : char **papszFields;
1600 : char sVal[1280];
1601 : int nCount;
1602 :
1603 3 : papszFields = SplitList( pszRPCinfo );
1604 3 : nCount = CSLCount(papszFields);
1605 :
1606 3 : if( nCount < 90 )
1607 : {
1608 0 : CSLDestroy( papszFields );
1609 0 : return;
1610 : }
1611 :
1612 3 : snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[0]));
1613 3 : SetMetadataItem("LINE_OFF",sVal,"RPC");
1614 3 : snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[5]));
1615 3 : SetMetadataItem("LINE_SCALE",sVal,"RPC");
1616 3 : snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[1]));
1617 3 : SetMetadataItem("SAMP_OFF",sVal,"RPC");
1618 3 : snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[6]));
1619 3 : SetMetadataItem("SAMP_SCALE",sVal,"RPC");
1620 3 : snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[2]));
1621 3 : SetMetadataItem("LAT_OFF",sVal,"RPC");
1622 3 : snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[7]));
1623 3 : SetMetadataItem("LAT_SCALE",sVal,"RPC");
1624 3 : snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[3]));
1625 3 : SetMetadataItem("LONG_OFF",sVal,"RPC");
1626 3 : snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[8]));
1627 3 : SetMetadataItem("LONG_SCALE",sVal,"RPC");
1628 3 : snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[4]));
1629 3 : SetMetadataItem("HEIGHT_OFF",sVal,"RPC");
1630 3 : snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[9]));
1631 3 : SetMetadataItem("HEIGHT_SCALE",sVal,"RPC");
1632 :
1633 3 : sVal[0] = '\0';
1634 : int i;
1635 63 : for(i = 0; i < 20; i++ )
1636 : snprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ",
1637 60 : atof(papszFields[10+i]));
1638 3 : SetMetadataItem("LINE_NUM_COEFF",sVal,"RPC");
1639 :
1640 3 : sVal[0] = '\0';
1641 63 : for(i = 0; i < 20; i++ )
1642 : snprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ",
1643 60 : atof(papszFields[30+i]));
1644 3 : SetMetadataItem("LINE_DEN_COEFF",sVal,"RPC");
1645 :
1646 3 : sVal[0] = '\0';
1647 63 : for(i = 0; i < 20; i++ )
1648 : snprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ",
1649 60 : atof(papszFields[50+i]));
1650 3 : SetMetadataItem("SAMP_NUM_COEFF",sVal,"RPC");
1651 :
1652 3 : sVal[0] = '\0';
1653 63 : for(i = 0; i < 20; i++ )
1654 : snprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ",
1655 60 : atof(papszFields[70+i]));
1656 3 : SetMetadataItem("SAMP_DEN_COEFF",sVal,"RPC");
1657 :
1658 : snprintf(sVal, sizeof(sVal), "%.16g",
1659 3 : atof(papszFields[3]) - atof(papszFields[8]));
1660 3 : SetMetadataItem("MIN_LONG",sVal,"RPC");
1661 :
1662 : snprintf(sVal, sizeof(sVal), "%.16g",
1663 3 : atof(papszFields[3]) + atof(papszFields[8]) );
1664 3 : SetMetadataItem("MAX_LONG",sVal,"RPC");
1665 :
1666 : snprintf(sVal, sizeof(sVal), "%.16g",
1667 3 : atof(papszFields[2]) - atof(papszFields[7]));
1668 3 : SetMetadataItem("MIN_LAT",sVal,"RPC");
1669 :
1670 : snprintf(sVal, sizeof(sVal), "%.16g",
1671 3 : atof(papszFields[2]) + atof(papszFields[7]));
1672 3 : SetMetadataItem("MAX_LAT",sVal,"RPC");
1673 :
1674 3 : if (nCount == 93)
1675 : {
1676 3 : SetMetadataItem("TILE_ROW_OFFSET",papszFields[90],"RPC");
1677 3 : SetMetadataItem("TILE_COL_OFFSET",papszFields[91],"RPC");
1678 3 : SetMetadataItem("ENVI_RPC_EMULATION",papszFields[92],"RPC");
1679 : }
1680 :
1681 : /* Handle the chipping case where the image is a subset. */
1682 : double rowOffset, colOffset;
1683 3 : rowOffset = (nCount == 93) ? atof(papszFields[90]) : 0;
1684 3 : colOffset = (nCount == 93) ? atof(papszFields[91]) : 0;
1685 3 : if (rowOffset || colOffset)
1686 : {
1687 0 : SetMetadataItem("ICHIP_SCALE_FACTOR", "1");
1688 0 : SetMetadataItem("ICHIP_ANAMORPH_CORR", "0");
1689 0 : SetMetadataItem("ICHIP_SCANBLK_NUM", "0");
1690 :
1691 0 : SetMetadataItem("ICHIP_OP_ROW_11", "0.5");
1692 0 : SetMetadataItem("ICHIP_OP_COL_11", "0.5");
1693 0 : SetMetadataItem("ICHIP_OP_ROW_12", "0.5");
1694 0 : SetMetadataItem("ICHIP_OP_COL_21", "0.5");
1695 0 : snprintf(sVal, sizeof(sVal), "%.16g", numCols - 0.5);
1696 0 : SetMetadataItem("ICHIP_OP_COL_12", sVal);
1697 0 : SetMetadataItem("ICHIP_OP_COL_22", sVal);
1698 0 : snprintf(sVal, sizeof(sVal), "%.16g", numRows - 0.5);
1699 0 : SetMetadataItem("ICHIP_OP_ROW_21", sVal);
1700 0 : SetMetadataItem("ICHIP_OP_ROW_22", sVal);
1701 :
1702 0 : snprintf(sVal, sizeof(sVal), "%.16g", rowOffset + 0.5);
1703 0 : SetMetadataItem("ICHIP_FI_ROW_11", sVal);
1704 0 : SetMetadataItem("ICHIP_FI_ROW_12", sVal);
1705 0 : snprintf(sVal, sizeof(sVal), "%.16g", colOffset + 0.5);
1706 0 : SetMetadataItem("ICHIP_FI_COL_11", sVal);
1707 0 : SetMetadataItem("ICHIP_FI_COL_21", sVal);
1708 0 : snprintf(sVal, sizeof(sVal), "%.16g", colOffset + numCols - 0.5);
1709 0 : SetMetadataItem("ICHIP_FI_COL_12", sVal);
1710 0 : SetMetadataItem("ICHIP_FI_COL_22", sVal);
1711 0 : snprintf(sVal, sizeof(sVal), "%.16g", rowOffset + numRows - 0.5);
1712 0 : SetMetadataItem("ICHIP_FI_ROW_21", sVal);
1713 0 : SetMetadataItem("ICHIP_FI_ROW_22", sVal);
1714 : }
1715 3 : CSLDestroy(papszFields);
1716 : }
1717 :
1718 114 : void ENVIDataset::ProcessStatsFile()
1719 : {
1720 : VSILFILE *fpStaFile;
1721 :
1722 114 : osStaFilename = CPLResetExtension( pszHDRFilename, "sta" );
1723 114 : fpStaFile = VSIFOpenL( osStaFilename, "rb" );
1724 :
1725 114 : if (!fpStaFile)
1726 : {
1727 113 : osStaFilename = "";
1728 113 : return;
1729 : }
1730 :
1731 : int lTestHeader[10],lOffset;
1732 :
1733 1 : if( VSIFReadL( lTestHeader, sizeof(int), 10, fpStaFile ) != 10 )
1734 : {
1735 0 : VSIFCloseL( fpStaFile );
1736 0 : osStaFilename = "";
1737 0 : return;
1738 : }
1739 :
1740 : int isFloat;
1741 1 : isFloat = (byteSwapInt(lTestHeader[0]) == 1111838282);
1742 :
1743 : int nb,i;
1744 : float * fStats;
1745 : double * dStats, dMin, dMax, dMean, dStd;
1746 :
1747 1 : nb=byteSwapInt(lTestHeader[3]);
1748 :
1749 1 : if (nb < 0 || nb > nBands)
1750 : {
1751 : CPLDebug("ENVI", ".sta file has statistics for %d bands, "
1752 0 : "whereas the dataset has only %d bands", nb, nBands);
1753 0 : nb = nBands;
1754 : }
1755 :
1756 1 : VSIFSeekL(fpStaFile,40+(nb+1)*4,SEEK_SET);
1757 :
1758 1 : if (VSIFReadL(&lOffset,sizeof(int),1,fpStaFile) == 1)
1759 : {
1760 1 : VSIFSeekL(fpStaFile,40+(nb+1)*8+byteSwapInt(lOffset)+nb,SEEK_SET);
1761 : // This should be the beginning of the statistics
1762 1 : if (isFloat)
1763 : {
1764 0 : fStats = (float*)CPLCalloc(nb*4,4);
1765 0 : if ((int)VSIFReadL(fStats,4,nb*4,fpStaFile) == nb*4)
1766 : {
1767 0 : for (i=0;i<nb;i++)
1768 : {
1769 : GetRasterBand(i+1)->SetStatistics(
1770 : byteSwapFloat(fStats[i]),
1771 : byteSwapFloat(fStats[nb+i]),
1772 : byteSwapFloat(fStats[2*nb+i]),
1773 0 : byteSwapFloat(fStats[3*nb+i]));
1774 : }
1775 : }
1776 0 : CPLFree(fStats);
1777 : }
1778 : else
1779 : {
1780 1 : dStats = (double*)CPLCalloc(nb*4,8);
1781 1 : if ((int)VSIFReadL(dStats,8,nb*4,fpStaFile) == nb*4)
1782 : {
1783 7 : for (i=0;i<nb;i++)
1784 : {
1785 6 : dMin = byteSwapDouble(dStats[i]);
1786 6 : dMax = byteSwapDouble(dStats[nb+i]);
1787 6 : dMean = byteSwapDouble(dStats[2*nb+i]);
1788 6 : dStd = byteSwapDouble(dStats[3*nb+i]);
1789 6 : if (dMin != dMax && dStd != 0)
1790 6 : GetRasterBand(i+1)->SetStatistics(dMin,dMax,dMean,dStd);
1791 : }
1792 : }
1793 1 : CPLFree(dStats);
1794 : }
1795 : }
1796 1 : VSIFCloseL( fpStaFile );
1797 : }
1798 :
1799 3 : int ENVIDataset::byteSwapInt(int swapMe)
1800 : {
1801 3 : CPL_MSBPTR32(&swapMe);
1802 3 : return swapMe;
1803 : }
1804 :
1805 0 : float ENVIDataset::byteSwapFloat(float swapMe)
1806 : {
1807 0 : CPL_MSBPTR32(&swapMe);
1808 0 : return swapMe;
1809 : }
1810 :
1811 24 : double ENVIDataset::byteSwapDouble(double swapMe)
1812 : {
1813 24 : CPL_MSBPTR64(&swapMe);
1814 24 : return swapMe;
1815 : }
1816 :
1817 :
1818 : /************************************************************************/
1819 : /* ReadHeader() */
1820 : /************************************************************************/
1821 :
1822 116 : int ENVIDataset::ReadHeader( VSILFILE * fpHdr )
1823 :
1824 : {
1825 :
1826 116 : CPLReadLineL( fpHdr );
1827 :
1828 : /* -------------------------------------------------------------------- */
1829 : /* Now start forming sets of name/value pairs. */
1830 : /* -------------------------------------------------------------------- */
1831 1175 : while( TRUE )
1832 : {
1833 : const char *pszNewLine;
1834 : char *pszWorkingLine;
1835 :
1836 1291 : pszNewLine = CPLReadLineL( fpHdr );
1837 1291 : if( pszNewLine == NULL )
1838 : break;
1839 :
1840 1175 : if( strstr(pszNewLine,"=") == NULL )
1841 0 : continue;
1842 :
1843 1175 : pszWorkingLine = CPLStrdup(pszNewLine);
1844 :
1845 : // Collect additional lines if we have open sqiggly bracket.
1846 1175 : if( strstr(pszWorkingLine,"{") != NULL
1847 : && strstr(pszWorkingLine,"}") == NULL )
1848 : {
1849 225 : do {
1850 225 : pszNewLine = CPLReadLineL( fpHdr );
1851 225 : if( pszNewLine )
1852 : {
1853 : pszWorkingLine = (char *)
1854 : CPLRealloc(pszWorkingLine,
1855 225 : strlen(pszWorkingLine)+strlen(pszNewLine)+1);
1856 225 : strcat( pszWorkingLine, pszNewLine );
1857 : }
1858 : } while( pszNewLine != NULL && strstr(pszNewLine,"}") == NULL );
1859 : }
1860 :
1861 : // Try to break input into name and value portions. Trim whitespace.
1862 : const char *pszValue;
1863 : int iEqual;
1864 :
1865 28665 : for( iEqual = 0;
1866 27490 : pszWorkingLine[iEqual] != '\0' && pszWorkingLine[iEqual] != '=';
1867 : iEqual++ ) {}
1868 :
1869 1175 : if( pszWorkingLine[iEqual] == '=' )
1870 : {
1871 : int i;
1872 :
1873 1175 : pszValue = pszWorkingLine + iEqual + 1;
1874 3525 : while( *pszValue == ' ' || *pszValue == '\t' )
1875 1175 : pszValue++;
1876 :
1877 1175 : pszWorkingLine[iEqual--] = '\0';
1878 7978 : while( iEqual > 0
1879 2814 : && (pszWorkingLine[iEqual] == ' '
1880 1175 : || pszWorkingLine[iEqual] == '\t') )
1881 1639 : pszWorkingLine[iEqual--] = '\0';
1882 :
1883 : // Convert spaces in the name to underscores.
1884 12106 : for( i = 0; pszWorkingLine[i] != '\0'; i++ )
1885 : {
1886 10931 : if( pszWorkingLine[i] == ' ' )
1887 690 : pszWorkingLine[i] = '_';
1888 : }
1889 :
1890 : papszHeader = CSLSetNameValue( papszHeader,
1891 1175 : pszWorkingLine, pszValue );
1892 : }
1893 :
1894 1175 : CPLFree( pszWorkingLine );
1895 : }
1896 :
1897 116 : return TRUE;
1898 : }
1899 :
1900 : /************************************************************************/
1901 : /* Open() */
1902 : /************************************************************************/
1903 :
1904 12384 : GDALDataset *ENVIDataset::Open( GDALOpenInfo * poOpenInfo )
1905 :
1906 : {
1907 : int i;
1908 :
1909 : /* -------------------------------------------------------------------- */
1910 : /* We assume the user is pointing to the binary (ie. .bil) file. */
1911 : /* -------------------------------------------------------------------- */
1912 12384 : if( poOpenInfo->nHeaderBytes < 2 )
1913 11361 : return NULL;
1914 :
1915 : /* -------------------------------------------------------------------- */
1916 : /* Do we have a .hdr file? Try upper and lower case, and */
1917 : /* replacing the extension as well as appending the extension */
1918 : /* to whatever we currently have. */
1919 : /* -------------------------------------------------------------------- */
1920 : const char *pszMode;
1921 1023 : CPLString osHdrFilename;
1922 1023 : VSILFILE *fpHeader = NULL;
1923 :
1924 1023 : if( poOpenInfo->eAccess == GA_Update )
1925 238 : pszMode = "r+";
1926 : else
1927 785 : pszMode = "r";
1928 :
1929 1023 : if (poOpenInfo->papszSiblingFiles == NULL)
1930 : {
1931 4 : osHdrFilename = CPLResetExtension( poOpenInfo->pszFilename, "hdr" );
1932 4 : fpHeader = VSIFOpenL( osHdrFilename, pszMode );
1933 :
1934 4 : if( fpHeader == NULL && VSIIsCaseSensitiveFS(osHdrFilename) )
1935 : {
1936 4 : osHdrFilename = CPLResetExtension( poOpenInfo->pszFilename, "HDR" );
1937 4 : fpHeader = VSIFOpenL( osHdrFilename, pszMode );
1938 : }
1939 :
1940 4 : if( fpHeader == NULL )
1941 : {
1942 : osHdrFilename = CPLFormFilename( NULL, poOpenInfo->pszFilename,
1943 4 : "hdr" );
1944 4 : fpHeader = VSIFOpenL( osHdrFilename, pszMode );
1945 : }
1946 :
1947 4 : if( fpHeader == NULL && VSIIsCaseSensitiveFS(osHdrFilename) )
1948 : {
1949 : osHdrFilename = CPLFormFilename( NULL, poOpenInfo->pszFilename,
1950 4 : "HDR" );
1951 4 : fpHeader = VSIFOpenL( osHdrFilename, pszMode );
1952 : }
1953 :
1954 : }
1955 : else
1956 : {
1957 : /* -------------------------------------------------------------------- */
1958 : /* Now we need to tear apart the filename to form a .HDR */
1959 : /* filename. */
1960 : /* -------------------------------------------------------------------- */
1961 1019 : CPLString osPath = CPLGetPath( poOpenInfo->pszFilename );
1962 1019 : CPLString osName = CPLGetFilename( poOpenInfo->pszFilename );
1963 :
1964 : int iFile = CSLFindString(poOpenInfo->papszSiblingFiles,
1965 1019 : CPLResetExtension( osName, "hdr" ) );
1966 1019 : if( iFile >= 0 )
1967 : {
1968 223 : osHdrFilename = CPLFormFilename( osPath, poOpenInfo->papszSiblingFiles[iFile],
1969 446 : NULL );
1970 223 : fpHeader = VSIFOpenL( osHdrFilename, pszMode );
1971 : }
1972 : else
1973 : {
1974 : iFile = CSLFindString(poOpenInfo->papszSiblingFiles,
1975 796 : CPLFormFilename( NULL, osName, "hdr" ));
1976 796 : if( iFile >= 0 )
1977 : {
1978 0 : osHdrFilename = CPLFormFilename( osPath, poOpenInfo->papszSiblingFiles[iFile],
1979 0 : NULL );
1980 0 : fpHeader = VSIFOpenL( osHdrFilename, pszMode );
1981 : }
1982 1019 : }
1983 : }
1984 :
1985 1023 : if( fpHeader == NULL )
1986 800 : return NULL;
1987 :
1988 : /* -------------------------------------------------------------------- */
1989 : /* Check that the first line says "ENVI". */
1990 : /* -------------------------------------------------------------------- */
1991 : char szTestHdr[4];
1992 :
1993 223 : if( VSIFReadL( szTestHdr, 4, 1, fpHeader ) != 1 )
1994 : {
1995 0 : VSIFCloseL( fpHeader );
1996 0 : return NULL;
1997 : }
1998 223 : if( strncmp(szTestHdr,"ENVI",4) != 0 )
1999 : {
2000 107 : VSIFCloseL( fpHeader );
2001 107 : return NULL;
2002 : }
2003 :
2004 : /* -------------------------------------------------------------------- */
2005 : /* Create a corresponding GDALDataset. */
2006 : /* -------------------------------------------------------------------- */
2007 : ENVIDataset *poDS;
2008 :
2009 116 : poDS = new ENVIDataset();
2010 232 : poDS->pszHDRFilename = CPLStrdup(osHdrFilename);
2011 116 : poDS->fp = fpHeader;
2012 :
2013 : /* -------------------------------------------------------------------- */
2014 : /* Read the header. */
2015 : /* -------------------------------------------------------------------- */
2016 116 : if( !poDS->ReadHeader( fpHeader ) )
2017 : {
2018 0 : delete poDS;
2019 0 : return NULL;
2020 : }
2021 :
2022 : /* -------------------------------------------------------------------- */
2023 : /* Has the user selected the .hdr file to open? */
2024 : /* -------------------------------------------------------------------- */
2025 116 : if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "hdr") )
2026 : {
2027 0 : delete poDS;
2028 : CPLError( CE_Failure, CPLE_AppDefined,
2029 : "The selected file is an ENVI header file, but to\n"
2030 : "open ENVI datasets, the data file should be selected\n"
2031 : "instead of the .hdr file. Please try again selecting\n"
2032 : "the data file corresponding to the header file:\n"
2033 : " %s\n",
2034 0 : poOpenInfo->pszFilename );
2035 0 : return NULL;
2036 : }
2037 :
2038 : /* -------------------------------------------------------------------- */
2039 : /* Has the user selected the .sta (stats) file to open? */
2040 : /* -------------------------------------------------------------------- */
2041 116 : if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "sta") )
2042 : {
2043 0 : delete poDS;
2044 : CPLError( CE_Failure, CPLE_AppDefined,
2045 : "The selected file is an ENVI statistics file.\n"
2046 : "To open ENVI datasets, the data file should be selected\n"
2047 : "instead of the .sta file. Please try again selecting\n"
2048 : "the data file corresponding to the statistics file:\n"
2049 : " %s\n",
2050 0 : poOpenInfo->pszFilename );
2051 0 : return NULL;
2052 : }
2053 :
2054 : /* -------------------------------------------------------------------- */
2055 : /* Extract required values from the .hdr. */
2056 : /* -------------------------------------------------------------------- */
2057 116 : int nLines = 0, nSamples = 0, nBands = 0, nHeaderSize = 0;
2058 116 : const char *pszInterleave = NULL;
2059 :
2060 116 : if( CSLFetchNameValue(poDS->papszHeader,"lines") )
2061 116 : nLines = atoi(CSLFetchNameValue(poDS->papszHeader,"lines"));
2062 :
2063 116 : if( CSLFetchNameValue(poDS->papszHeader,"samples") )
2064 116 : nSamples = atoi(CSLFetchNameValue(poDS->papszHeader,"samples"));
2065 :
2066 116 : if( CSLFetchNameValue(poDS->papszHeader,"bands") )
2067 116 : nBands = atoi(CSLFetchNameValue(poDS->papszHeader,"bands"));
2068 :
2069 116 : pszInterleave = CSLFetchNameValue(poDS->papszHeader,"interleave");
2070 :
2071 : /* In case, there is no interleave keyword, we try to derive it from the */
2072 : /* file extension. */
2073 116 : if( pszInterleave == NULL )
2074 : {
2075 0 : const char* pszExtension = CPLGetExtension(poOpenInfo->pszFilename);
2076 0 : pszInterleave = pszExtension;
2077 : }
2078 :
2079 116 : if ( !EQUALN(pszInterleave, "BSQ",3) &&
2080 : !EQUALN(pszInterleave, "BIP",3) &&
2081 : !EQUALN(pszInterleave, "BIL",3) )
2082 : {
2083 0 : CPLDebug("ENVI", "Unset or unknown value for 'interleave' keyword --> assuming BSQ interleaving");
2084 0 : pszInterleave = "bsq";
2085 : }
2086 :
2087 116 : if (!GDALCheckDatasetDimensions(nSamples, nLines) || !GDALCheckBandCount(nBands, FALSE))
2088 : {
2089 2 : delete poDS;
2090 : CPLError( CE_Failure, CPLE_AppDefined,
2091 : "The file appears to have an associated ENVI header, but\n"
2092 : "one or more of the samples, lines and bands\n"
2093 2 : "keywords appears to be missing or invalid." );
2094 2 : return NULL;
2095 : }
2096 :
2097 114 : if( CSLFetchNameValue(poDS->papszHeader,"header_offset") )
2098 114 : nHeaderSize = atoi(CSLFetchNameValue(poDS->papszHeader,"header_offset"));
2099 :
2100 : /* -------------------------------------------------------------------- */
2101 : /* Translate the datatype. */
2102 : /* -------------------------------------------------------------------- */
2103 114 : GDALDataType eType = GDT_Byte;
2104 :
2105 114 : if( CSLFetchNameValue(poDS->papszHeader,"data_type" ) != NULL )
2106 : {
2107 114 : switch( atoi(CSLFetchNameValue(poDS->papszHeader,"data_type" )) )
2108 : {
2109 : case 1:
2110 68 : eType = GDT_Byte;
2111 68 : break;
2112 :
2113 : case 2:
2114 6 : eType = GDT_Int16;
2115 6 : break;
2116 :
2117 : case 3:
2118 6 : eType = GDT_Int32;
2119 6 : break;
2120 :
2121 : case 4:
2122 6 : eType = GDT_Float32;
2123 6 : break;
2124 :
2125 : case 5:
2126 6 : eType = GDT_Float64;
2127 6 : break;
2128 :
2129 : case 6:
2130 5 : eType = GDT_CFloat32;
2131 5 : break;
2132 :
2133 : case 9:
2134 5 : eType = GDT_CFloat64;
2135 5 : break;
2136 :
2137 : case 12:
2138 6 : eType = GDT_UInt16;
2139 6 : break;
2140 :
2141 : case 13:
2142 6 : eType = GDT_UInt32;
2143 6 : break;
2144 :
2145 : /* 14=Int64, 15=UInt64 */
2146 :
2147 : default:
2148 0 : delete poDS;
2149 : CPLError( CE_Failure, CPLE_AppDefined,
2150 : "The file does not have a value for the data_type\n"
2151 0 : "that is recognised by the GDAL ENVI driver.");
2152 0 : return NULL;
2153 : }
2154 : }
2155 :
2156 : /* -------------------------------------------------------------------- */
2157 : /* Translate the byte order. */
2158 : /* -------------------------------------------------------------------- */
2159 114 : int bNativeOrder = TRUE;
2160 :
2161 114 : if( CSLFetchNameValue(poDS->papszHeader,"byte_order" ) != NULL )
2162 : {
2163 : #ifdef CPL_LSB
2164 : bNativeOrder = atoi(CSLFetchNameValue(poDS->papszHeader,
2165 114 : "byte_order" )) == 0;
2166 : #else
2167 : bNativeOrder = atoi(CSLFetchNameValue(poDS->papszHeader,
2168 : "byte_order" )) != 0;
2169 : #endif
2170 : }
2171 :
2172 : /* -------------------------------------------------------------------- */
2173 : /* Warn about unsupported file types virtual mosaic and meta file.*/
2174 : /* -------------------------------------------------------------------- */
2175 114 : if( CSLFetchNameValue(poDS->papszHeader,"file_type" ) != NULL )
2176 : {
2177 : // when the file type is one of these we return an invalid file type err
2178 : //'envi meta file'
2179 : //'envi virtual mosaic'
2180 : //'envi spectral library'
2181 : //'envi fft result'
2182 :
2183 : // when the file type is one of these we open it
2184 : //'envi standard'
2185 : //'envi classification'
2186 :
2187 : // when the file type is anything else we attempt to open it as a raster.
2188 :
2189 : const char * pszEnviFileType;
2190 114 : pszEnviFileType = CSLFetchNameValue(poDS->papszHeader,"file_type");
2191 :
2192 : // envi gdal does not support any of these
2193 : // all others we will attempt to open
2194 114 : if(EQUAL(pszEnviFileType, "envi meta file") ||
2195 : EQUAL(pszEnviFileType, "envi virtual mosaic") ||
2196 : EQUAL(pszEnviFileType, "envi spectral library"))
2197 : {
2198 : CPLError( CE_Failure, CPLE_OpenFailed,
2199 : "File %s contains an invalid file type in the ENVI .hdr\n"
2200 : "GDAL does not support '%s' type files.",
2201 0 : poOpenInfo->pszFilename, pszEnviFileType );
2202 0 : delete poDS;
2203 0 : return NULL;
2204 : }
2205 : }
2206 :
2207 : /* -------------------------------------------------------------------- */
2208 : /* Detect (gzipped) compressed datasets. */
2209 : /* -------------------------------------------------------------------- */
2210 114 : int bIsCompressed = FALSE;
2211 114 : if( CSLFetchNameValue(poDS->papszHeader,"file_compression" ) != NULL )
2212 : {
2213 1 : if( atoi(CSLFetchNameValue(poDS->papszHeader,"file_compression" ))
2214 : != 0 )
2215 : {
2216 1 : bIsCompressed = TRUE;
2217 : }
2218 : }
2219 :
2220 : /* -------------------------------------------------------------------- */
2221 : /* Capture some information from the file that is of interest. */
2222 : /* -------------------------------------------------------------------- */
2223 114 : poDS->nRasterXSize = nSamples;
2224 114 : poDS->nRasterYSize = nLines;
2225 114 : poDS->eAccess = poOpenInfo->eAccess;
2226 :
2227 : /* -------------------------------------------------------------------- */
2228 : /* Reopen file in update mode if necessary. */
2229 : /* -------------------------------------------------------------------- */
2230 114 : CPLString osImageFilename(poOpenInfo->pszFilename);
2231 114 : if (bIsCompressed)
2232 1 : osImageFilename = "/vsigzip/" + osImageFilename;
2233 114 : if( poOpenInfo->eAccess == GA_Update )
2234 : {
2235 51 : if (bIsCompressed)
2236 : {
2237 0 : delete poDS;
2238 : CPLError( CE_Failure, CPLE_OpenFailed,
2239 0 : "Cannot open compressed file in update mode.\n");
2240 0 : return NULL;
2241 : }
2242 51 : poDS->fpImage = VSIFOpenL( osImageFilename, "rb+" );
2243 : }
2244 : else
2245 63 : poDS->fpImage = VSIFOpenL( osImageFilename, "rb" );
2246 :
2247 114 : if( poDS->fpImage == NULL )
2248 : {
2249 0 : delete poDS;
2250 : CPLError( CE_Failure, CPLE_OpenFailed,
2251 : "Failed to re-open %s within ENVI driver.\n",
2252 0 : poOpenInfo->pszFilename );
2253 0 : return NULL;
2254 : }
2255 :
2256 : /* -------------------------------------------------------------------- */
2257 : /* Compute the line offset. */
2258 : /* -------------------------------------------------------------------- */
2259 114 : int nDataSize = GDALGetDataTypeSize(eType)/8;
2260 : int nPixelOffset, nLineOffset;
2261 : vsi_l_offset nBandOffset;
2262 114 : int bIntOverflow = FALSE;
2263 :
2264 114 : if( EQUALN(pszInterleave, "bil", 3) )
2265 : {
2266 0 : poDS->interleave = BIL;
2267 0 : poDS->SetMetadataItem( "INTERLEAVE", "LINE", "IMAGE_STRUCTURE" );
2268 0 : if (nSamples > INT_MAX / (nDataSize * nBands)) bIntOverflow = TRUE;
2269 0 : nLineOffset = nDataSize * nSamples * nBands;
2270 0 : nPixelOffset = nDataSize;
2271 0 : nBandOffset = (vsi_l_offset)nDataSize * nSamples;
2272 : }
2273 114 : else if( EQUALN(pszInterleave, "bip", 3) )
2274 : {
2275 0 : poDS->interleave = BIP;
2276 0 : poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" );
2277 0 : if (nSamples > INT_MAX / (nDataSize * nBands)) bIntOverflow = TRUE;
2278 0 : nLineOffset = nDataSize * nSamples * nBands;
2279 0 : nPixelOffset = nDataSize * nBands;
2280 0 : nBandOffset = nDataSize;
2281 : }
2282 : else /* bsq */
2283 : {
2284 114 : poDS->interleave = BSQ;
2285 114 : poDS->SetMetadataItem( "INTERLEAVE", "BAND", "IMAGE_STRUCTURE" );
2286 114 : if (nSamples > INT_MAX / nDataSize) bIntOverflow = TRUE;
2287 114 : nLineOffset = nDataSize * nSamples;
2288 114 : nPixelOffset = nDataSize;
2289 114 : nBandOffset = (vsi_l_offset)nLineOffset * nLines;
2290 : }
2291 :
2292 114 : if (bIntOverflow)
2293 : {
2294 0 : delete poDS;
2295 : CPLError( CE_Failure, CPLE_AppDefined,
2296 0 : "Int overflow occured.");
2297 0 : return NULL;
2298 : }
2299 :
2300 : /* -------------------------------------------------------------------- */
2301 : /* Create band information objects. */
2302 : /* -------------------------------------------------------------------- */
2303 114 : poDS->nBands = nBands;
2304 303 : for( i = 0; i < poDS->nBands; i++ )
2305 : {
2306 : poDS->SetBand( i + 1,
2307 : new ENVIRasterBand(poDS, i + 1, poDS->fpImage,
2308 : nHeaderSize + nBandOffset * i,
2309 : nPixelOffset, nLineOffset, eType,
2310 189 : bNativeOrder, TRUE) );
2311 : }
2312 :
2313 : /* -------------------------------------------------------------------- */
2314 : /* Apply band names if we have them. */
2315 : /* Use wavelength for more descriptive information if possible */
2316 : /* -------------------------------------------------------------------- */
2317 114 : if( CSLFetchNameValue( poDS->papszHeader, "band_names" ) != NULL ||
2318 : CSLFetchNameValue( poDS->papszHeader, "wavelength" ) != NULL)
2319 : {
2320 : char **papszBandNames =
2321 : poDS->SplitList( CSLFetchNameValue( poDS->papszHeader,
2322 62 : "band_names" ) );
2323 : char **papszWL =
2324 : poDS->SplitList( CSLFetchNameValue( poDS->papszHeader,
2325 62 : "wavelength" ) );
2326 :
2327 62 : const char *pszWLUnits = NULL;
2328 62 : int nWLCount = CSLCount(papszWL);
2329 62 : if (papszWL)
2330 : {
2331 : /* If WL information is present, process wavelength units */
2332 : pszWLUnits = CSLFetchNameValue( poDS->papszHeader,
2333 0 : "wavelength_units" );
2334 0 : if (pszWLUnits)
2335 : {
2336 : /* Don't show unknown or index units */
2337 0 : if (EQUAL(pszWLUnits,"Unknown") ||
2338 : EQUAL(pszWLUnits,"Index") )
2339 0 : pszWLUnits=0;
2340 : }
2341 0 : if (pszWLUnits)
2342 : {
2343 : /* set wavelength units to dataset metadata */
2344 0 : poDS->SetMetadataItem("wavelength_units", pszWLUnits);
2345 : }
2346 : }
2347 :
2348 154 : for( i = 0; i < nBands; i++ )
2349 : {
2350 92 : CPLString osBandId, osBandName, osWavelength;
2351 :
2352 : /* First set up the wavelength names and units if available */
2353 92 : if (papszWL && nWLCount > i)
2354 : {
2355 0 : osWavelength = papszWL[i];
2356 0 : if (pszWLUnits)
2357 : {
2358 0 : osWavelength += " ";
2359 0 : osWavelength += pszWLUnits;
2360 : }
2361 : }
2362 :
2363 : /* Build the final name for this band */
2364 92 : if (papszBandNames && CSLCount(papszBandNames) > i)
2365 : {
2366 92 : osBandName = papszBandNames[i];
2367 92 : if (strlen(osWavelength) > 0)
2368 : {
2369 0 : osBandName += " (";
2370 0 : osBandName += osWavelength;
2371 0 : osBandName += ")";
2372 : }
2373 : }
2374 : else /* WL but no band names */
2375 0 : osBandName = osWavelength;
2376 :
2377 : /* Description is for internal GDAL usage */
2378 92 : poDS->GetRasterBand(i + 1)->SetDescription( osBandName );
2379 :
2380 : /* Metadata field named Band_1, etc. needed for ArcGIS integration */
2381 92 : osBandId = CPLSPrintf("Band_%i", i+1);
2382 92 : poDS->SetMetadataItem(osBandId, osBandName);
2383 :
2384 : /* Set wavelength metadata to band */
2385 92 : if (papszWL && nWLCount > i)
2386 : {
2387 : poDS->GetRasterBand(i+1)->SetMetadataItem(
2388 0 : "wavelength", papszWL[i]);
2389 :
2390 0 : if (pszWLUnits)
2391 : {
2392 : poDS->GetRasterBand(i+1)->SetMetadataItem(
2393 0 : "wavelength_units", pszWLUnits);
2394 : }
2395 : }
2396 :
2397 : }
2398 62 : CSLDestroy( papszWL );
2399 62 : CSLDestroy( papszBandNames );
2400 : }
2401 : /* -------------------------------------------------------------------- */
2402 : /* Apply class names if we have them. */
2403 : /* -------------------------------------------------------------------- */
2404 114 : if( CSLFetchNameValue( poDS->papszHeader, "class_names" ) != NULL )
2405 : {
2406 : char **papszClassNames =
2407 : poDS->SplitList( CSLFetchNameValue( poDS->papszHeader,
2408 3 : "class_names" ) );
2409 :
2410 3 : poDS->GetRasterBand(1)->SetCategoryNames( papszClassNames );
2411 3 : CSLDestroy( papszClassNames );
2412 : }
2413 :
2414 : /* -------------------------------------------------------------------- */
2415 : /* Apply colormap if we have one. */
2416 : /* -------------------------------------------------------------------- */
2417 114 : if( CSLFetchNameValue( poDS->papszHeader, "class_lookup" ) != NULL )
2418 : {
2419 : char **papszClassColors =
2420 : poDS->SplitList( CSLFetchNameValue( poDS->papszHeader,
2421 3 : "class_lookup" ) );
2422 3 : int nColorValueCount = CSLCount(papszClassColors);
2423 3 : GDALColorTable oCT;
2424 :
2425 9 : for( i = 0; i*3+2 < nColorValueCount; i++ )
2426 : {
2427 : GDALColorEntry sEntry;
2428 :
2429 6 : sEntry.c1 = (short) atoi(papszClassColors[i*3+0]);
2430 6 : sEntry.c2 = (short) atoi(papszClassColors[i*3+1]);
2431 6 : sEntry.c3 = (short) atoi(papszClassColors[i*3+2]);
2432 6 : sEntry.c4 = 255;
2433 6 : oCT.SetColorEntry( i, &sEntry );
2434 : }
2435 :
2436 3 : CSLDestroy( papszClassColors );
2437 :
2438 3 : poDS->GetRasterBand(1)->SetColorTable( &oCT );
2439 3 : poDS->GetRasterBand(1)->SetColorInterpretation( GCI_PaletteIndex );
2440 : }
2441 :
2442 : /* -------------------------------------------------------------------- */
2443 : /* Set the nodata value if it is present */
2444 : /* -------------------------------------------------------------------- */
2445 114 : if( CSLFetchNameValue(poDS->papszHeader,"data_ignore_value" ) != NULL )
2446 : {
2447 0 : for( i = 0; i < poDS->nBands; i++ )
2448 : ((RawRasterBand*)poDS->GetRasterBand(i+1))->SetNoDataValue(atof(
2449 0 : CSLFetchNameValue(poDS->papszHeader,"data_ignore_value")));
2450 : }
2451 :
2452 : /* -------------------------------------------------------------------- */
2453 : /* Set all the header metadata into the ENVI domain */
2454 : /* -------------------------------------------------------------------- */
2455 : {
2456 114 : char** pTmp = poDS->papszHeader;
2457 1387 : while (*pTmp != NULL)
2458 : {
2459 1159 : char** pTokens = CSLTokenizeString2(*pTmp, "=", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
2460 1159 : if (pTokens[0] != NULL && pTokens[1] != NULL && pTokens[2] == NULL)
2461 : {
2462 1159 : poDS->SetMetadataItem(pTokens[0], pTokens[1], "ENVI");
2463 : }
2464 1159 : CSLDestroy(pTokens);
2465 1159 : pTmp++;
2466 : }
2467 : }
2468 :
2469 : /* -------------------------------------------------------------------- */
2470 : /* Read the stats file if it is present */
2471 : /* -------------------------------------------------------------------- */
2472 114 : poDS->ProcessStatsFile();
2473 :
2474 : /* -------------------------------------------------------------------- */
2475 : /* Look for mapinfo */
2476 : /* -------------------------------------------------------------------- */
2477 114 : if( CSLFetchNameValue( poDS->papszHeader, "map_info" ) != NULL )
2478 : {
2479 : poDS->bFoundMapinfo =
2480 : poDS->ProcessMapinfo(
2481 52 : CSLFetchNameValue(poDS->papszHeader,"map_info") );
2482 : }
2483 :
2484 : /* -------------------------------------------------------------------- */
2485 : /* Look for RPC mapinfo */
2486 : /* -------------------------------------------------------------------- */
2487 114 : if( !poDS->bFoundMapinfo &&
2488 : CSLFetchNameValue( poDS->papszHeader, "rpc_info" ) != NULL )
2489 : {
2490 : poDS->ProcessRPCinfo(
2491 : CSLFetchNameValue(poDS->papszHeader,"rpc_info"),
2492 3 : poDS->nRasterXSize, poDS->nRasterYSize);
2493 : }
2494 :
2495 : /* -------------------------------------------------------------------- */
2496 : /* Initialize any PAM information. */
2497 : /* -------------------------------------------------------------------- */
2498 114 : poDS->SetDescription( poOpenInfo->pszFilename );
2499 114 : poDS->TryLoadXML();
2500 :
2501 : /* -------------------------------------------------------------------- */
2502 : /* Check for overviews. */
2503 : /* -------------------------------------------------------------------- */
2504 114 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
2505 :
2506 114 : return( poDS );
2507 : }
2508 :
2509 183 : int ENVIDataset::GetEnviType(GDALDataType eType)
2510 : {
2511 : int iENVIType;
2512 183 : switch( eType )
2513 : {
2514 : case GDT_Byte:
2515 97 : iENVIType = 1;
2516 97 : break;
2517 : case GDT_Int16:
2518 10 : iENVIType = 2;
2519 10 : break;
2520 : case GDT_Int32:
2521 10 : iENVIType = 3;
2522 10 : break;
2523 : case GDT_Float32:
2524 10 : iENVIType = 4;
2525 10 : break;
2526 : case GDT_Float64:
2527 10 : iENVIType = 5;
2528 10 : break;
2529 : case GDT_CFloat32:
2530 9 : iENVIType = 6;
2531 9 : break;
2532 : case GDT_CFloat64:
2533 9 : iENVIType = 9;
2534 9 : break;
2535 : case GDT_UInt16:
2536 10 : iENVIType = 12;
2537 10 : break;
2538 : case GDT_UInt32:
2539 10 : iENVIType = 13;
2540 10 : break;
2541 :
2542 : /* 14=Int64, 15=UInt64 */
2543 :
2544 : default:
2545 : CPLError( CE_Failure, CPLE_AppDefined,
2546 : "Attempt to create ENVI .hdr labelled dataset with an illegal\n"
2547 : "data type (%s).\n",
2548 8 : GDALGetDataTypeName(eType) );
2549 8 : return 1;
2550 : }
2551 175 : return iENVIType;
2552 : }
2553 :
2554 : /************************************************************************/
2555 : /* Create() */
2556 : /************************************************************************/
2557 :
2558 69 : GDALDataset *ENVIDataset::Create( const char * pszFilename,
2559 : int nXSize, int nYSize, int nBands,
2560 : GDALDataType eType,
2561 : char ** papszOptions )
2562 :
2563 : {
2564 : /* -------------------------------------------------------------------- */
2565 : /* Verify input options. */
2566 : /* -------------------------------------------------------------------- */
2567 69 : int iENVIType = GetEnviType(eType);
2568 69 : if (0 == iENVIType)
2569 0 : return 0;
2570 :
2571 : /* -------------------------------------------------------------------- */
2572 : /* Try to create the file. */
2573 : /* -------------------------------------------------------------------- */
2574 : VSILFILE *fp;
2575 :
2576 69 : fp = VSIFOpenL( pszFilename, "wb" );
2577 :
2578 69 : if( fp == NULL )
2579 : {
2580 : CPLError( CE_Failure, CPLE_OpenFailed,
2581 : "Attempt to create file `%s' failed.\n",
2582 17 : pszFilename );
2583 17 : return NULL;
2584 : }
2585 :
2586 : /* -------------------------------------------------------------------- */
2587 : /* Just write out a couple of bytes to establish the binary */
2588 : /* file, and then close it. */
2589 : /* -------------------------------------------------------------------- */
2590 52 : VSIFWriteL( (void *) "\0\0", 2, 1, fp );
2591 52 : VSIFCloseL( fp );
2592 :
2593 : /* -------------------------------------------------------------------- */
2594 : /* Create the .hdr filename. */
2595 : /* -------------------------------------------------------------------- */
2596 : const char *pszHDRFilename;
2597 : const char *pszSuffix;
2598 :
2599 52 : pszSuffix = CSLFetchNameValue( papszOptions, "SUFFIX" );
2600 52 : if ( pszSuffix && EQUALN( pszSuffix, "ADD", 3 ))
2601 0 : pszHDRFilename = CPLFormFilename( NULL, pszFilename, "hdr" );
2602 : else
2603 52 : pszHDRFilename = CPLResetExtension(pszFilename, "hdr" );
2604 :
2605 : /* -------------------------------------------------------------------- */
2606 : /* Open the file. */
2607 : /* -------------------------------------------------------------------- */
2608 52 : fp = VSIFOpenL( pszHDRFilename, "wt" );
2609 52 : if( fp == NULL )
2610 : {
2611 : CPLError( CE_Failure, CPLE_OpenFailed,
2612 : "Attempt to create file `%s' failed.\n",
2613 0 : pszHDRFilename );
2614 0 : return NULL;
2615 : }
2616 :
2617 : /* -------------------------------------------------------------------- */
2618 : /* Write out the header. */
2619 : /* -------------------------------------------------------------------- */
2620 : int iBigEndian;
2621 : const char *pszInterleaving;
2622 :
2623 : #ifdef CPL_LSB
2624 52 : iBigEndian = 0;
2625 : #else
2626 : iBigEndian = 1;
2627 : #endif
2628 :
2629 52 : VSIFPrintfL( fp, "ENVI\n" );
2630 : VSIFPrintfL( fp, "samples = %d\nlines = %d\nbands = %d\n",
2631 52 : nXSize, nYSize, nBands );
2632 52 : VSIFPrintfL( fp, "header offset = 0\nfile type = ENVI Standard\n" );
2633 52 : VSIFPrintfL( fp, "data type = %d\n", iENVIType );
2634 52 : pszInterleaving = CSLFetchNameValue( papszOptions, "INTERLEAVE" );
2635 52 : if ( pszInterleaving )
2636 : {
2637 0 : if ( EQUALN( pszInterleaving, "bip", 3 ) )
2638 0 : pszInterleaving = "bip"; // interleaved by pixel
2639 0 : else if ( EQUALN( pszInterleaving, "bil", 3 ) )
2640 0 : pszInterleaving = "bil"; // interleaved by line
2641 : else
2642 0 : pszInterleaving = "bsq"; // band sequental by default
2643 : }
2644 : else
2645 52 : pszInterleaving = "bsq";
2646 52 : VSIFPrintfL( fp, "interleave = %s\n", pszInterleaving);
2647 52 : VSIFPrintfL( fp, "byte order = %d\n", iBigEndian );
2648 :
2649 52 : VSIFCloseL( fp );
2650 :
2651 52 : return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
2652 : }
2653 :
2654 : /************************************************************************/
2655 : /* ENVIRasterBand() */
2656 : /************************************************************************/
2657 :
2658 189 : ENVIRasterBand::ENVIRasterBand( GDALDataset *poDS, int nBand, void * fpRaw,
2659 : vsi_l_offset nImgOffset, int nPixelOffset,
2660 : int nLineOffset,
2661 : GDALDataType eDataType, int bNativeOrder,
2662 : int bIsVSIL, int bOwnsFP ) :
2663 : RawRasterBand(poDS, nBand, fpRaw, nImgOffset, nPixelOffset,
2664 189 : nLineOffset, eDataType, bNativeOrder, bIsVSIL, bOwnsFP)
2665 : {
2666 189 : }
2667 :
2668 : /************************************************************************/
2669 : /* SetDescription() */
2670 : /************************************************************************/
2671 :
2672 97 : void ENVIRasterBand::SetDescription( const char * pszDescription )
2673 : {
2674 97 : ((ENVIDataset*)poDS)->bHeaderDirty = TRUE;
2675 97 : RawRasterBand::SetDescription(pszDescription);
2676 97 : }
2677 :
2678 : /************************************************************************/
2679 : /* SetCategoryNames() */
2680 : /************************************************************************/
2681 :
2682 4 : CPLErr ENVIRasterBand::SetCategoryNames( char ** papszCategoryNames )
2683 : {
2684 4 : ((ENVIDataset*)poDS)->bHeaderDirty = TRUE;
2685 4 : return RawRasterBand::SetCategoryNames(papszCategoryNames);
2686 : }
2687 :
2688 : /************************************************************************/
2689 : /* GDALRegister_ENVI() */
2690 : /************************************************************************/
2691 :
2692 610 : void GDALRegister_ENVI()
2693 : {
2694 : GDALDriver *poDriver;
2695 :
2696 610 : if( GDALGetDriverByName( "ENVI" ) == NULL )
2697 : {
2698 588 : poDriver = new GDALDriver();
2699 :
2700 588 : poDriver->SetDescription( "ENVI" );
2701 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
2702 588 : "ENVI .hdr Labelled" );
2703 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
2704 588 : "frmt_various.html#ENVI" );
2705 588 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "" );
2706 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
2707 : "Byte Int16 UInt16 Int32 UInt32 "
2708 588 : "Float32 Float64 CFloat32 CFloat64" );
2709 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
2710 : "<CreationOptionList>"
2711 : " <Option name='SUFFIX' type='string-select'>"
2712 : " <Value>ADD</Value>"
2713 : " </Option>"
2714 : " <Option name='INTERLEAVE' type='string-select'>"
2715 : " <Value>BIP</Value>"
2716 : " <Value>BIL</Value>"
2717 : " <Value>BSQ</Value>"
2718 : " </Option>"
2719 588 : "</CreationOptionList>" );
2720 :
2721 588 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
2722 588 : poDriver->pfnOpen = ENVIDataset::Open;
2723 588 : poDriver->pfnCreate = ENVIDataset::Create;
2724 :
2725 588 : GetGDALDriverManager()->RegisterDriver( poDriver );
2726 : }
2727 610 : }
|