LCOV - code coverage report
Current view: top level - frmts/idrisi - IdrisiDataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 536 1248 42.9 %
Date: 2024-05-04 12:52:34 Functions: 29 45 64.4 %

          Line data    Source code
       1             : /*****************************************************************************
       2             :  *
       3             :  * Project:  Idrisi Raster Image File Driver
       4             :  * Purpose:  Read/write Idrisi Raster Image Format RST
       5             :  * Author:   Ivan Lucena, [lucena_ivan at hotmail.com]
       6             :  *
       7             :  * Revised by Hongmei Zhu, February, 2013
       8             :  * honzhu@clarku.edu
       9             :  * Clark Labs/Clark University
      10             :  *
      11             :  ******************************************************************************
      12             :  * Copyright( c ) 2006, Ivan Lucena
      13             :  * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
      14             :  *
      15             :  * Permission is hereby granted, free of charge, to any person obtaining a
      16             :  * copy of this software and associated documentation files( the "Software" ),
      17             :  * to deal in the Software without restriction, including without limitation
      18             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      19             :  * and/or sell copies of the Software, and to permit persons to whom the
      20             :  * Software is furnished to do so, subject to the following conditions:
      21             :  *
      22             :  * The above copyright notice and this permission notice shall be included
      23             :  * in all copies or substantial portions of the Software.
      24             :  *
      25             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      26             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      27             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      28             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      29             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      30             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      31             :  * DEALINGS IN THE SOFTWARE.
      32             :  ****************************************************************************/
      33             : 
      34             : #include "cpl_conv.h"
      35             : #include "cpl_string.h"
      36             : #include "cpl_csv.h"
      37             : #include "gdal_frmts.h"
      38             : #include "gdal_pam.h"
      39             : #include "gdal_alg.h"
      40             : #include "gdal_priv.h"
      41             : #include "gdal_rat.h"
      42             : #include "ogr_spatialref.h"
      43             : #include "idrisi.h"
      44             : 
      45             : #include "proj_experimental.h"
      46             : #include "ogr_proj_p.h"
      47             : 
      48             : #include <cmath>
      49             : 
      50             : #ifdef _WIN32
      51             : #define PATHDELIM '\\'
      52             : #else
      53             : #define PATHDELIM '/'
      54             : #endif
      55             : 
      56             : //----- Safe numeric conversion, NULL as zero
      57             : #define atoi_nz(s) (s == nullptr ? (int)0 : atoi(s))
      58             : #define CPLAtof_nz(s) (s == nullptr ? (double)0.0 : CPLAtof(s))
      59             : 
      60             : //----- file extensions:
      61             : static const char *const extRST = "rst";
      62             : static const char *const extRDC = "rdc";
      63             : static const char *const extSMP = "smp";
      64             : static const char *const extREF = "ref";
      65             : // static const char * const extRSTu        = "RST";
      66             : static const char *const extRDCu = "RDC";
      67             : static const char *const extSMPu = "SMP";
      68             : static const char *const extREFu = "REF";
      69             : 
      70             : //----- field names on rdc file:
      71             : static const char *const rdcFILE_FORMAT = "file format ";
      72             : static const char *const rdcFILE_TITLE = "file title  ";
      73             : static const char *const rdcDATA_TYPE = "data type   ";
      74             : static const char *const rdcFILE_TYPE = "file type   ";
      75             : static const char *const rdcCOLUMNS = "columns     ";
      76             : static const char *const rdcROWS = "rows        ";
      77             : static const char *const rdcREF_SYSTEM = "ref. system ";
      78             : static const char *const rdcREF_UNITS = "ref. units  ";
      79             : static const char *const rdcUNIT_DIST = "unit dist.  ";
      80             : static const char *const rdcMIN_X = "min. X      ";
      81             : static const char *const rdcMAX_X = "max. X      ";
      82             : static const char *const rdcMIN_Y = "min. Y      ";
      83             : static const char *const rdcMAX_Y = "max. Y      ";
      84             : static const char *const rdcPOSN_ERROR = "pos'n error ";
      85             : static const char *const rdcRESOLUTION = "resolution  ";
      86             : static const char *const rdcMIN_VALUE = "min. value  ";
      87             : static const char *const rdcMAX_VALUE = "max. value  ";
      88             : static const char *const rdcDISPLAY_MIN = "display min ";
      89             : static const char *const rdcDISPLAY_MAX = "display max ";
      90             : static const char *const rdcVALUE_UNITS = "value units ";
      91             : static const char *const rdcVALUE_ERROR = "value error ";
      92             : static const char *const rdcFLAG_VALUE = "flag value  ";
      93             : static const char *const rdcFLAG_DEFN = "flag def'n  ";
      94             : static const char *const rdcFLAG_DEFN2 = "flag def`n  ";
      95             : static const char *const rdcLEGEND_CATS = "legend cats ";
      96             : static const char *const rdcLINEAGES = "lineage     ";
      97             : static const char *const rdcCOMMENTS = "comment     ";
      98             : static const char *const rdcCODE_N = "code %6d ";
      99             : 
     100             : //----- ".ref" file field names:
     101             : static const char *const refREF_SYSTEM = "ref. system ";
     102             : static const char *const refREF_SYSTEM2 = "ref.system  ";
     103             : static const char *const refPROJECTION = "projection  ";
     104             : static const char *const refDATUM = "datum       ";
     105             : static const char *const refDELTA_WGS84 = "delta WGS84 ";
     106             : static const char *const refELLIPSOID = "ellipsoid   ";
     107             : static const char *const refMAJOR_SAX = "major s-ax  ";
     108             : static const char *const refMINOR_SAX = "minor s-ax  ";
     109             : static const char *const refORIGIN_LONG = "origin long ";
     110             : static const char *const refORIGIN_LAT = "origin lat  ";
     111             : static const char *const refORIGIN_X = "origin X    ";
     112             : static const char *const refORIGIN_Y = "origin Y    ";
     113             : static const char *const refSCALE_FAC = "scale fac   ";
     114             : static const char *const refUNITS = "units       ";
     115             : static const char *const refPARAMETERS = "parameters  ";
     116             : static const char *const refSTANDL_1 = "stand ln 1  ";
     117             : static const char *const refSTANDL_2 = "stand ln 2  ";
     118             : 
     119             : //----- standard values:
     120             : static const char *const rstVERSION = "Idrisi Raster A.1";
     121             : static const char *const rstBYTE = "byte";
     122             : static const char *const rstINTEGER = "integer";
     123             : static const char *const rstREAL = "real";
     124             : static const char *const rstRGB24 = "rgb24";
     125             : static const char *const rstDEGREE = "deg";
     126             : static const char *const rstMETER = "m";
     127             : static const char *const rstLATLONG = "latlong";
     128             : static const char *const rstLATLONG2 = "lat/long";
     129             : static const char *const rstPLANE = "plane";
     130             : static const char *const rstUTM = "utm-%d%c";
     131             : static const char *const rstSPC = "spc%2d%2s%d";
     132             : 
     133             : //----- palette file( .smp ) header size:
     134             : constexpr int smpHEADERSIZE = 18;
     135             : 
     136             : //----- check if file exists:
     137             : bool FileExists(const char *pszPath);
     138             : 
     139             : //----- Reference Table
     140             : struct ReferenceTab
     141             : {
     142             :     int nCode;
     143             :     const char *pszName;
     144             : };
     145             : 
     146             : //----- USA State's reference table to USGS PCS Code
     147             : constexpr ReferenceTab aoUSStateTable[] = {
     148             :     {101, "al"},  {201, "az"},  {301, "ar"},  {401, "ca"},  {501, "co"},
     149             :     {600, "ct"},  {700, "de"},  {901, "fl"},  {1001, "ga"}, {1101, "id"},
     150             :     {1201, "il"}, {1301, "in"}, {1401, "ia"}, {1501, "ks"}, {1601, "ky"},
     151             :     {1701, "la"}, {1801, "me"}, {1900, "md"}, {2001, "ma"}, {2111, "mi"},
     152             :     {2201, "mn"}, {2301, "ms"}, {2401, "mo"}, {2500, "mt"}, {2600, "ne"},
     153             :     {2701, "nv"}, {2800, "nh"}, {2900, "nj"}, {3001, "nm"}, {3101, "ny"},
     154             :     {3200, "nc"}, {3301, "nd"}, {3401, "oh"}, {3501, "ok"}, {3601, "or"},
     155             :     {3701, "pa"}, {3800, "ri"}, {3900, "sc"}, {4001, "sd"}, {4100, "tn"},
     156             :     {4201, "tx"}, {4301, "ut"}, {4400, "vt"}, {4501, "va"}, {4601, "wa"},
     157             :     {4701, "wv"}, {4801, "wv"}, {4901, "wy"}, {5001, "ak"}, {5101, "hi"},
     158             :     {5200, "pr"}};
     159             : 
     160             : //---- The origin table for USA State Plane Systems
     161             : struct OriginTab83
     162             : {
     163             :     double longitude;
     164             :     double latitude;
     165             :     const char *spcs;
     166             : };
     167             : 
     168             : constexpr int ORIGIN_COUNT = 148;
     169             : 
     170             : //---- USA State plane coordinate system in IDRISI
     171             : static const OriginTab83 SPCS83Origin[] = {
     172             :     {85.83, 30.50, "SPC83AL1"},  {87.50, 30.00, "SPC83AL2"},
     173             :     {176.00, 51.00, "SPC83AK0"}, {142.00, 54.00, "SPC83AK2"},
     174             :     {146.00, 54.00, "SPC83AK3"}, {150.00, 54.00, "SPC83AK4"},
     175             :     {154.00, 54.00, "SPC83AK5"}, {158.00, 54.00, "SPC83AK6"},
     176             :     {162.00, 54.00, "SPC83AK7"}, {166.00, 54.00, "SPC83AK8"},
     177             :     {170.00, 54.00, "SPC83AK9"}, {110.17, 31.00, "SPC83AZ1"},
     178             :     {111.92, 31.00, "SPC83AZ2"}, {113.75, 31.00, "SPC83AZ3"},
     179             :     {92.00, 34.33, "SPC83AR1"},  {92.00, 32.67, "SPC83AR2"},
     180             :     {122.00, 39.33, "SPC83CA1"}, {122.00, 37.67, "SPC83CA2"},
     181             :     {120.50, 36.50, "SPC83CA3"}, {119.00, 35.33, "SPC83CA4"},
     182             :     {118.00, 33.50, "SPC83CA5"}, {116.25, 32.17, "SPC83CA6"},
     183             :     {105.50, 39.33, "SPC83CO1"}, {105.50, 37.83, "SPC83CO2"},
     184             :     {105.50, 36.67, "SPC83CO3"}, {72.75, 40.83, "SPC83CT1"},
     185             :     {75.42, 38.00, "SPC83DE1"},  {81.00, 24.33, "SPC83FL1"},
     186             :     {82.00, 24.33, "SPC83FL2"},  {84.50, 29.00, "SPC83FL3"},
     187             :     {82.17, 30.00, "SPC83GA1"},  {84.17, 30.00, "SPC83GA2"},
     188             :     {155.50, 18.83, "SPC83HI1"}, {156.67, 20.33, "SPC83HI2"},
     189             :     {158.00, 21.17, "SPC83HI3"}, {159.50, 21.83, "SPC83HI4"},
     190             :     {160.17, 21.67, "SPC83HI5"}, {112.17, 41.67, "SPC83ID1"},
     191             :     {114.00, 41.67, "SPC83ID2"}, {115.75, 41.67, "SPC83ID3"},
     192             :     {88.33, 36.67, "SPC83IL1"},  {90.17, 36.67, "SPC83IL1"},
     193             :     {85.67, 37.50, "SPC83IN1"},  {87.08, 37.50, "SPC83IN2"},
     194             :     {93.50, 41.50, "SPC83IA1"},  {93.50, 40.00, "SPC83IA1"},
     195             :     {98.00, 38.33, "SPC83KS1"},  {98.50, 36.67, "SPC83KS2"},
     196             :     {84.25, 37.50, "SPC83KY1"},  {85.75, 36.33, "SPC83KY2"},
     197             :     {92.50, 30.50, "SPC83LA1"},  {91.33, 28.50, "SPC83LA2"},
     198             :     {91.33, 25.50, "SPC83LA3"},  {92.50, 30.67, "SPC27LA1"},  // NAD27 system
     199             :     {91.33, 28.67, "SPC27LA2"},  {91.33, 25.67, "SPC27LA3"},  //
     200             :     {68.50, 43.67, "SPC83ME1"},  {68.50, 43.83, "SPC27ME1"},  // NAD27
     201             :     {70.17, 42.83, "SPC83ME2"},  {77.00, 37.67, "SPC83MD1"},  //
     202             :     {77.00, 37.83, "SPC27MD1"},                               // NAD27
     203             :     {71.50, 41.00, "SPC83MA1"},  {70.50, 41.00, "SPC83MA2"},
     204             :     {87.00, 44.78, "SPC83MI1"},  {84.37, 43.32, "SPC83MI2"},
     205             :     {84.37, 41.50, "SPC83MI3"},  {84.33, 43.32, "SPC27MI2"},  // NAD27 L
     206             :     {84.33, 41.50, "SPC27MI3"},                               // NAD27 L
     207             :     {83.67, 41.50, "SPC27MI4"},                               // NAD27 TM
     208             :     {85.75, 41.50, "SPC27MI5"},                               // NAD27 TM
     209             :     {88.75, 41.50, "SPC27MI6"},                               // NAD27 TM
     210             :     {93.10, 46.50, "SPC83MN1"},  {94.25, 45.00, "SPC83MN2"},
     211             :     {94.00, 43.00, "SPC83MN3"},  {88.83, 29.50, "SPC83MS1"},
     212             :     {90.33, 29.50, "SPC83MS2"},  {88.83, 29.67, "SPC83MS1"},  // NAD27
     213             :     {90.33, 30.50, "SPC83MS2"},                               //
     214             :     {90.50, 35.83, "SPC83MO1"},  {92.50, 35.83, "SPC83MO2"},
     215             :     {94.50, 36.17, "SPC83MO3"},  {109.50, 44.25, "SPC83MT1"},
     216             :     {109.50, 47.00, "SPC27MT1"},                               // NAD27
     217             :     {109.50, 45.83, "SPC27MT2"}, {109.50, 44.00, "SPC27MT3"},  //
     218             :     {100.00, 39.83, "SPC83NE1"}, {115.58, 34.75, "SPC83NV1"},
     219             :     {116.67, 34.75, "SPC83NV2"}, {118.58, 34.75, "SPC83NV3"},
     220             :     {71.67, 42.50, "SPC83NH1"},  {74.50, 38.83, "SPC83NJ1"},
     221             :     {74.67, 38.83, "SPC27NJ1"},  // NAD27
     222             :     {104.33, 31.00, "SPC83NM1"}, {106.25, 31.00, "SPC83NM2"},
     223             :     {107.83, 31.00, "SPC83NM3"}, {74.50, 38.83, "SPC83NY1"},
     224             :     {76.58, 40.00, "SPC83NY2"},  {78.58, 40.00, "SPC83NY3"},
     225             :     {74.00, 40.17, "SPC83NY4"},  {74.33, 40.00, "SPC27NY1"},  // NAD27
     226             :     {74.00, 40.50, "SPC27NY4"},                               //
     227             :     {79.00, 33.75, "SPC83NC1"},  {100.50, 47.00, "SPC83ND1"},
     228             :     {100.50, 45.67, "SPC83ND2"}, {82.50, 39.67, "SPC83OH1"},
     229             :     {82.50, 38.00, "SPC83OH2"},  {98.00, 35.00, "SPC83OK1"},
     230             :     {98.00, 33.33, "SPC83OK2"},  {120.50, 43.67, "SPC83OR1"},
     231             :     {120.50, 41.67, "SPC83OR2"}, {77.75, 40.17, "SPC83PA1"},
     232             :     {77.75, 39.33, "SPC83PA2"},  {71.50, 41.08, "SPC83RI1"},
     233             :     {81.00, 31.83, "SPC83SC1"},  {81.00, 33.00, "SPC27SC1"},  // NAD27
     234             :     {81.00, 31.83, "SPC27SC2"},                               // NAD27
     235             :     {100.00, 43.83, "SPC83SD1"}, {100.33, 42.33, "SPC83SD2"},
     236             :     {86.00, 34.33, "SPC83TN1"},  {86.00, 34.67, "SPC27TN1"},  // NAD27
     237             :     {101.50, 34.00, "SPC83TX1"},                              //
     238             :     {98.50, 31.67, "SPC83TX2"},  {100.33, 29.67, "SPC83TX3"},
     239             :     {99.00, 27.83, "SPC83TX4"},  {98.50, 25.67, "SPC83TX5"},
     240             :     {97.50, 31.67, "SPC27TX2"},  // NAD27
     241             :     {111.50, 40.33, "SPC83UT1"}, {111.50, 38.33, "SPC83UT2"},
     242             :     {111.50, 36.67, "SPC83UT3"}, {72.50, 42.50, "SPC83VT1"},
     243             :     {78.50, 37.67, "SPC83VA1"},  {78.50, 36.33, "SPC83VA2"},
     244             :     {120.83, 47.00, "SPC83WA1"}, {120.50, 45.33, "SPC83WA2"},
     245             :     {79.50, 38.50, "SPC83WV1"},  {81.00, 37.00, "SPC83WV2"},
     246             :     {90.00, 45.17, "SPC83WI1"},  {90.00, 43.83, "SPC83WI2"},
     247             :     {90.00, 42.00, "SPC83WI3"},  {105.17, 40.50, "SPC83WY1"},
     248             :     {107.33, 40.50, "SPC83WY2"}, {108.75, 40.50, "SPC83WY3"},
     249             :     {110.08, 40.50, "SPC83WY4"}, {105.17, 40.67, "SPC27WY1"},  // NAD27
     250             :     {105.33, 40.67, "SPC27WY2"}, {108.75, 40.67, "SPC27WY3"},
     251             :     {110.08, 40.67, "SPC27WY4"},  //
     252             :     {66.43, 17.83, "SPC83PR1"}};
     253             : 
     254             : // Get IDRISI State Plane name by origin
     255             : char *GetSpcs(double dfLon, double dfLat);
     256             : 
     257             : // change NAD from 83 to 27
     258             : void NAD83to27(char *pszOutRef, char *pszInRef);
     259             : 
     260             : #define US_STATE_COUNT (sizeof(aoUSStateTable) / sizeof(ReferenceTab))
     261             : 
     262             : //----- Get the Code of a US State
     263             : int GetStateCode(const char *pszState);
     264             : 
     265             : //----- Get the state name of a Code
     266             : const char *GetStateName(int nCode);
     267             : 
     268             : //----- Conversion Table definition
     269             : struct ConversionTab
     270             : {
     271             :     const char *pszName;
     272             :     int nDefaultI;
     273             :     int nDefaultG;
     274             :     double dfConv;
     275             : };
     276             : 
     277             : //----- Linear Unit Conversion Table
     278             : static const ConversionTab aoLinearUnitsConv[] = {
     279             :     {"m", /*  0 */ 0, 1, 1.0},
     280             :     {SRS_UL_METER, /*  1 */ 0, 1, 1.0},
     281             :     {"meters", /*  2 */ 0, 1, 1.0},
     282             :     {"metre", /*  3 */ 0, 1, 1.0},
     283             : 
     284             :     {"ft", /*  4 */ 4, 5, 0.3048},
     285             :     {SRS_UL_FOOT, /*  5 */ 4, 5, 0.3048},
     286             :     {"feet", /*  6 */ 4, 5, 0.3048},
     287             :     {"foot_us", /*  7 */ 4, 5, 0.3048006},
     288             :     {"u.s. foot", /*  8 */ 4, 5, 0.3048006},
     289             : 
     290             :     {"mi", /*  9 */ 9, 10, 1612.9},
     291             :     {"mile", /* 10 */ 9, 10, 1612.9},
     292             :     {"miles", /* 11 */ 9, 10, 1612.9},
     293             : 
     294             :     {"km", /* 12 */ 12, 13, 1000.0},
     295             :     {"kilometers", /* 13 */ 12, 13, 1000.0},
     296             :     {"kilometer", /* 14 */ 12, 13, 1000.0},
     297             :     {"kilometre", /* 15 */ 12, 13, 1000.0},
     298             : 
     299             :     {"deg", /* 16 */ 16, 17, 0.0},
     300             :     {SRS_UA_DEGREE, /* 17 */ 16, 17, 0.0},
     301             :     {"degrees", /* 18 */ 16, 17, 0.0},
     302             : 
     303             :     {"rad", /* 19 */ 19, 20, 0.0},
     304             :     {SRS_UA_RADIAN, /* 20 */ 19, 20, 0.0},
     305             :     {"radians", /* 21 */ 19, 20, 0.0}};
     306             : #define LINEAR_UNITS_COUNT (sizeof(aoLinearUnitsConv) / sizeof(ConversionTab))
     307             : 
     308             : //----- Get the index of a given linear unit
     309             : static int GetUnitIndex(const char *pszUnitName);
     310             : 
     311             : //----- Get the default name
     312             : static char *GetUnitDefault(const char *pszUnitName,
     313             :                             const char *pszToMeter = nullptr);
     314             : 
     315             : //----- Get the "to meter"
     316             : static int GetToMeterIndex(const char *pszToMeter);
     317             : 
     318             : //----- CSLSaveCRLF
     319             : static int SaveAsCRLF(char **papszStrList, const char *pszFname);
     320             : 
     321             : /************************************************************************/
     322             : /*                     myCSLFetchNameValue()                            */
     323             : /************************************************************************/
     324             : 
     325        1192 : static const char *myCSLFetchNameValue(char **papszStrList, const char *pszName)
     326             : {
     327        1192 :     if (papszStrList == nullptr || pszName == nullptr)
     328           4 :         return nullptr;
     329             : 
     330        1188 :     size_t nLen = strlen(pszName);
     331        4541 :     while (nLen > 0 && pszName[nLen - 1] == ' ')
     332        3353 :         nLen--;
     333       20461 :     while (*papszStrList != nullptr)
     334             :     {
     335       20449 :         if (EQUALN(*papszStrList, pszName, nLen))
     336             :         {
     337             :             size_t i;
     338        4451 :             for (i = nLen; (*papszStrList)[i] == ' '; ++i)
     339             :             {
     340             :             }
     341        1176 :             if ((*papszStrList)[i] == '=' || (*papszStrList)[i] == ':')
     342             :             {
     343        1176 :                 return (*papszStrList) + i + 1;
     344             :             }
     345             :         }
     346       19273 :         ++papszStrList;
     347             :     }
     348          12 :     return nullptr;
     349             : }
     350             : 
     351             : /************************************************************************/
     352             : /*                   myCSLSetNameValueSeparator()                       */
     353             : /************************************************************************/
     354             : 
     355          86 : static void myCSLSetNameValueSeparator(char **papszList,
     356             :                                        const char *pszSeparator)
     357             : {
     358          86 :     const int nLines = CSLCount(papszList);
     359             : 
     360        1926 :     for (int iLine = 0; iLine < nLines; ++iLine)
     361             :     {
     362        1840 :         char *pszSep = strchr(papszList[iLine], '=');
     363        1840 :         if (pszSep == nullptr)
     364        1164 :             pszSep = strchr(papszList[iLine], ':');
     365        1840 :         if (pszSep == nullptr)
     366           0 :             continue;
     367        1840 :         *pszSep = '\0';
     368        1840 :         const char *pszKey = papszList[iLine];
     369        1840 :         const char *pszValue = pszSep + 1;
     370        2626 :         while (*pszValue == ' ')
     371         786 :             pszValue++;
     372             : 
     373        3680 :         char *pszNewLine = static_cast<char *>(CPLMalloc(
     374        1840 :             strlen(pszValue) + strlen(pszKey) + strlen(pszSeparator) + 1));
     375        1840 :         strcpy(pszNewLine, pszKey);
     376        1840 :         strcat(pszNewLine, pszSeparator);
     377        1840 :         strcat(pszNewLine, pszValue);
     378        1840 :         CPLFree(papszList[iLine]);
     379        1840 :         papszList[iLine] = pszNewLine;
     380             :     }
     381          86 : }
     382             : 
     383             : //----- Classes pre-definition:
     384             : class IdrisiRasterBand;
     385             : 
     386             : //  ----------------------------------------------------------------------------
     387             : //        Idrisi GDALDataset
     388             : //  ----------------------------------------------------------------------------
     389             : 
     390             : class IdrisiDataset final : public GDALPamDataset
     391             : {
     392             :     friend class IdrisiRasterBand;
     393             : 
     394             :   private:
     395             :     VSILFILE *fp;
     396             : 
     397             :     char *pszFilename;
     398             :     char *pszDocFilename;
     399             :     char **papszRDC;
     400             :     double adfGeoTransform[6];
     401             : 
     402             :     mutable OGRSpatialReference m_oSRS{};
     403             :     char **papszCategories;
     404             :     char *pszUnitType;
     405             :     // Move GeoReference2Wkt() into header file.
     406             :     CPLErr Wkt2GeoReference(const OGRSpatialReference &oSRS,
     407             :                             char **pszRefSystem, char **pszRefUnit);
     408             : 
     409             :   protected:
     410             :     GDALColorTable *poColorTable;
     411             : 
     412             :   public:
     413             :     IdrisiDataset();
     414             :     virtual ~IdrisiDataset();
     415             : 
     416             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
     417             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     418             :                                int nBands, GDALDataType eType,
     419             :                                char **papszOptions);
     420             :     static GDALDataset *CreateCopy(const char *pszFilename,
     421             :                                    GDALDataset *poSrcDS, int bStrict,
     422             :                                    char **papszOptions,
     423             :                                    GDALProgressFunc pfnProgress,
     424             :                                    void *pProgressData);
     425             :     virtual char **GetFileList(void) override;
     426             :     virtual CPLErr GetGeoTransform(double *padfTransform) override;
     427             :     virtual CPLErr SetGeoTransform(double *padfTransform) override;
     428             : 
     429             :     const OGRSpatialReference *GetSpatialRef() const override;
     430             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
     431             : };
     432             : 
     433             : //  ----------------------------------------------------------------------------
     434             : //        Idrisi GDALPamRasterBand
     435             : //  ----------------------------------------------------------------------------
     436             : 
     437             : class IdrisiRasterBand final : public GDALPamRasterBand
     438             : {
     439             :     friend class IdrisiDataset;
     440             : 
     441             :     GDALRasterAttributeTable *poDefaultRAT;
     442             : 
     443             :   private:
     444             :     int nRecordSize;
     445             :     GByte *pabyScanLine;
     446             : 
     447             :   public:
     448             :     IdrisiRasterBand(IdrisiDataset *poDS, int nBand, GDALDataType eDataType);
     449             :     virtual ~IdrisiRasterBand();
     450             : 
     451             :     virtual double GetNoDataValue(int *pbSuccess = nullptr) override;
     452             :     virtual double GetMinimum(int *pbSuccess = nullptr) override;
     453             :     virtual double GetMaximum(int *pbSuccess = nullptr) override;
     454             :     virtual CPLErr IReadBlock(int nBlockXOff, int nBlockYOff,
     455             :                               void *pImage) override;
     456             :     virtual CPLErr IWriteBlock(int nBlockXOff, int nBlockYOff,
     457             :                                void *pImage) override;
     458             :     virtual GDALColorTable *GetColorTable() override;
     459             :     virtual GDALColorInterp GetColorInterpretation() override;
     460             :     virtual char **GetCategoryNames() override;
     461             :     virtual const char *GetUnitType() override;
     462             : 
     463             :     virtual CPLErr SetCategoryNames(char **papszCategoryNames) override;
     464             :     virtual CPLErr SetNoDataValue(double dfNoDataValue) override;
     465             :     virtual CPLErr SetColorTable(GDALColorTable *poColorTable) override;
     466             :     virtual CPLErr SetUnitType(const char *pszUnitType) override;
     467             :     virtual CPLErr SetStatistics(double dfMin, double dfMax, double dfMean,
     468             :                                  double dfStdDev) override;
     469             :     CPLErr SetMinMax(double dfMin, double dfMax);
     470             :     virtual GDALRasterAttributeTable *GetDefaultRAT() override;
     471             :     virtual CPLErr SetDefaultRAT(const GDALRasterAttributeTable *) override;
     472             : 
     473             :     float fMaximum;
     474             :     float fMinimum;
     475             :     bool bFirstVal;
     476             : };
     477             : 
     478             : //  ------------------------------------------------------------------------  //
     479             : //                        Implementation of IdrisiDataset                     //
     480             : //  ------------------------------------------------------------------------  //
     481             : 
     482             : /************************************************************************/
     483             : /*                           IdrisiDataset()                            */
     484             : /************************************************************************/
     485             : 
     486          36 : IdrisiDataset::IdrisiDataset()
     487             :     : fp(nullptr), pszFilename(nullptr), pszDocFilename(nullptr),
     488             :       papszRDC(nullptr), papszCategories(nullptr), pszUnitType(nullptr),
     489          36 :       poColorTable(new GDALColorTable())
     490             : {
     491          36 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     492          36 :     adfGeoTransform[0] = 0.0;
     493          36 :     adfGeoTransform[1] = 1.0;
     494          36 :     adfGeoTransform[2] = 0.0;
     495          36 :     adfGeoTransform[3] = 0.0;
     496          36 :     adfGeoTransform[4] = 0.0;
     497          36 :     adfGeoTransform[5] = 1.0;
     498          36 : }
     499             : 
     500             : /************************************************************************/
     501             : /*                           ~IdrisiDataset()                           */
     502             : /************************************************************************/
     503             : 
     504          72 : IdrisiDataset::~IdrisiDataset()
     505             : {
     506          36 :     FlushCache(true);
     507             : 
     508          36 :     if (papszRDC != nullptr && eAccess == GA_Update)
     509             :     {
     510             :         // int bSuccessMin = FALSE;
     511             :         // int bSuccessMax = FALSE;
     512             : 
     513          20 :         double dfMin = 0.0;
     514          20 :         double dfMax = 0.0;
     515          20 :         double dfMean = 0.0;
     516          20 :         double dfStdDev = 0.0;
     517             : 
     518          40 :         for (int i = 0; i < nBands; i++)
     519             :         {
     520          20 :             IdrisiRasterBand *poBand = (IdrisiRasterBand *)GetRasterBand(i + 1);
     521          20 :             poBand->ComputeStatistics(false, &dfMin, &dfMax, &dfMean, &dfStdDev,
     522          20 :                                       nullptr, nullptr);
     523             :             /*
     524             :               dfMin = poBand->GetMinimum( &bSuccessMin );
     525             :               dfMax = poBand->GetMaximum( &bSuccessMax );
     526             : 
     527             :               if( ! ( bSuccessMin && bSuccessMax ) )
     528             :               {
     529             :               poBand->GetStatistics( false, true, &dfMin, &dfMax, NULL, NULL );
     530             :               }
     531             :             */
     532          20 :             poBand->SetMinMax(dfMin, dfMax);
     533             :         }
     534             : 
     535          20 :         myCSLSetNameValueSeparator(papszRDC, ": ");
     536          20 :         SaveAsCRLF(papszRDC, pszDocFilename);
     537             :     }
     538          36 :     CSLDestroy(papszRDC);
     539             : 
     540          36 :     if (poColorTable)
     541             :     {
     542          36 :         delete poColorTable;
     543             :     }
     544          36 :     CPLFree(pszFilename);
     545          36 :     CPLFree(pszDocFilename);
     546          36 :     CSLDestroy(papszCategories);
     547          36 :     CPLFree(pszUnitType);
     548             : 
     549          36 :     if (fp != nullptr)
     550          36 :         VSIFCloseL(fp);
     551          72 : }
     552             : 
     553             : /************************************************************************/
     554             : /*                                Open()                                */
     555             : /************************************************************************/
     556             : 
     557       30417 : GDALDataset *IdrisiDataset::Open(GDALOpenInfo *poOpenInfo)
     558             : {
     559       34602 :     if ((poOpenInfo->fpL == nullptr) ||
     560        4185 :         (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), extRST) ==
     561             :          FALSE))  // modified
     562       30376 :         return nullptr;
     563             : 
     564             :     // --------------------------------------------------------------------
     565             :     //      Check the documentation file .rdc
     566             :     // --------------------------------------------------------------------
     567             : 
     568             :     const char *pszLDocFilename =
     569          41 :         CPLResetExtension(poOpenInfo->pszFilename, extRDC);
     570             : 
     571          40 :     if (!FileExists(pszLDocFilename))
     572             :     {
     573           0 :         pszLDocFilename = CPLResetExtension(poOpenInfo->pszFilename, extRDCu);
     574             : 
     575           0 :         if (!FileExists(pszLDocFilename))
     576             :         {
     577           0 :             return nullptr;
     578             :         }
     579             :     }
     580             : 
     581          40 :     char **papszLRDC = CSLLoad(pszLDocFilename);
     582             : 
     583          40 :     myCSLSetNameValueSeparator(papszLRDC, ":");
     584             : 
     585          40 :     const char *pszVersion = myCSLFetchNameValue(papszLRDC, rdcFILE_FORMAT);
     586             : 
     587          40 :     if (pszVersion == nullptr || !EQUAL(pszVersion, rstVERSION))
     588             :     {
     589           4 :         CSLDestroy(papszLRDC);
     590           4 :         return nullptr;
     591             :     }
     592             : 
     593             :     // --------------------------------------------------------------------
     594             :     //      Create a corresponding GDALDataset
     595             :     // --------------------------------------------------------------------
     596             : 
     597          36 :     IdrisiDataset *poDS = new IdrisiDataset();
     598          36 :     poDS->eAccess = poOpenInfo->eAccess;
     599          36 :     poDS->pszFilename = CPLStrdup(poOpenInfo->pszFilename);
     600             : 
     601          36 :     if (poOpenInfo->eAccess == GA_ReadOnly)
     602             :     {
     603          16 :         poDS->fp = VSIFOpenL(poDS->pszFilename, "rb");
     604             :     }
     605             :     else
     606             :     {
     607          20 :         poDS->fp = VSIFOpenL(poDS->pszFilename, "r+b");
     608             :     }
     609             : 
     610          36 :     if (poDS->fp == nullptr)
     611             :     {
     612           0 :         CSLDestroy(papszLRDC);
     613           0 :         delete poDS;
     614           0 :         return nullptr;
     615             :     }
     616             : 
     617          36 :     poDS->pszDocFilename = CPLStrdup(pszLDocFilename);
     618          36 :     poDS->papszRDC = CSLDuplicate(papszLRDC);
     619          36 :     CSLDestroy(papszLRDC);
     620             : 
     621             :     // --------------------------------------------------------------------
     622             :     //      Load information from rdc
     623             :     // --------------------------------------------------------------------
     624             : 
     625          36 :     poDS->nRasterXSize =
     626          36 :         atoi_nz(myCSLFetchNameValue(poDS->papszRDC, rdcCOLUMNS));
     627          36 :     poDS->nRasterYSize = atoi_nz(myCSLFetchNameValue(poDS->papszRDC, rdcROWS));
     628          36 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
     629             :     {
     630           6 :         delete poDS;
     631           6 :         return nullptr;
     632             :     }
     633             : 
     634             :     // --------------------------------------------------------------------
     635             :     //      Create band information
     636             :     // --------------------------------------------------------------------
     637             : 
     638          30 :     const char *pszDataType = myCSLFetchNameValue(poDS->papszRDC, rdcDATA_TYPE);
     639          30 :     if (pszDataType == nullptr)
     640             :     {
     641           0 :         delete poDS;
     642           0 :         return nullptr;
     643             :     }
     644             : 
     645          30 :     if (EQUAL(pszDataType, rstBYTE))
     646             :     {
     647           5 :         poDS->nBands = 1;
     648           5 :         poDS->SetBand(1, new IdrisiRasterBand(poDS, 1, GDT_Byte));
     649             :     }
     650          25 :     else if (EQUAL(pszDataType, rstINTEGER))
     651             :     {
     652           9 :         poDS->nBands = 1;
     653           9 :         poDS->SetBand(1, new IdrisiRasterBand(poDS, 1, GDT_Int16));
     654             :     }
     655          16 :     else if (EQUAL(pszDataType, rstREAL))
     656             :     {
     657          10 :         poDS->nBands = 1;
     658          10 :         poDS->SetBand(1, new IdrisiRasterBand(poDS, 1, GDT_Float32));
     659             :     }
     660           6 :     else if (EQUAL(pszDataType, rstRGB24))
     661             :     {
     662           6 :         poDS->nBands = 3;
     663           6 :         poDS->SetBand(1, new IdrisiRasterBand(poDS, 1, GDT_Byte));
     664           6 :         poDS->SetBand(2, new IdrisiRasterBand(poDS, 2, GDT_Byte));
     665           6 :         poDS->SetBand(3, new IdrisiRasterBand(poDS, 3, GDT_Byte));
     666             :     }
     667             :     else
     668             :     {
     669           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unknown data type : %s",
     670             :                  pszDataType);
     671           0 :         delete poDS;
     672           0 :         return nullptr;
     673             :     }
     674             : 
     675          72 :     for (int i = 0; i < poDS->nBands; i++)
     676             :     {
     677          42 :         IdrisiRasterBand *band = (IdrisiRasterBand *)poDS->GetRasterBand(i + 1);
     678          42 :         if (band->pabyScanLine == nullptr)
     679             :         {
     680           0 :             delete poDS;
     681           0 :             return nullptr;
     682             :         }
     683             :     }
     684             : 
     685             :     // --------------------------------------------------------------------
     686             :     //      Load the transformation matrix
     687             :     // --------------------------------------------------------------------
     688             : 
     689          30 :     const char *pszMinX = myCSLFetchNameValue(poDS->papszRDC, rdcMIN_X);
     690          30 :     const char *pszMaxX = myCSLFetchNameValue(poDS->papszRDC, rdcMAX_X);
     691          30 :     const char *pszMinY = myCSLFetchNameValue(poDS->papszRDC, rdcMIN_Y);
     692          30 :     const char *pszMaxY = myCSLFetchNameValue(poDS->papszRDC, rdcMAX_Y);
     693          30 :     const char *pszUnit = myCSLFetchNameValue(poDS->papszRDC, rdcUNIT_DIST);
     694             : 
     695          30 :     if (pszMinX != nullptr && strlen(pszMinX) > 0 && pszMaxX != nullptr &&
     696          30 :         strlen(pszMaxX) > 0 && pszMinY != nullptr && strlen(pszMinY) > 0 &&
     697          30 :         pszMaxY != nullptr && strlen(pszMaxY) > 0 && pszUnit != nullptr &&
     698          30 :         strlen(pszUnit) > 0)
     699             :     {
     700             :         double dfMinX, dfMaxX, dfMinY, dfMaxY, dfUnit, dfXPixSz, dfYPixSz;
     701             : 
     702          30 :         dfMinX = CPLAtof_nz(pszMinX);
     703          30 :         dfMaxX = CPLAtof_nz(pszMaxX);
     704          30 :         dfMinY = CPLAtof_nz(pszMinY);
     705          30 :         dfMaxY = CPLAtof_nz(pszMaxY);
     706          30 :         dfUnit = CPLAtof_nz(pszUnit);
     707             : 
     708          30 :         dfMinX = dfMinX * dfUnit;
     709          30 :         dfMaxX = dfMaxX * dfUnit;
     710          30 :         dfMinY = dfMinY * dfUnit;
     711          30 :         dfMaxY = dfMaxY * dfUnit;
     712             : 
     713          30 :         dfYPixSz = (dfMinY - dfMaxY) / poDS->nRasterYSize;
     714          30 :         dfXPixSz = (dfMaxX - dfMinX) / poDS->nRasterXSize;
     715             : 
     716          30 :         poDS->adfGeoTransform[0] = dfMinX;
     717          30 :         poDS->adfGeoTransform[1] = dfXPixSz;
     718          30 :         poDS->adfGeoTransform[2] = 0.0;
     719          30 :         poDS->adfGeoTransform[3] = dfMaxY;
     720          30 :         poDS->adfGeoTransform[4] = 0.0;
     721          30 :         poDS->adfGeoTransform[5] = dfYPixSz;
     722             :     }
     723             : 
     724             :     // --------------------------------------------------------------------
     725             :     //      Set Color Table in the presence of a smp file
     726             :     // --------------------------------------------------------------------
     727             : 
     728          30 :     if (poDS->nBands != 3)
     729             :     {
     730             :         const char *pszSMPFilename =
     731          24 :             CPLResetExtension(poDS->pszFilename, extSMP);
     732          24 :         VSILFILE *fpSMP = VSIFOpenL(pszSMPFilename, "rb");
     733          24 :         if (fpSMP != nullptr)
     734             :         {
     735             :             int dfMaxValue =
     736           0 :                 atoi_nz(myCSLFetchNameValue(poDS->papszRDC, rdcMAX_VALUE));
     737             :             int nCatCount =
     738           0 :                 atoi_nz(myCSLFetchNameValue(poDS->papszRDC, rdcLEGEND_CATS));
     739           0 :             if (nCatCount == 0)
     740           0 :                 dfMaxValue = 255;
     741           0 :             VSIFSeekL(fpSMP, smpHEADERSIZE, SEEK_SET);
     742             :             GDALColorEntry oEntry;
     743             :             unsigned char aucRGB[3];
     744           0 :             int i = 0;
     745           0 :             while ((VSIFReadL(&aucRGB, sizeof(aucRGB), 1, fpSMP)) &&
     746             :                    (i <= dfMaxValue))
     747             :             {
     748           0 :                 oEntry.c1 = (short)aucRGB[0];
     749           0 :                 oEntry.c2 = (short)aucRGB[1];
     750           0 :                 oEntry.c3 = (short)aucRGB[2];
     751           0 :                 oEntry.c4 = (short)255;
     752           0 :                 poDS->poColorTable->SetColorEntry(i, &oEntry);
     753           0 :                 i++;
     754             :             }
     755           0 :             VSIFCloseL(fpSMP);
     756             :         }
     757             :     }
     758             : 
     759             :     // --------------------------------------------------------------------
     760             :     //      Check for Unit Type
     761             :     // --------------------------------------------------------------------
     762             : 
     763             :     const char *pszValueUnit =
     764          30 :         myCSLFetchNameValue(poDS->papszRDC, rdcVALUE_UNITS);
     765             : 
     766          30 :     if (pszValueUnit == nullptr)
     767           0 :         poDS->pszUnitType = CPLStrdup("unspecified");
     768             :     else
     769             :     {
     770          30 :         if (STARTS_WITH_CI(pszValueUnit, "meter"))
     771             :         {
     772           0 :             poDS->pszUnitType = CPLStrdup("m");
     773             :         }
     774          30 :         else if (STARTS_WITH_CI(pszValueUnit, "feet"))
     775             :         {
     776           0 :             poDS->pszUnitType = CPLStrdup("ft");
     777             :         }
     778             :         else
     779          30 :             poDS->pszUnitType = CPLStrdup(pszValueUnit);
     780             :     }
     781             : 
     782             :     // --------------------------------------------------------------------
     783             :     //      Check for category names.
     784             :     // --------------------------------------------------------------------
     785             : 
     786             :     int nCatCount =
     787          30 :         atoi_nz(myCSLFetchNameValue(poDS->papszRDC, rdcLEGEND_CATS));
     788             : 
     789          30 :     if (nCatCount > 0)
     790             :     {
     791             :         // ----------------------------------------------------------------
     792             :         //      Sequentialize categories names, from 0 to the last "code n"
     793             :         // ----------------------------------------------------------------
     794             : 
     795           0 :         int nLine = -1;
     796           0 :         for (int i = 0; (i < CSLCount(poDS->papszRDC)) && (nLine == -1); i++)
     797           0 :             if (EQUALN(poDS->papszRDC[i], rdcLEGEND_CATS, 11))
     798           0 :                 nLine = i;  // get the line where legend cats is
     799             : 
     800           0 :         if (nLine > 0)
     801             :         {
     802           0 :             int nCode = 0;
     803           0 :             int nCount = 0;
     804           0 :             sscanf(poDS->papszRDC[++nLine], rdcCODE_N,
     805             :                    &nCode);  // assign legend cats to nCode
     806           0 :             for (int i = 0; (i < 255) && (nCount < nCatCount); i++)
     807             :             {
     808           0 :                 if (i == nCode)
     809             :                 {
     810           0 :                     poDS->papszCategories = CSLAddString(
     811             :                         poDS->papszCategories,
     812           0 :                         CPLParseNameValue(poDS->papszRDC[nLine], nullptr));
     813           0 :                     nCount++;
     814           0 :                     if (nCount < nCatCount)
     815           0 :                         sscanf(poDS->papszRDC[++nLine], rdcCODE_N, &nCode);
     816             :                 }
     817             :                 else
     818           0 :                     poDS->papszCategories =
     819           0 :                         CSLAddString(poDS->papszCategories, "");
     820             :             }
     821             :         }
     822             :     }
     823             : 
     824             :     /* -------------------------------------------------------------------- */
     825             :     /*      Automatic Generated Color Table                                 */
     826             :     /* -------------------------------------------------------------------- */
     827             : 
     828          30 :     if (poDS->papszCategories != nullptr &&
     829           0 :         (poDS->poColorTable->GetColorEntryCount() == 0))
     830             :     {
     831           0 :         int nEntryCount = CSLCount(poDS->papszCategories);
     832             : 
     833             :         GDALColorEntry sFromColor;
     834           0 :         sFromColor.c1 = (short)(255);
     835           0 :         sFromColor.c2 = (short)(0);
     836           0 :         sFromColor.c3 = (short)(0);
     837           0 :         sFromColor.c4 = (short)(255);
     838             : 
     839             :         GDALColorEntry sToColor;
     840           0 :         sToColor.c1 = (short)(0);
     841           0 :         sToColor.c2 = (short)(0);
     842           0 :         sToColor.c3 = (short)(255);
     843           0 :         sToColor.c4 = (short)(255);
     844             : 
     845           0 :         poDS->poColorTable->CreateColorRamp(0, &sFromColor, (nEntryCount - 1),
     846             :                                             &sToColor);
     847             :     }
     848             : 
     849             :     /* -------------------------------------------------------------------- */
     850             :     /*      Initialize any PAM information.                                 */
     851             :     /* -------------------------------------------------------------------- */
     852             : 
     853          30 :     poDS->SetDescription(poOpenInfo->pszFilename);
     854          30 :     poDS->TryLoadXML();
     855             : 
     856             :     /* -------------------------------------------------------------------- */
     857             :     /*      Check for external overviews.                                   */
     858             :     /* -------------------------------------------------------------------- */
     859             : 
     860          30 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
     861             : 
     862          30 :     return poDS;
     863             : }
     864             : 
     865             : /************************************************************************/
     866             : /*                               Create()                               */
     867             : /************************************************************************/
     868             : 
     869          50 : GDALDataset *IdrisiDataset::Create(const char *pszFilename, int nXSize,
     870             :                                    int nYSize, int nBandsIn, GDALDataType eType,
     871             :                                    char ** /* papszOptions */)
     872             : {
     873             :     // --------------------------------------------------------------------
     874             :     //      Check input options
     875             :     // --------------------------------------------------------------------
     876             : 
     877          50 :     if (nBandsIn != 1 && nBandsIn != 3)
     878             :     {
     879           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     880             :                  "Attempt to create IDRISI dataset with an illegal number of "
     881             :                  "bands(%d)."
     882             :                  " Try again by selecting a specific band if possible. \n",
     883             :                  nBandsIn);
     884           4 :         return nullptr;
     885             :     }
     886             : 
     887          46 :     if (nBandsIn == 3 && eType != GDT_Byte)
     888             :     {
     889          13 :         CPLError(
     890             :             CE_Failure, CPLE_AppDefined,
     891             :             "Attempt to create IDRISI dataset with an unsupported combination "
     892             :             "of the number of bands(%d) and data type(%s). \n",
     893             :             nBandsIn, GDALGetDataTypeName(eType));
     894          13 :         return nullptr;
     895             :     }
     896             : 
     897             :     // ----------------------------------------------------------------
     898             :     //  Create the header file with minimum information
     899             :     // ----------------------------------------------------------------
     900             : 
     901          33 :     const char *pszLDataType = nullptr;
     902             : 
     903          33 :     switch (eType)
     904             :     {
     905          17 :         case GDT_Byte:
     906          17 :             if (nBandsIn == 1)
     907          14 :                 pszLDataType = rstBYTE;
     908             :             else
     909           3 :                 pszLDataType = rstRGB24;
     910          17 :             break;
     911           2 :         case GDT_Int16:
     912           2 :             pszLDataType = rstINTEGER;
     913           2 :             break;
     914           3 :         case GDT_Float32:
     915           3 :             pszLDataType = rstREAL;
     916           3 :             break;
     917             :         //--- process compatible data types
     918           1 :         case (GDT_UInt16):
     919           1 :             pszLDataType = rstINTEGER;
     920           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     921             :                      "This process requires a conversion from %s to signed "
     922             :                      "16-bit %s, "
     923             :                      "which may cause data loss.\n",
     924             :                      GDALGetDataTypeName(eType), rstINTEGER);
     925           1 :             break;
     926           1 :         case GDT_UInt32:
     927           1 :             pszLDataType = rstINTEGER;
     928           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     929             :                      "This process requires a conversion from %s to signed "
     930             :                      "16-bit %s, "
     931             :                      "which may cause data loss.\n",
     932             :                      GDALGetDataTypeName(eType), rstINTEGER);
     933           1 :             break;
     934           1 :         case GDT_Int32:
     935           1 :             pszLDataType = rstINTEGER;
     936           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     937             :                      "This process requires a conversion from %s to signed "
     938             :                      "16-bit %s, "
     939             :                      "which may cause data loss.\n",
     940             :                      GDALGetDataTypeName(eType), rstINTEGER);
     941           1 :             break;
     942           1 :         case GDT_Float64:
     943           1 :             pszLDataType = rstREAL;
     944           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     945             :                      "This process requires a conversion from %s to float "
     946             :                      "32-bit %s, "
     947             :                      "which may cause data loss.\n",
     948             :                      GDALGetDataTypeName(eType), rstREAL);
     949           1 :             break;
     950           7 :         default:
     951           7 :             CPLError(CE_Failure, CPLE_AppDefined,
     952             :                      "Attempt to create IDRISI dataset with an illegal "
     953             :                      "data type(%s).\n",
     954             :                      GDALGetDataTypeName(eType));
     955           7 :             return nullptr;
     956             :     };
     957             : 
     958          26 :     char **papszLRDC = nullptr;
     959          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcFILE_FORMAT, rstVERSION);
     960          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcFILE_TITLE, "");
     961          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcDATA_TYPE, pszLDataType);
     962          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcFILE_TYPE, "binary");
     963             :     papszLRDC =
     964          26 :         CSLAddNameValue(papszLRDC, rdcCOLUMNS, CPLSPrintf("%d", nXSize));
     965          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcROWS, CPLSPrintf("%d", nYSize));
     966          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcREF_SYSTEM, "plane");
     967          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcREF_UNITS, "m");
     968          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcUNIT_DIST, "1");
     969          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcMIN_X, "0");
     970          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcMAX_X, CPLSPrintf("%d", nXSize));
     971          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcMIN_Y, "0");
     972          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcMAX_Y, CPLSPrintf("%d", nYSize));
     973          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcPOSN_ERROR, "unspecified");
     974          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcRESOLUTION, "1.0");
     975          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcMIN_VALUE, "0");
     976          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcMAX_VALUE, "0");
     977          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcDISPLAY_MIN, "0");
     978          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcDISPLAY_MAX, "0");
     979          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcVALUE_UNITS, "unspecified");
     980          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcVALUE_ERROR, "unspecified");
     981          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcFLAG_VALUE, "none");
     982          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcFLAG_DEFN, "none");
     983          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcLEGEND_CATS, "0");
     984          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcLINEAGES, "");
     985          26 :     papszLRDC = CSLAddNameValue(papszLRDC, rdcCOMMENTS, "");
     986             : 
     987          26 :     const char *pszLDocFilename = CPLResetExtension(pszFilename, extRDC);
     988             : 
     989          26 :     myCSLSetNameValueSeparator(papszLRDC, ": ");
     990          26 :     SaveAsCRLF(papszLRDC, pszLDocFilename);
     991          26 :     CSLDestroy(papszLRDC);
     992             : 
     993             :     // ----------------------------------------------------------------
     994             :     //  Create an empty data file
     995             :     // ----------------------------------------------------------------
     996             : 
     997          26 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb+");
     998             : 
     999          26 :     if (fp == nullptr)
    1000             :     {
    1001           2 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1002             :                  "Attempt to create file %s' failed.\n", pszFilename);
    1003           2 :         return nullptr;
    1004             :     }
    1005             : 
    1006          36 :     const int nTargetDTSize = EQUAL(pszLDataType, rstBYTE)      ? 1
    1007          19 :                               : EQUAL(pszLDataType, rstINTEGER) ? 2
    1008           7 :                               : EQUAL(pszLDataType, rstRGB24)   ? 3
    1009             :                                                                 : 4;
    1010          24 :     VSIFTruncateL(fp,
    1011          24 :                   static_cast<vsi_l_offset>(nXSize) * nYSize * nTargetDTSize);
    1012          24 :     VSIFCloseL(fp);
    1013             : 
    1014          24 :     return (IdrisiDataset *)GDALOpen(pszFilename, GA_Update);
    1015             : }
    1016             : 
    1017             : /************************************************************************/
    1018             : /*                             CreateCopy()                             */
    1019             : /************************************************************************/
    1020             : 
    1021          29 : GDALDataset *IdrisiDataset::CreateCopy(const char *pszFilename,
    1022             :                                        GDALDataset *poSrcDS, int bStrict,
    1023             :                                        char **papszOptions,
    1024             :                                        GDALProgressFunc pfnProgress,
    1025             :                                        void *pProgressData)
    1026             : {
    1027          29 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    1028           0 :         return nullptr;
    1029             : 
    1030             :     // ------------------------------------------------------------------------
    1031             :     //      Check number of bands
    1032             :     // ------------------------------------------------------------------------
    1033          29 :     if (!(poSrcDS->GetRasterCount() == 1) && !(poSrcDS->GetRasterCount() == 3))
    1034             :     {
    1035           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    1036             :                  "Attempt to create IDRISI dataset with an illegal number of "
    1037             :                  "bands(%d)."
    1038             :                  " Try again by selecting a specific band if possible.\n",
    1039             :                  poSrcDS->GetRasterCount());
    1040           4 :         return nullptr;
    1041             :     }
    1042          27 :     if ((poSrcDS->GetRasterCount() == 3) &&
    1043           2 :         ((poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte) ||
    1044           2 :          (poSrcDS->GetRasterBand(2)->GetRasterDataType() != GDT_Byte) ||
    1045           2 :          (poSrcDS->GetRasterBand(3)->GetRasterDataType() != GDT_Byte)))
    1046             :     {
    1047           0 :         CPLError(
    1048             :             CE_Failure, CPLE_AppDefined,
    1049             :             "Attempt to create IDRISI dataset with an unsupported "
    1050             :             "data type when there are three bands. Only BYTE allowed.\n"
    1051             :             "Try again by selecting a specific band to convert if possible.\n");
    1052           0 :         return nullptr;
    1053             :     }
    1054             : 
    1055             :     // ------------------------------------------------------------------------
    1056             :     //      Check Data types
    1057             :     // ------------------------------------------------------------------------
    1058             : 
    1059          46 :     for (int i = 1; i <= poSrcDS->GetRasterCount(); i++)
    1060             :     {
    1061          29 :         GDALDataType eType = poSrcDS->GetRasterBand(i)->GetRasterDataType();
    1062             : 
    1063          29 :         if (bStrict)
    1064             :         {
    1065          25 :             if (eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Float32)
    1066             :             {
    1067           8 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1068             :                          "Attempt to create IDRISI dataset in strict mode "
    1069             :                          "with an illegal data type(%s).\n",
    1070             :                          GDALGetDataTypeName(eType));
    1071           8 :                 return nullptr;
    1072             :             }
    1073             :         }
    1074             :         else
    1075             :         {
    1076           4 :             if (eType != GDT_Byte && eType != GDT_Int16 &&
    1077           0 :                 eType != GDT_UInt16 && eType != GDT_UInt32 &&
    1078           0 :                 eType != GDT_Int32 && eType != GDT_Float32 &&
    1079             :                 eType != GDT_Float64)
    1080             :             {
    1081           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1082             :                          "Attempt to create IDRISI dataset with an illegal "
    1083             :                          "data type(%s).\n",
    1084             :                          GDALGetDataTypeName(eType));
    1085           0 :                 return nullptr;
    1086             :             }
    1087             :         }
    1088             :     }
    1089             : 
    1090             :     // --------------------------------------------------------------------
    1091             :     //      Define data type
    1092             :     // --------------------------------------------------------------------
    1093             : 
    1094          17 :     GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
    1095          17 :     GDALDataType eType = poBand->GetRasterDataType();
    1096             : 
    1097          17 :     int bSuccessMin = FALSE;
    1098          17 :     int bSuccessMax = FALSE;
    1099             : 
    1100          17 :     double dfMin = poBand->GetMinimum(&bSuccessMin);
    1101          17 :     double dfMax = poBand->GetMaximum(&bSuccessMax);
    1102             : 
    1103          17 :     if (!(bSuccessMin && bSuccessMax))
    1104             :     {
    1105           6 :         poBand->GetStatistics(false, true, &dfMin, &dfMax, nullptr, nullptr);
    1106             :     }
    1107             : 
    1108          17 :     if (!((eType == GDT_Byte) || (eType == GDT_Int16) ||
    1109             :           (eType == GDT_Float32)))
    1110             :     {
    1111           0 :         if (eType == GDT_Float64)
    1112             :         {
    1113           0 :             eType = GDT_Float32;
    1114             :         }
    1115             :         else
    1116             :         {
    1117           0 :             if ((dfMin < (double)SHRT_MIN) || (dfMax > (double)SHRT_MAX))
    1118             :             {
    1119           0 :                 eType = GDT_Float32;
    1120             :             }
    1121             :             else
    1122             :             {
    1123           0 :                 eType = GDT_Int16;
    1124             :             }
    1125             :         }
    1126             :     }
    1127             : 
    1128             :     // --------------------------------------------------------------------
    1129             :     //      Create the dataset
    1130             :     // --------------------------------------------------------------------
    1131             : 
    1132          17 :     IdrisiDataset *poDS = (IdrisiDataset *)IdrisiDataset::Create(
    1133             :         pszFilename, poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
    1134             :         poSrcDS->GetRasterCount(), eType, papszOptions);
    1135             : 
    1136          17 :     if (poDS == nullptr)
    1137          12 :         return nullptr;
    1138             : 
    1139             :     // --------------------------------------------------------------------
    1140             :     //      Copy information to the dataset
    1141             :     // --------------------------------------------------------------------
    1142             : 
    1143             :     double adfGeoTransform[6];
    1144           5 :     if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None)
    1145             :     {
    1146           5 :         poDS->SetGeoTransform(adfGeoTransform);
    1147             :     }
    1148             : 
    1149           5 :     if (!EQUAL(poSrcDS->GetProjectionRef(), ""))
    1150             :     {
    1151           5 :         poDS->SetProjection(poSrcDS->GetProjectionRef());
    1152             :     }
    1153             : 
    1154             :     // --------------------------------------------------------------------
    1155             :     //      Copy information to the raster band(s)
    1156             :     // --------------------------------------------------------------------
    1157             : 
    1158          14 :     for (int i = 1; i <= poDS->nBands; i++)
    1159             :     {
    1160           9 :         GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(i);
    1161             :         IdrisiRasterBand *poDstBand =
    1162           9 :             (IdrisiRasterBand *)poDS->GetRasterBand(i);
    1163             : 
    1164           9 :         if (poDS->nBands == 1)
    1165             :         {
    1166           3 :             poDstBand->SetUnitType(poSrcBand->GetUnitType());
    1167           3 :             poDstBand->SetColorTable(poSrcBand->GetColorTable());
    1168           3 :             poDstBand->SetCategoryNames(poSrcBand->GetCategoryNames());
    1169             : 
    1170           3 :             const GDALRasterAttributeTable *poRAT = poSrcBand->GetDefaultRAT();
    1171             : 
    1172           3 :             if (poRAT != nullptr)
    1173             :             {
    1174           0 :                 poDstBand->SetDefaultRAT(poRAT);
    1175             :             }
    1176             :         }
    1177             : 
    1178           9 :         dfMin = poSrcBand->GetMinimum(nullptr);
    1179           9 :         dfMax = poSrcBand->GetMaximum(nullptr);
    1180           9 :         poDstBand->SetMinMax(dfMin, dfMax);
    1181             :         int bHasNoDataValue;
    1182           9 :         double dfNoDataValue = poSrcBand->GetNoDataValue(&bHasNoDataValue);
    1183           9 :         if (bHasNoDataValue)
    1184           0 :             poDstBand->SetNoDataValue(dfNoDataValue);
    1185             :     }
    1186             : 
    1187             :     // --------------------------------------------------------------------
    1188             :     //      Copy image data
    1189             :     // --------------------------------------------------------------------
    1190             : 
    1191           5 :     if (GDALDatasetCopyWholeRaster((GDALDatasetH)poSrcDS, (GDALDatasetH)poDS,
    1192             :                                    nullptr, pfnProgress,
    1193           5 :                                    pProgressData) != CE_None)
    1194             :     {
    1195           0 :         delete poDS;
    1196           0 :         return nullptr;
    1197             :     }
    1198             : 
    1199             :     // --------------------------------------------------------------------
    1200             :     //      Finalize
    1201             :     // --------------------------------------------------------------------
    1202             : 
    1203           5 :     poDS->FlushCache(false);
    1204             : 
    1205           5 :     return poDS;
    1206             : }
    1207             : 
    1208             : /************************************************************************/
    1209             : /*                            GetFileList()                             */
    1210             : /************************************************************************/
    1211             : 
    1212           4 : char **IdrisiDataset::GetFileList()
    1213             : {
    1214           4 :     char **papszFileList = GDALPamDataset::GetFileList();
    1215             : 
    1216             :     // --------------------------------------------------------------------
    1217             :     //      Symbol table file
    1218             :     // --------------------------------------------------------------------
    1219             : 
    1220           4 :     const char *pszAssociated = CPLResetExtension(pszFilename, extSMP);
    1221             : 
    1222           4 :     if (FileExists(pszAssociated))
    1223             :     {
    1224           0 :         papszFileList = CSLAddString(papszFileList, pszAssociated);
    1225             :     }
    1226             :     else
    1227             :     {
    1228           4 :         pszAssociated = CPLResetExtension(pszFilename, extSMPu);
    1229             : 
    1230           4 :         if (FileExists(pszAssociated))
    1231             :         {
    1232           0 :             papszFileList = CSLAddString(papszFileList, pszAssociated);
    1233             :         }
    1234             :     }
    1235             : 
    1236             :     // --------------------------------------------------------------------
    1237             :     //      Documentation file
    1238             :     // --------------------------------------------------------------------
    1239             : 
    1240           4 :     pszAssociated = CPLResetExtension(pszFilename, extRDC);
    1241             : 
    1242           4 :     if (FileExists(pszAssociated))
    1243             :     {
    1244           4 :         papszFileList = CSLAddString(papszFileList, pszAssociated);
    1245             :     }
    1246             :     else
    1247             :     {
    1248           0 :         pszAssociated = CPLResetExtension(pszFilename, extRDCu);
    1249             : 
    1250           0 :         if (FileExists(pszAssociated))
    1251             :         {
    1252           0 :             papszFileList = CSLAddString(papszFileList, pszAssociated);
    1253             :         }
    1254             :     }
    1255             : 
    1256             :     // --------------------------------------------------------------------
    1257             :     //      Reference file
    1258             :     // --------------------------------------------------------------------
    1259             : 
    1260           4 :     pszAssociated = CPLResetExtension(pszFilename, extREF);
    1261             : 
    1262           4 :     if (FileExists(pszAssociated))
    1263             :     {
    1264           0 :         papszFileList = CSLAddString(papszFileList, pszAssociated);
    1265             :     }
    1266             :     else
    1267             :     {
    1268           4 :         pszAssociated = CPLResetExtension(pszFilename, extREFu);
    1269             : 
    1270           4 :         if (FileExists(pszAssociated))
    1271             :         {
    1272           0 :             papszFileList = CSLAddString(papszFileList, pszAssociated);
    1273             :         }
    1274             :     }
    1275             : 
    1276           4 :     return papszFileList;
    1277             : }
    1278             : 
    1279             : /************************************************************************/
    1280             : /*                          GetGeoTransform()                           */
    1281             : /************************************************************************/
    1282             : 
    1283           9 : CPLErr IdrisiDataset::GetGeoTransform(double *padfTransform)
    1284             : {
    1285           9 :     if (GDALPamDataset::GetGeoTransform(padfTransform) != CE_None)
    1286             :     {
    1287           9 :         memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
    1288             :         /*
    1289             : if( adfGeoTransform[0] == 0.0
    1290             : &&  adfGeoTransform[1] == 1.0
    1291             : &&  adfGeoTransform[2] == 0.0
    1292             : &&  adfGeoTransform[3] == 0.0
    1293             : &&  adfGeoTransform[4] == 0.0
    1294             : &&  adfGeoTransform[5] == 1.0 )
    1295             :     return CE_Failure;
    1296             :         */
    1297             :     }
    1298             : 
    1299           9 :     return CE_None;
    1300             : }
    1301             : 
    1302             : /************************************************************************/
    1303             : /*                          SetGeoTransform()                           */
    1304             : /************************************************************************/
    1305             : 
    1306          13 : CPLErr IdrisiDataset::SetGeoTransform(double *padfTransform)
    1307             : {
    1308          13 :     if (padfTransform[2] != 0.0 || padfTransform[4] != 0.0)
    1309             :     {
    1310           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1311             :                  "Attempt to set rotated geotransform on Idrisi Raster file.\n"
    1312             :                  "Idrisi Raster does not support rotation.\n");
    1313           0 :         return CE_Failure;
    1314             :     }
    1315             : 
    1316             :     // --------------------------------------------------------------------
    1317             :     // Update the .rdc file
    1318             :     // --------------------------------------------------------------------
    1319             : 
    1320          13 :     double dfXPixSz = padfTransform[1];
    1321          13 :     double dfYPixSz = padfTransform[5];
    1322          13 :     double dfMinX = padfTransform[0];
    1323          13 :     double dfMaxX = (dfXPixSz * nRasterXSize) + dfMinX;
    1324             : 
    1325             :     double dfMinY, dfMaxY;
    1326          13 :     if (dfYPixSz < 0)
    1327             :     {
    1328          13 :         dfMaxY = padfTransform[3];
    1329          13 :         dfMinY = (dfYPixSz * nRasterYSize) + padfTransform[3];
    1330             :     }
    1331             :     else
    1332             :     {
    1333           0 :         dfMaxY = (dfYPixSz * nRasterYSize) + padfTransform[3];
    1334           0 :         dfMinY = padfTransform[3];
    1335             :     }
    1336             : 
    1337          13 :     papszRDC = CSLSetNameValue(papszRDC, rdcMIN_X, CPLSPrintf("%.7f", dfMinX));
    1338          13 :     papszRDC = CSLSetNameValue(papszRDC, rdcMAX_X, CPLSPrintf("%.7f", dfMaxX));
    1339          13 :     papszRDC = CSLSetNameValue(papszRDC, rdcMIN_Y, CPLSPrintf("%.7f", dfMinY));
    1340          13 :     papszRDC = CSLSetNameValue(papszRDC, rdcMAX_Y, CPLSPrintf("%.7f", dfMaxY));
    1341          13 :     papszRDC = CSLSetNameValue(papszRDC, rdcRESOLUTION,
    1342             :                                CPLSPrintf("%.7f", fabs(dfYPixSz)));
    1343             : 
    1344             :     // --------------------------------------------------------------------
    1345             :     // Update the Dataset attribute
    1346             :     // --------------------------------------------------------------------
    1347             : 
    1348          13 :     memcpy(adfGeoTransform, padfTransform, sizeof(double) * 6);
    1349             : 
    1350          13 :     return CE_None;
    1351             : }
    1352             : 
    1353             : /************************************************************************/
    1354             : /*                          GetSpatialRef()                             */
    1355             : /************************************************************************/
    1356             : 
    1357           1 : const OGRSpatialReference *IdrisiDataset::GetSpatialRef() const
    1358             : {
    1359           1 :     const auto poSRS = GDALPamDataset::GetSpatialRef();
    1360           1 :     if (poSRS)
    1361           0 :         return poSRS;
    1362             : 
    1363           1 :     if (m_oSRS.IsEmpty())
    1364             :     {
    1365           1 :         const char *pszRefSystem = myCSLFetchNameValue(papszRDC, rdcREF_SYSTEM);
    1366           1 :         const char *pszRefUnit = myCSLFetchNameValue(papszRDC, rdcREF_UNITS);
    1367           1 :         if (pszRefSystem != nullptr && pszRefUnit != nullptr)
    1368           1 :             IdrisiGeoReference2Wkt(pszFilename, pszRefSystem, pszRefUnit,
    1369           1 :                                    m_oSRS);
    1370             :     }
    1371           1 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
    1372             : }
    1373             : 
    1374             : /************************************************************************/
    1375             : /*                           SetProjection()                            */
    1376             : /************************************************************************/
    1377             : 
    1378          13 : CPLErr IdrisiDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    1379             : {
    1380          13 :     m_oSRS.Clear();
    1381          13 :     if (poSRS)
    1382          13 :         m_oSRS = *poSRS;
    1383             : 
    1384          13 :     char *pszRefSystem = nullptr;
    1385          13 :     char *pszRefUnit = nullptr;
    1386             : 
    1387          13 :     CPLErr eResult = Wkt2GeoReference(m_oSRS, &pszRefSystem, &pszRefUnit);
    1388             : 
    1389          13 :     papszRDC = CSLSetNameValue(papszRDC, rdcREF_SYSTEM, pszRefSystem);
    1390          13 :     papszRDC = CSLSetNameValue(papszRDC, rdcREF_UNITS, pszRefUnit);
    1391             : 
    1392          13 :     CPLFree(pszRefSystem);
    1393          13 :     CPLFree(pszRefUnit);
    1394             : 
    1395          13 :     return eResult;
    1396             : }
    1397             : 
    1398             : /************************************************************************/
    1399             : /*                          IdrisiRasterBand()                          */
    1400             : /************************************************************************/
    1401             : 
    1402          42 : IdrisiRasterBand::IdrisiRasterBand(IdrisiDataset *poDSIn, int nBandIn,
    1403          42 :                                    GDALDataType eDataTypeIn)
    1404             :     : poDefaultRAT(nullptr),
    1405         126 :       nRecordSize(poDSIn->GetRasterXSize() * poDSIn->nBands *
    1406          42 :                   GDALGetDataTypeSizeBytes(eDataTypeIn)),
    1407          42 :       pabyScanLine(static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
    1408             :           poDSIn->GetRasterXSize(), GDALGetDataTypeSizeBytes(eDataTypeIn),
    1409             :           poDSIn->nBands))),
    1410          84 :       fMaximum(0.0), fMinimum(0.0), bFirstVal(true)
    1411             : {
    1412          42 :     poDS = poDSIn;
    1413          42 :     nBand = nBandIn;
    1414          42 :     eDataType = eDataTypeIn;
    1415          42 :     nBlockYSize = 1;
    1416          42 :     nBlockXSize = poDS->GetRasterXSize();
    1417          42 : }
    1418             : 
    1419             : /************************************************************************/
    1420             : /*                         ~IdrisiRasterBand()                          */
    1421             : /************************************************************************/
    1422             : 
    1423          84 : IdrisiRasterBand::~IdrisiRasterBand()
    1424             : {
    1425          42 :     CPLFree(pabyScanLine);
    1426             : 
    1427          42 :     if (poDefaultRAT)
    1428             :     {
    1429           0 :         delete poDefaultRAT;
    1430             :     }
    1431          84 : }
    1432             : 
    1433             : /************************************************************************/
    1434             : /*                             IReadBlock()                             */
    1435             : /************************************************************************/
    1436             : 
    1437        1392 : CPLErr IdrisiRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    1438             :                                     void *pImage)
    1439             : {
    1440        1392 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1441             : 
    1442        1392 :     if (VSIFSeekL(poGDS->fp, vsi_l_offset(nRecordSize) * nBlockYOff, SEEK_SET) <
    1443             :         0)
    1444             :     {
    1445           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1446             :                  "Can't seek(%s) block with X offset %d and Y offset %d.\n%s",
    1447             :                  poGDS->pszFilename, nBlockXOff, nBlockYOff,
    1448           0 :                  VSIStrerror(errno));
    1449           0 :         return CE_Failure;
    1450             :     }
    1451             : 
    1452        1392 :     if ((int)VSIFReadL(pabyScanLine, 1, nRecordSize, poGDS->fp) < nRecordSize)
    1453             :     {
    1454           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1455             :                  "Can't read(%s) block with X offset %d and Y offset %d.\n%s",
    1456             :                  poGDS->pszFilename, nBlockXOff, nBlockYOff,
    1457           0 :                  VSIStrerror(errno));
    1458           0 :         return CE_Failure;
    1459             :     }
    1460             : 
    1461        1392 :     if (poGDS->nBands == 3)
    1462             :     {
    1463       43380 :         for (int i = 0, j = (3 - nBand); i < nBlockXSize; i++, j += 3)
    1464             :         {
    1465       42800 :             ((GByte *)pImage)[i] = pabyScanLine[j];
    1466             :         }
    1467             :     }
    1468             :     else
    1469             :     {
    1470         812 :         memcpy(pImage, pabyScanLine, nRecordSize);
    1471             :     }
    1472             : 
    1473             : #ifdef CPL_MSB
    1474             :     if (eDataType == GDT_Float32)
    1475             :         GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
    1476             : #endif
    1477             : 
    1478        1392 :     return CE_None;
    1479             : }
    1480             : 
    1481             : /************************************************************************/
    1482             : /*                            IWriteBlock()                             */
    1483             : /************************************************************************/
    1484             : 
    1485         230 : CPLErr IdrisiRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
    1486             :                                      void *pImage)
    1487             : {
    1488         230 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1489             : 
    1490             : #ifdef CPL_MSB
    1491             :     // Swap in input buffer if needed.
    1492             :     if (eDataType == GDT_Float32)
    1493             :         GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
    1494             : #endif
    1495             : 
    1496         230 :     if (poGDS->nBands == 1)
    1497             :     {
    1498          50 :         memcpy(pabyScanLine, pImage, nRecordSize);
    1499             :     }
    1500             :     else
    1501             :     {
    1502         180 :         if (nBand > 1)
    1503             :         {
    1504         120 :             VSIFSeekL(poGDS->fp, vsi_l_offset(nRecordSize) * nBlockYOff,
    1505             :                       SEEK_SET);
    1506         120 :             VSIFReadL(pabyScanLine, 1, nRecordSize, poGDS->fp);
    1507             :         }
    1508             :         int i, j;
    1509        7980 :         for (i = 0, j = (3 - nBand); i < nBlockXSize; i++, j += 3)
    1510             :         {
    1511        7800 :             pabyScanLine[j] = ((GByte *)pImage)[i];
    1512             :         }
    1513             :     }
    1514             : 
    1515             : #ifdef CPL_MSB
    1516             :     // Swap input buffer back to original form.
    1517             :     if (eDataType == GDT_Float32)
    1518             :         GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
    1519             : #endif
    1520             : 
    1521         230 :     VSIFSeekL(poGDS->fp, vsi_l_offset(nRecordSize) * nBlockYOff, SEEK_SET);
    1522             : 
    1523         230 :     if ((int)VSIFWriteL(pabyScanLine, 1, nRecordSize, poGDS->fp) < nRecordSize)
    1524             :     {
    1525           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1526             :                  "Can't write(%s) block with X offset %d and Y offset %d.\n%s",
    1527             :                  poGDS->pszFilename, nBlockXOff, nBlockYOff,
    1528           0 :                  VSIStrerror(errno));
    1529           0 :         return CE_Failure;
    1530             :     }
    1531             : 
    1532         230 :     int bHasNoDataValue = FALSE;
    1533         230 :     float fNoDataValue = (float)GetNoDataValue(&bHasNoDataValue);
    1534             : 
    1535             :     // --------------------------------------------------------------------
    1536             :     //      Search for the minimum and maximum values
    1537             :     // --------------------------------------------------------------------
    1538             : 
    1539         230 :     if (eDataType == GDT_Float32)
    1540             :     {
    1541         530 :         for (int i = 0; i < nBlockXSize; i++)
    1542             :         {
    1543         500 :             float fVal = ((float *)pabyScanLine)[i];  // this is fine
    1544         500 :             if (!bHasNoDataValue || fVal != fNoDataValue)
    1545             :             {
    1546         500 :                 if (bFirstVal)
    1547             :                 {
    1548           2 :                     fMinimum = fVal;
    1549           2 :                     fMaximum = fVal;
    1550           2 :                     bFirstVal = false;
    1551             :                 }
    1552             :                 else
    1553             :                 {
    1554         498 :                     if (fVal < fMinimum)
    1555           3 :                         fMinimum = fVal;
    1556         498 :                     if (fVal > fMaximum)
    1557           9 :                         fMaximum = fVal;
    1558             :                 }
    1559             :             }
    1560             :         }
    1561             :     }
    1562         200 :     else if (eDataType == GDT_Int16)
    1563             :     {
    1564         110 :         for (int i = 0; i < nBlockXSize; i++)
    1565             :         {
    1566         100 :             float fVal = (float)((GInt16 *)pabyScanLine)[i];
    1567         100 :             if (!bHasNoDataValue || fVal != fNoDataValue)
    1568             :             {
    1569         100 :                 if (bFirstVal)
    1570             :                 {
    1571           1 :                     fMinimum = fVal;
    1572           1 :                     fMaximum = fVal;
    1573           1 :                     bFirstVal = false;
    1574             :                 }
    1575             :                 else
    1576             :                 {
    1577          99 :                     if (fVal < fMinimum)
    1578           0 :                         fMinimum = fVal;
    1579          99 :                     if (fVal > fMaximum)
    1580           0 :                         fMaximum = fVal;
    1581             :                 }
    1582             :             }
    1583             :         }
    1584             :     }
    1585         190 :     else if (poGDS->nBands == 1)
    1586             :     {
    1587         110 :         for (int i = 0; i < nBlockXSize; i++)
    1588             :         {
    1589         100 :             float fVal = (float)((GByte *)pabyScanLine)[i];
    1590         100 :             if (!bHasNoDataValue || fVal != fNoDataValue)
    1591             :             {
    1592         100 :                 if (bFirstVal)
    1593             :                 {
    1594           1 :                     fMinimum = fVal;
    1595           1 :                     fMaximum = fVal;
    1596           1 :                     bFirstVal = false;
    1597             :                 }
    1598             :                 else
    1599             :                 {
    1600          99 :                     if (fVal < fMinimum)
    1601           0 :                         fMinimum =
    1602             :                             fVal;  // I don't change this part, keep it as it is
    1603          99 :                     if (fVal > fMaximum)
    1604           0 :                         fMaximum = fVal;
    1605             :                 }
    1606             :             }
    1607             :         }
    1608             :     }
    1609             :     else
    1610             :     {
    1611        7980 :         for (int i = 0, j = (3 - nBand); i < nBlockXSize; i++, j += 3)
    1612             :         {
    1613        7800 :             float fVal = (float)((GByte *)pabyScanLine)[j];
    1614        7800 :             if (!bHasNoDataValue || fVal != fNoDataValue)
    1615             :             {
    1616        7800 :                 if (bFirstVal)
    1617             :                 {
    1618           6 :                     fMinimum = fVal;
    1619           6 :                     fMaximum = fVal;
    1620           6 :                     bFirstVal = false;
    1621             :                 }
    1622             :                 else
    1623             :                 {
    1624        7794 :                     if (fVal < fMinimum)
    1625           0 :                         fMinimum = fVal;
    1626        7794 :                     if (fVal > fMaximum)
    1627          28 :                         fMaximum = fVal;
    1628             :                 }
    1629             :             }
    1630             :         }
    1631             :     }
    1632             : 
    1633         230 :     return CE_None;
    1634             : }
    1635             : 
    1636             : /************************************************************************/
    1637             : /*                             GetMinimum()                             */
    1638             : /************************************************************************/
    1639             : 
    1640           0 : double IdrisiRasterBand::GetMinimum(int *pbSuccess)
    1641             : {
    1642           0 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1643             : 
    1644           0 :     if (myCSLFetchNameValue(poGDS->papszRDC, rdcMIN_VALUE) == nullptr)
    1645           0 :         return GDALPamRasterBand::GetMinimum(pbSuccess);
    1646             : 
    1647             :     double adfMinValue[3];
    1648           0 :     CPLsscanf(myCSLFetchNameValue(poGDS->papszRDC, rdcMIN_VALUE), "%lf %lf %lf",
    1649             :               &adfMinValue[0], &adfMinValue[1], &adfMinValue[2]);
    1650             : 
    1651           0 :     if (pbSuccess)
    1652             :     {
    1653           0 :         *pbSuccess = true;
    1654             :     }
    1655             : 
    1656           0 :     return adfMinValue[this->nBand - 1];
    1657             : }
    1658             : 
    1659             : /************************************************************************/
    1660             : /*                             GetMaximum()                             */
    1661             : /************************************************************************/
    1662             : 
    1663           0 : double IdrisiRasterBand::GetMaximum(int *pbSuccess)
    1664             : {
    1665           0 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1666             : 
    1667           0 :     if (myCSLFetchNameValue(poGDS->papszRDC, rdcMAX_VALUE) == nullptr)
    1668           0 :         return GDALPamRasterBand::GetMaximum(pbSuccess);
    1669             : 
    1670             :     double adfMaxValue[3];
    1671           0 :     CPLsscanf(myCSLFetchNameValue(poGDS->papszRDC, rdcMAX_VALUE), "%lf %lf %lf",
    1672             :               &adfMaxValue[0], &adfMaxValue[1], &adfMaxValue[2]);
    1673             : 
    1674           0 :     if (pbSuccess)
    1675             :     {
    1676           0 :         *pbSuccess = true;
    1677             :     }
    1678             : 
    1679           0 :     return adfMaxValue[this->nBand - 1];
    1680             : }
    1681             : 
    1682             : /************************************************************************/
    1683             : /*                           GetNoDataValue()                           */
    1684             : /************************************************************************/
    1685             : 
    1686         276 : double IdrisiRasterBand::GetNoDataValue(int *pbSuccess)
    1687             : {
    1688         276 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1689             : 
    1690         276 :     const char *pszFlagDefn = nullptr;
    1691             : 
    1692         276 :     if (myCSLFetchNameValue(poGDS->papszRDC, rdcFLAG_DEFN) != nullptr)
    1693         276 :         pszFlagDefn = myCSLFetchNameValue(poGDS->papszRDC, rdcFLAG_DEFN);
    1694           0 :     else if (myCSLFetchNameValue(poGDS->papszRDC, rdcFLAG_DEFN2) != nullptr)
    1695           0 :         pszFlagDefn = myCSLFetchNameValue(poGDS->papszRDC, rdcFLAG_DEFN2);
    1696             : 
    1697             :     // ------------------------------------------------------------------------
    1698             :     // If Flag_Def is not "none", Flag_Value means "background"
    1699             :     // or "missing data"
    1700             :     // ------------------------------------------------------------------------
    1701             : 
    1702             :     double dfNoData;
    1703         276 :     if (pszFlagDefn != nullptr && !EQUAL(pszFlagDefn, "none"))
    1704             :     {
    1705           0 :         dfNoData =
    1706           0 :             CPLAtof_nz(myCSLFetchNameValue(poGDS->papszRDC, rdcFLAG_VALUE));
    1707           0 :         if (pbSuccess)
    1708           0 :             *pbSuccess = TRUE;
    1709             :     }
    1710             :     else
    1711             :     {
    1712         276 :         dfNoData = -9999.0; /* this value should be ignored */
    1713         276 :         if (pbSuccess)
    1714         276 :             *pbSuccess = FALSE;
    1715             :     }
    1716             : 
    1717         276 :     return dfNoData;
    1718             : }
    1719             : 
    1720             : /************************************************************************/
    1721             : /*                           SetNoDataValue()                           */
    1722             : /************************************************************************/
    1723             : 
    1724           0 : CPLErr IdrisiRasterBand::SetNoDataValue(double dfNoDataValue)
    1725             : {
    1726           0 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1727             : 
    1728           0 :     poGDS->papszRDC = CSLSetNameValue(poGDS->papszRDC, rdcFLAG_VALUE,
    1729             :                                       CPLSPrintf("%.7g", dfNoDataValue));
    1730           0 :     poGDS->papszRDC =
    1731           0 :         CSLSetNameValue(poGDS->papszRDC, rdcFLAG_DEFN, "missing data");
    1732             : 
    1733           0 :     return CE_None;
    1734             : }
    1735             : 
    1736             : /************************************************************************/
    1737             : /*                       GetColorInterpretation()                       */
    1738             : /************************************************************************/
    1739             : 
    1740           0 : GDALColorInterp IdrisiRasterBand::GetColorInterpretation()
    1741             : {
    1742           0 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1743             : 
    1744           0 :     if (poGDS->nBands == 3)
    1745             :     {
    1746           0 :         switch (nBand)
    1747             :         {
    1748           0 :             case 1:
    1749           0 :                 return GCI_BlueBand;
    1750           0 :             case 2:
    1751           0 :                 return GCI_GreenBand;
    1752           0 :             case 3:
    1753           0 :                 return GCI_RedBand;
    1754             :         }
    1755             :     }
    1756           0 :     else if (poGDS->poColorTable->GetColorEntryCount() > 0)
    1757             :     {
    1758           0 :         return GCI_PaletteIndex;
    1759             :     }
    1760           0 :     return GCI_GrayIndex;
    1761             : }
    1762             : 
    1763             : /************************************************************************/
    1764             : /*                          GetCategoryNames()                          */
    1765             : /************************************************************************/
    1766             : 
    1767           0 : char **IdrisiRasterBand::GetCategoryNames()
    1768             : {
    1769           0 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1770             : 
    1771           0 :     return poGDS->papszCategories;
    1772             : }
    1773             : 
    1774             : /************************************************************************/
    1775             : /*                          SetCategoryNames()                          */
    1776             : /************************************************************************/
    1777             : 
    1778           3 : CPLErr IdrisiRasterBand::SetCategoryNames(char **papszCategoryNames)
    1779             : {
    1780           3 :     const int nCatCount = CSLCount(papszCategoryNames);
    1781             : 
    1782           3 :     if (nCatCount == 0)
    1783           3 :         return CE_None;
    1784             : 
    1785           0 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1786             : 
    1787           0 :     CSLDestroy(poGDS->papszCategories);
    1788           0 :     poGDS->papszCategories = CSLDuplicate(papszCategoryNames);
    1789             : 
    1790             :     // ------------------------------------------------------
    1791             :     //        Search for the "Legend cats  : N" line
    1792             :     // ------------------------------------------------------
    1793             : 
    1794           0 :     int nLine = -1;
    1795           0 :     for (int i = 0; (i < CSLCount(poGDS->papszRDC)) && (nLine == -1); i++)
    1796           0 :         if (EQUALN(poGDS->papszRDC[i], rdcLEGEND_CATS, 12))
    1797           0 :             nLine = i;
    1798             : 
    1799           0 :     if (nLine < 0)
    1800           0 :         return CE_None;
    1801             : 
    1802           0 :     int nCount = atoi_nz(myCSLFetchNameValue(poGDS->papszRDC, rdcLEGEND_CATS));
    1803             : 
    1804             :     // ------------------------------------------------------
    1805             :     //        Delete old instance of the category names
    1806             :     // ------------------------------------------------------
    1807             : 
    1808           0 :     if (nCount > 0)
    1809           0 :         poGDS->papszRDC =
    1810           0 :             CSLRemoveStrings(poGDS->papszRDC, nLine + 1, nCount, nullptr);
    1811             : 
    1812           0 :     nCount = 0;
    1813             : 
    1814           0 :     for (int i = 0; i < nCatCount; i++)
    1815             :     {
    1816           0 :         if ((strlen(papszCategoryNames[i]) > 0))
    1817             :         {
    1818           0 :             poGDS->papszRDC =
    1819           0 :                 CSLInsertString(poGDS->papszRDC, (nLine + nCount + 1),
    1820             :                                 CPLSPrintf("%s:%s", CPLSPrintf(rdcCODE_N, i),
    1821           0 :                                            papszCategoryNames[i]));
    1822           0 :             nCount++;
    1823             :         }
    1824             :     }
    1825             : 
    1826           0 :     poGDS->papszRDC =
    1827           0 :         CSLSetNameValue(poGDS->papszRDC, rdcLEGEND_CATS,
    1828             :                         CPLSPrintf("%d", nCount));  // this is fine
    1829             : 
    1830           0 :     return CE_None;
    1831             : }
    1832             : 
    1833             : /************************************************************************/
    1834             : /*                           GetColorTable()                            */
    1835             : /************************************************************************/
    1836             : 
    1837           0 : GDALColorTable *IdrisiRasterBand::GetColorTable()
    1838             : {
    1839           0 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1840             : 
    1841           0 :     if (poGDS->poColorTable->GetColorEntryCount() == 0)
    1842             :     {
    1843           0 :         return nullptr;
    1844             :     }
    1845             : 
    1846           0 :     return poGDS->poColorTable;
    1847             : }
    1848             : 
    1849             : /************************************************************************/
    1850             : /*                           SetColorTable()                            */
    1851             : /************************************************************************/
    1852             : 
    1853           3 : CPLErr IdrisiRasterBand::SetColorTable(GDALColorTable *poColorTable)
    1854             : {
    1855           3 :     if (poColorTable == nullptr)
    1856             :     {
    1857           3 :         return CE_None;
    1858             :     }
    1859             : 
    1860           0 :     if (poColorTable->GetColorEntryCount() == 0)
    1861             :     {
    1862           0 :         return CE_None;
    1863             :     }
    1864             : 
    1865           0 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1866             : 
    1867           0 :     delete poGDS->poColorTable;
    1868             : 
    1869           0 :     poGDS->poColorTable = poColorTable->Clone();
    1870             : 
    1871           0 :     const char *pszSMPFilename = CPLResetExtension(poGDS->pszFilename, extSMP);
    1872           0 :     VSILFILE *fpSMP = VSIFOpenL(pszSMPFilename, "w");
    1873             : 
    1874           0 :     if (fpSMP != nullptr)
    1875             :     {
    1876           0 :         VSIFWriteL("[Idrisi]", 8, 1, fpSMP);
    1877           0 :         GByte nPlatform = 1;
    1878           0 :         VSIFWriteL(&nPlatform, 1, 1, fpSMP);
    1879           0 :         GByte nVersion = 11;
    1880           0 :         VSIFWriteL(&nVersion, 1, 1, fpSMP);
    1881           0 :         GByte nDepth = 8;
    1882           0 :         VSIFWriteL(&nDepth, 1, 1, fpSMP);
    1883           0 :         GByte nHeadSz = 18;
    1884           0 :         VSIFWriteL(&nHeadSz, 1, 1, fpSMP);
    1885           0 :         GUInt16 nCount = 255;
    1886           0 :         VSIFWriteL(&nCount, 2, 1, fpSMP);
    1887           0 :         GUInt16 nMix = 0;
    1888           0 :         VSIFWriteL(&nMix, 2, 1, fpSMP);
    1889           0 :         GUInt16 nMax = 255;
    1890           0 :         VSIFWriteL(&nMax, 2, 1, fpSMP);
    1891             : 
    1892             :         GDALColorEntry oEntry;
    1893             :         GByte aucRGB[3];
    1894             : 
    1895           0 :         for (int i = 0; i < poColorTable->GetColorEntryCount(); i++)
    1896             :         {
    1897           0 :             poColorTable->GetColorEntryAsRGB(i, &oEntry);
    1898           0 :             aucRGB[0] = (GByte)oEntry.c1;
    1899           0 :             aucRGB[1] = (GByte)oEntry.c2;
    1900           0 :             aucRGB[2] = (GByte)oEntry.c3;
    1901           0 :             VSIFWriteL(&aucRGB, 3, 1, fpSMP);
    1902             :         }
    1903             :         /* smp files always have 256 occurrences. */
    1904           0 :         for (int i = poColorTable->GetColorEntryCount(); i <= 255; i++)
    1905             :         {
    1906           0 :             poColorTable->GetColorEntryAsRGB(i, &oEntry);
    1907           0 :             aucRGB[0] = (GByte)0;
    1908           0 :             aucRGB[1] = (GByte)0;
    1909           0 :             aucRGB[2] = (GByte)0;
    1910           0 :             VSIFWriteL(&aucRGB, 3, 1, fpSMP);
    1911             :         }
    1912           0 :         VSIFCloseL(fpSMP);
    1913             :     }
    1914             : 
    1915           0 :     return CE_None;
    1916             : }
    1917             : 
    1918             : /************************************************************************/
    1919             : /*                           GetUnitType()                              */
    1920             : /************************************************************************/
    1921             : 
    1922           0 : const char *IdrisiRasterBand::GetUnitType()
    1923             : {
    1924           0 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1925             : 
    1926           0 :     return poGDS->pszUnitType;
    1927             : }
    1928             : 
    1929             : /************************************************************************/
    1930             : /*                            SetUnitType()                             */
    1931             : /************************************************************************/
    1932             : 
    1933           3 : CPLErr IdrisiRasterBand::SetUnitType(const char *pszUnitType)
    1934             : {
    1935           3 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1936             : 
    1937           3 :     if (strlen(pszUnitType) == 0)
    1938             :     {
    1939           3 :         poGDS->papszRDC =
    1940           3 :             CSLSetNameValue(poGDS->papszRDC, rdcVALUE_UNITS, "unspecified");
    1941             :     }
    1942             :     else
    1943             :     {
    1944           0 :         poGDS->papszRDC =
    1945           0 :             CSLSetNameValue(poGDS->papszRDC, rdcVALUE_UNITS, pszUnitType);
    1946             :     }
    1947             : 
    1948           3 :     return CE_None;
    1949             : }
    1950             : 
    1951             : /************************************************************************/
    1952             : /*                             SetMinMax()                              */
    1953             : /************************************************************************/
    1954             : 
    1955          49 : CPLErr IdrisiRasterBand::SetMinMax(double dfMin, double dfMax)
    1956             : {
    1957          49 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    1958             : 
    1959          49 :     fMinimum = (float)dfMin;
    1960          49 :     fMaximum = (float)dfMax;
    1961             : 
    1962          49 :     double adfMin[3] = {0.0, 0.0, 0.0};
    1963          49 :     double adfMax[3] = {0.0, 0.0, 0.0};
    1964             : 
    1965          49 :     if (myCSLFetchNameValue(poGDS->papszRDC, rdcMIN_VALUE) != nullptr)
    1966          49 :         CPLsscanf(myCSLFetchNameValue(poGDS->papszRDC, rdcMIN_VALUE),
    1967             :                   "%lf %lf %lf", &adfMin[0], &adfMin[1], &adfMin[2]);
    1968          49 :     if (myCSLFetchNameValue(poGDS->papszRDC, rdcMAX_VALUE) != nullptr)
    1969          49 :         CPLsscanf(myCSLFetchNameValue(poGDS->papszRDC, rdcMAX_VALUE),
    1970             :                   "%lf %lf %lf", &adfMax[0], &adfMax[1], &adfMax[2]);
    1971             : 
    1972          49 :     adfMin[nBand - 1] = dfMin;
    1973          49 :     adfMax[nBand - 1] = dfMax;
    1974             : 
    1975          49 :     if (poGDS->nBands == 3)
    1976             :     {
    1977          24 :         poGDS->papszRDC = CSLSetNameValue(
    1978             :             poGDS->papszRDC, rdcMIN_VALUE,
    1979             :             CPLSPrintf("%.8g %.8g %.8g", adfMin[0], adfMin[1], adfMin[2]));
    1980          24 :         poGDS->papszRDC = CSLSetNameValue(
    1981             :             poGDS->papszRDC, rdcMAX_VALUE,
    1982             :             CPLSPrintf("%.8g %.8g %.8g", adfMax[0], adfMax[1], adfMax[2]));
    1983          24 :         poGDS->papszRDC = CSLSetNameValue(
    1984             :             poGDS->papszRDC, rdcDISPLAY_MIN,
    1985             :             CPLSPrintf("%.8g %.8g %.8g", adfMin[0], adfMin[1], adfMin[2]));
    1986          24 :         poGDS->papszRDC = CSLSetNameValue(
    1987             :             poGDS->papszRDC, rdcDISPLAY_MAX,
    1988             :             CPLSPrintf("%.8g %.8g %.8g", adfMax[0], adfMax[1], adfMax[2]));
    1989             :     }
    1990             :     else
    1991             :     {
    1992          25 :         poGDS->papszRDC = CSLSetNameValue(poGDS->papszRDC, rdcMIN_VALUE,
    1993             :                                           CPLSPrintf("%.8g", adfMin[0]));
    1994          25 :         poGDS->papszRDC = CSLSetNameValue(poGDS->papszRDC, rdcMAX_VALUE,
    1995             :                                           CPLSPrintf("%.8g", adfMax[0]));
    1996          25 :         poGDS->papszRDC = CSLSetNameValue(poGDS->papszRDC, rdcDISPLAY_MIN,
    1997             :                                           CPLSPrintf("%.8g", adfMin[0]));
    1998          25 :         poGDS->papszRDC = CSLSetNameValue(poGDS->papszRDC, rdcDISPLAY_MAX,
    1999             :                                           CPLSPrintf("%.8g", adfMax[0]));
    2000             :     }
    2001             : 
    2002          49 :     return CE_None;
    2003             : }
    2004             : 
    2005             : /************************************************************************/
    2006             : /*                           SetStatistics()                            */
    2007             : /************************************************************************/
    2008             : 
    2009          20 : CPLErr IdrisiRasterBand::SetStatistics(double dfMin, double dfMax,
    2010             :                                        double dfMean, double dfStdDev)
    2011             : {
    2012          20 :     SetMinMax(dfMin, dfMax);
    2013             : 
    2014          20 :     return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
    2015             : }
    2016             : 
    2017             : /************************************************************************/
    2018             : /*                           SetDefaultRAT()                            */
    2019             : /************************************************************************/
    2020             : 
    2021           0 : CPLErr IdrisiRasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT)
    2022             : {
    2023           0 :     if (!poRAT)
    2024             :     {
    2025           0 :         return CE_Failure;
    2026             :     }
    2027             : 
    2028             :     // ----------------------------------------------------------
    2029             :     // Get field indices
    2030             :     // ----------------------------------------------------------
    2031             : 
    2032           0 :     int iValue = -1;
    2033           0 :     int iRed = poRAT->GetColOfUsage(GFU_Red);
    2034           0 :     int iGreen = poRAT->GetColOfUsage(GFU_Green);
    2035           0 :     int iBlue = poRAT->GetColOfUsage(GFU_Blue);
    2036             : 
    2037           0 :     GDALColorTable *poCT = nullptr;
    2038           0 :     char **papszNames = nullptr;
    2039             : 
    2040           0 :     int nFact = 1;
    2041             : 
    2042             :     // ----------------------------------------------------------
    2043             :     // Seek for "Value" field index (AGIS standards field name)
    2044             :     // ----------------------------------------------------------
    2045             : 
    2046           0 :     if (GetColorTable() == nullptr ||
    2047           0 :         GetColorTable()->GetColorEntryCount() == 0)
    2048             :     {
    2049           0 :         for (int i = 0; i < poRAT->GetColumnCount(); i++)
    2050             :         {
    2051           0 :             if (STARTS_WITH_CI(poRAT->GetNameOfCol(i), "Value"))
    2052             :             {
    2053           0 :                 iValue = i;
    2054           0 :                 break;
    2055             :             }
    2056             :         }
    2057             : 
    2058           0 :         if (iRed != -1 && iGreen != -1 && iBlue != -1)
    2059             :         {
    2060           0 :             poCT = new GDALColorTable();
    2061           0 :             nFact = poRAT->GetTypeOfCol(iRed) == GFT_Real ? 255 : 1;
    2062             :         }
    2063             :     }
    2064             : 
    2065             :     // ----------------------------------------------------------
    2066             :     // Seek for Name field index
    2067             :     // ----------------------------------------------------------
    2068             : 
    2069           0 :     int iName = -1;
    2070           0 :     if (CSLCount(GetCategoryNames()) == 0)
    2071             :     {
    2072           0 :         iName = poRAT->GetColOfUsage(GFU_Name);
    2073           0 :         if (iName == -1)
    2074             :         {
    2075           0 :             for (int i = 0; i < poRAT->GetColumnCount(); i++)
    2076             :             {
    2077           0 :                 if (STARTS_WITH_CI(poRAT->GetNameOfCol(i), "Class_Name"))
    2078             :                 {
    2079           0 :                     iName = i;
    2080           0 :                     break;
    2081             :                 }
    2082           0 :                 else if (STARTS_WITH_CI(poRAT->GetNameOfCol(i), "Categor"))
    2083             :                 {
    2084           0 :                     iName = i;
    2085           0 :                     break;
    2086             :                 }
    2087           0 :                 else if (STARTS_WITH_CI(poRAT->GetNameOfCol(i), "Name"))
    2088             :                 {
    2089           0 :                     iName = i;
    2090           0 :                     break;
    2091             :                 }
    2092             :             }
    2093             :         }
    2094             : 
    2095             :         /* if still can't find it use the first String column */
    2096             : 
    2097           0 :         if (iName == -1)
    2098             :         {
    2099           0 :             for (int i = 0; i < poRAT->GetColumnCount(); i++)
    2100             :             {
    2101           0 :                 if (poRAT->GetTypeOfCol(i) == GFT_String)
    2102             :                 {
    2103           0 :                     iName = i;
    2104           0 :                     break;
    2105             :                 }
    2106             :             }
    2107             :         }
    2108             : 
    2109             :         // ----------------------------------------------------------
    2110             :         // Incomplete Attribute Table;
    2111             :         // ----------------------------------------------------------
    2112             : 
    2113           0 :         if (iName == -1)
    2114             :         {
    2115           0 :             iName = iValue;
    2116             :         }
    2117             :     }
    2118             : 
    2119             :     // ----------------------------------------------------------
    2120             :     // Load values
    2121             :     // ----------------------------------------------------------
    2122             : 
    2123             :     GDALColorEntry sColor;
    2124           0 :     int iEntry = 0;
    2125           0 :     int iOut = 0;
    2126           0 :     int nEntryCount = poRAT->GetRowCount();
    2127           0 :     int nValue = 0;
    2128             : 
    2129           0 :     if (iValue != -1)
    2130             :     {
    2131           0 :         nValue = poRAT->GetValueAsInt(iEntry, iValue);
    2132             :     }
    2133             : 
    2134           0 :     for (iOut = 0; iOut < 65535 && (iEntry < nEntryCount); iOut++)
    2135             :     {
    2136           0 :         if (iOut == nValue)
    2137             :         {
    2138           0 :             if (poCT)
    2139             :             {
    2140           0 :                 const double dRed = poRAT->GetValueAsDouble(iEntry, iRed);
    2141           0 :                 const double dGreen = poRAT->GetValueAsDouble(iEntry, iGreen);
    2142           0 :                 const double dBlue = poRAT->GetValueAsDouble(iEntry, iBlue);
    2143           0 :                 sColor.c1 = (short)(dRed * nFact);
    2144           0 :                 sColor.c2 = (short)(dGreen * nFact);
    2145           0 :                 sColor.c3 = (short)(dBlue * nFact);
    2146           0 :                 sColor.c4 = (short)(255 / nFact);
    2147           0 :                 poCT->SetColorEntry(iEntry, &sColor);
    2148             :             }
    2149             : 
    2150           0 :             if (iName != -1)
    2151             :             {
    2152           0 :                 papszNames = CSLAddString(
    2153           0 :                     papszNames, poRAT->GetValueAsString(iEntry, iName));
    2154             :             }
    2155             : 
    2156             :             /* Advance on the table */
    2157             : 
    2158           0 :             if ((++iEntry) < nEntryCount)
    2159             :             {
    2160           0 :                 if (iValue != -1)
    2161           0 :                     nValue = poRAT->GetValueAsInt(iEntry, iValue);
    2162             :                 else
    2163           0 :                     nValue = iEntry;
    2164             :             }
    2165             :         }
    2166           0 :         else if (iOut < nValue)
    2167             :         {
    2168           0 :             if (poCT)
    2169             :             {
    2170           0 :                 sColor.c1 = (short)0;
    2171           0 :                 sColor.c2 = (short)0;
    2172           0 :                 sColor.c3 = (short)0;
    2173           0 :                 sColor.c4 = (short)255;
    2174           0 :                 poCT->SetColorEntry(iEntry, &sColor);
    2175             :             }
    2176             : 
    2177           0 :             if (iName != -1)
    2178           0 :                 papszNames = CSLAddString(papszNames, "");
    2179             :         }
    2180             :     }
    2181             : 
    2182             :     // ----------------------------------------------------------
    2183             :     // Set Color Table
    2184             :     // ----------------------------------------------------------
    2185             : 
    2186           0 :     if (poCT)
    2187             :     {
    2188           0 :         SetColorTable(poCT);
    2189           0 :         delete poCT;
    2190             :     }
    2191             : 
    2192             :     // ----------------------------------------------------------
    2193             :     // Update Category Names
    2194             :     // ----------------------------------------------------------
    2195             : 
    2196           0 :     if (papszNames)
    2197             :     {
    2198           0 :         SetCategoryNames(papszNames);
    2199           0 :         CSLDestroy(papszNames);
    2200             :     }
    2201             : 
    2202             :     // ----------------------------------------------------------
    2203             :     // Update Attribute Table
    2204             :     // ----------------------------------------------------------
    2205             : 
    2206           0 :     if (poDefaultRAT)
    2207             :     {
    2208           0 :         delete poDefaultRAT;
    2209             :     }
    2210             : 
    2211           0 :     poDefaultRAT = poRAT->Clone();
    2212             : 
    2213           0 :     return CE_None;
    2214             : }
    2215             : 
    2216             : /************************************************************************/
    2217             : /*                           GetDefaultRAT()                            */
    2218             : /************************************************************************/
    2219             : 
    2220           0 : GDALRasterAttributeTable *IdrisiRasterBand::GetDefaultRAT()
    2221             : {
    2222           0 :     IdrisiDataset *poGDS = (IdrisiDataset *)poDS;
    2223             : 
    2224           0 :     if (poGDS->papszCategories == nullptr)
    2225             :     {
    2226           0 :         return nullptr;
    2227             :     }
    2228             : 
    2229           0 :     bool bHasColorTable = poGDS->poColorTable->GetColorEntryCount() > 0;
    2230             : 
    2231             :     // ----------------------------------------------------------
    2232             :     // Create the bands Attribute Table
    2233             :     // ----------------------------------------------------------
    2234             : 
    2235           0 :     if (poDefaultRAT)
    2236             :     {
    2237           0 :         delete poDefaultRAT;
    2238             :     }
    2239             : 
    2240           0 :     poDefaultRAT = new GDALDefaultRasterAttributeTable();
    2241             : 
    2242             :     // ----------------------------------------------------------
    2243             :     // Create (Value, Red, Green, Blue, Alpha, Class_Name) fields
    2244             :     // ----------------------------------------------------------
    2245             : 
    2246           0 :     poDefaultRAT->CreateColumn("Value", GFT_Integer, GFU_Generic);
    2247           0 :     poDefaultRAT->CreateColumn("Value_1", GFT_Integer, GFU_MinMax);
    2248             : 
    2249           0 :     if (bHasColorTable)
    2250             :     {
    2251           0 :         poDefaultRAT->CreateColumn("Red", GFT_Integer, GFU_Red);
    2252           0 :         poDefaultRAT->CreateColumn("Green", GFT_Integer, GFU_Green);
    2253           0 :         poDefaultRAT->CreateColumn("Blue", GFT_Integer, GFU_Blue);
    2254           0 :         poDefaultRAT->CreateColumn("Alpha", GFT_Integer, GFU_Alpha);
    2255             :     }
    2256           0 :     poDefaultRAT->CreateColumn("Class_name", GFT_String, GFU_Name);
    2257             : 
    2258             :     // ----------------------------------------------------------
    2259             :     // Loop through the Category Names.
    2260             :     // ----------------------------------------------------------
    2261             : 
    2262             :     GDALColorEntry sEntry;
    2263           0 :     int iName = poDefaultRAT->GetColOfUsage(GFU_Name);
    2264           0 :     int nEntryCount = CSLCount(poGDS->papszCategories);
    2265           0 :     int iRows = 0;
    2266             : 
    2267           0 :     for (int iEntry = 0; iEntry < nEntryCount; iEntry++)
    2268             :     {
    2269           0 :         if (EQUAL(poGDS->papszCategories[iEntry], ""))
    2270             :         {
    2271           0 :             continue;  // Eliminate the empty ones
    2272             :         }
    2273           0 :         poDefaultRAT->SetRowCount(poDefaultRAT->GetRowCount() + 1);
    2274           0 :         poDefaultRAT->SetValue(iRows, 0, iEntry);
    2275           0 :         poDefaultRAT->SetValue(iRows, 1, iEntry);
    2276           0 :         if (bHasColorTable)
    2277             :         {
    2278           0 :             poGDS->poColorTable->GetColorEntryAsRGB(iEntry, &sEntry);
    2279           0 :             poDefaultRAT->SetValue(iRows, 2, sEntry.c1);
    2280           0 :             poDefaultRAT->SetValue(iRows, 3, sEntry.c2);
    2281           0 :             poDefaultRAT->SetValue(iRows, 4, sEntry.c3);
    2282           0 :             poDefaultRAT->SetValue(iRows, 5, sEntry.c4);
    2283             :         }
    2284           0 :         poDefaultRAT->SetValue(iRows, iName, poGDS->papszCategories[iEntry]);
    2285           0 :         iRows++;
    2286             :     }
    2287             : 
    2288           0 :     return poDefaultRAT;
    2289             : }
    2290             : 
    2291             : /************************************************************************/
    2292             : /*                       IdrisiGeoReference2Wkt()                       */
    2293             : /************************************************************************/
    2294             : 
    2295             : /***
    2296             :  * Converts Idrisi geographic reference information to OpenGIS WKT.
    2297             :  *
    2298             :  * The Idrisi metadata file contain two fields that describe the
    2299             :  * geographic reference, RefSystem and RefUnit.
    2300             :  *
    2301             :  * RefSystem can contains the world "plane" or the name of a georeference
    2302             :  * file <refsystem>.ref that details the geographic reference
    2303             :  * system( coordinate system and projection parameters ). RefUnits
    2304             :  * indicates the unit of the image bounds.
    2305             :  *
    2306             :  * The georeference files are generally located in the product installation
    2307             :  * folder $IDRISIDIR\Georef, but they are first looked for in the same
    2308             :  * folder as the data file.
    2309             :  *
    2310             :  * If a Reference system names can be recognized by a name convention
    2311             :  * it will be interpreted without the need to read the georeference file.
    2312             :  * That includes "latlong" and all the UTM and State Plane zones.
    2313             :  *
    2314             :  * RefSystem "latlong" means that the data is not project and the coordinate
    2315             :  * system is WGS84. RefSystem "plane" means that the there is no coordinate
    2316             :  * system but the it is possible to calculate areas and distance by looking
    2317             :  * at the RefUnits.
    2318             :  *
    2319             :  * If the environment variable IDRISIDIR is not set and the georeference file
    2320             :  * need to be read then the projection string will result as unknown.
    2321             :  ***/
    2322             : 
    2323           4 : CPLErr IdrisiGeoReference2Wkt(const char *pszFilename, const char *pszRefSystem,
    2324             :                               const char *pszRefUnits,
    2325             :                               OGRSpatialReference &oSRS)
    2326             : {
    2327             :     // ---------------------------------------------------------
    2328             :     //  Plane
    2329             :     // ---------------------------------------------------------
    2330             : 
    2331           4 :     if (EQUAL(pszRefSystem, rstPLANE))
    2332             :     {
    2333           0 :         oSRS.SetLocalCS("Plane");
    2334           0 :         int nUnit = GetUnitIndex(pszRefUnits);
    2335           0 :         if (nUnit > -1)
    2336             :         {
    2337           0 :             int nDeft = aoLinearUnitsConv[nUnit].nDefaultG;
    2338           0 :             oSRS.SetLinearUnits(aoLinearUnitsConv[nDeft].pszName,
    2339           0 :                                 aoLinearUnitsConv[nDeft].dfConv);
    2340             :         }
    2341           0 :         return CE_None;
    2342             :     }
    2343             : 
    2344             :     // ---------------------------------------------------------
    2345             :     //  Latlong
    2346             :     // ---------------------------------------------------------
    2347             : 
    2348           4 :     if (EQUAL(pszRefSystem, rstLATLONG) || EQUAL(pszRefSystem, rstLATLONG2))
    2349             :     {
    2350           1 :         oSRS.SetWellKnownGeogCS("WGS84");
    2351           1 :         return CE_None;
    2352             :     }
    2353             : 
    2354             :     // ---------------------------------------------------------
    2355             :     //  Prepare for scanning in lower case
    2356             :     // ---------------------------------------------------------
    2357             : 
    2358           3 :     char *pszRefSystemLower = CPLStrdup(pszRefSystem);
    2359           3 :     CPLStrlwr(pszRefSystemLower);
    2360             : 
    2361             :     // ---------------------------------------------------------
    2362             :     //  UTM naming convention( ex.: utm-30n )
    2363             :     // ---------------------------------------------------------
    2364             : 
    2365           3 :     if (EQUALN(pszRefSystem, rstUTM, 3))
    2366             :     {
    2367             :         int nZone;
    2368             :         char cNorth;
    2369           3 :         sscanf(pszRefSystemLower, rstUTM, &nZone, &cNorth);
    2370           3 :         oSRS.SetWellKnownGeogCS("WGS84");
    2371           3 :         oSRS.SetUTM(nZone, (cNorth == 'n'));
    2372           3 :         CPLFree(pszRefSystemLower);
    2373           3 :         return CE_None;
    2374             :     }
    2375             : 
    2376             :     // ---------------------------------------------------------
    2377             :     //  State Plane naming convention( ex.: spc83ma1 )
    2378             :     // ---------------------------------------------------------
    2379             : 
    2380           0 :     if (EQUALN(pszRefSystem, rstSPC, 3))
    2381             :     {
    2382             :         int nNAD;
    2383             :         int nZone;
    2384             :         char szState[3];
    2385           0 :         sscanf(pszRefSystemLower, rstSPC, &nNAD, szState, &nZone);
    2386           0 :         int nSPCode = GetStateCode(szState);
    2387           0 :         if (nSPCode != -1)
    2388             :         {
    2389           0 :             nZone = (nZone == 1 ? nSPCode : nSPCode + nZone - 1);
    2390             : 
    2391           0 :             if (oSRS.SetStatePlane(nZone, (nNAD == 83)) != OGRERR_FAILURE)
    2392             :             {
    2393           0 :                 CPLFree(pszRefSystemLower);
    2394           0 :                 return CE_None;
    2395             :             }
    2396             : 
    2397             :             // ----------------------------------------------------------
    2398             :             //  If SetStatePlane fails, set GeoCS as NAD Datum and let it
    2399             :             //  try to read the projection info from georeference file( * )
    2400             :             // ----------------------------------------------------------
    2401             : 
    2402           0 :             oSRS.SetWellKnownGeogCS(CPLSPrintf("NAD%d", nNAD));
    2403             :         }
    2404             :     }
    2405             : 
    2406           0 :     CPLFree(pszRefSystemLower);
    2407           0 :     pszRefSystemLower = nullptr;
    2408             : 
    2409             :     // ------------------------------------------------------------------
    2410             :     //  Search for georeference file <RefSystem>.ref
    2411             :     // ------------------------------------------------------------------
    2412             : 
    2413           0 :     const char *pszFName = CPLSPrintf("%s%c%s.ref", CPLGetDirname(pszFilename),
    2414             :                                       PATHDELIM, pszRefSystem);
    2415             : 
    2416           0 :     if (!FileExists(pszFName))
    2417             :     {
    2418             :         // ------------------------------------------------------------------
    2419             :         //  Look at $IDRISIDIR\Georef\<RefSystem>.ref
    2420             :         // ------------------------------------------------------------------
    2421             : 
    2422           0 :         const char *pszIdrisiDir = CPLGetConfigOption("IDRISIDIR", nullptr);
    2423             : 
    2424           0 :         if ((pszIdrisiDir) != nullptr)
    2425             :         {
    2426           0 :             pszFName = CPLSPrintf("%s%cgeoref%c%s.ref", pszIdrisiDir, PATHDELIM,
    2427             :                                   PATHDELIM, pszRefSystem);
    2428             :         }
    2429             :     }
    2430             : 
    2431             :     // ------------------------------------------------------------------
    2432             :     //  Cannot find georeference file
    2433             :     // ------------------------------------------------------------------
    2434             : 
    2435           0 :     if (!FileExists(pszFName))
    2436             :     {
    2437           0 :         CPLDebug("RST", "Cannot find Idrisi georeference file %s",
    2438             :                  pszRefSystem);
    2439             : 
    2440           0 :         if (oSRS.IsGeographic() == FALSE) /* see State Plane remarks( * ) */
    2441             :         {
    2442           0 :             oSRS.SetLocalCS("Unknown");
    2443           0 :             int nUnit = GetUnitIndex(pszRefUnits);
    2444           0 :             if (nUnit > -1)
    2445             :             {
    2446           0 :                 int nDeft = aoLinearUnitsConv[nUnit].nDefaultG;
    2447           0 :                 oSRS.SetLinearUnits(aoLinearUnitsConv[nDeft].pszName,
    2448           0 :                                     aoLinearUnitsConv[nDeft].dfConv);
    2449             :             }
    2450             :         }
    2451           0 :         return CE_Failure;
    2452             :     }
    2453             : 
    2454             :     // ------------------------------------------------------------------
    2455             :     //  Read values from georeference file
    2456             :     // ------------------------------------------------------------------
    2457             : 
    2458           0 :     char **papszRef = CSLLoad(pszFName);
    2459           0 :     myCSLSetNameValueSeparator(papszRef, ":");
    2460             : 
    2461           0 :     char *pszGeorefName = nullptr;
    2462             : 
    2463           0 :     const char *pszREF_SYSTEM = myCSLFetchNameValue(papszRef, refREF_SYSTEM);
    2464           0 :     if (pszREF_SYSTEM != nullptr && EQUAL(pszREF_SYSTEM, "") == FALSE)
    2465             :     {
    2466           0 :         pszGeorefName = CPLStrdup(pszREF_SYSTEM);
    2467             :     }
    2468             :     else
    2469             :     {
    2470             :         pszGeorefName =
    2471           0 :             CPLStrdup(myCSLFetchNameValue(papszRef, refREF_SYSTEM2));
    2472             :     }
    2473           0 :     char *pszProjName = CPLStrdup(myCSLFetchNameValue(papszRef, refPROJECTION));
    2474           0 :     char *pszDatum = CPLStrdup(myCSLFetchNameValue(papszRef, refDATUM));
    2475           0 :     char *pszEllipsoid = CPLStrdup(myCSLFetchNameValue(papszRef, refELLIPSOID));
    2476             :     const double dfCenterLat =
    2477           0 :         CPLAtof_nz(myCSLFetchNameValue(papszRef, refORIGIN_LAT));
    2478             :     const double dfCenterLong =
    2479           0 :         CPLAtof_nz(myCSLFetchNameValue(papszRef, refORIGIN_LONG));
    2480             :     const double dfSemiMajor =
    2481           0 :         CPLAtof_nz(myCSLFetchNameValue(papszRef, refMAJOR_SAX));
    2482             :     const double dfSemiMinor =
    2483           0 :         CPLAtof_nz(myCSLFetchNameValue(papszRef, refMINOR_SAX));
    2484             :     const double dfFalseEasting =
    2485           0 :         CPLAtof_nz(myCSLFetchNameValue(papszRef, refORIGIN_X));
    2486             :     const double dfFalseNorthing =
    2487           0 :         CPLAtof_nz(myCSLFetchNameValue(papszRef, refORIGIN_Y));
    2488             :     const double dfStdP1 =
    2489           0 :         CPLAtof_nz(myCSLFetchNameValue(papszRef, refSTANDL_1));
    2490             :     const double dfStdP2 =
    2491           0 :         CPLAtof_nz(myCSLFetchNameValue(papszRef, refSTANDL_2));
    2492             :     double dfScale;
    2493           0 :     double adfToWGS84[3] = {0.0, 0.0, 0.0};
    2494             : 
    2495           0 :     const char *pszToWGS84 = myCSLFetchNameValue(papszRef, refDELTA_WGS84);
    2496           0 :     if (pszToWGS84)
    2497           0 :         CPLsscanf(pszToWGS84, "%lf %lf %lf", &adfToWGS84[0], &adfToWGS84[1],
    2498             :                   &adfToWGS84[2]);
    2499             : 
    2500           0 :     const char *pszSCALE_FAC = myCSLFetchNameValue(papszRef, refSCALE_FAC);
    2501           0 :     if (pszSCALE_FAC == nullptr || EQUAL(pszSCALE_FAC, "na"))
    2502           0 :         dfScale = 1.0;
    2503             :     else
    2504           0 :         dfScale = CPLAtof_nz(pszSCALE_FAC);
    2505             : 
    2506           0 :     CSLDestroy(papszRef);
    2507             : 
    2508             :     // ----------------------------------------------------------------------
    2509             :     //  Set the Geographic Coordinate System
    2510             :     // ----------------------------------------------------------------------
    2511             : 
    2512           0 :     if (oSRS.IsGeographic() == FALSE) /* see State Plane remarks(*) */
    2513             :     {
    2514           0 :         int nEPSG = 0;
    2515             : 
    2516             :         // ----------------------------------------------------------------------
    2517             :         //  Is it a WGS84 equivalent?
    2518             :         // ----------------------------------------------------------------------
    2519             : 
    2520           0 :         if ((STARTS_WITH_CI(pszEllipsoid, "WGS")) &&
    2521           0 :             (strstr(pszEllipsoid, "84")) && (STARTS_WITH_CI(pszDatum, "WGS")) &&
    2522           0 :             (strstr(pszDatum, "84")) && (adfToWGS84[0] == 0.0) &&
    2523           0 :             (adfToWGS84[1] == 0.0) && (adfToWGS84[2] == 0.0))
    2524             :         {
    2525           0 :             nEPSG = 4326;
    2526             :         }
    2527             : 
    2528             :         // ----------------------------------------------------------------------
    2529             :         //  Match GCS's DATUM_NAME by using 'ApproxString' over Datum
    2530             :         // ----------------------------------------------------------------------
    2531             : 
    2532           0 :         if (nEPSG == 0)
    2533             :         {
    2534           0 :             const PJ_TYPE nObjType = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
    2535             :             auto datumList =
    2536           0 :                 proj_create_from_name(OSRGetProjTLSContext(), "EPSG", pszDatum,
    2537             :                                       &nObjType, 1, true, 1, nullptr);
    2538           0 :             if (datumList && proj_list_get_count(datumList) == 1)
    2539             :             {
    2540             :                 auto datum =
    2541           0 :                     proj_list_get(OSRGetProjTLSContext(), datumList, 0);
    2542           0 :                 if (datum)
    2543             :                 {
    2544           0 :                     const char *datumCode = proj_get_id_code(datum, 0);
    2545           0 :                     if (datumCode)
    2546             :                     {
    2547           0 :                         auto crsList = proj_query_geodetic_crs_from_datum(
    2548             :                             OSRGetProjTLSContext(), "EPSG", "EPSG", datumCode,
    2549             :                             "geographic 2D");
    2550           0 :                         if (crsList && proj_list_get_count(crsList) != 0)
    2551             :                         {
    2552           0 :                             auto crs = proj_list_get(OSRGetProjTLSContext(),
    2553             :                                                      crsList, 0);
    2554           0 :                             if (crs)
    2555             :                             {
    2556           0 :                                 const char *crsCode = proj_get_id_code(crs, 0);
    2557           0 :                                 if (crsCode)
    2558             :                                 {
    2559           0 :                                     nEPSG = atoi(crsCode);
    2560             :                                 }
    2561           0 :                                 proj_destroy(crs);
    2562             :                             }
    2563             :                         }
    2564           0 :                         proj_list_destroy(crsList);
    2565             :                     }
    2566           0 :                     proj_destroy(datum);
    2567             :                 }
    2568             :             }
    2569           0 :             proj_list_destroy(datumList);
    2570             :         }
    2571             : 
    2572             :         // ----------------------------------------------------------------------
    2573             :         //  Match GCS's COORD_REF_SYS_NAME by using 'ApproxString' over Datum
    2574             :         // ----------------------------------------------------------------------
    2575             : 
    2576           0 :         if (nEPSG == 0)
    2577             :         {
    2578           0 :             const PJ_TYPE nObjType = PJ_TYPE_GEOGRAPHIC_2D_CRS;
    2579             :             auto crsList =
    2580           0 :                 proj_create_from_name(OSRGetProjTLSContext(), "EPSG", pszDatum,
    2581             :                                       &nObjType, 1, true, 1, nullptr);
    2582           0 :             if (crsList && proj_list_get_count(crsList) != 0)
    2583             :             {
    2584           0 :                 auto crs = proj_list_get(OSRGetProjTLSContext(), crsList, 0);
    2585           0 :                 if (crs)
    2586             :                 {
    2587           0 :                     const char *crsCode = proj_get_id_code(crs, 0);
    2588           0 :                     if (crsCode)
    2589             :                     {
    2590           0 :                         nEPSG = atoi(crsCode);
    2591             :                     }
    2592           0 :                     proj_destroy(crs);
    2593             :                 }
    2594             :             }
    2595           0 :             proj_list_destroy(crsList);
    2596             :         }
    2597             : 
    2598           0 :         if (nEPSG != 0)
    2599             :         {
    2600           0 :             oSRS.importFromEPSG(nEPSG);
    2601             :         }
    2602             :         else
    2603             :         {
    2604             :             // --------------------------------------------------
    2605             :             //  Create GeogCS based on the georeference file info
    2606             :             // --------------------------------------------------
    2607             : 
    2608           0 :             oSRS.SetGeogCS(pszRefSystem, pszDatum, pszEllipsoid, dfSemiMajor,
    2609             :                            (dfSemiMinor == dfSemiMajor)
    2610             :                                ? 0.0
    2611           0 :                                : (-1.0 / (dfSemiMinor / dfSemiMajor - 1.0)));
    2612             :         }
    2613             : 
    2614             :         // ----------------------------------------------------------------------
    2615             :         //  Note: That will override EPSG info:
    2616             :         // ----------------------------------------------------------------------
    2617             : 
    2618           0 :         oSRS.SetTOWGS84(adfToWGS84[0], adfToWGS84[1], adfToWGS84[2]);
    2619             :     }
    2620             : 
    2621             :     // ----------------------------------------------------------------------
    2622             :     //  If the georeference file tells that it is a non project system:
    2623             :     // ----------------------------------------------------------------------
    2624             : 
    2625           0 :     if (EQUAL(pszProjName, "none"))
    2626             :     {
    2627           0 :         CPLFree(pszGeorefName);
    2628           0 :         CPLFree(pszProjName);
    2629           0 :         CPLFree(pszDatum);
    2630           0 :         CPLFree(pszEllipsoid);
    2631             : 
    2632           0 :         return CE_None;
    2633             :     }
    2634             : 
    2635             :     // ----------------------------------------------------------------------
    2636             :     //  Create Projection information based on georeference file info
    2637             :     // ----------------------------------------------------------------------
    2638             : 
    2639             :     //  Idrisi user's Manual,   Supported Projection:
    2640             :     //
    2641             :     //      Mercator
    2642             :     //      Transverse Mercator
    2643             :     //      Gauss-Kruger
    2644             :     //      Lambert Conformal Conic
    2645             :     //      Plate Carree
    2646             :     //      Hammer Aitoff
    2647             :     //      Lambert North Polar Azimuthal Equal Area
    2648             :     //      Lambert South Polar Azimuthal Equal Area
    2649             :     //      Lambert Transverse Azimuthal Equal Area
    2650             :     //      Lambert Oblique Polar Azimuthal Equal Area
    2651             :     //      North Polar Stereographic
    2652             :     //      South Polar Stereographic
    2653             :     //      Transverse Stereographic
    2654             :     //      Oblique Stereographic
    2655             :     //      Albers Equal Area Conic
    2656             :     //      Sinusoidal
    2657             :     //      Cylindrical Equal Area
    2658             : 
    2659           0 :     if (EQUAL(pszProjName, "Mercator"))
    2660             :     {
    2661           0 :         oSRS.SetMercator(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
    2662             :                          dfFalseNorthing);
    2663             :     }
    2664           0 :     else if (EQUAL(pszProjName, "Transverse Mercator"))
    2665             :     {
    2666           0 :         oSRS.SetTM(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
    2667             :                    dfFalseNorthing);
    2668             :     }
    2669           0 :     else if (EQUAL(pszProjName, "Gauss-Kruger"))
    2670             :     {
    2671           0 :         oSRS.SetTM(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
    2672             :                    dfFalseNorthing);
    2673             :     }
    2674           0 :     else if (EQUAL(pszProjName, "Lambert Conformal Conic"))
    2675             :     {
    2676           0 :         oSRS.SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, dfFalseEasting,
    2677             :                     dfFalseNorthing);
    2678             :     }
    2679           0 :     else if (EQUAL(pszProjName, "Plate Carr"
    2680             :                                 "\xE9"
    2681             :                                 "e")) /* 'eacute' in ISO-8859-1 */
    2682             :     {
    2683           0 :         oSRS.SetEquirectangular(dfCenterLat, dfCenterLong, dfFalseEasting,
    2684             :                                 dfFalseNorthing);
    2685             :     }
    2686           0 :     else if (EQUAL(pszProjName, "Hammer Aitoff"))
    2687             :     {
    2688           0 :         oSRS.SetProjection(pszProjName);
    2689           0 :         oSRS.SetProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
    2690           0 :         oSRS.SetProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
    2691           0 :         oSRS.SetProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
    2692           0 :         oSRS.SetProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
    2693             :     }
    2694           0 :     else if (EQUAL(pszProjName, "Lambert North Polar Azimuthal Equal Area") ||
    2695           0 :              EQUAL(pszProjName, "Lambert South Polar Azimuthal Equal Area") ||
    2696           0 :              EQUAL(pszProjName, "Lambert Transverse Azimuthal Equal Area") ||
    2697           0 :              EQUAL(pszProjName, "Lambert Oblique Polar Azimuthal Equal Area"))
    2698             :     {
    2699           0 :         oSRS.SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
    2700             :                      dfFalseNorthing);
    2701             :     }
    2702           0 :     else if (EQUAL(pszProjName, "North Polar Stereographic") ||
    2703           0 :              EQUAL(pszProjName, "South Polar Stereographic"))
    2704             :     {
    2705           0 :         oSRS.SetPS(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
    2706             :                    dfFalseNorthing);
    2707             :     }
    2708           0 :     else if (EQUAL(pszProjName, "Transverse Stereographic"))
    2709             :     {
    2710           0 :         oSRS.SetStereographic(dfCenterLat, dfCenterLong, dfScale,
    2711             :                               dfFalseEasting, dfFalseNorthing);
    2712             :     }
    2713           0 :     else if (EQUAL(pszProjName, "Oblique Stereographic"))
    2714             :     {
    2715           0 :         oSRS.SetOS(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
    2716             :                    dfFalseNorthing);
    2717             :     }
    2718           0 :     else if (EQUAL(pszProjName, "Alber's Equal Area Conic") ||
    2719           0 :              EQUAL(pszProjName, "Albers Equal Area Conic"))
    2720             :     {
    2721           0 :         oSRS.SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
    2722             :                      dfFalseEasting, dfFalseNorthing);
    2723             :     }
    2724           0 :     else if (EQUAL(pszProjName, "Sinusoidal"))
    2725             :     {
    2726           0 :         oSRS.SetSinusoidal(dfCenterLong, dfFalseEasting, dfFalseNorthing);
    2727             :     }
    2728           0 :     else if (EQUAL(pszProjName, "CylindricalEA") ||
    2729           0 :              EQUAL(pszProjName, "Cylindrical Equal Area"))
    2730             :     {
    2731           0 :         oSRS.SetCEA(dfStdP1, dfCenterLong, dfFalseEasting, dfFalseNorthing);
    2732             :     }
    2733             :     else
    2734             :     {
    2735           0 :         CPLError(
    2736             :             CE_Warning, CPLE_NotSupported,
    2737             :             "Projection not listed on Idrisi User's Manual( v.15.0/2005 ).\n\t"
    2738             :             "[\"%s\" in georeference file \"%s\"]",
    2739             :             pszProjName, pszFName);
    2740           0 :         oSRS.Clear();
    2741             : 
    2742           0 :         CPLFree(pszGeorefName);
    2743           0 :         CPLFree(pszProjName);
    2744           0 :         CPLFree(pszDatum);
    2745           0 :         CPLFree(pszEllipsoid);
    2746             : 
    2747           0 :         return CE_Warning;
    2748             :     }
    2749             : 
    2750             :     // ----------------------------------------------------------------------
    2751             :     //  Set the Linear Units
    2752             :     // ----------------------------------------------------------------------
    2753             : 
    2754           0 :     int nUnit = GetUnitIndex(pszRefUnits);
    2755           0 :     if (nUnit > -1)
    2756             :     {
    2757           0 :         int nDeft = aoLinearUnitsConv[nUnit].nDefaultG;
    2758           0 :         oSRS.SetLinearUnits(aoLinearUnitsConv[nDeft].pszName,
    2759           0 :                             aoLinearUnitsConv[nDeft].dfConv);
    2760             :     }
    2761             :     else
    2762             :     {
    2763           0 :         oSRS.SetLinearUnits("unknown", 1.0);
    2764             :     }
    2765             : 
    2766             :     // ----------------------------------------------------------------------
    2767             :     //  Name ProjCS with the name on the georeference file
    2768             :     // ----------------------------------------------------------------------
    2769             : 
    2770           0 :     oSRS.SetProjCS(pszGeorefName);
    2771             : 
    2772           0 :     CPLFree(pszGeorefName);
    2773           0 :     CPLFree(pszProjName);
    2774           0 :     CPLFree(pszDatum);
    2775           0 :     CPLFree(pszEllipsoid);
    2776             : 
    2777           0 :     return CE_None;
    2778             : }
    2779             : 
    2780             : /************************************************************************/
    2781             : /*                        Wkt2GeoReference()                            */
    2782             : /************************************************************************/
    2783             : 
    2784             : /***
    2785             :  * Converts OpenGIS WKT to Idrisi geographic reference information.
    2786             :  *
    2787             :  * That function will fill up the two parameters RefSystem and RefUnit
    2788             :  * that goes into the Idrisi metadata. But it could also create
    2789             :  * a accompanying georeference file to the output if necessary.
    2790             :  *
    2791             :  * First it will try to identify the ProjString as Local, WGS84 or
    2792             :  * one of the Idrisi name convention reference systems
    2793             :  * otherwise, if the projection system is supported by Idrisi,
    2794             :  * it will create a accompanying georeference files.
    2795             :  ***/
    2796             : 
    2797          13 : CPLErr IdrisiDataset::Wkt2GeoReference(const OGRSpatialReference &oSRS,
    2798             :                                        char **pszRefSystem, char **pszRefUnit)
    2799             : {
    2800             :     // -----------------------------------------------------
    2801             :     //  Plane with default "Meters"
    2802             :     // -----------------------------------------------------
    2803             : 
    2804          13 :     if (oSRS.IsEmpty())
    2805             :     {
    2806           0 :         *pszRefSystem = CPLStrdup(rstPLANE);
    2807           0 :         *pszRefUnit = CPLStrdup(rstMETER);
    2808           0 :         return CE_None;
    2809             :     }
    2810             : 
    2811             :     // -----------------------------------------------------
    2812             :     //  Local => Plane + Linear Unit
    2813             :     // -----------------------------------------------------
    2814             : 
    2815          13 :     if (oSRS.IsLocal())
    2816             :     {
    2817           0 :         *pszRefSystem = CPLStrdup(rstPLANE);
    2818           0 :         *pszRefUnit = GetUnitDefault(oSRS.GetAttrValue("UNIT"),
    2819             :                                      CPLSPrintf("%f", oSRS.GetLinearUnits()));
    2820           0 :         return CE_None;
    2821             :     }
    2822             : 
    2823             :     // -----------------------------------------------------
    2824             :     //  Test to identify WGS84 => Latlong + Angular Unit
    2825             :     // -----------------------------------------------------
    2826             : 
    2827          13 :     if (oSRS.IsGeographic())
    2828             :     {
    2829          13 :         char *pszSpheroid = CPLStrdup(oSRS.GetAttrValue("SPHEROID"));
    2830          13 :         char *pszAuthName = CPLStrdup(oSRS.GetAuthorityName("GEOGCS"));
    2831          13 :         char *pszDatum = CPLStrdup(oSRS.GetAttrValue("DATUM"));
    2832          13 :         int nGCSCode = -1;
    2833          13 :         if (EQUAL(pszAuthName, "EPSG"))
    2834             :         {
    2835           1 :             nGCSCode = atoi(oSRS.GetAuthorityCode("GEOGCS"));
    2836             :         }
    2837          13 :         if ((nGCSCode == 4326) ||
    2838          12 :             ((STARTS_WITH_CI(pszSpheroid, "WGS")) &&
    2839          12 :              (strstr(pszSpheroid, "84")) && (STARTS_WITH_CI(pszDatum, "WGS")) &&
    2840          12 :              (strstr(pszDatum, "84"))))
    2841             :         {
    2842          13 :             *pszRefSystem = CPLStrdup(rstLATLONG);
    2843          13 :             *pszRefUnit = CPLStrdup(rstDEGREE);
    2844             : 
    2845          13 :             CPLFree(pszSpheroid);
    2846          13 :             CPLFree(pszAuthName);
    2847          13 :             CPLFree(pszDatum);
    2848             : 
    2849          13 :             return CE_None;
    2850             :         }
    2851             : 
    2852           0 :         CPLFree(pszSpheroid);
    2853           0 :         CPLFree(pszAuthName);
    2854           0 :         CPLFree(pszDatum);
    2855             :     }
    2856             : 
    2857             :     // -----------------------------------------------------
    2858             :     //  Prepare to match some projections
    2859             :     // -----------------------------------------------------
    2860             : 
    2861           0 :     const char *pszProjName = oSRS.GetAttrValue("PROJECTION");
    2862             : 
    2863           0 :     if (pszProjName == nullptr)
    2864             :     {
    2865           0 :         pszProjName = "";
    2866             :     }
    2867             : 
    2868             :     // -----------------------------------------------------
    2869             :     //  Check for UTM zones
    2870             :     // -----------------------------------------------------
    2871             : 
    2872           0 :     if (EQUAL(pszProjName, SRS_PT_TRANSVERSE_MERCATOR))
    2873             :     {
    2874           0 :         int nZone = oSRS.GetUTMZone();
    2875             : 
    2876           0 :         if ((nZone != 0) && (EQUAL(oSRS.GetAttrValue("DATUM"), SRS_DN_WGS84)))
    2877             :         {
    2878           0 :             double dfNorth = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
    2879           0 :             *pszRefSystem = CPLStrdup(
    2880             :                 CPLSPrintf(rstUTM, nZone, (dfNorth == 0.0 ? 'n' : 's')));
    2881           0 :             *pszRefUnit = CPLStrdup(rstMETER);
    2882           0 :             return CE_None;
    2883             :         }
    2884             :     }
    2885             : 
    2886             :     // -----------------------------------------------------
    2887             :     //  Check for State Plane
    2888             :     // -----------------------------------------------------
    2889             : 
    2890           0 :     if (EQUAL(pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ||
    2891           0 :         EQUAL(pszProjName, SRS_PT_TRANSVERSE_MERCATOR))
    2892             :     {
    2893           0 :         CPLString osPCSCode;
    2894           0 :         const char *pszID = oSRS.GetAuthorityCode("PROJCS");
    2895           0 :         if (pszID != nullptr && strlen(pszID) > 0)
    2896             :         {
    2897             :             const char *pszPCSCode =
    2898           0 :                 CSVGetField(CSVFilename("stateplane.csv"), "EPSG_PCS_CODE",
    2899             :                             pszID, CC_Integer, "ID");
    2900           0 :             osPCSCode = (pszPCSCode) ? pszPCSCode : "";
    2901           0 :             if (!osPCSCode.empty())
    2902             :             {
    2903           0 :                 int nZone = osPCSCode.back() - '0';
    2904           0 :                 int nSPCode = atoi(osPCSCode);
    2905             : 
    2906           0 :                 if (nZone == 0)
    2907           0 :                     nZone = 1;
    2908             :                 else
    2909           0 :                     nSPCode = nSPCode - nZone + 1;
    2910             : 
    2911           0 :                 int nNADYear = 83;
    2912           0 :                 if (nSPCode > 10000)
    2913             :                 {
    2914           0 :                     nNADYear = 27;
    2915           0 :                     nSPCode -= 10000;
    2916             :                 }
    2917           0 :                 char *pszState = CPLStrdup(GetStateName(nSPCode));
    2918           0 :                 if (!EQUAL(pszState, ""))
    2919             :                 {
    2920           0 :                     *pszRefSystem = CPLStrdup(
    2921             :                         CPLSPrintf(rstSPC, nNADYear, pszState, nZone));
    2922           0 :                     *pszRefUnit =
    2923           0 :                         GetUnitDefault(oSRS.GetAttrValue("UNIT"),
    2924             :                                        CPLSPrintf("%f", oSRS.GetLinearUnits()));
    2925           0 :                     CPLFree(pszState);
    2926           0 :                     return CE_None;
    2927             :                 }
    2928           0 :                 CPLFree(pszState);
    2929             :             }
    2930             :         }  //
    2931             : 
    2932             :         // if EPSG code is missing, go to following steps to work with origin
    2933             : 
    2934           0 :         const char *pszNAD83 = "83";
    2935           0 :         const char *pszNAD27 = "27";
    2936           0 :         bool bIsOldNAD = false;
    2937             : 
    2938           0 :         const char *pszDatumValue = oSRS.GetAttrValue("DATUM", 0);
    2939           0 :         if ((strstr(pszDatumValue, pszNAD83) == nullptr) &&
    2940           0 :             (strstr(pszDatumValue, pszNAD27) != nullptr))
    2941             :             // strcpy(pszNAD, "27");
    2942           0 :             bIsOldNAD = true;
    2943             : 
    2944           0 :         if ((oSRS.FindProjParm("central_meridian", nullptr) != -1) &&
    2945           0 :             (oSRS.FindProjParm("latitude_of_origin", nullptr) != -1))
    2946             :         {
    2947           0 :             double dfLon = oSRS.GetProjParm("central_meridian");
    2948           0 :             double dfLat = oSRS.GetProjParm("latitude_of_origin");
    2949           0 :             dfLon = (int)(fabs(dfLon) * 100 + 0.5) / 100.0;
    2950           0 :             dfLat = (int)(fabs(dfLat) * 100 + 0.5) / 100.0;
    2951           0 :             *pszRefSystem = CPLStrdup(GetSpcs(dfLon, dfLat));
    2952             :         }
    2953             : 
    2954           0 :         if (*pszRefSystem != nullptr)
    2955             :         {
    2956             :             // Convert 83 TO 27
    2957           0 :             if (bIsOldNAD)
    2958             :             {
    2959             :                 char pszOutRefSystem[9];
    2960           0 :                 NAD83to27(pszOutRefSystem, *pszRefSystem);
    2961           0 :                 *pszRefSystem = CPLStrdup(pszOutRefSystem);
    2962             :             }
    2963           0 :             *pszRefUnit =
    2964           0 :                 GetUnitDefault(oSRS.GetAttrValue("UNIT"),
    2965             :                                CPLSPrintf("%f", oSRS.GetLinearUnits()));
    2966           0 :             return CE_None;
    2967             :         }
    2968             :     }
    2969             : 
    2970           0 :     const char *pszProjectionOut = nullptr;
    2971             : 
    2972           0 :     if (oSRS.IsProjected())
    2973             :     {
    2974             :         // ---------------------------------------------------------
    2975             :         //  Check for supported projections
    2976             :         // ---------------------------------------------------------
    2977             : 
    2978           0 :         if (EQUAL(pszProjName, SRS_PT_MERCATOR_1SP))
    2979             :         {
    2980           0 :             pszProjectionOut = "Mercator";
    2981             :         }
    2982           0 :         else if (EQUAL(pszProjName, SRS_PT_TRANSVERSE_MERCATOR))
    2983             :         {
    2984           0 :             pszProjectionOut = "Transverse Mercator";
    2985             :         }
    2986           0 :         else if (EQUAL(pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
    2987             :         {
    2988           0 :             pszProjectionOut = "Lambert Conformal Conic";
    2989             :         }
    2990           0 :         else if (EQUAL(pszProjName, SRS_PT_EQUIRECTANGULAR))
    2991             :         {
    2992           0 :             pszProjectionOut = "Plate Carr"
    2993             :                                "\xE9"
    2994             :                                "e"; /* 'eacute' in ISO-8859-1 */
    2995             :         }
    2996           0 :         else if (EQUAL(pszProjName, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA))
    2997             :         {
    2998             :             double dfCenterLat =
    2999           0 :                 oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0, nullptr);
    3000           0 :             if (dfCenterLat == 0.0)
    3001           0 :                 pszProjectionOut = "Lambert Transverse Azimuthal Equal Area";
    3002           0 :             else if (fabs(dfCenterLat) == 90.0)
    3003           0 :                 pszProjectionOut = "Lambert Oblique Polar Azimuthal Equal Area";
    3004           0 :             else if (dfCenterLat > 0.0)
    3005           0 :                 pszProjectionOut = "Lambert North Oblique Azimuthal Equal Area";
    3006             :             else
    3007           0 :                 pszProjectionOut = "Lambert South Oblique Azimuthal Equal Area";
    3008             :         }
    3009           0 :         else if (EQUAL(pszProjName, SRS_PT_POLAR_STEREOGRAPHIC))
    3010             :         {
    3011           0 :             if (oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0, nullptr) > 0)
    3012           0 :                 pszProjectionOut = "North Polar Stereographic";
    3013             :             else
    3014           0 :                 pszProjectionOut = "South Polar Stereographic";
    3015             :         }
    3016           0 :         else if (EQUAL(pszProjName, SRS_PT_STEREOGRAPHIC))
    3017             :         {
    3018           0 :             pszProjectionOut = "Transverse Stereographic";
    3019             :         }
    3020           0 :         else if (EQUAL(pszProjName, SRS_PT_OBLIQUE_STEREOGRAPHIC))
    3021             :         {
    3022           0 :             pszProjectionOut = "Oblique Stereographic";
    3023             :         }
    3024           0 :         else if (EQUAL(pszProjName, SRS_PT_SINUSOIDAL))
    3025             :         {
    3026           0 :             pszProjectionOut = "Sinusoidal";
    3027             :         }
    3028           0 :         else if (EQUAL(pszProjName, SRS_PT_ALBERS_CONIC_EQUAL_AREA))
    3029             :         {
    3030           0 :             pszProjectionOut = "Alber's Equal Area Conic";
    3031             :         }
    3032           0 :         else if (EQUAL(pszProjName, SRS_PT_CYLINDRICAL_EQUAL_AREA))
    3033             :         {
    3034           0 :             pszProjectionOut = "Cylindrical Equal Area";
    3035             :         }
    3036             : 
    3037             :         // ---------------------------------------------------------
    3038             :         //  Failure, Projection system not supported
    3039             :         // ---------------------------------------------------------
    3040             : 
    3041           0 :         if (pszProjectionOut == nullptr)
    3042             :         {
    3043           0 :             CPLDebug("RST", "Not supported by RST driver: PROJECTION[\"%s\"]",
    3044             :                      pszProjName);
    3045             : 
    3046           0 :             *pszRefSystem = CPLStrdup(rstPLANE);
    3047           0 :             *pszRefUnit = CPLStrdup(rstMETER);
    3048           0 :             return CE_Failure;
    3049             :         }
    3050             :     }
    3051             :     else
    3052             :     {
    3053           0 :         pszProjectionOut = "none";
    3054             :     }
    3055             : 
    3056             :     // ---------------------------------------------------------
    3057             :     //  Prepare to write ref file
    3058             :     // ---------------------------------------------------------
    3059             : 
    3060           0 :     char *pszGeorefName = CPLStrdup("Unknown");
    3061           0 :     char *pszDatum = CPLStrdup(oSRS.GetAttrValue("DATUM"));
    3062           0 :     char *pszEllipsoid = CPLStrdup(oSRS.GetAttrValue("SPHEROID"));
    3063           0 :     double dfSemiMajor = oSRS.GetSemiMajor();
    3064           0 :     double dfSemiMinor = oSRS.GetSemiMinor();
    3065             :     double adfToWGS84[3];
    3066           0 :     oSRS.GetTOWGS84(adfToWGS84, 3);
    3067             : 
    3068           0 :     double dfCenterLat = 0.0;
    3069           0 :     double dfCenterLong = 0.0;
    3070           0 :     double dfFalseNorthing = 0.0;
    3071           0 :     double dfFalseEasting = 0.0;
    3072           0 :     double dfScale = 1.0;
    3073           0 :     int nParameters = 0;
    3074           0 :     double dfStdP1 = 0.0;
    3075           0 :     double dfStdP2 = 0.0;
    3076           0 :     char *pszAngularUnit = CPLStrdup(oSRS.GetAttrValue("GEOGCS|UNIT"));
    3077           0 :     char *pszLinearUnit = nullptr;
    3078             : 
    3079           0 :     if (oSRS.IsProjected())
    3080             :     {
    3081           0 :         CPLFree(pszGeorefName);
    3082           0 :         pszGeorefName = CPLStrdup(oSRS.GetAttrValue("PROJCS"));
    3083           0 :         dfCenterLat = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0, nullptr);
    3084           0 :         dfCenterLong = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0, nullptr);
    3085           0 :         dfFalseNorthing = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING, 0.0, nullptr);
    3086           0 :         dfFalseEasting = oSRS.GetProjParm(SRS_PP_FALSE_EASTING, 0.0, nullptr);
    3087           0 :         dfScale = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 0.0, nullptr);
    3088           0 :         dfStdP1 = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1, -0.1, nullptr);
    3089           0 :         if (EQUAL(pszProjectionOut, "Cylindrical Equal Area"))
    3090             :         {
    3091           0 :             dfStdP2 = -dfStdP1;
    3092           0 :             dfScale = 1.0;
    3093             :         }
    3094             :         else
    3095             :         {
    3096             :             dfStdP2 =
    3097           0 :                 oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2, -0.1, nullptr);
    3098             :         }
    3099           0 :         if (dfStdP1 != -0.1)
    3100             :         {
    3101           0 :             nParameters = 1;
    3102           0 :             if (dfStdP2 != -0.1)
    3103           0 :                 nParameters = 2;
    3104             :         }
    3105           0 :         pszLinearUnit = GetUnitDefault(oSRS.GetAttrValue("PROJCS|UNIT"),
    3106             :                                        CPLSPrintf("%f", oSRS.GetLinearUnits()));
    3107             :     }
    3108             :     else
    3109             :     {
    3110           0 :         pszLinearUnit = GetUnitDefault(pszAngularUnit);
    3111             :     }
    3112             : 
    3113             :     // ---------------------------------------------------------
    3114             :     //  Create a companion georeference file for this dataset
    3115             :     // ---------------------------------------------------------
    3116             : 
    3117           0 :     char **papszRef = nullptr;
    3118           0 :     papszRef = CSLAddNameValue(papszRef, refREF_SYSTEM, pszGeorefName);
    3119           0 :     papszRef = CSLAddNameValue(papszRef, refPROJECTION, pszProjectionOut);
    3120           0 :     papszRef = CSLAddNameValue(papszRef, refDATUM, pszDatum);
    3121           0 :     papszRef = CSLAddNameValue(papszRef, refDELTA_WGS84,
    3122             :                                CPLSPrintf("%.3g %.3g %.3g", adfToWGS84[0],
    3123             :                                           adfToWGS84[1], adfToWGS84[2]));
    3124           0 :     papszRef = CSLAddNameValue(papszRef, refELLIPSOID, pszEllipsoid);
    3125           0 :     papszRef = CSLAddNameValue(papszRef, refMAJOR_SAX,
    3126             :                                CPLSPrintf("%.3f", dfSemiMajor));
    3127           0 :     papszRef = CSLAddNameValue(papszRef, refMINOR_SAX,
    3128             :                                CPLSPrintf("%.3f", dfSemiMinor));
    3129           0 :     papszRef = CSLAddNameValue(papszRef, refORIGIN_LONG,
    3130             :                                CPLSPrintf("%.9g", dfCenterLong));
    3131           0 :     papszRef = CSLAddNameValue(papszRef, refORIGIN_LAT,
    3132             :                                CPLSPrintf("%.9g", dfCenterLat));
    3133           0 :     papszRef = CSLAddNameValue(papszRef, refORIGIN_X,
    3134             :                                CPLSPrintf("%.9g", dfFalseEasting));
    3135           0 :     papszRef = CSLAddNameValue(papszRef, refORIGIN_Y,
    3136             :                                CPLSPrintf("%.9g", dfFalseNorthing));
    3137             :     papszRef =
    3138           0 :         CSLAddNameValue(papszRef, refSCALE_FAC, CPLSPrintf("%.9g", dfScale));
    3139           0 :     papszRef = CSLAddNameValue(papszRef, refUNITS, pszLinearUnit);
    3140           0 :     papszRef = CSLAddNameValue(papszRef, refPARAMETERS,
    3141             :                                CPLSPrintf("%1d", nParameters));
    3142           0 :     if (nParameters > 0)
    3143             :         papszRef =
    3144           0 :             CSLAddNameValue(papszRef, refSTANDL_1, CPLSPrintf("%.9g", dfStdP1));
    3145           0 :     if (nParameters > 1)
    3146             :         papszRef =
    3147           0 :             CSLAddNameValue(papszRef, refSTANDL_2, CPLSPrintf("%.9g", dfStdP2));
    3148           0 :     myCSLSetNameValueSeparator(papszRef, ": ");
    3149           0 :     SaveAsCRLF(papszRef, CPLResetExtension(pszFilename, extREF));
    3150           0 :     CSLDestroy(papszRef);
    3151             : 
    3152           0 :     *pszRefSystem = CPLStrdup(CPLGetBasename(pszFilename));
    3153           0 :     *pszRefUnit = CPLStrdup(pszLinearUnit);
    3154             : 
    3155           0 :     CPLFree(pszGeorefName);
    3156           0 :     CPLFree(pszDatum);
    3157           0 :     CPLFree(pszEllipsoid);
    3158           0 :     CPLFree(pszLinearUnit);
    3159           0 :     CPLFree(pszAngularUnit);
    3160             : 
    3161           0 :     return CE_None;
    3162             : }
    3163             : 
    3164             : /************************************************************************/
    3165             : /*                             FileExists()                             */
    3166             : /************************************************************************/
    3167             : 
    3168          60 : bool FileExists(const char *pszPath)
    3169             : {
    3170             :     VSIStatBufL sStat;
    3171             : 
    3172          60 :     return VSIStatL(pszPath, &sStat) == 0;
    3173             : }
    3174             : 
    3175             : /************************************************************************/
    3176             : /*                            GetStateCode()                            */
    3177             : /************************************************************************/
    3178             : 
    3179           0 : int GetStateCode(const char *pszState)
    3180             : {
    3181           0 :     for (unsigned int i = 0; i < US_STATE_COUNT; i++)
    3182             :     {
    3183           0 :         if (EQUAL(pszState, aoUSStateTable[i].pszName))
    3184             :         {
    3185           0 :             return aoUSStateTable[i].nCode;
    3186             :         }
    3187             :     }
    3188           0 :     return -1;
    3189             : }
    3190             : 
    3191             : /************************************************************************/
    3192             : /*                            GetStateName()                            */
    3193             : /************************************************************************/
    3194             : 
    3195           0 : const char *GetStateName(int nCode)
    3196             : {
    3197           0 :     for (unsigned int i = 0; i < US_STATE_COUNT; i++)
    3198             :     {
    3199           0 :         if (nCode == aoUSStateTable[i].nCode)
    3200             :         {
    3201           0 :             return aoUSStateTable[i].pszName;
    3202             :         }
    3203             :     }
    3204           0 :     return nullptr;
    3205             : }
    3206             : 
    3207             : /************************************************************************/
    3208             : /*                            GetSpcs()                                 */
    3209             : /************************************************************************/
    3210             : 
    3211           0 : char *GetSpcs(double dfLon, double dfLat)
    3212             : {
    3213           0 :     for (int i = 0; i < ORIGIN_COUNT; i++)
    3214             :     {
    3215           0 :         if ((dfLon == SPCS83Origin[i].longitude) &&
    3216           0 :             (dfLat == SPCS83Origin[i].latitude))
    3217             :         {
    3218           0 :             return (char *)SPCS83Origin[i].spcs;
    3219             :         }
    3220             :     }
    3221           0 :     return nullptr;
    3222             : }
    3223             : 
    3224             : /************************************************************************/
    3225             : /*                            NAD83to27()                               */
    3226             : /************************************************************************/
    3227           0 : void NAD83to27(char *pszOutRef, char *pszInRef)
    3228             : {
    3229           0 :     char *pOutput = pszOutRef;
    3230           0 :     char *pInput = pszInRef;
    3231           0 :     strncpy(pOutput, pInput, 3);
    3232             : 
    3233           0 :     pOutput = pOutput + 3;
    3234           0 :     pInput = pInput + 3;
    3235             : 
    3236           0 :     memcpy(pOutput, "27", 2);
    3237           0 :     pOutput = pOutput + 2;
    3238           0 :     pInput = pInput + 2;
    3239           0 :     strcpy(pOutput, pInput);
    3240           0 : }
    3241             : 
    3242             : /************************************************************************/
    3243             : /*                            GetUnitIndex()                            */
    3244             : /************************************************************************/
    3245             : 
    3246           0 : int GetUnitIndex(const char *pszUnitName)
    3247             : {
    3248           0 :     for (int i = 0; i < (int)LINEAR_UNITS_COUNT; i++)
    3249             :     {
    3250           0 :         if (EQUAL(pszUnitName, aoLinearUnitsConv[i].pszName))
    3251             :         {
    3252           0 :             return i;
    3253             :         }
    3254             :     }
    3255           0 :     return -1;
    3256             : }
    3257             : 
    3258             : /************************************************************************/
    3259             : /*                            GetToMeterIndex()                         */
    3260             : /************************************************************************/
    3261             : 
    3262           0 : int GetToMeterIndex(const char *pszToMeter)
    3263             : {
    3264           0 :     const double dfToMeter = CPLAtof_nz(pszToMeter);
    3265             : 
    3266           0 :     if (dfToMeter != 0.0)
    3267             :     {
    3268           0 :         for (int i = 0; i < (int)LINEAR_UNITS_COUNT; i++)
    3269             :         {
    3270           0 :             if (std::abs(aoLinearUnitsConv[i].dfConv - dfToMeter) < 0.00001)
    3271             :             {
    3272           0 :                 return i;
    3273             :             }
    3274             :         }
    3275             :     }
    3276             : 
    3277           0 :     return -1;
    3278             : }
    3279             : 
    3280             : /************************************************************************/
    3281             : /*                            GetUnitDefault()                          */
    3282             : /************************************************************************/
    3283             : 
    3284           0 : char *GetUnitDefault(const char *pszUnitName, const char *pszToMeter)
    3285             : {
    3286           0 :     int nIndex = GetUnitIndex(pszUnitName);
    3287             : 
    3288           0 :     if (nIndex == -1 && pszToMeter != nullptr)
    3289             :     {
    3290           0 :         nIndex = GetToMeterIndex(pszToMeter);
    3291             :     }
    3292             : 
    3293           0 :     if (nIndex == -1)
    3294             :     {
    3295           0 :         return CPLStrdup("Unknown");
    3296             :     }
    3297             : 
    3298           0 :     return CPLStrdup(
    3299           0 :         aoLinearUnitsConv[aoLinearUnitsConv[nIndex].nDefaultI].pszName);
    3300             : }
    3301             : 
    3302             : /************************************************************************/
    3303             : /*                               CSLSaveCRLF()                          */
    3304             : /************************************************************************/
    3305             : 
    3306             : /***
    3307             :  * Write a stringlist to a CR + LF terminated text file.
    3308             :  *
    3309             :  * Returns the number of lines written, or 0 if the file could not
    3310             :  * be written.
    3311             :  */
    3312             : 
    3313          46 : int SaveAsCRLF(char **papszStrList, const char *pszFname)
    3314             : {
    3315          46 :     VSILFILE *fp = VSIFOpenL(pszFname, "wt");
    3316          46 :     int nLines = 0;
    3317             : 
    3318          46 :     if (papszStrList)
    3319             :     {
    3320          46 :         if (fp != nullptr)
    3321             :         {
    3322         800 :             while (*papszStrList != nullptr)
    3323             :             {
    3324         766 :                 if (VSIFPrintfL(fp, "%s\r\n", *papszStrList) < 1)
    3325             :                 {
    3326          10 :                     CPLError(CE_Failure, CPLE_FileIO,
    3327             :                              "CSLSaveCRLF(\"%s\") failed: unable to write to "
    3328             :                              "output file.",
    3329             :                              pszFname);
    3330          10 :                     break;
    3331             :                 }
    3332             : 
    3333         756 :                 nLines++;
    3334         756 :                 papszStrList++;
    3335             :             }
    3336             : 
    3337          44 :             VSIFCloseL(fp);
    3338             :         }
    3339             :         else
    3340             :         {
    3341           2 :             CPLError(CE_Failure, CPLE_OpenFailed,
    3342             :                      "CSLSaveCRLF(\"%s\") failed: unable to open output file.",
    3343             :                      pszFname);
    3344             :         }
    3345             :     }
    3346             : 
    3347          46 :     return nLines;
    3348             : }
    3349             : 
    3350             : /************************************************************************/
    3351             : /*                        GDALRegister_IDRISI()                         */
    3352             : /************************************************************************/
    3353             : 
    3354        1520 : void GDALRegister_IDRISI()
    3355             : {
    3356        1520 :     if (GDALGetDriverByName("RST") != nullptr)
    3357         301 :         return;
    3358             : 
    3359        1219 :     GDALDriver *poDriver = new GDALDriver();
    3360             : 
    3361        1219 :     poDriver->SetDescription("RST");
    3362        1219 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    3363        1219 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, rstVERSION);
    3364        1219 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/Idrisi.html");
    3365        1219 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, extRST);
    3366        1219 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte Int16 Float32");
    3367             : 
    3368        1219 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    3369             : 
    3370        1219 :     poDriver->pfnOpen = IdrisiDataset::Open;
    3371        1219 :     poDriver->pfnCreate = IdrisiDataset::Create;
    3372        1219 :     poDriver->pfnCreateCopy = IdrisiDataset::CreateCopy;
    3373             : 
    3374        1219 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3375             : }

Generated by: LCOV version 1.14