LCOV - code coverage report
Current view: top level - frmts/ilwis - ilwisdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 799 1111 71.9 %
Date: 2024-05-04 12:52:34 Functions: 50 58 86.2 %

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

Generated by: LCOV version 1.14