LCOV - code coverage report
Current view: top level - frmts/ilwis - ilwisdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 794 1111 71.5 %
Date: 2025-10-01 17:07:58 Functions: 49 57 86.0 %

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

Generated by: LCOV version 1.14