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