1 /******************************************************************************
2  * $Id: e00griddataset.cpp 27942 2014-11-11 00:57:41Z rouault $
3  *
4  * Project:  E00 grid driver
5  * Purpose:  GDALDataset driver for E00 grid dataset.
6  * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
7  *
8  ******************************************************************************
9  * Copyright (c) 2011, Even Rouault <even dot rouault at mines-paris dot org>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_vsi_virtual.h"
31 #include "cpl_string.h"
32 #include "ogr_spatialref.h"
33 #include "gdal_pam.h"
34 
35 /* Private import of e00read.c */
36 #define E00ReadOpen         GDALE00GRIDReadOpen
37 #define E00ReadCallbackOpen GDALE00GRIDReadCallbackOpen
38 #define E00ReadClose        GDALE00GRIDReadClose
39 #define E00ReadNextLine     GDALE00GRIDReadNextLine
40 #define E00ReadRewind       GDALE00GRIDReadRewind
41 #include "e00read.c"
42 
43 #define E00_INT_SIZE    10
44 #define E00_INT14_SIZE  14
45 #define E00_FLOAT_SIZE  14
46 #define E00_DOUBLE_SIZE 21
47 #define VALS_PER_LINE   5
48 
49 CPL_CVSID("$Id: e00griddataset.cpp 27942 2014-11-11 00:57:41Z rouault $");
50 
51 CPL_C_START
52 void    GDALRegister_E00GRID(void);
53 CPL_C_END
54 
55 /* g++ -fPIC -Wall -g frmts/e00grid/e00griddataset.cpp -shared -o gdal_E00GRID.so -Iport -Igcore -Iogr -L. -lgdal */
56 
57 /* Test data ; (google for "EXP  0" "GRD  2")
58 
59 ftp://msdis.missouri.edu/pub/dem/24k/county/
60 http://dusk.geo.orst.edu/djl/samoa/data/samoa_bathy.e00
61 http://dusk.geo.orst.edu/djl/samoa/FBNMS/RasterGrids-Metadata/ntae02_3m_utm.e00
62 http://www.navdat.org/coverages/elevation/iddem1.e00        (int32)
63 http://delta-vision.projects.atlas.ca.gov/lidar/bare_earth.grids/sac0165.e00
64 http://ag.arizona.edu/SRER/maps_e00/srer_dem.e00
65 http://ok.water.usgs.gov/projects/norlan/spatial/ntopo0408-10.e00 (compressed)
66 http://wrri.nmsu.edu/publish/techrpt/tr322/GIS/dem.e00 (compressed)
67 */
68 
69 /************************************************************************/
70 /* ==================================================================== */
71 /*                            E00GRIDDataset                            */
72 /* ==================================================================== */
73 /************************************************************************/
74 
75 class E00GRIDRasterBand;
76 
77 class E00GRIDDataset : public GDALPamDataset
78 {
79     friend class E00GRIDRasterBand;
80 
81     E00ReadPtr  e00ReadPtr;
82     VSILFILE   *fp;
83     vsi_l_offset nDataStart;
84     int         nBytesEOL;
85 
86     vsi_l_offset  nPosBeforeReadLine;
87     vsi_l_offset* panOffsets;
88     int         nLastYOff;
89     int         nMaxYOffset;
90 
91     double      adfGeoTransform[6];
92     CPLString   osProjection;
93 
94     double      dfNoData;
95 
96     char**      papszPrj;
97 
98     const char* ReadLine();
99 
100     int         bHasReadMetadata;
101     void        ReadMetadata();
102 
103     int         bHasStats;
104     double      dfMin, dfMax, dfMean, dfStddev;
105 
106     static const char* ReadNextLine(void * ptr);
107     static void        Rewind(void* ptr);
108 
109   public:
110                  E00GRIDDataset();
111     virtual     ~E00GRIDDataset();
112 
113     virtual CPLErr GetGeoTransform( double * );
114     virtual const char* GetProjectionRef();
115 
116     static GDALDataset *Open( GDALOpenInfo * );
117     static int          Identify( GDALOpenInfo * );
118 };
119 
120 /************************************************************************/
121 /* ==================================================================== */
122 /*                          E00GRIDRasterBand                           */
123 /* ==================================================================== */
124 /************************************************************************/
125 
126 class E00GRIDRasterBand : public GDALPamRasterBand
127 {
128     friend class E00GRIDDataset;
129 
130   public:
131 
132                 E00GRIDRasterBand( E00GRIDDataset *, int, GDALDataType );
133 
134     virtual CPLErr      IReadBlock( int, int, void * );
135 
136     virtual double      GetNoDataValue( int *pbSuccess = NULL );
137     virtual const char *GetUnitType();
138     virtual double      GetMinimum( int *pbSuccess = NULL );
139     virtual double      GetMaximum( int *pbSuccess = NULL );
140     virtual CPLErr      GetStatistics( int bApproxOK, int bForce,
141                                        double *pdfMin, double *pdfMax,
142                                        double *pdfMean, double *padfStdDev );
143 };
144 
145 
146 /************************************************************************/
147 /*                         E00GRIDRasterBand()                          */
148 /************************************************************************/
149 
E00GRIDRasterBand(E00GRIDDataset * poDS,int nBand,GDALDataType eDT)150 E00GRIDRasterBand::E00GRIDRasterBand( E00GRIDDataset *poDS, int nBand,
151                                       GDALDataType eDT )
152 
153 {
154     this->poDS = poDS;
155     this->nBand = nBand;
156 
157     eDataType = eDT;
158 
159     nBlockXSize = poDS->GetRasterXSize();
160     nBlockYSize = 1;
161 }
162 
163 /************************************************************************/
164 /*                             IReadBlock()                             */
165 /************************************************************************/
166 
IReadBlock(CPL_UNUSED int nBlockXOff,int nBlockYOff,void * pImage)167 CPLErr E00GRIDRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff,
168                                       int nBlockYOff,
169                                       void * pImage )
170 {
171     E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS;
172 
173     char szVal[E00_FLOAT_SIZE+1];
174     szVal[E00_FLOAT_SIZE] = 0;
175 
176     int i;
177     float* pafImage = (float*)pImage;
178     int* panImage = (int*)pImage;
179     const float fNoData = (const float)poGDS->dfNoData;
180 
181     /* A new data line begins on a new text line. So if the xsize */
182     /* is not a multiple of VALS_PER_LINE, there are padding values */
183     /* that must be ignored */
184     const int nRoundedBlockXSize = ((nBlockXSize + VALS_PER_LINE - 1) /
185                                             VALS_PER_LINE) * VALS_PER_LINE;
186 
187     if (poGDS->e00ReadPtr)
188     {
189         if (poGDS->nLastYOff < 0)
190         {
191             E00ReadRewind(poGDS->e00ReadPtr);
192             for(i=0;i<6;i++)
193                 E00ReadNextLine(poGDS->e00ReadPtr);
194         }
195 
196         if (nBlockYOff == poGDS->nLastYOff + 1)
197         {
198         }
199         else if (nBlockYOff <= poGDS->nMaxYOffset)
200         {
201             //CPLDebug("E00GRID", "Skip to %d from %d", nBlockYOff, poGDS->nLastYOff);
202             VSIFSeekL(poGDS->fp, poGDS->panOffsets[nBlockYOff], SEEK_SET);
203             poGDS->nPosBeforeReadLine = poGDS->panOffsets[nBlockYOff];
204             poGDS->e00ReadPtr->iInBufPtr = 0;
205             poGDS->e00ReadPtr->szInBuf[0] = '\0';
206         }
207         else if (nBlockYOff > poGDS->nLastYOff + 1)
208         {
209             //CPLDebug("E00GRID", "Forward skip to %d from %d", nBlockYOff, poGDS->nLastYOff);
210             for(i=poGDS->nLastYOff + 1; i < nBlockYOff;i++)
211                 IReadBlock(0, i, pImage);
212         }
213 
214         if (nBlockYOff > poGDS->nMaxYOffset)
215         {
216             poGDS->panOffsets[nBlockYOff] = poGDS->nPosBeforeReadLine +
217                                             poGDS->e00ReadPtr->iInBufPtr;
218             poGDS->nMaxYOffset = nBlockYOff;
219         }
220 
221         const char* pszLine = NULL;
222         for(i=0;i<nBlockXSize;i++)
223         {
224             if ((i % VALS_PER_LINE) == 0)
225             {
226                 pszLine = E00ReadNextLine(poGDS->e00ReadPtr);
227                 if (pszLine == NULL || strlen(pszLine) < 5 * E00_FLOAT_SIZE)
228                     return CE_Failure;
229             }
230             if (eDataType == GDT_Float32)
231             {
232                 pafImage[i] = (float) CPLAtof(pszLine + (i%VALS_PER_LINE) * E00_FLOAT_SIZE);
233                 /* Workaround single vs double precision problems */
234                 if (fNoData != 0 && fabs((pafImage[i] - fNoData)/fNoData) < 1e-6)
235                     pafImage[i] = fNoData;
236             }
237             else
238             {
239                 panImage[i] = atoi(pszLine + (i%VALS_PER_LINE) * E00_FLOAT_SIZE);
240             }
241         }
242 
243         poGDS->nLastYOff = nBlockYOff;
244 
245         return CE_None;
246     }
247 
248     vsi_l_offset nValsToSkip = (vsi_l_offset)nBlockYOff * nRoundedBlockXSize;
249     vsi_l_offset nLinesToSkip = nValsToSkip / VALS_PER_LINE;
250     int nBytesPerLine = VALS_PER_LINE * E00_FLOAT_SIZE + poGDS->nBytesEOL;
251     vsi_l_offset nPos = poGDS->nDataStart + nLinesToSkip * nBytesPerLine;
252     VSIFSeekL(poGDS->fp, nPos, SEEK_SET);
253 
254     for(i=0;i<nBlockXSize;i++)
255     {
256         if (VSIFReadL(szVal, E00_FLOAT_SIZE, 1, poGDS->fp) != 1)
257             return CE_Failure;
258 
259         if (eDataType == GDT_Float32)
260         {
261             pafImage[i] = (float) CPLAtof(szVal);
262             /* Workaround single vs double precision problems */
263             if (fNoData != 0 && fabs((pafImage[i] - fNoData)/fNoData) < 1e-6)
264                 pafImage[i] = fNoData;
265         }
266         else
267         {
268             panImage[i] = atoi(szVal);
269         }
270 
271         if (((i+1) % VALS_PER_LINE) == 0)
272             VSIFReadL(szVal, poGDS->nBytesEOL, 1, poGDS->fp);
273     }
274 
275     return CE_None;
276 }
277 
278 /************************************************************************/
279 /*                           GetNoDataValue()                           */
280 /************************************************************************/
281 
GetNoDataValue(int * pbSuccess)282 double E00GRIDRasterBand::GetNoDataValue( int *pbSuccess )
283 {
284     E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS;
285 
286     if (pbSuccess)
287         *pbSuccess = TRUE;
288 
289     if (eDataType == GDT_Float32)
290         return (double)(float) poGDS->dfNoData;
291     else
292         return (double)(int)poGDS->dfNoData;
293 }
294 
295 /************************************************************************/
296 /*                             GetUnitType()                            */
297 /************************************************************************/
298 
GetUnitType()299 const char * E00GRIDRasterBand::GetUnitType()
300 {
301     E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS;
302 
303     poGDS->ReadMetadata();
304 
305     if (poGDS->papszPrj == NULL)
306         return GDALPamRasterBand::GetUnitType();
307 
308     char** papszIter = poGDS->papszPrj;
309     const char* pszRet = "";
310     while(*papszIter)
311     {
312         if (EQUALN(*papszIter, "Zunits", 6))
313         {
314             char** papszTokens = CSLTokenizeString(*papszIter);
315             if (CSLCount(papszTokens) == 2)
316             {
317                 if (EQUAL(papszTokens[1], "FEET"))
318                     pszRet = "ft";
319                 else if (EQUAL(papszTokens[1], "METERS"))
320                     pszRet = "m";
321             }
322             CSLDestroy(papszTokens);
323             break;
324         }
325         papszIter ++;
326     }
327 
328     return pszRet;
329 }
330 
331 /************************************************************************/
332 /*                           GetMinimum()                               */
333 /************************************************************************/
334 
GetMinimum(int * pbSuccess)335 double E00GRIDRasterBand::GetMinimum( int *pbSuccess )
336 {
337     E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS;
338 
339     poGDS->ReadMetadata();
340 
341     if (poGDS->bHasStats)
342     {
343         if( pbSuccess != NULL )
344             *pbSuccess = TRUE;
345 
346         return poGDS->dfMin;
347     }
348 
349     return GDALPamRasterBand::GetMinimum( pbSuccess );
350 }
351 
352 /************************************************************************/
353 /*                           GetMaximum()                               */
354 /************************************************************************/
355 
GetMaximum(int * pbSuccess)356 double E00GRIDRasterBand::GetMaximum( int *pbSuccess )
357 {
358     E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS;
359 
360     poGDS->ReadMetadata();
361 
362     if (poGDS->bHasStats)
363     {
364         if( pbSuccess != NULL )
365             *pbSuccess = TRUE;
366 
367         return poGDS->dfMax;
368     }
369 
370     return GDALPamRasterBand::GetMaximum( pbSuccess );
371 }
372 
373 /************************************************************************/
374 /*                            GetStatistics()                           */
375 /************************************************************************/
376 
GetStatistics(int bApproxOK,int bForce,double * pdfMin,double * pdfMax,double * pdfMean,double * pdfStdDev)377 CPLErr E00GRIDRasterBand::GetStatistics( int bApproxOK, int bForce,
378                                          double *pdfMin, double *pdfMax,
379                                          double *pdfMean, double *pdfStdDev )
380 {
381     E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS;
382 
383     poGDS->ReadMetadata();
384 
385     if (poGDS->bHasStats)
386     {
387         if (pdfMin)
388             *pdfMin = poGDS->dfMin;
389         if (pdfMax)
390             *pdfMax = poGDS->dfMax;
391         if (pdfMean)
392             *pdfMean = poGDS->dfMean;
393         if (pdfStdDev)
394             *pdfStdDev = poGDS->dfStddev;
395         return CE_None;
396     }
397 
398     return GDALPamRasterBand::GetStatistics(bApproxOK, bForce,
399                                             pdfMin, pdfMax,
400                                             pdfMean, pdfStdDev);
401 }
402 
403 /************************************************************************/
404 /*                           E00GRIDDataset()                           */
405 /************************************************************************/
406 
E00GRIDDataset()407 E00GRIDDataset::E00GRIDDataset()
408 {
409     e00ReadPtr = NULL;
410     fp = NULL;
411     nDataStart = 0;
412     nBytesEOL = 1;
413 
414     nPosBeforeReadLine = 0;
415     panOffsets = NULL;
416     nLastYOff = -1;
417     nMaxYOffset = -1;
418 
419     adfGeoTransform[0] = 0;
420     adfGeoTransform[1] = 1;
421     adfGeoTransform[2] = 0;
422     adfGeoTransform[3] = 0;
423     adfGeoTransform[4] = 0;
424     adfGeoTransform[5] = 1;
425 
426     dfNoData = 0;
427 
428     papszPrj = NULL;
429 
430     bHasReadMetadata = FALSE;
431 
432     bHasStats = FALSE;
433     dfMin = 0;
434     dfMax = 0;
435     dfMean = 0;
436     dfStddev = 0;
437 }
438 
439 /************************************************************************/
440 /*                           ~E00GRIDDataset()                          */
441 /************************************************************************/
442 
~E00GRIDDataset()443 E00GRIDDataset::~E00GRIDDataset()
444 
445 {
446     FlushCache();
447     if (fp)
448         VSIFCloseL(fp);
449     CSLDestroy(papszPrj);
450     E00ReadClose(e00ReadPtr);
451     CPLFree(panOffsets);
452 }
453 
454 /************************************************************************/
455 /*                             Identify()                               */
456 /************************************************************************/
457 
Identify(GDALOpenInfo * poOpenInfo)458 int E00GRIDDataset::Identify( GDALOpenInfo * poOpenInfo )
459 {
460     if (poOpenInfo->nHeaderBytes == 0)
461         return FALSE;
462 
463     if (!(EQUALN((const char*)poOpenInfo->pabyHeader, "EXP  0", 6) ||
464           EQUALN((const char*)poOpenInfo->pabyHeader, "EXP  1", 6)))
465         return FALSE;
466 
467     /* FIXME: handle GRD  3 if that ever exists ? */
468     if (strstr((const char*)poOpenInfo->pabyHeader, "GRD  2") == NULL)
469         return FALSE;
470 
471     return TRUE;
472 }
473 
474 /************************************************************************/
475 /*                            ReadNextLine()                            */
476 /************************************************************************/
477 
ReadNextLine(void * ptr)478 const char* E00GRIDDataset::ReadNextLine(void * ptr)
479 {
480     E00GRIDDataset* poDS = (E00GRIDDataset*) ptr;
481     poDS->nPosBeforeReadLine = VSIFTellL(poDS->fp);
482     return CPLReadLine2L(poDS->fp, 256, NULL);
483 }
484 
485 /************************************************************************/
486 /*                                Rewind()                              */
487 /************************************************************************/
488 
Rewind(void * ptr)489 void E00GRIDDataset::Rewind(void * ptr)
490 {
491     E00GRIDDataset* poDS = (E00GRIDDataset*) ptr;
492     VSIRewindL(poDS->fp);
493 }
494 
495 /************************************************************************/
496 /*                                Open()                                */
497 /************************************************************************/
498 
Open(GDALOpenInfo * poOpenInfo)499 GDALDataset *E00GRIDDataset::Open( GDALOpenInfo * poOpenInfo )
500 
501 {
502     int         i;
503     GDALDataType eDT = GDT_Float32;
504 
505     if (!Identify(poOpenInfo))
506         return NULL;
507 
508 /* -------------------------------------------------------------------- */
509 /*      Find dataset characteristics                                    */
510 /* -------------------------------------------------------------------- */
511     VSILFILE* fp = VSIFOpenL(poOpenInfo->pszFilename, "rb");
512     if (fp == NULL)
513         return NULL;
514 
515     if (poOpenInfo->eAccess == GA_Update)
516     {
517         CPLError( CE_Failure, CPLE_NotSupported,
518                   "The E00GRID driver does not support update access to existing"
519                   " datasets.\n" );
520         VSIFCloseL(fp);
521         return NULL;
522     }
523 
524 /* -------------------------------------------------------------------- */
525 /*      Create a corresponding GDALDataset.                             */
526 /* -------------------------------------------------------------------- */
527     E00GRIDDataset         *poDS;
528 
529     poDS = new E00GRIDDataset();
530     if (strstr((const char*)poOpenInfo->pabyHeader, "\r\n") != NULL)
531         poDS->nBytesEOL = 2;
532     poDS->fp = fp;
533 
534     const char* pszLine;
535     /* read EXP  0 or EXP  1 line */
536     pszLine = CPLReadLine2L(fp, 81, NULL);
537     if (pszLine == NULL)
538     {
539         CPLDebug("E00GRID", "Bad 1st line");
540         delete poDS;
541         return NULL;
542     }
543     int bCompressed = EQUALN(pszLine, "EXP  1", 6);
544 
545     E00ReadPtr e00ReadPtr = NULL;
546     if (bCompressed)
547     {
548         VSIRewindL(fp);
549         e00ReadPtr = E00ReadCallbackOpen(poDS,
550                                          E00GRIDDataset::ReadNextLine,
551                                          E00GRIDDataset::Rewind);
552         if (e00ReadPtr == NULL)
553         {
554             delete poDS;
555             return NULL;
556         }
557         E00ReadNextLine(e00ReadPtr);
558         poDS->e00ReadPtr = e00ReadPtr;
559     }
560 
561     /* skip GRD  2 line */
562     if (e00ReadPtr)
563         pszLine = E00ReadNextLine(e00ReadPtr);
564     else
565         pszLine = CPLReadLine2L(fp, 81, NULL);
566     if (pszLine == NULL || !EQUALN(pszLine, "GRD  2", 6))
567     {
568         CPLDebug("E00GRID", "Bad 2nd line");
569         delete poDS;
570         return NULL;
571     }
572 
573     /* read ncols, nrows and nodata value */
574     if (e00ReadPtr)
575         pszLine = E00ReadNextLine(e00ReadPtr);
576     else
577         pszLine = CPLReadLine2L(fp, 81, NULL);
578     if (pszLine == NULL || strlen(pszLine) <
579                 E00_INT_SIZE+E00_INT_SIZE+2+E00_DOUBLE_SIZE)
580     {
581         CPLDebug("E00GRID", "Bad 3rd line");
582         delete poDS;
583         return NULL;
584     }
585 
586     int nRasterXSize = atoi(pszLine);
587     int nRasterYSize = atoi(pszLine + E00_INT_SIZE);
588 
589     if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
590     {
591         delete poDS;
592         return NULL;
593     }
594 
595     if (EQUALN(pszLine + E00_INT_SIZE + E00_INT_SIZE, " 1", 2))
596         eDT = GDT_Int32;
597     else if (EQUALN(pszLine + E00_INT_SIZE + E00_INT_SIZE, " 2", 2))
598         eDT = GDT_Float32;
599     else
600     {
601         CPLDebug("E00GRID", "Unknown data type : %s", pszLine);
602     }
603 
604     double dfNoData = CPLAtof(pszLine + E00_INT_SIZE + E00_INT_SIZE + 2);
605 
606     /* read pixel size */
607     if (e00ReadPtr)
608         pszLine = E00ReadNextLine(e00ReadPtr);
609     else
610         pszLine = CPLReadLine2L(fp, 81, NULL);
611     if (pszLine == NULL || strlen(pszLine) < 2*E00_DOUBLE_SIZE)
612     {
613         CPLDebug("E00GRID", "Bad 4th line");
614         delete poDS;
615         return NULL;
616     }
617 /*
618     double dfPixelX = CPLAtof(pszLine);
619     double dfPixelY = CPLAtof(pszLine + E00_DOUBLE_SIZE);
620 */
621 
622     /* read xmin, ymin */
623     if (e00ReadPtr)
624         pszLine = E00ReadNextLine(e00ReadPtr);
625     else
626         pszLine = CPLReadLine2L(fp, 81, NULL);
627     if (pszLine == NULL || strlen(pszLine) < 2*E00_DOUBLE_SIZE)
628     {
629         CPLDebug("E00GRID", "Bad 5th line");
630         delete poDS;
631         return NULL;
632     }
633     double dfMinX = CPLAtof(pszLine);
634     double dfMinY = CPLAtof(pszLine + E00_DOUBLE_SIZE);
635 
636     /* read xmax, ymax */
637     if (e00ReadPtr)
638         pszLine = E00ReadNextLine(e00ReadPtr);
639     else
640         pszLine = CPLReadLine2L(fp, 81, NULL);
641     if (pszLine == NULL || strlen(pszLine) < 2*E00_DOUBLE_SIZE)
642     {
643         CPLDebug("E00GRID", "Bad 6th line");
644         delete poDS;
645         return NULL;
646     }
647     double dfMaxX = CPLAtof(pszLine);
648     double dfMaxY = CPLAtof(pszLine + E00_DOUBLE_SIZE);
649 
650     poDS->nRasterXSize = nRasterXSize;
651     poDS->nRasterYSize = nRasterYSize;
652     poDS->dfNoData = dfNoData;
653     poDS->adfGeoTransform[0] = dfMinX;
654     poDS->adfGeoTransform[1] = (dfMaxX - dfMinX) / nRasterXSize;
655     poDS->adfGeoTransform[2] = 0;
656     poDS->adfGeoTransform[3] = dfMaxY;
657     poDS->adfGeoTransform[4] = 0;
658     poDS->adfGeoTransform[5] = - (dfMaxY - dfMinY) / nRasterYSize;
659     poDS->nDataStart = VSIFTellL(fp);
660     if (bCompressed)
661     {
662         poDS->panOffsets = (vsi_l_offset*)
663                         VSIMalloc2(sizeof(vsi_l_offset), nRasterYSize);
664         if (poDS->panOffsets == NULL)
665         {
666             delete poDS;
667             return NULL;
668         }
669     }
670 /* -------------------------------------------------------------------- */
671 /*      Create band information objects.                                */
672 /* -------------------------------------------------------------------- */
673     poDS->nBands = 1;
674     for( i = 0; i < poDS->nBands; i++ )
675         poDS->SetBand( i+1, new E00GRIDRasterBand( poDS, i+1, eDT ) );
676 
677 /* -------------------------------------------------------------------- */
678 /*      Initialize any PAM information.                                 */
679 /* -------------------------------------------------------------------- */
680     poDS->SetDescription( poOpenInfo->pszFilename );
681     poDS->TryLoadXML();
682 
683 /* -------------------------------------------------------------------- */
684 /*      Support overviews.                                              */
685 /* -------------------------------------------------------------------- */
686     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
687     return( poDS );
688 }
689 
690 /************************************************************************/
691 /*                          GetGeoTransform()                           */
692 /************************************************************************/
693 
GetGeoTransform(double * padfTransform)694 CPLErr E00GRIDDataset::GetGeoTransform( double * padfTransform )
695 
696 {
697     memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
698 
699     return( CE_None );
700 }
701 
702 
703 /************************************************************************/
704 /*                             ReadLine()                               */
705 /************************************************************************/
706 
ReadLine()707 const char* E00GRIDDataset::ReadLine()
708 {
709     if (e00ReadPtr)
710         return E00ReadNextLine(e00ReadPtr);
711     else
712         return CPLReadLine2L(fp, 81, NULL);
713 }
714 
715 /************************************************************************/
716 /*                         GetProjectionRef()                           */
717 /************************************************************************/
718 
GetProjectionRef()719 const char* E00GRIDDataset::GetProjectionRef()
720 
721 {
722     ReadMetadata();
723     return osProjection.c_str();
724 }
725 
726 /************************************************************************/
727 /*                          ReadMetadata()                              */
728 /************************************************************************/
729 
ReadMetadata()730 void E00GRIDDataset::ReadMetadata()
731 
732 {
733     if (bHasReadMetadata)
734         return;
735 
736     bHasReadMetadata = TRUE;
737 
738     if (e00ReadPtr == NULL)
739     {
740         int nRoundedBlockXSize = ((nRasterXSize + VALS_PER_LINE - 1) /
741                                                 VALS_PER_LINE) * VALS_PER_LINE;
742         vsi_l_offset nValsToSkip =
743                                (vsi_l_offset)nRasterYSize * nRoundedBlockXSize;
744         vsi_l_offset nLinesToSkip = nValsToSkip / VALS_PER_LINE;
745         int nBytesPerLine = VALS_PER_LINE * E00_FLOAT_SIZE + nBytesEOL;
746         vsi_l_offset nPos = nDataStart + nLinesToSkip * nBytesPerLine;
747         VSIFSeekL(fp, nPos, SEEK_SET);
748     }
749     else
750     {
751         nLastYOff = -1;
752 
753         const unsigned int BUFFER_SIZE = 65536;
754         const unsigned int NEEDLE_SIZE = 3*5;
755         const unsigned int nToRead = BUFFER_SIZE - NEEDLE_SIZE;
756         char* pabyBuffer = (char*)CPLCalloc(1, BUFFER_SIZE+NEEDLE_SIZE);
757         int nRead;
758         int bEOGFound = FALSE;
759 
760         VSIFSeekL(fp, 0, SEEK_END);
761         vsi_l_offset nEndPos = VSIFTellL(fp);
762         if (nEndPos > BUFFER_SIZE)
763             nEndPos -= BUFFER_SIZE;
764         else
765             nEndPos = 0;
766         VSIFSeekL(fp, nEndPos, SEEK_SET);
767 
768 #define GOTO_NEXT_CHAR() \
769     i ++; \
770     if (pabyBuffer[i] == 13 || pabyBuffer[i] == 10) \
771     { \
772         i++; \
773         if (pabyBuffer[i] == 10) \
774             i++; \
775     } \
776 
777         while ((nRead = VSIFReadL(pabyBuffer, 1, nToRead, fp)) != 0)
778         {
779             int i;
780             for(i = 0; i < nRead; i++)
781             {
782                 if (pabyBuffer[i] == 'E')
783                 {
784                     GOTO_NEXT_CHAR();
785                     if (pabyBuffer[i] == 'O')
786                     {
787                         GOTO_NEXT_CHAR();
788                         if (pabyBuffer[i] == 'G')
789                         {
790                             GOTO_NEXT_CHAR();
791                             if (pabyBuffer[i] == '~')
792                             {
793                                 GOTO_NEXT_CHAR();
794                                 if (pabyBuffer[i] == '}')
795                                 {
796                                     bEOGFound = TRUE;
797                                     break;
798                                 }
799                             }
800                         }
801                     }
802                 }
803             }
804 
805             if (bEOGFound)
806             {
807                 VSIFSeekL(fp, VSIFTellL(fp) - nRead + i + 1, SEEK_SET);
808                 e00ReadPtr->iInBufPtr = 0;
809                 e00ReadPtr->szInBuf[0] = '\0';
810                 break;
811             }
812 
813             if (nEndPos == 0)
814                 break;
815 
816             if ((unsigned int)nRead == nToRead)
817             {
818                 memmove(pabyBuffer + nToRead, pabyBuffer, NEEDLE_SIZE);
819                 if (nEndPos >= (vsi_l_offset)nToRead)
820                     nEndPos -= nToRead;
821                 else
822                     nEndPos = 0;
823                 VSIFSeekL(fp, nEndPos, SEEK_SET);
824             }
825             else
826                 break;
827         }
828         CPLFree(pabyBuffer);
829         if (!bEOGFound)
830             return;
831     }
832 
833     const char* pszLine;
834     int bPRJFound = FALSE;
835     int bStatsFound = FALSE;
836     while((pszLine = ReadLine()) != NULL)
837     {
838         if (EQUALN(pszLine, "PRJ  2", 6))
839         {
840             bPRJFound = TRUE;
841             while((pszLine = ReadLine()) != NULL)
842             {
843                 if (EQUAL(pszLine, "EOP"))
844                 {
845                     break;
846                 }
847                 papszPrj = CSLAddString(papszPrj, pszLine);
848             }
849 
850             OGRSpatialReference oSRS;
851             if( oSRS.importFromESRI( papszPrj ) != OGRERR_NONE )
852             {
853                 CPLError( CE_Warning, CPLE_AppDefined,
854                             "Failed to parse PRJ section, ignoring." );
855             }
856             else
857             {
858                 char* pszWKT = NULL;
859                 if (oSRS.exportToWkt(&pszWKT) == OGRERR_NONE && pszWKT != NULL)
860                     osProjection = pszWKT;
861                 CPLFree(pszWKT);
862             }
863             if (bStatsFound)
864                 break;
865         }
866         else if (strcmp(pszLine, "STDV              8-1  254-1  15 3 60-1  -1  -1-1                   4-") == 0)
867         {
868             bStatsFound = TRUE;
869             pszLine = ReadLine();
870             if (pszLine)
871             {
872                 CPLString osStats = pszLine;
873                 pszLine = ReadLine();
874                 if (pszLine)
875                 {
876                     osStats += pszLine;
877                     char** papszTokens = CSLTokenizeString(osStats);
878                     if (CSLCount(papszTokens) == 4)
879                     {
880                         dfMin = CPLAtof(papszTokens[0]);
881                         dfMax = CPLAtof(papszTokens[1]);
882                         dfMean = CPLAtof(papszTokens[2]);
883                         dfStddev = CPLAtof(papszTokens[3]);
884                         bHasStats = TRUE;
885                     }
886                     CSLDestroy(papszTokens);
887                 }
888             }
889             if (bPRJFound)
890                 break;
891         }
892     }
893 }
894 
895 /************************************************************************/
896 /*                       GDALRegister_E00GRID()                         */
897 /************************************************************************/
898 
GDALRegister_E00GRID()899 void GDALRegister_E00GRID()
900 
901 {
902     GDALDriver  *poDriver;
903 
904     if( GDALGetDriverByName( "E00GRID" ) == NULL )
905     {
906         poDriver = new GDALDriver();
907 
908         poDriver->SetDescription( "E00GRID" );
909         poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
910         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
911                                    "Arc/Info Export E00 GRID" );
912         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
913                                    "frmt_various.html#E00GRID" );
914         poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "e00" );
915 
916 
917         poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
918 
919         poDriver->pfnOpen = E00GRIDDataset::Open;
920         poDriver->pfnIdentify = E00GRIDDataset::Identify;
921 
922         GetGDALDriverManager()->RegisterDriver( poDriver );
923     }
924 }
925