1 /******************************************************************************
2  *
3  * Project:  ILWIS Driver
4  * Purpose:  GDALDataset driver for ILWIS translator for read/write support.
5  * Author:   Lichun Wang, lichun@itc.nl
6  *
7  ******************************************************************************
8  * Copyright (c) 2004, ITC
9  * Copyright (c) 2008-2010, Even Rouault <even dot rouault at spatialys.com>
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 "ilwisdataset.h"
31 #include <cfloat>
32 #include <climits>
33 
34 #include <algorithm>
35 #include <limits>
36 #include <memory>
37 #include <string>
38 
39 #include "gdal_frmts.h"
40 
41 CPL_CVSID("$Id: ilwisdataset.cpp fa752ad6eabafaf630a704e1892a9d837d683cb3 2021-03-06 17:04:38 +0100 Even Rouault $")
42 
43 namespace GDAL
44 {
45 
46 // IniFile.cpp: implementation of the IniFile class.
47 //
48 //////////////////////////////////////////////////////////////////////
operator ()(const std::string & s1,const std::string & s2) const49 bool CompareAsNum::operator() (const std::string& s1, const std::string& s2) const
50 {
51     int Num1 = atoi(s1.c_str());
52     int Num2 = atoi(s2.c_str());
53     return Num1 < Num2;
54 }
55 
TrimSpaces(const std::string & input)56 static std::string TrimSpaces(const std::string& input)
57 {
58     // find first non space
59     if ( input.empty() )
60         return std::string();
61 
62     const size_t iFirstNonSpace = input.find_first_not_of(' ');
63     const size_t iFindLastSpace = input.find_last_not_of(' ');
64     if (iFirstNonSpace == std::string::npos || iFindLastSpace == std::string::npos)
65         return std::string();
66 
67     return input.substr(iFirstNonSpace, iFindLastSpace - iFirstNonSpace + 1);
68 }
69 
GetLine(VSILFILE * fil)70 static std::string GetLine(VSILFILE* fil)
71 {
72     const char *p = CPLReadLineL( fil );
73     if (p == nullptr)
74         return std::string();
75 
76     CPLString osWrk = p;
77     osWrk.Trim();
78     return std::string(osWrk);
79 }
80 
81 //////////////////////////////////////////////////////////////////////
82 // Construction/Destruction
83 //////////////////////////////////////////////////////////////////////
IniFile(const std::string & filenameIn)84 IniFile::IniFile( const std::string& filenameIn ) :
85     filename(filenameIn),
86     bChanged(false)  // Start tracking changes.
87 {
88     Load();
89 }
90 
~IniFile()91 IniFile::~IniFile()
92 {
93     if( bChanged )
94     {
95         Store();
96         bChanged = false;
97     }
98 
99     for( Sections::iterator iter = sections.begin();
100          iter != sections.end();
101          ++iter )
102     {
103         (*(*iter).second).clear();
104         delete (*iter).second;
105     }
106 
107     sections.clear();
108 }
109 
SetKeyValue(const std::string & section,const std::string & key,const std::string & value)110 void IniFile::SetKeyValue(const std::string& section, const std::string& key, const std::string& value)
111 {
112     Sections::iterator iterSect = sections.find(section);
113     if (iterSect == sections.end())
114     {
115         // Add a new section, with one new key/value entry
116         SectionEntries *entries = new SectionEntries;
117         (*entries)[key] = value;
118         sections[section] = entries;
119     }
120     else
121     {
122         // Add one new key/value entry in an existing section
123         SectionEntries *entries = (*iterSect).second;
124         (*entries)[key] = value;
125     }
126     bChanged = true;
127 }
128 
GetKeyValue(const std::string & section,const std::string & key)129 std::string IniFile::GetKeyValue(const std::string& section, const std::string& key)
130 {
131     Sections::iterator iterSect = sections.find(section);
132     if (iterSect != sections.end())
133     {
134         SectionEntries *entries = (*iterSect).second;
135         SectionEntries::iterator iterEntry = (*entries).find(key);
136         if (iterEntry != (*entries).end())
137             return (*iterEntry).second;
138     }
139 
140     return std::string();
141 }
142 
RemoveKeyValue(const std::string & section,const std::string & key)143 void IniFile::RemoveKeyValue(const std::string& section, const std::string& key)
144 {
145     Sections::iterator iterSect = sections.find(section);
146     if (iterSect != sections.end())
147     {
148         // The section exists, now erase entry "key"
149         SectionEntries *entries = (*iterSect).second;
150         (*entries).erase(key);
151         bChanged = true;
152     }
153 }
154 
RemoveSection(const std::string & section)155 void IniFile::RemoveSection(const std::string& section)
156 {
157     Sections::iterator iterSect = sections.find(section);
158     if (iterSect != sections.end())
159     {
160         // The section exists, so remove it and all its entries.
161         SectionEntries *entries = (*iterSect).second;
162         (*entries).clear();
163         sections.erase(iterSect);
164         bChanged = true;
165     }
166 }
167 
Load()168 void IniFile::Load()
169 {
170     VSILFILE *filIni = VSIFOpenL(filename.c_str(), "r");
171     if (filIni == nullptr)
172         return;
173 
174     std::string section, key, value;
175     enum ParseState { FindSection, FindKey, ReadFindKey, StoreKey, None } state
176         = FindSection;
177     std::string s;
178     while (!VSIFEofL(filIni) || !s.empty() )
179     {
180         switch (state)
181         {
182           case FindSection:
183             s = GetLine(filIni);
184             if (s.empty())
185                 continue;
186 
187             if (s[0] == '[')
188             {
189                 size_t iLast = s.find_first_of(']');
190                 if (iLast != std::string::npos)
191                 {
192                     section = s.substr(1, iLast - 1);
193                     state = ReadFindKey;
194                 }
195             }
196             else
197                 state = FindKey;
198             break;
199           case ReadFindKey:
200             s = GetLine(filIni); // fall through (no break)
201             CPL_FALLTHROUGH
202           case FindKey:
203           {
204               size_t iEqu = s.find_first_of('=');
205               if (iEqu != std::string::npos)
206               {
207                   key = s.substr(0, iEqu);
208                   value = s.substr(iEqu + 1);
209                   state = StoreKey;
210               }
211               else
212                   state = ReadFindKey;
213           }
214           break;
215           case StoreKey:
216             SetKeyValue(section, key, value);
217             state = FindSection;
218             break;
219 
220           case None:
221             // Do we need to do anything?  Perhaps this never occurs.
222             break;
223         }
224     }
225 
226     bChanged = false;
227 
228     VSIFCloseL(filIni);
229 }
230 
Store()231 void IniFile::Store()
232 {
233     VSILFILE *filIni = VSIFOpenL(filename.c_str(), "w+");
234     if (filIni == nullptr)
235         return;
236 
237     Sections::iterator iterSect;
238     for (iterSect = sections.begin(); iterSect != sections.end(); ++iterSect)
239     {
240         CPLString osLine;
241 
242         // write the section name
243         osLine.Printf( "[%s]\r\n", (*iterSect).first.c_str());
244         VSIFWriteL( osLine.c_str(), 1, osLine.size(), filIni );
245         SectionEntries *entries = (*iterSect).second;
246         SectionEntries::iterator iterEntry;
247         for (iterEntry = (*entries).begin(); iterEntry != (*entries).end(); ++iterEntry)
248         {
249             std::string key = (*iterEntry).first;
250             osLine.Printf( "%s=%s\r\n",
251                            TrimSpaces(key).c_str(), (*iterEntry).second.c_str());
252             VSIFWriteL( osLine.c_str(), 1, osLine.size(), filIni );
253         }
254 
255         VSIFWriteL( "\r\n", 1, 2, filIni );
256     }
257 
258     VSIFCloseL( filIni );
259 }
260 
261 // End of the implementation of IniFile class. ///////////////////////
262 //////////////////////////////////////////////////////////////////////
263 
intConv(double x)264 static int intConv(double x) {
265     if ((x == rUNDEF) || (x > INT_MAX) || (x < INT_MIN))
266         return iUNDEF;
267 
268     return (int)floor(x + 0.5);
269 }
270 
ReadElement(const std::string & section,const std::string & entry,const std::string & filename)271 std::string ReadElement(const std::string& section, const std::string& entry, const std::string& filename)
272 {
273     if (section.empty())
274         return std::string();
275     if (entry.empty())
276         return std::string();
277     if (filename.empty())
278         return std::string();
279 
280     IniFile MyIniFile (filename);
281 
282     return MyIniFile.GetKeyValue(section, entry);
283 }
284 
WriteElement(const std::string & sSection,const std::string & sEntry,const std::string & fn,const std::string & sValue)285 bool WriteElement(const std::string& sSection, const std::string& sEntry,
286                   const std::string& fn, const std::string& sValue)
287 {
288     if (fn.empty())
289         return false;
290 
291     IniFile MyIniFile (fn);
292 
293     MyIniFile.SetKeyValue(sSection, sEntry, sValue);
294     return true;
295 }
296 
WriteElement(const std::string & sSection,const std::string & sEntry,const std::string & fn,int nValue)297 bool WriteElement(const std::string& sSection, const std::string&sEntry,
298                   const std::string& fn, int nValue)
299 {
300     if (fn.empty())
301         return false;
302 
303     char strdouble[45];
304     snprintf(strdouble, sizeof(strdouble), "%d", nValue);
305     std::string sValue = std::string(strdouble);
306     return WriteElement(sSection, sEntry, fn, sValue);
307 }
308 
WriteElement(const std::string & sSection,const std::string & sEntry,const std::string & fn,double dValue)309 bool WriteElement(const std::string& sSection, const std::string&sEntry,
310                   const std::string& fn, double dValue)
311 {
312     if (fn.empty())
313         return false;
314 
315     char strdouble[45];
316     CPLsnprintf(strdouble, sizeof(strdouble), "%.6f", dValue);
317     std::string sValue = std::string(strdouble);
318     return WriteElement(sSection, sEntry, fn, sValue);
319 }
320 
GetRowCol(const std::string & str,int & Row,int & Col)321 static CPLErr GetRowCol(const std::string& str,int &Row, int &Col)
322 {
323     std::string delimStr = " ,;";
324     size_t iPos = str.find_first_of(delimStr);
325     if (iPos != std::string::npos)
326     {
327         Row = atoi(str.substr(0, iPos).c_str());
328     }
329     else
330     {
331         CPLError( CE_Failure, CPLE_AppDefined,
332                   "Read of RowCol failed.");
333         return CE_Failure;
334     }
335     iPos = str.find_last_of(delimStr);
336     if (iPos != std::string::npos)
337     {
338         Col = atoi(str.substr(iPos+1, str.length()-iPos).c_str());
339     }
340     return CE_None;
341 }
342 
343 //! Converts ILWIS data type to GDAL data type.
ILWIS2GDALType(ilwisStoreType stStoreType)344 static GDALDataType ILWIS2GDALType(ilwisStoreType stStoreType)
345 {
346   GDALDataType eDataType = GDT_Unknown;
347 
348   switch (stStoreType){
349     case stByte: {
350       eDataType = GDT_Byte;
351       break;
352     }
353     case stInt:{
354       eDataType = GDT_Int16;
355       break;
356     }
357     case stLong:{
358       eDataType = GDT_Int32;
359       break;
360     }
361     case stFloat:{
362       eDataType = GDT_Float32;
363       break;
364     }
365     case stReal:{
366       eDataType = GDT_Float64;
367       break;
368     }
369     default: {
370       break;
371     }
372   }
373 
374   return eDataType;
375 }
376 
377 //Determine store type of ILWIS raster
GDALType2ILWIS(GDALDataType type)378 static std::string GDALType2ILWIS(GDALDataType type)
379 {
380     std::string sStoreType = "";
381     switch( type )
382     {
383       case GDT_Byte:{
384           sStoreType = "Byte";
385           break;
386       }
387       case GDT_Int16:
388       case GDT_UInt16:{
389           sStoreType = "Int";
390           break;
391       }
392       case GDT_Int32:
393       case GDT_UInt32:{
394           sStoreType = "Long";
395           break;
396       }
397       case GDT_Float32:{
398           sStoreType = "Float";
399           break;
400       }
401       case GDT_Float64:{
402           sStoreType = "Real";
403           break;
404       }
405       default:{
406           CPLError( CE_Failure, CPLE_NotSupported,
407                     "Data type %s not supported by ILWIS format.\n",
408                     GDALGetDataTypeName( type ) );
409           break;
410       }
411     }
412     return sStoreType;
413 }
414 
GetStoreType(std::string pszFileName,ilwisStoreType & stStoreType)415 static CPLErr GetStoreType(std::string pszFileName, ilwisStoreType &stStoreType)
416 {
417     std::string st = ReadElement("MapStore", "Type", pszFileName.c_str());
418 
419     if( EQUAL(st.c_str(),"byte"))
420     {
421         stStoreType = stByte;
422     }
423     else if( EQUAL(st.c_str(),"int"))
424     {
425         stStoreType = stInt;
426     }
427     else if( EQUAL(st.c_str(),"long"))
428     {
429         stStoreType = stLong;
430     }
431     else if( EQUAL(st.c_str(),"float"))
432     {
433         stStoreType = stFloat;
434     }
435     else if( EQUAL(st.c_str(),"real"))
436     {
437         stStoreType = stReal;
438     }
439     else
440     {
441         CPLError( CE_Failure, CPLE_AppDefined,
442                   "Unsupported ILWIS store type.");
443         return CE_Failure;
444     }
445     return CE_None;
446 }
447 
ILWISDataset()448 ILWISDataset::ILWISDataset() :
449     pszProjection(CPLStrdup("")),
450     bGeoDirty(FALSE),
451     bNewDataset(FALSE)
452 {
453     adfGeoTransform[0] = 0.0;
454     adfGeoTransform[1] = 1.0;
455     adfGeoTransform[2] = 0.0;
456     adfGeoTransform[3] = 0.0;
457     adfGeoTransform[4] = 0.0;
458     adfGeoTransform[5] = 1.0;
459 }
460 
461 /************************************************************************/
462 /*                  ~ILWISDataset()                                     */
463 /************************************************************************/
464 
~ILWISDataset()465 ILWISDataset::~ILWISDataset()
466 
467 {
468     ILWISDataset::FlushCache();
469     CPLFree( pszProjection );
470 }
471 
472 /************************************************************************/
473 /*                        CollectTransformCoef()                        */
474 /*                                                                      */
475 /*      Collect the geotransform, support for the GeoRefCorners         */
476 /*      georeferencing only; We use the extent of the coordinates       */
477 /*      to determine the pixelsize in X and Y direction. Then calculate */
478 /*      the transform coefficients from the extent and pixelsize        */
479 /************************************************************************/
480 
CollectTransformCoef(std::string & pszRefName)481 void ILWISDataset::CollectTransformCoef(std::string& pszRefName)
482 
483 {
484     pszRefName = "";
485     std::string georef;
486     if ( EQUAL(pszFileType.c_str(),"Map") )
487         georef = ReadElement("Map", "GeoRef", osFileName);
488     else
489         georef = ReadElement("MapList", "GeoRef", osFileName);
490 
491     //Capture the geotransform, only if the georef is not 'none',
492     //otherwise, the default transform should be returned.
493     if( !georef.empty() && !EQUAL(georef.c_str(),"none"))
494     {
495         //Form the geo-referencing name
496         std::string pszBaseName = std::string(CPLGetBasename(georef.c_str()) );
497         std::string pszPath = std::string(CPLGetPath( osFileName ));
498         pszRefName = std::string(CPLFormFilename(pszPath.c_str(),
499                                             pszBaseName.c_str(),"grf" ));
500 
501         //Check the geo-reference type,support for the GeoRefCorners only
502         std::string georeftype = ReadElement("GeoRef", "Type", pszRefName);
503         if (EQUAL(georeftype.c_str(),"GeoRefCorners"))
504         {
505             //Center or top-left corner of the pixel approach?
506             std::string IsCorner = ReadElement("GeoRefCorners", "CornersOfCorners", pszRefName);
507 
508             //Collect the extent of the coordinates
509             std::string sMinX = ReadElement("GeoRefCorners", "MinX", pszRefName);
510             std::string sMinY = ReadElement("GeoRefCorners", "MinY", pszRefName);
511             std::string sMaxX = ReadElement("GeoRefCorners", "MaxX", pszRefName);
512             std::string sMaxY = ReadElement("GeoRefCorners", "MaxY", pszRefName);
513 
514             //Calculate pixel size in X and Y direction from the extent
515             double deltaX = CPLAtof(sMaxX.c_str()) - CPLAtof(sMinX.c_str());
516             double deltaY = CPLAtof(sMaxY.c_str()) - CPLAtof(sMinY.c_str());
517 
518             double PixelSizeX = deltaX / (double)nRasterXSize;
519             double PixelSizeY = deltaY / (double)nRasterYSize;
520 
521             if (EQUAL(IsCorner.c_str(),"Yes"))
522             {
523                 adfGeoTransform[0] = CPLAtof(sMinX.c_str());
524                 adfGeoTransform[3] = CPLAtof(sMaxY.c_str());
525             }
526             else
527             {
528                 adfGeoTransform[0] = CPLAtof(sMinX.c_str()) - PixelSizeX/2.0;
529                 adfGeoTransform[3] = CPLAtof(sMaxY.c_str()) + PixelSizeY/2.0;
530             }
531 
532             adfGeoTransform[1] = PixelSizeX;
533             adfGeoTransform[2] = 0.0;
534             adfGeoTransform[4] = 0.0;
535             adfGeoTransform[5] = -PixelSizeY;
536         }
537     }
538 }
539 
540 /************************************************************************/
541 /*                         WriteGeoReference()                          */
542 /*                                                                      */
543 /*      Try to write a geo-reference file for the dataset to create     */
544 /************************************************************************/
545 
WriteGeoReference()546 CPLErr ILWISDataset::WriteGeoReference()
547 {
548     // Check whether we should write out a georeference file.
549     // Dataset must be north up.
550     if( adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0
551         || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0
552         || adfGeoTransform[4] != 0.0 || fabs(adfGeoTransform[5]) != 1.0 )
553     {
554         SetGeoTransform( adfGeoTransform ); // is this needed?
555         if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0)
556         {
557             int   nXSize = GetRasterXSize();
558             int   nYSize = GetRasterYSize();
559             double dLLLat = (adfGeoTransform[3]
560                       + nYSize * adfGeoTransform[5] );
561             double dLLLong = (adfGeoTransform[0] );
562             double dURLat  = (adfGeoTransform[3] );
563             double dURLong = (adfGeoTransform[0]
564                        + nXSize * adfGeoTransform[1] );
565 
566             std::string grFileName = CPLResetExtension(osFileName, "grf" );
567             WriteElement("Ilwis", "Type", grFileName, "GeoRef");
568             WriteElement("GeoRef", "lines", grFileName, nYSize);
569             WriteElement("GeoRef", "columns", grFileName, nXSize);
570             WriteElement("GeoRef", "Type", grFileName, "GeoRefCorners");
571             WriteElement("GeoRefCorners", "CornersOfCorners", grFileName, "Yes");
572             WriteElement("GeoRefCorners", "MinX", grFileName, dLLLong);
573             WriteElement("GeoRefCorners", "MinY", grFileName, dLLLat);
574             WriteElement("GeoRefCorners", "MaxX", grFileName, dURLong);
575             WriteElement("GeoRefCorners", "MaxY", grFileName, dURLat);
576 
577             //Re-write the GeoRef property to raster ODF
578             //Form band file name
579             std::string sBaseName = std::string(CPLGetBasename(osFileName) );
580             std::string sPath = std::string(CPLGetPath(osFileName));
581             if (nBands == 1)
582             {
583                 WriteElement("Map", "GeoRef", osFileName, sBaseName + ".grf");
584             }
585             else
586             {
587                 for( int iBand = 0; iBand < nBands; iBand++ )
588                 {
589                     if (iBand == 0)
590                       WriteElement("MapList", "GeoRef", osFileName, sBaseName + ".grf");
591                     char szName[100];
592                     snprintf(szName, sizeof(szName), "%s_band_%d", sBaseName.c_str(),iBand + 1 );
593                     std::string pszODFName = std::string(CPLFormFilename(sPath.c_str(),szName,"mpr"));
594                     WriteElement("Map", "GeoRef", pszODFName, sBaseName + ".grf");
595                 }
596             }
597         }
598     }
599     return CE_None;
600 }
601 
602 /************************************************************************/
603 /*                          GetProjectionRef()                          */
604 /************************************************************************/
605 
_GetProjectionRef()606 const char *ILWISDataset::_GetProjectionRef()
607 
608 {
609    return pszProjection;
610 }
611 
612 /************************************************************************/
613 /*                           SetProjection()                            */
614 /************************************************************************/
615 
_SetProjection(const char * pszNewProjection)616 CPLErr ILWISDataset::_SetProjection( const char * pszNewProjection )
617 
618 {
619     CPLFree( pszProjection );
620     pszProjection = CPLStrdup( pszNewProjection );
621     bGeoDirty = TRUE;
622 
623     return CE_None;
624 }
625 
626 /************************************************************************/
627 /*                          GetGeoTransform()                           */
628 /************************************************************************/
629 
GetGeoTransform(double * padfTransform)630 CPLErr ILWISDataset::GetGeoTransform( double * padfTransform )
631 
632 {
633     memcpy( padfTransform,  adfGeoTransform, sizeof(double) * 6 );
634     return CE_None;
635 }
636 
637 /************************************************************************/
638 /*                          SetGeoTransform()                           */
639 /************************************************************************/
640 
SetGeoTransform(double * padfTransform)641 CPLErr ILWISDataset::SetGeoTransform( double * padfTransform )
642 
643 {
644     memmove( adfGeoTransform, padfTransform, sizeof(double)*6 );
645 
646     if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0)
647         bGeoDirty = TRUE;
648 
649     return CE_None;
650 }
651 
CheckASCII(unsigned char * buf,int size)652 static bool CheckASCII(unsigned char * buf, int size)
653 {
654     for (int i = 0; i < size; ++i)
655     {
656         if (!isascii(buf[i]))
657             return false;
658     }
659 
660     return true;
661 }
662 /************************************************************************/
663 /*                       Open()                                         */
664 /************************************************************************/
665 
Open(GDALOpenInfo * poOpenInfo)666 GDALDataset *ILWISDataset::Open( GDALOpenInfo * poOpenInfo )
667 
668 {
669 /* -------------------------------------------------------------------- */
670 /*      Does this look like an ILWIS file                               */
671 /* -------------------------------------------------------------------- */
672     if( poOpenInfo->nHeaderBytes < 1 )
673         return nullptr;
674 
675     std::string sExt = CPLGetExtension( poOpenInfo->pszFilename );
676     if (!EQUAL(sExt.c_str(),"mpr") && !EQUAL(sExt.c_str(),"mpl"))
677         return nullptr;
678 
679     if (!CheckASCII(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes))
680         return nullptr;
681 
682     std::string ilwistype = ReadElement("Ilwis", "Type", poOpenInfo->pszFilename);
683     if( ilwistype.empty())
684         return nullptr;
685 
686     std::string sFileType;  // map or map list
687     int    iBandCount;
688     std::string mapsize;
689     const std::string maptype = ReadElement("BaseMap", "Type", poOpenInfo->pszFilename);
690     //const std::string sBaseName = std::string(CPLGetBasename(poOpenInfo->pszFilename) );
691     const std::string sPath = std::string(CPLGetPath( poOpenInfo->pszFilename));
692 
693     //Verify whether it is a map list or a map
694     if( EQUAL(ilwistype.c_str(),"MapList") )
695     {
696         sFileType = std::string("MapList");
697         std::string sMaps = ReadElement("MapList", "Maps", poOpenInfo->pszFilename);
698         iBandCount = atoi(sMaps.c_str());
699         mapsize = ReadElement("MapList", "Size", poOpenInfo->pszFilename);
700         for (int iBand = 0; iBand < iBandCount; ++iBand )
701         {
702             //Form the band file name.
703             char cBandName[45];
704             snprintf( cBandName, sizeof(cBandName), "Map%d", iBand);
705             std::string sBandName = ReadElement("MapList", std::string(cBandName), poOpenInfo->pszFilename);
706             std::string pszBandBaseName = std::string(CPLGetBasename(sBandName.c_str()) );
707             std::string pszBandPath = std::string(CPLGetPath( sBandName.c_str()));
708             if ( pszBandPath.empty() )
709             {
710                 sBandName = std::string(CPLFormFilename(sPath.c_str(),
711                                                    pszBandBaseName.c_str(),"mpr" ));
712             }
713             // Verify the file extension, it must be an ILWIS raw data file
714             // with extension .mp#, otherwise, unsupported
715             // This drive only supports a map list which stores a set
716             // of ILWIS raster maps,
717             std::string sMapStoreName = ReadElement("MapStore", "Data", sBandName);
718             sExt = CPLGetExtension( sMapStoreName.c_str() );
719             if ( !STARTS_WITH_CI(sExt.c_str(), "mp#"))
720             {
721                 CPLError( CE_Failure, CPLE_AppDefined,
722                           "Unsupported ILWIS data file. \n"
723                           "can't treat as raster.\n" );
724                 return nullptr;
725             }
726         }
727     }
728     else if(EQUAL(ilwistype.c_str(),"BaseMap") && EQUAL(maptype.c_str(),"Map"))
729     {
730         sFileType = "Map";
731         iBandCount = 1;
732         mapsize = ReadElement("Map", "Size", poOpenInfo->pszFilename);
733         //std::string sMapType = ReadElement("Map", "Type", poOpenInfo->pszFilename);
734         ilwisStoreType stStoreType;
735         if (
736             GetStoreType(std::string(poOpenInfo->pszFilename), stStoreType) != CE_None )
737         {
738             //CPLError( CE_Failure, CPLE_AppDefined,
739             //          "Unsupported ILWIS data file. \n"
740             //          "can't treat as raster.\n" );
741             return nullptr;
742         }
743     }
744     else
745     {
746         CPLError( CE_Failure, CPLE_AppDefined,
747                   "Unsupported ILWIS data file. \n"
748                   "can't treat as raster.\n" );
749         return nullptr;
750     }
751 
752 /* -------------------------------------------------------------------- */
753 /*      Create a corresponding GDALDataset.                             */
754 /* -------------------------------------------------------------------- */
755     ILWISDataset *poDS = new ILWISDataset();
756 
757 /* -------------------------------------------------------------------- */
758 /*      Capture raster size from ILWIS file (.mpr).                     */
759 /* -------------------------------------------------------------------- */
760     int Row = 0;
761     int Col = 0;
762     if ( GetRowCol(mapsize, Row, Col) != CE_None)
763     {
764         delete poDS;
765         return nullptr;
766     }
767     if( !GDALCheckDatasetDimensions(Col, Row) )
768     {
769         delete poDS;
770         return nullptr;
771     }
772     poDS->nRasterXSize = Col;
773     poDS->nRasterYSize = Row;
774     poDS->osFileName = poOpenInfo->pszFilename;
775     poDS->pszFileType = sFileType;
776 /* -------------------------------------------------------------------- */
777 /*      Create band information objects.                                */
778 /* -------------------------------------------------------------------- */
779     //poDS->pszFileName = new char[strlen(poOpenInfo->pszFilename) + 1];
780     poDS->nBands = iBandCount;
781     for( int iBand = 0; iBand < poDS->nBands; iBand++ )
782     {
783         poDS->SetBand( iBand+1, new ILWISRasterBand( poDS, iBand+1, std::string() ) );
784     }
785 
786 /* -------------------------------------------------------------------- */
787 /*      Collect the geotransform coefficients                           */
788 /* -------------------------------------------------------------------- */
789     std::string pszGeoRef;
790     poDS->CollectTransformCoef(pszGeoRef);
791 
792 /* -------------------------------------------------------------------- */
793 /*      Translation from ILWIS coordinate system definition             */
794 /* -------------------------------------------------------------------- */
795     if( !pszGeoRef.empty() && !EQUAL(pszGeoRef.c_str(),"none"))
796     {
797 
798         // Fetch coordinate system
799         std::string csy = ReadElement("GeoRef", "CoordSystem", pszGeoRef);
800         std::string pszProj;
801 
802         if( !csy.empty() && !EQUAL(csy.c_str(),"unknown.csy"))
803         {
804 
805             //Form the coordinate system file name
806             if( !(STARTS_WITH_CI(csy.c_str(), "latlon.csy")) &&
807                 !(STARTS_WITH_CI(csy.c_str(), "LatlonWGS84.csy")))
808             {
809                 std::string pszBaseName = std::string(CPLGetBasename(csy.c_str()) );
810                 std::string pszPath = std::string(CPLGetPath( poDS->osFileName ));
811                 csy = std::string(CPLFormFilename(pszPath.c_str(),
812                                              pszBaseName.c_str(),"csy" ));
813                 pszProj = ReadElement("CoordSystem", "Type", csy);
814                 if (pszProj.empty() ) //default to projection
815                     pszProj = "Projection";
816             }
817             else
818             {
819                 pszProj = "LatLon";
820             }
821 
822             if( (STARTS_WITH_CI(pszProj.c_str(), "LatLon")) ||
823                 (STARTS_WITH_CI(pszProj.c_str(), "Projection")))
824                 poDS->ReadProjection( csy );
825         }
826     }
827 
828 /* -------------------------------------------------------------------- */
829 /*      Initialize any PAM information.                                 */
830 /* -------------------------------------------------------------------- */
831     poDS->SetDescription( poOpenInfo->pszFilename );
832     poDS->TryLoadXML();
833 
834 /* -------------------------------------------------------------------- */
835 /*      Check for external overviews.                                   */
836 /* -------------------------------------------------------------------- */
837     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() );
838 
839     return poDS;
840 }
841 
842 /************************************************************************/
843 /*                             FlushCache()                             */
844 /************************************************************************/
845 
FlushCache()846 void ILWISDataset::FlushCache()
847 
848 {
849     GDALDataset::FlushCache();
850 
851     if( bGeoDirty == TRUE )
852     {
853         WriteGeoReference();
854         WriteProjection();
855         bGeoDirty = FALSE;
856     }
857 }
858 
859 /************************************************************************/
860 /*                               Create()                               */
861 /*                                                                      */
862 /*      Create a new ILWIS file.                                         */
863 /************************************************************************/
864 
Create(const char * pszFilename,int nXSize,int nYSize,int nBands,GDALDataType eType,CPL_UNUSED char ** papszParamList)865 GDALDataset *ILWISDataset::Create(const char* pszFilename,
866                                   int nXSize, int nYSize,
867                                   int nBands, GDALDataType eType,
868                                   CPL_UNUSED char** papszParamList)
869 {
870 /* -------------------------------------------------------------------- */
871 /*      Verify input options.                                           */
872 /* -------------------------------------------------------------------- */
873     if( eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Int32
874         && eType != GDT_Float32 && eType != GDT_Float64 && eType != GDT_UInt16 && eType != GDT_UInt32)
875     {
876         CPLError( CE_Failure, CPLE_AppDefined,
877                   "Attempt to create ILWIS dataset with an illegal\n"
878                   "data type (%s).\n",
879                   GDALGetDataTypeName(eType) );
880 
881         return nullptr;
882     }
883 
884 /* -------------------------------------------------------------------- */
885 /*      Translate the data type.                                        */
886 /*      Determine store type of ILWIS raster                            */
887 /* -------------------------------------------------------------------- */
888     std::string sDomain= "value.dom";
889     double stepsize = 1;
890     std::string sStoreType = GDALType2ILWIS(eType);
891     if( EQUAL(sStoreType.c_str(),""))
892         return nullptr;
893     else if( EQUAL(sStoreType.c_str(),"Real") || EQUAL(sStoreType.c_str(),"float"))
894         stepsize = 0;
895 
896     const std::string pszBaseName = std::string(CPLGetBasename( pszFilename ));
897     const std::string pszPath = std::string(CPLGetPath( pszFilename ));
898 
899 /* -------------------------------------------------------------------- */
900 /*      Write out object definition file for each band                  */
901 /* -------------------------------------------------------------------- */
902     std::string pszODFName;
903     std::string pszDataBaseName;
904     std::string pszFileName;
905 
906     char strsize[45];
907     snprintf(strsize, sizeof(strsize), "%d %d", nYSize, nXSize);
908 
909     //Form map/maplist name.
910     std::unique_ptr<IniFile> globalFile;
911     if ( nBands == 1 )
912     {
913         pszODFName = std::string(CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpr"));
914         pszDataBaseName = pszBaseName;
915         pszFileName = CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpr");
916     }
917     else
918     {
919         pszFileName = CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpl");
920         auto iniFile = new IniFile(std::string(pszFileName));
921         iniFile->SetKeyValue("Ilwis", "Type", "MapList");
922         iniFile->SetKeyValue("MapList", "GeoRef", "none.grf");
923         iniFile->SetKeyValue("MapList", "Size", std::string(strsize));
924         iniFile->SetKeyValue("MapList", "Maps", CPLSPrintf("%d", nBands));
925         globalFile.reset(iniFile);
926     }
927 
928     for( int iBand = 0; iBand < nBands; iBand++ )
929     {
930         if ( nBands > 1 )
931         {
932             char szBandName[100];
933             snprintf(szBandName, sizeof(szBandName), "%s_band_%d", pszBaseName.c_str(),iBand + 1 );
934             pszODFName = std::string(szBandName) + ".mpr";
935             pszDataBaseName = std::string(szBandName);
936             snprintf(szBandName, sizeof(szBandName), "Map%d", iBand);
937             globalFile->SetKeyValue("MapList", std::string(szBandName), pszODFName);
938             pszODFName = CPLFormFilename(pszPath.c_str(),pszDataBaseName.c_str(),"mpr");
939         }
940 /* -------------------------------------------------------------------- */
941 /*      Write data definition per band (.mpr)                           */
942 /* -------------------------------------------------------------------- */
943 
944         IniFile ODFFile (pszODFName);
945 
946         ODFFile.SetKeyValue("Ilwis", "Type", "BaseMap");
947         ODFFile.SetKeyValue("BaseMap", "Type", "Map");
948         ODFFile.SetKeyValue("Map", "Type", "MapStore");
949 
950         ODFFile.SetKeyValue("BaseMap", "Domain", sDomain);
951         std::string pszDataName = pszDataBaseName + ".mp#";
952         ODFFile.SetKeyValue("MapStore", "Data", pszDataName);
953         ODFFile.SetKeyValue("MapStore", "Structure", "Line");
954         // sStoreType is used by ILWISRasterBand constructor to determine eDataType
955         ODFFile.SetKeyValue("MapStore", "Type", sStoreType);
956 
957         // For now write-out a "Range" that is as broad as possible.
958         // If later a better range is found (by inspecting metadata in the source dataset),
959         // the "Range" will be overwritten by a better version.
960         double adfMinMax[2] = {-9999999.9, 9999999.9};
961         char strdouble[45];
962         CPLsnprintf(strdouble, sizeof(strdouble), "%.3f:%.3f:%3f:offset=0", adfMinMax[0], adfMinMax[1],stepsize);
963         std::string range(strdouble);
964         ODFFile.SetKeyValue("BaseMap", "Range", range);
965 
966         ODFFile.SetKeyValue("Map", "GeoRef", "none.grf");
967         ODFFile.SetKeyValue("Map", "Size", std::string(strsize));
968 
969 /* -------------------------------------------------------------------- */
970 /*      Try to create the data file.                                    */
971 /* -------------------------------------------------------------------- */
972         pszDataName = CPLResetExtension(pszODFName.c_str(), "mp#" );
973 
974         VSILFILE  *fp = VSIFOpenL( pszDataName.c_str(), "wb" );
975 
976         if( fp == nullptr )
977         {
978             CPLError( CE_Failure, CPLE_OpenFailed,
979                       "Unable to create file %s.\n", pszDataName.c_str() );
980             return nullptr;
981         }
982         VSIFCloseL( fp );
983     }
984 
985     globalFile.reset();
986 
987     ILWISDataset *poDS = new ILWISDataset();
988     poDS->nRasterXSize = nXSize;
989     poDS->nRasterYSize = nYSize;
990     poDS->nBands = nBands;
991     poDS->eAccess = GA_Update;
992     poDS->bNewDataset = TRUE;
993     poDS->SetDescription(pszFilename);
994     poDS->osFileName = pszFileName;
995     poDS->pszIlwFileName = std::string(pszFileName);
996     if ( nBands == 1 )
997         poDS->pszFileType = "Map";
998     else
999         poDS->pszFileType = "MapList";
1000 
1001 /* -------------------------------------------------------------------- */
1002 /*      Create band information objects.                                */
1003 /* -------------------------------------------------------------------- */
1004 
1005     for( int iBand = 1; iBand <= poDS->nBands; iBand++ )
1006     {
1007         std::string sBandName;
1008         if( poDS->nBands > 1 )
1009         {
1010             sBandName = CPLSPrintf("%s_band_%d.mpr", pszBaseName.c_str(), iBand);
1011         }
1012         poDS->SetBand( iBand, new ILWISRasterBand( poDS, iBand, sBandName ) );
1013     }
1014 
1015     return poDS;
1016 }
1017 
1018 /************************************************************************/
1019 /*                             CreateCopy()                             */
1020 /************************************************************************/
1021 
1022 GDALDataset *
CreateCopy(const char * pszFilename,GDALDataset * poSrcDS,int,char ** papszOptions,GDALProgressFunc pfnProgress,void * pProgressData)1023 ILWISDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
1024                           int /* bStrict */, char ** papszOptions,
1025                           GDALProgressFunc pfnProgress, void * pProgressData )
1026 
1027 {
1028     if( !pfnProgress( 0.0, nullptr, pProgressData ) )
1029         return nullptr;
1030 
1031     const int nXSize = poSrcDS->GetRasterXSize();
1032     const int nYSize = poSrcDS->GetRasterYSize();
1033     const int nBands = poSrcDS->GetRasterCount();
1034 
1035 /* -------------------------------------------------------------------- */
1036 /*      Create the basic dataset.                                       */
1037 /* -------------------------------------------------------------------- */
1038     GDALDataType eType = GDT_Byte;
1039     for( int iBand = 0; iBand < nBands; iBand++ )
1040     {
1041         GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 );
1042         eType = GDALDataTypeUnion( eType, poBand->GetRasterDataType() );
1043     }
1044 
1045     ILWISDataset *poDS = (ILWISDataset *) Create( pszFilename,
1046                                                   poSrcDS->GetRasterXSize(),
1047                                                   poSrcDS->GetRasterYSize(),
1048                                                   nBands,
1049                                                   eType, papszOptions );
1050 
1051     if( poDS == nullptr )
1052         return nullptr;
1053     const std::string pszBaseName = std::string(CPLGetBasename( pszFilename ));
1054     const std::string pszPath = std::string(CPLGetPath( pszFilename ));
1055 
1056 /* -------------------------------------------------------------------- */
1057 /*  Copy and geo-transform and projection information.                  */
1058 /* -------------------------------------------------------------------- */
1059     double adfGeoTransform[6];
1060     std::string georef = "none.grf";
1061 
1062     // Check whether we should create georeference file.
1063     // Source dataset must be north up.
1064     if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None
1065         && (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0
1066             || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0
1067             || adfGeoTransform[4] != 0.0 || fabs(adfGeoTransform[5]) != 1.0))
1068     {
1069         poDS->SetGeoTransform( adfGeoTransform );
1070         if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0)
1071             georef = pszBaseName + ".grf";
1072     }
1073 
1074     const char *pszProj = poSrcDS->GetProjectionRef();
1075     if( pszProj != nullptr && strlen(pszProj) > 0 )
1076         poDS->SetProjection( pszProj );
1077 
1078 /* -------------------------------------------------------------------- */
1079 /*      Create the output raster files for each band                    */
1080 /* -------------------------------------------------------------------- */
1081 
1082     for( int iBand = 0; iBand < nBands; iBand++ )
1083     {
1084         VSILFILE *fpData = nullptr;
1085 
1086         GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 );
1087         ILWISRasterBand *desBand = (ILWISRasterBand *) poDS->GetRasterBand( iBand+1 );
1088 
1089 /* -------------------------------------------------------------------- */
1090 /*      Translate the data type.                                        */
1091 /* -------------------------------------------------------------------- */
1092         int nLineSize =  nXSize * GDALGetDataTypeSize(eType) / 8;
1093 
1094         //Determine the nodata value
1095         int bHasNoDataValue;
1096         double dNoDataValue = poBand->GetNoDataValue(&bHasNoDataValue);
1097 
1098         //Determine store type of ILWIS raster
1099         const std::string sStoreType = GDALType2ILWIS( eType );
1100         double stepsize = 1;
1101         if( EQUAL(sStoreType.c_str(),""))
1102             return nullptr;
1103         else if( EQUAL(sStoreType.c_str(),"Real") || EQUAL(sStoreType.c_str(),"float"))
1104             stepsize = 0;
1105 
1106         //Form the image file name, create the object definition file.
1107         std::string pszODFName;
1108         //std::string pszDataBaseName;
1109         if (nBands == 1)
1110         {
1111             pszODFName = std::string(CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpr"));
1112             //pszDataBaseName = pszBaseName;
1113         }
1114         else
1115         {
1116             char szName[100];
1117             snprintf(szName, sizeof(szName), "%s_band_%d", pszBaseName.c_str(),iBand + 1 );
1118             pszODFName = std::string(CPLFormFilename(pszPath.c_str(),szName,"mpr"));
1119             //pszDataBaseName = std::string(szName);
1120         }
1121 /* -------------------------------------------------------------------- */
1122 /*      Write data definition file for each band (.mpr)                 */
1123 /* -------------------------------------------------------------------- */
1124 
1125         double adfMinMax[2];
1126         int    bGotMin, bGotMax;
1127 
1128         adfMinMax[0] = poBand->GetMinimum( &bGotMin );
1129         adfMinMax[1] = poBand->GetMaximum( &bGotMax );
1130         if( ! (bGotMin && bGotMax) )
1131             GDALComputeRasterMinMax((GDALRasterBandH)poBand, FALSE, adfMinMax);
1132         if ((!CPLIsNan(adfMinMax[0])) && CPLIsFinite(adfMinMax[0]) && (!CPLIsNan(adfMinMax[1])) && CPLIsFinite(adfMinMax[1]))
1133         {
1134             // only write a range if we got a correct one from the source dataset (otherwise ILWIS can't show the map properly)
1135             char strdouble[45];
1136             CPLsnprintf(strdouble, sizeof(strdouble), "%.3f:%.3f:%3f:offset=0", adfMinMax[0], adfMinMax[1],stepsize);
1137             std::string range = std::string(strdouble);
1138             WriteElement("BaseMap", "Range", pszODFName, range);
1139         }
1140         WriteElement("Map", "GeoRef", pszODFName, georef);
1141 
1142 /* -------------------------------------------------------------------- */
1143 /*      Loop over image, copy the image data.                           */
1144 /* -------------------------------------------------------------------- */
1145         //For file name for raw data, and create binary files.
1146         //std::string pszDataFileName = CPLResetExtension(pszODFName.c_str(), "mp#" );
1147 
1148         fpData = desBand->fpRaw;
1149         if( fpData == nullptr )
1150         {
1151             CPLError( CE_Failure, CPLE_OpenFailed,
1152                       "Attempt to create file `%s' failed.\n",
1153                       pszFilename );
1154             return nullptr;
1155         }
1156 
1157         GByte *pData = (GByte *) CPLMalloc( nLineSize );
1158 
1159         CPLErr eErr = CE_None;
1160         for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ )
1161         {
1162             eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
1163                                      pData, nXSize, 1, eType,
1164                                      0, 0, nullptr );
1165 
1166             if( eErr == CE_None )
1167             {
1168                 if (bHasNoDataValue)
1169                 {
1170                     // pData may have entries with value = dNoDataValue
1171                     // ILWIS uses a fixed value for nodata, depending on the data-type
1172                     // Therefore translate the NoDataValue from each band to ILWIS
1173                     for (int iCol = 0; iCol < nXSize; iCol++ )
1174                     {
1175                         if( EQUAL(sStoreType.c_str(),"Byte"))
1176                         {
1177                             if ( ((GByte * )pData)[iCol] == dNoDataValue )
1178                                 (( GByte * )pData)[iCol] = 0;
1179                         }
1180                         else if( EQUAL(sStoreType.c_str(),"Int"))
1181                         {
1182                             if ( ((GInt16 * )pData)[iCol] == dNoDataValue )
1183                                 (( GInt16 * )pData)[iCol] = shUNDEF;
1184                         }
1185                         else if( EQUAL(sStoreType.c_str(),"Long"))
1186                         {
1187                             if ( ((GInt32 * )pData)[iCol] == dNoDataValue )
1188                                 (( GInt32 * )pData)[iCol] = iUNDEF;
1189                         }
1190                         else if( EQUAL(sStoreType.c_str(),"float"))
1191                         {
1192                             if ((((float * )pData)[iCol] == dNoDataValue ) || (CPLIsNan((( float * )pData)[iCol])))
1193                                 (( float * )pData)[iCol] = flUNDEF;
1194                         }
1195                         else if( EQUAL(sStoreType.c_str(),"Real"))
1196                         {
1197                             if ((((double * )pData)[iCol] == dNoDataValue ) || (CPLIsNan((( double * )pData)[iCol])))
1198                                 (( double * )pData)[iCol] = rUNDEF;
1199                         }
1200                     }
1201                 }
1202                 int iSize = static_cast<int>(VSIFWriteL( pData, 1, nLineSize, desBand->fpRaw ));
1203                 if ( iSize < 1 )
1204                 {
1205                     CPLFree( pData );
1206                     //CPLFree( pData32 );
1207                     CPLError( CE_Failure, CPLE_FileIO,
1208                               "Write of file failed with fwrite error.");
1209                     return nullptr;
1210                 }
1211             }
1212             if( !pfnProgress(iLine / (nYSize * nBands), nullptr, pProgressData ) )
1213                 return nullptr;
1214         }
1215         VSIFFlushL( fpData );
1216         CPLFree( pData );
1217     }
1218 
1219     poDS->FlushCache();
1220 
1221     if( !pfnProgress( 1.0, nullptr, pProgressData ) )
1222     {
1223         CPLError( CE_Failure, CPLE_UserInterrupt,
1224                   "User terminated" );
1225         delete poDS;
1226 
1227         GDALDriver *poILWISDriver =
1228             (GDALDriver *) GDALGetDriverByName( "ILWIS" );
1229         poILWISDriver->Delete( pszFilename );
1230         return nullptr;
1231     }
1232 
1233     poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
1234 
1235     return poDS;
1236 }
1237 
1238 /************************************************************************/
1239 /*                       ILWISRasterBand()                              */
1240 /************************************************************************/
1241 
ILWISRasterBand(ILWISDataset * poDSIn,int nBandIn,const std::string & sBandNameIn)1242 ILWISRasterBand::ILWISRasterBand( ILWISDataset *poDSIn, int nBandIn,
1243                                   const std::string& sBandNameIn) :
1244     fpRaw(nullptr),
1245     nSizePerPixel(0)
1246 {
1247     poDS = poDSIn;
1248     nBand = nBandIn;
1249 
1250     std::string sBandName;
1251     if( EQUAL(poDSIn->pszFileType.c_str(), "Map"))
1252     {
1253         sBandName = std::string(poDSIn->osFileName);
1254     }
1255     else  // Map list.
1256     {
1257         // Form the band name.
1258         char cBandName[45];
1259         snprintf( cBandName, sizeof(cBandName), "Map%d", nBand-1);
1260         if( sBandNameIn.empty() )
1261         {
1262             sBandName = ReadElement("MapList", std::string(cBandName), std::string(poDSIn->osFileName));
1263         }
1264         else
1265         {
1266             sBandName = sBandNameIn;
1267         }
1268         std::string sInputPath = std::string(CPLGetPath( poDSIn->osFileName));
1269         std::string sBandPath = std::string(CPLGetPath( sBandName.c_str()));
1270         std::string sBandBaseName = std::string(CPLGetBasename( sBandName.c_str()));
1271         if ( sBandPath.empty() )
1272             sBandName = std::string(CPLFormFilename(sInputPath.c_str(),sBandBaseName.c_str(),"mpr" ));
1273         else
1274           sBandName = std::string(CPLFormFilename(sBandPath.c_str(),sBandBaseName.c_str(),"mpr" ));
1275     }
1276 
1277     if( poDSIn->bNewDataset )
1278     {
1279       // Called from Create():
1280       // eDataType is defaulted to GDT_Byte by GDALRasterBand::GDALRasterBand
1281       // Here we set it to match the value of sStoreType (that was set in ILWISDataset::Create)
1282       // Unfortunately we can't take advantage of the ILWIS "ValueRange" object that would use
1283       // the most compact storeType possible, without going through all values.
1284         GetStoreType(sBandName, psInfo.stStoreType);
1285         eDataType = ILWIS2GDALType(psInfo.stStoreType);
1286     }
1287     else // Called from Open(), thus convert ILWIS type from ODF to eDataType
1288     {
1289         GetILWISInfo(sBandName);
1290     }
1291 
1292     nBlockXSize = poDS->GetRasterXSize();
1293     nBlockYSize = 1;
1294     switch( psInfo.stStoreType )
1295     {
1296       case stByte:
1297         nSizePerPixel = GDALGetDataTypeSizeBytes(GDT_Byte);
1298         break;
1299       case stInt:
1300         nSizePerPixel = GDALGetDataTypeSizeBytes(GDT_Int16) ;
1301         break;
1302       case stLong:
1303         nSizePerPixel = GDALGetDataTypeSizeBytes(GDT_Int32);
1304         break;
1305       case stFloat:
1306         nSizePerPixel = GDALGetDataTypeSizeBytes(GDT_Float32);
1307         break;
1308       case stReal:
1309         nSizePerPixel = GDALGetDataTypeSizeBytes(GDT_Float64);
1310         break;
1311     }
1312     ILWISOpen(sBandName);
1313 }
1314 
1315 /************************************************************************/
1316 /*                          ~ILWISRasterBand()                          */
1317 /************************************************************************/
1318 
~ILWISRasterBand()1319 ILWISRasterBand::~ILWISRasterBand()
1320 
1321 {
1322     if( fpRaw != nullptr )
1323     {
1324         VSIFCloseL( fpRaw );
1325         fpRaw = nullptr;
1326     }
1327 }
1328 
1329 /************************************************************************/
1330 /*                             ILWISOpen()                             */
1331 /************************************************************************/
ILWISOpen(const std::string & pszFileName)1332 void ILWISRasterBand::ILWISOpen( const std::string& pszFileName )
1333 {
1334     ILWISDataset* dataset = (ILWISDataset*) poDS;
1335     std::string pszDataFile
1336         = std::string(CPLResetExtension( pszFileName.c_str(), "mp#" ));
1337 
1338     fpRaw = VSIFOpenL( pszDataFile.c_str(), (dataset->eAccess == GA_Update) ? "rb+" : "rb");
1339 }
1340 
1341 /************************************************************************/
1342 /*                 ReadValueDomainProperties()                          */
1343 /************************************************************************/
1344 // Helper function for GetILWISInfo, to avoid code-duplication
1345 // Unfortunately with side-effect (changes members psInfo and eDataType)
ReadValueDomainProperties(const std::string & pszFileName)1346 void ILWISRasterBand::ReadValueDomainProperties(const std::string& pszFileName)
1347 {
1348     std::string rangeString = ReadElement("BaseMap", "Range", pszFileName.c_str());
1349     psInfo.vr = ValueRange(rangeString);
1350     double rStep = psInfo.vr.get_rStep();
1351     if ( rStep != 0 )
1352     {
1353         psInfo.bUseValueRange = true; // use ILWIS ValueRange object to convert from "raw" to "value"
1354         double rMin = psInfo.vr.get_rLo();
1355         double rMax = psInfo.vr.get_rHi();
1356         if (rStep >= INT_MIN && rStep <= INT_MAX && rStep - (int)rStep == 0.0) // Integer values
1357         {
1358             if ( rMin >= 0 && rMax <= UCHAR_MAX)
1359               eDataType =  GDT_Byte;
1360             else if ( rMin >= SHRT_MIN && rMax <= SHRT_MAX)
1361               eDataType =  GDT_Int16;
1362             else if ( rMin >= 0 && rMax <= USHRT_MAX)
1363               eDataType =  GDT_UInt16;
1364             else if ( rMin >= INT_MIN && rMax <= INT_MAX)
1365               eDataType =  GDT_Int32;
1366             else if ( rMin >= 0 && rMax <= UINT_MAX)
1367               eDataType =  GDT_UInt32;
1368             else
1369               eDataType = GDT_Float64;
1370         }
1371         else // Floating point values
1372         {
1373             if ((rMin >= std::numeric_limits<float>::lowest()) &&
1374                 (rMax <= std::numeric_limits<float>::max()) &&
1375                 (fabs(rStep) >= FLT_EPSILON)) // is "float" good enough?
1376               eDataType = GDT_Float32;
1377             else
1378               eDataType = GDT_Float64;
1379         }
1380     }
1381     else
1382     {
1383         if (psInfo.stStoreType == stFloat) // is "float" good enough?
1384           eDataType = GDT_Float32;
1385         else
1386           eDataType = GDT_Float64;
1387     }
1388 }
1389 
1390 /************************************************************************/
1391 /*                       GetILWISInfo()                                 */
1392 /************************************************************************/
1393 // Calculates members psInfo and eDataType
GetILWISInfo(const std::string & pszFileName)1394 CPLErr ILWISRasterBand::GetILWISInfo(const std::string& pszFileName)
1395 {
1396     // Fill the psInfo struct with defaults.
1397     // Get the store type from the ODF
1398     if (GetStoreType(pszFileName, psInfo.stStoreType) != CE_None)
1399     {
1400         return CE_Failure;
1401     }
1402     psInfo.bUseValueRange = false;
1403     psInfo.stDomain = "";
1404 
1405     // ILWIS has several (currently 22) predefined "system-domains", that influence the data-type
1406     // The user can also create domains. The possible types for these are "class", "identifier", "bool" and "value"
1407     // The last one has Type=DomainValue
1408     // Here we make an effort to determine the most-compact gdal-type (eDataType) that is suitable
1409     // for the data in the current ILWIS band.
1410     // First check against all predefined domain names (the "system-domains")
1411     // If no match is found, read the domain ODF from disk, and get its type
1412     // We have hardcoded the system domains here, because ILWIS may not be installed, and even if it is,
1413     // we don't know where (thus it is useless to attempt to read a system-domain-file).
1414 
1415     std::string domName = ReadElement("BaseMap", "Domain", pszFileName.c_str());
1416     std::string pszBaseName = std::string(CPLGetBasename( domName.c_str() ));
1417     std::string pszPath = std::string(CPLGetPath( pszFileName.c_str() ));
1418 
1419     // Check against all "system-domains"
1420     if ( EQUAL(pszBaseName.c_str(),"value") // is it a system domain with Type=DomainValue?
1421         || EQUAL(pszBaseName.c_str(),"count")
1422         || EQUAL(pszBaseName.c_str(),"distance")
1423         || EQUAL(pszBaseName.c_str(),"min1to1")
1424         || EQUAL(pszBaseName.c_str(),"nilto1")
1425         || EQUAL(pszBaseName.c_str(),"noaa")
1426         || EQUAL(pszBaseName.c_str(),"perc")
1427         || EQUAL(pszBaseName.c_str(),"radar") )
1428     {
1429         ReadValueDomainProperties(pszFileName);
1430     }
1431     else if( EQUAL(pszBaseName.c_str(),"bool")
1432              || EQUAL(pszBaseName.c_str(),"byte")
1433              || EQUAL(pszBaseName.c_str(),"bit")
1434              || EQUAL(pszBaseName.c_str(),"image")
1435              || EQUAL(pszBaseName.c_str(),"colorcmp")
1436              || EQUAL(pszBaseName.c_str(),"flowdirection")
1437              || EQUAL(pszBaseName.c_str(),"hortonratio")
1438              || EQUAL(pszBaseName.c_str(),"yesno") )
1439     {
1440         eDataType = GDT_Byte;
1441         if( EQUAL(pszBaseName.c_str(),"image")
1442             || EQUAL(pszBaseName.c_str(),"colorcmp"))
1443             psInfo.stDomain = pszBaseName;
1444     }
1445     else if( EQUAL(pszBaseName.c_str(),"color")
1446              || EQUAL(pszBaseName.c_str(),"none" )
1447              || EQUAL(pszBaseName.c_str(),"coordbuf")
1448              || EQUAL(pszBaseName.c_str(),"binary")
1449              || EQUAL(pszBaseName.c_str(),"string") )
1450     {
1451         CPLError( CE_Failure, CPLE_AppDefined,
1452                   "Unsupported ILWIS domain type.");
1453         return CE_Failure;
1454     }
1455     else
1456     {
1457         // No match found. Assume it is a self-created domain. Read its type and decide the GDAL type.
1458         std::string pszDomainFileName = std::string(CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"dom" ));
1459         std::string domType = ReadElement("Domain", "Type", pszDomainFileName.c_str());
1460         if( EQUAL(domType.c_str(),"domainvalue") ) // is it a self-created domain of type=DomainValue?
1461         {
1462             ReadValueDomainProperties(pszFileName);
1463         }
1464         else if((!EQUAL(domType.c_str(),"domainbit"))
1465                 && (!EQUAL(domType.c_str(),"domainstring"))
1466                 && (!EQUAL(domType.c_str(),"domaincolor"))
1467                 && (!EQUAL(domType.c_str(),"domainbinary"))
1468                 && (!EQUAL(domType.c_str(),"domaincoordBuf"))
1469                 && (!EQUAL(domType.c_str(),"domaincoord")))
1470         {
1471             // Type is "DomainClass", "DomainBool" or "DomainIdentifier".
1472             // For now we set the GDAL storeType be the same as the ILWIS storeType
1473             // The user will have to convert the classes manually.
1474             eDataType = ILWIS2GDALType(psInfo.stStoreType);
1475         }
1476         else
1477         {
1478             CPLError( CE_Failure, CPLE_AppDefined,
1479                       "Unsupported ILWIS domain type.");
1480             return CE_Failure;
1481         }
1482     }
1483 
1484     return CE_None;
1485 }
1486 
1487 /** This driver defines a Block to be the entire raster; The method reads
1488     each line as a block. it reads the data into pImage.
1489 
1490     @param nBlockXOff This must be zero for this driver
1491     @param pImage Dump the data here
1492 
1493     @return A CPLErr code. This implementation returns a CE_Failure if the
1494     block offsets are non-zero, If successful, returns CE_None. */
1495 /************************************************************************/
1496 /*                             IReadBlock()                             */
1497 /************************************************************************/
IReadBlock(CPL_UNUSED int nBlockXOff,int nBlockYOff,void * pImage)1498 CPLErr ILWISRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, int nBlockYOff,
1499                                     void * pImage )
1500 {
1501     // pImage is empty; this function fills it with data from fpRaw
1502     // (ILWIS data to foreign data)
1503 
1504     // If the x block offset is non-zero, something is wrong.
1505     CPLAssert( nBlockXOff == 0 );
1506 
1507     int nBlockSize =  nBlockXSize * nBlockYSize * nSizePerPixel;
1508     if( fpRaw == nullptr )
1509     {
1510         CPLError( CE_Failure, CPLE_OpenFailed,
1511                   "Failed to open ILWIS data file.");
1512         return CE_Failure;
1513     }
1514 
1515 /* -------------------------------------------------------------------- */
1516 /*      Handle the case of a strip in a writable file that doesn't      */
1517 /*      exist yet, but that we want to read.  Just set to zeros and     */
1518 /*      return.                                                         */
1519 /* -------------------------------------------------------------------- */
1520     ILWISDataset* poIDS = (ILWISDataset*) poDS;
1521 
1522 #ifdef notdef
1523     if( poIDS->bNewDataset && (poIDS->eAccess == GA_Update))
1524     {
1525         FillWithNoData(pImage);
1526         return CE_None;
1527     }
1528 #endif
1529 
1530     VSIFSeekL( fpRaw, nBlockSize*nBlockYOff, SEEK_SET );
1531     void *pData = (char *)CPLMalloc(nBlockSize);
1532     if (VSIFReadL( pData, 1, nBlockSize, fpRaw ) < 1)
1533     {
1534         if( poIDS->bNewDataset )
1535         {
1536             FillWithNoData(pImage);
1537             return CE_None;
1538         }
1539         else
1540         {
1541             CPLFree( pData );
1542             CPLError( CE_Failure, CPLE_FileIO,
1543                       "Read of file failed with fread error.");
1544             return CE_Failure;
1545         }
1546     }
1547 
1548     // Copy the data from pData to pImage, and convert the store-type
1549     // The data in pData has store-type = psInfo.stStoreType
1550     // The data in pImage has store-type = eDataType
1551     // They may not match, because we have chosen the most compact store-type,
1552     // and for GDAL this may be different than for ILWIS.
1553 
1554     switch (psInfo.stStoreType)
1555     {
1556       case stByte:
1557         for( int iCol = 0; iCol < nBlockXSize; iCol++ )
1558         {
1559           double rV = psInfo.bUseValueRange ? psInfo.vr.rValue(((GByte *) pData)[iCol]) : ((GByte *) pData)[iCol];
1560           SetValue(pImage, iCol, rV);
1561         }
1562         break;
1563       case stInt:
1564         for( int iCol = 0; iCol < nBlockXSize; iCol++ )
1565         {
1566           double rV = psInfo.bUseValueRange ? psInfo.vr.rValue(((GInt16 *) pData)[iCol]) : ((GInt16 *) pData)[iCol];
1567           SetValue(pImage, iCol, rV);
1568         }
1569       break;
1570       case stLong:
1571         for( int iCol = 0; iCol < nBlockXSize; iCol++ )
1572         {
1573           double rV = psInfo.bUseValueRange ? psInfo.vr.rValue(((GInt32 *) pData)[iCol]) : ((GInt32 *) pData)[iCol];
1574           SetValue(pImage, iCol, rV);
1575         }
1576         break;
1577       case stFloat:
1578         for( int iCol = 0; iCol < nBlockXSize; iCol++ )
1579           ((float *) pImage)[iCol] = ((float *) pData)[iCol];
1580         break;
1581       case stReal:
1582         for( int iCol = 0; iCol < nBlockXSize; iCol++ )
1583           ((double *) pImage)[iCol] = ((double *) pData)[iCol];
1584         break;
1585       default:
1586         CPLAssert(false);
1587     }
1588 
1589     // Officially we should also translate "nodata" values, but at this point
1590     // we can't tell what's the "nodata" value of the destination (foreign) dataset
1591 
1592     CPLFree( pData );
1593 
1594     return CE_None;
1595 }
1596 
SetValue(void * pImage,int i,double rV)1597 void ILWISRasterBand::SetValue(void *pImage, int i, double rV) {
1598     switch ( eDataType ) {
1599     case GDT_Byte:
1600       ((GByte *)pImage)[i] = (GByte) rV;
1601       break;
1602     case GDT_UInt16:
1603       ((GUInt16 *) pImage)[i] = (GUInt16) rV;
1604       break;
1605     case GDT_Int16:
1606       ((GInt16 *) pImage)[i] = (GInt16) rV;
1607       break;
1608     case GDT_UInt32:
1609       ((GUInt32 *) pImage)[i] = (GUInt32) rV;
1610       break;
1611     case GDT_Int32:
1612       ((GInt32 *) pImage)[i] = (GInt32) rV;
1613       break;
1614     case GDT_Float32:
1615       ((float *) pImage)[i] = (float) rV;
1616       break;
1617     case GDT_Float64:
1618       ((double *) pImage)[i] = rV;
1619       break;
1620     default:
1621       CPLAssert(false);
1622     }
1623 }
1624 
GetValue(void * pImage,int i)1625 double ILWISRasterBand::GetValue(void *pImage, int i) {
1626   double rV = 0; // Does GDAL have an official nodata value?
1627     switch ( eDataType ) {
1628     case GDT_Byte:
1629       rV = ((GByte *)pImage)[i];
1630       break;
1631     case GDT_UInt16:
1632       rV = ((GUInt16 *) pImage)[i];
1633       break;
1634     case GDT_Int16:
1635       rV = ((GInt16 *) pImage)[i];
1636       break;
1637     case GDT_UInt32:
1638       rV = ((GUInt32 *) pImage)[i];
1639       break;
1640     case GDT_Int32:
1641       rV = ((GInt32 *) pImage)[i];
1642       break;
1643     case GDT_Float32:
1644       rV = ((float *) pImage)[i];
1645       break;
1646     case GDT_Float64:
1647       rV = ((double *) pImage)[i];
1648       break;
1649     default:
1650       CPLAssert(false);
1651     }
1652     return rV;
1653 }
1654 
FillWithNoData(void * pImage)1655 void ILWISRasterBand::FillWithNoData(void * pImage)
1656 {
1657     if (psInfo.stStoreType == stByte)
1658         memset(pImage, 0, nBlockXSize * nBlockYSize);
1659     else
1660     {
1661         switch (psInfo.stStoreType)
1662         {
1663           case stInt:
1664             ((GInt16*)pImage)[0] = shUNDEF;
1665             break;
1666           case stLong:
1667             ((GInt32*)pImage)[0] = iUNDEF;
1668             break;
1669           case stFloat:
1670             ((float*)pImage)[0] = flUNDEF;
1671             break;
1672           case stReal:
1673             ((double*)pImage)[0] = rUNDEF;
1674             break;
1675           default: // should there be handling for stByte?
1676             break;
1677         }
1678         int iItemSize = GDALGetDataTypeSize(eDataType) / 8;
1679         for (int i = 1; i < nBlockXSize * nBlockYSize; ++i)
1680             memcpy( ((char*)pImage) + iItemSize * i, (char*)pImage + iItemSize * (i - 1), iItemSize);
1681     }
1682 }
1683 
1684 /************************************************************************/
1685 /*                            IWriteBlock()                             */
1686 /*                                                                      */
1687 /************************************************************************/
1688 
IWriteBlock(CPL_UNUSED int nBlockXOff,int nBlockYOff,void * pImage)1689 CPLErr ILWISRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
1690                                     void* pImage)
1691 {
1692     // pImage has data; this function reads this data and stores it to fpRaw
1693     // (foreign data to ILWIS data)
1694 
1695     // Note that this function will not overwrite existing data in fpRaw, but
1696     // it will "fill gaps" marked by "nodata" values
1697 
1698     ILWISDataset* dataset = (ILWISDataset*) poDS;
1699 
1700     CPLAssert( dataset != nullptr
1701                && nBlockXOff == 0
1702                && nBlockYOff >= 0
1703                && pImage != nullptr );
1704 
1705     int nXSize = dataset->GetRasterXSize();
1706     int nBlockSize = nBlockXSize * nBlockYSize * nSizePerPixel;
1707     void *pData = CPLMalloc(nBlockSize);
1708 
1709     VSIFSeekL( fpRaw, nBlockSize * nBlockYOff, SEEK_SET );
1710 
1711     bool fDataExists = (VSIFReadL( pData, 1, nBlockSize, fpRaw ) >= 1);
1712 
1713     // Copy the data from pImage to pData, and convert the store-type
1714     // The data in pData has store-type = psInfo.stStoreType
1715     // The data in pImage has store-type = eDataType
1716     // They may not match, because we have chosen the most compact store-type,
1717     // and for GDAL this may be different than for ILWIS.
1718 
1719     if( fDataExists )
1720     {
1721         // fpRaw (thus pData) already has data
1722         // Take care to not overwrite it
1723         // thus only fill in gaps (nodata values)
1724         switch (psInfo.stStoreType)
1725         {
1726           case stByte:
1727             for (int iCol = 0; iCol < nXSize; iCol++ )
1728                 if ((( GByte * )pData)[iCol] == 0)
1729                 {
1730                     double rV = GetValue(pImage, iCol);
1731                     (( GByte * )pData)[iCol] = (GByte)
1732                         (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV);
1733                 }
1734             break;
1735           case stInt:
1736             for (int iCol = 0; iCol < nXSize; iCol++ )
1737                 if ((( GInt16 * )pData)[iCol] == shUNDEF)
1738                 {
1739                     double rV = GetValue(pImage, iCol);
1740                     (( GInt16 * )pData)[iCol] = (GInt16)
1741                         (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV);
1742                 }
1743             break;
1744           case stLong:
1745             for (int iCol = 0; iCol < nXSize; iCol++ )
1746                 if ((( GInt32 * )pData)[iCol] == iUNDEF)
1747                 {
1748                     double rV = GetValue(pImage, iCol);
1749                     (( GInt32 * )pData)[iCol] = (GInt32)
1750                         (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV);
1751                 }
1752             break;
1753           case stFloat:
1754             for (int iCol = 0; iCol < nXSize; iCol++ )
1755                 if ((( float * )pData)[iCol] == flUNDEF)
1756                     (( float * )pData)[iCol] = ((float* )pImage)[iCol];
1757             break;
1758           case stReal:
1759             for (int iCol = 0; iCol < nXSize; iCol++ )
1760                 if ((( double * )pData)[iCol] == rUNDEF)
1761                     (( double * )pData)[iCol] = ((double* )pImage)[iCol];
1762             break;
1763         }
1764     }
1765     else
1766     {
1767         // fpRaw (thus pData) is still empty, just write the data
1768         switch (psInfo.stStoreType)
1769         {
1770           case stByte:
1771             for (int iCol = 0; iCol < nXSize; iCol++ )
1772             {
1773                 double rV = GetValue(pImage, iCol);
1774                 (( GByte * )pData)[iCol] = (GByte)
1775                     (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV);
1776             }
1777             break;
1778           case stInt:
1779             for (int iCol = 0; iCol < nXSize; iCol++ )
1780             {
1781                 double rV = GetValue(pImage, iCol);
1782                 (( GInt16 * )pData)[iCol] = (GInt16)
1783                     (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV);
1784             }
1785             break;
1786           case stLong:
1787             for (int iCol = 0; iCol < nXSize; iCol++ )
1788             {
1789                 double rV = GetValue(pImage, iCol);
1790                 ((GInt32 *)pData)[iCol] = (GInt32)
1791                     (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV);
1792             }
1793             break;
1794           case stFloat:
1795             for (int iCol = 0; iCol < nXSize; iCol++ )
1796                 (( float * )pData)[iCol] = ((float* )pImage)[iCol];
1797             break;
1798           case stReal:
1799             for (int iCol = 0; iCol < nXSize; iCol++ )
1800                 (( double * )pData)[iCol] = ((double* )pImage)[iCol];
1801             break;
1802         }
1803     }
1804 
1805     // Officially we should also translate "nodata" values, but at this point
1806     // we can't tell what's the "nodata" value of the source (foreign) dataset
1807 
1808     VSIFSeekL( fpRaw, nBlockSize * nBlockYOff, SEEK_SET );
1809 
1810     if (VSIFWriteL( pData, 1, nBlockSize, fpRaw ) < 1)
1811     {
1812         CPLFree( pData );
1813         CPLError( CE_Failure, CPLE_FileIO,
1814                   "Write of file failed with fwrite error.");
1815         return CE_Failure;
1816     }
1817 
1818     CPLFree( pData );
1819     return CE_None;
1820 }
1821 
1822 /************************************************************************/
1823 /*                           GetNoDataValue()                           */
1824 /************************************************************************/
GetNoDataValue(int * pbSuccess)1825 double ILWISRasterBand::GetNoDataValue( int *pbSuccess )
1826 
1827 {
1828     if( pbSuccess )
1829         *pbSuccess = TRUE;
1830 
1831     if( eDataType == GDT_Float64 )
1832         return rUNDEF;
1833     if( eDataType == GDT_Int32)
1834         return iUNDEF;
1835     if( eDataType == GDT_Int16)
1836         return shUNDEF;
1837     if( eDataType == GDT_Float32)
1838         return flUNDEF;
1839     if( pbSuccess &&
1840              (EQUAL(psInfo.stDomain.c_str(),"image")
1841               || EQUAL(psInfo.stDomain.c_str(),"colorcmp")))
1842     {
1843         *pbSuccess = FALSE;
1844     }
1845 
1846     // TODO: Defaults to pbSuccess TRUE.  Is the unhandled case really success?
1847     return 0.0;
1848 }
1849 
1850 /************************************************************************/
1851 /*                      ValueRange()                                    */
1852 /************************************************************************/
1853 
doubleConv(const char * s)1854 static double doubleConv(const char* s)
1855 {
1856     if (s == nullptr) return rUNDEF;
1857     char *begin = const_cast<char*>(s);
1858 
1859     // skip leading spaces; strtol will return 0 on a std::string with only spaces
1860     // which is not what we want
1861     while (isspace((unsigned char)*begin)) ++begin;
1862 
1863     if (strlen(begin) == 0) return rUNDEF;
1864     errno = 0;
1865     char *endptr = nullptr;
1866     const double r = CPLStrtod(begin, &endptr);
1867     if ((0 == *endptr) && (errno==0))
1868         return r;
1869     while (*endptr != 0) { // check trailing spaces
1870         if (*endptr != ' ')
1871             return rUNDEF;
1872         endptr++;
1873     }
1874     return r;
1875 }
1876 
ValueRange(const std::string & sRng)1877 ValueRange::ValueRange( const std::string& sRng ) :
1878     _rLo(0.0),
1879     _rHi(0.0),
1880     _rStep(0.0),
1881     _iDec(0),
1882     _r0(0.0),
1883     iRawUndef(0),
1884     _iWidth(0),
1885     st(stByte)
1886 {
1887     char* sRange = new char[sRng.length() + 1];
1888     for( unsigned int i = 0; i < sRng.length(); ++i )
1889         sRange[i] = sRng[i];
1890     sRange[sRng.length()] = 0;
1891 
1892     char *p1 = strchr(sRange, ':');
1893     if( nullptr == p1 )
1894     {
1895         delete[] sRange;
1896         init();
1897         return;
1898     }
1899 
1900     char *p3 = strstr(sRange, ",offset=");
1901     if( nullptr == p3 )
1902         p3 = strstr(sRange, ":offset=");
1903     _r0 = rUNDEF;
1904     if( nullptr != p3 )
1905     {
1906         _r0 = doubleConv(p3+8);
1907         *p3 = 0;
1908     }
1909     char *p2 = strrchr(sRange, ':');
1910     _rStep = 1;
1911     if( p1 != p2 )
1912     { // step
1913         _rStep = doubleConv(p2+1);
1914         *p2 = 0;
1915     }
1916 
1917     p2 = strchr(sRange, ':');
1918     if( p2 != nullptr )
1919     {
1920         *p2 = 0;
1921         _rLo = CPLAtof(sRange);
1922         _rHi = CPLAtof(p2+1);
1923     }
1924     else
1925     {
1926         _rLo = CPLAtof(sRange);
1927         _rHi = _rLo;
1928     }
1929     init(_r0);
1930 
1931     delete [] sRange;
1932 }
1933 
ValueRange(double min,double max)1934 ValueRange::ValueRange( double min, double max )  // step = 1
1935 {
1936     _rLo = min;
1937     _rHi = max;
1938     _rStep = 1;
1939     init();
1940 }
1941 
ValueRange(double min,double max,double step)1942 ValueRange::ValueRange( double min, double max, double step )
1943 {
1944     _rLo = min;
1945     _rHi = max;
1946     _rStep = step;
1947     init();
1948 }
1949 
stNeeded(unsigned int iNr)1950 static ilwisStoreType stNeeded(unsigned int iNr)
1951 {
1952     if (iNr <= 256)
1953         return stByte;
1954     if (iNr <= SHRT_MAX)
1955         return stInt;
1956     return stLong;
1957 }
1958 
init()1959 void ValueRange::init()
1960 {
1961     init(rUNDEF);
1962 }
1963 
init(double rRaw0)1964 void ValueRange::init( double rRaw0 )
1965 {
1966         _iDec = 0;
1967         if (_rStep < 0)
1968             _rStep = 0;
1969         double r = _rStep;
1970         if (r <= 1e-20)
1971             _iDec = 3;
1972         else while (r - floor(r) > 1e-20) {
1973             r *= 10;
1974             _iDec++;
1975             if (_iDec > 10)
1976                 break;
1977         }
1978 
1979         short iBeforeDec = 1;
1980         double rMax = std::max(fabs(get_rLo()), fabs(get_rHi()));
1981         if (rMax != 0)
1982             iBeforeDec = (short)floor(log10(rMax)) + 1;
1983         if (get_rLo() < 0)
1984             iBeforeDec++;
1985         _iWidth = (short) (iBeforeDec + _iDec);
1986         if (_iDec > 0)
1987             _iWidth++;
1988         if (_iWidth > 12)
1989             _iWidth = 12;
1990         if (_rStep < 1e-06)
1991         {
1992             st = stReal;
1993             _rStep = 0;
1994         }
1995         else {
1996             r = get_rHi() - get_rLo();
1997             if (r <= UINT_MAX) {
1998                 r /= _rStep;
1999                 r += 1;
2000             }
2001             r += 1;
2002             if (r > INT_MAX)
2003                 st = stReal;
2004             else {
2005                 st = stNeeded((unsigned int)floor(r+0.5));
2006                 if (st < stByte)
2007                     st = stByte;
2008             }
2009         }
2010         if (rUNDEF != rRaw0)
2011             _r0 = rRaw0;
2012         else {
2013             _r0 = 0;
2014             if (st <= stByte)
2015                 _r0 = -1;
2016         }
2017         if (st > stInt)
2018             iRawUndef = iUNDEF;
2019         else if (st == stInt)
2020             iRawUndef = shUNDEF;
2021         else
2022             iRawUndef = 0;
2023 }
2024 
ToString() const2025 std::string ValueRange::ToString() const
2026 {
2027     char buffer[200];
2028     if (fabs(get_rLo()) > 1.0e20 || fabs(get_rHi()) > 1.0e20)
2029         CPLsnprintf(buffer, sizeof(buffer), "%g:%g:%f:offset=%g", get_rLo(), get_rHi(), get_rStep(), get_rRaw0());
2030     else if (get_iDec() >= 0)
2031         CPLsnprintf(buffer, sizeof(buffer), "%.*f:%.*f:%.*f:offset=%.0f", get_iDec(), get_rLo(), get_iDec(), get_rHi(), get_iDec(), get_rStep(), get_rRaw0());
2032     else
2033         CPLsnprintf(buffer, sizeof(buffer), "%f:%f:%f:offset=%.0f", get_rLo(), get_rHi(), get_rStep(), get_rRaw0());
2034     return std::string(buffer);
2035 }
2036 
rValue(int iRawIn) const2037 double ValueRange::rValue(int iRawIn) const
2038 {
2039     if (iRawIn == iUNDEF || iRawIn == iRawUndef)
2040         return rUNDEF;
2041     double rVal = iRawIn + _r0;
2042     rVal *= _rStep;
2043     if (get_rLo() == get_rHi())
2044         return rVal;
2045     const double rEpsilon = _rStep == 0.0 ? 1e-6 : _rStep / 3.0; // avoid any rounding problems with an epsilon directly based on the
2046     // the stepsize
2047     if ((rVal - get_rLo() < -rEpsilon) || (rVal - get_rHi() > rEpsilon))
2048         return rUNDEF;
2049     return rVal;
2050 }
2051 
iRaw(double rValueIn) const2052 int ValueRange::iRaw(double rValueIn) const
2053 {
2054     if (rValueIn == rUNDEF) // || !fContains(rValue))
2055         return iUNDEF;
2056     const double rEpsilon = _rStep == 0.0 ? 1e-6 : _rStep / 3.0;
2057     if (rValueIn - get_rLo() < -rEpsilon) // take a little rounding tolerance
2058         return iUNDEF;
2059     else if (rValueIn - get_rHi() > rEpsilon) // take a little rounding tolerance
2060         return iUNDEF;
2061     rValueIn /= _rStep;
2062     double rVal = floor(rValueIn+0.5);
2063     rVal -= _r0;
2064     return intConv(rVal);
2065 }
2066 
2067 } // namespace GDAL
2068 
2069 /************************************************************************/
2070 /*                    GDALRegister_ILWIS()                              */
2071 /************************************************************************/
2072 
GDALRegister_ILWIS()2073 void GDALRegister_ILWIS()
2074 
2075 {
2076     if( GDALGetDriverByName( "ILWIS" ) != nullptr )
2077         return;
2078 
2079     GDALDriver *poDriver = new GDALDriver();
2080 
2081     poDriver->SetDescription( "ILWIS" );
2082     poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
2083     poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "ILWIS Raster Map" );
2084     poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "mpr mpl" );
2085     poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
2086                                "Byte Int16 Int32 Float64" );
2087     poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
2088 
2089     poDriver->pfnOpen = GDAL::ILWISDataset::Open;
2090     poDriver->pfnCreate = GDAL::ILWISDataset::Create;
2091     poDriver->pfnCreateCopy = GDAL::ILWISDataset::CreateCopy;
2092 
2093     GetGDALDriverManager()->RegisterDriver( poDriver );
2094 }
2095