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 : char **papszOptions);
404 : static GDALDataset *CreateCopy(const char *pszFilename,
405 : GDALDataset *poSrcDS, int bStrict,
406 : char **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);
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 37222 : GDALDataset *IdrisiDataset::Open(GDALOpenInfo *poOpenInfo)
536 : {
537 42579 : if ((poOpenInfo->fpL == nullptr) ||
538 5357 : (poOpenInfo->IsExtensionEqualToCI(extRST) == FALSE)) // modified
539 37182 : 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[0] = dfMinX;
696 30 : poDS->m_gt[1] = dfXPixSz;
697 30 : poDS->m_gt[2] = 0.0;
698 30 : poDS->m_gt[3] = dfMaxY;
699 30 : poDS->m_gt[4] = 0.0;
700 30 : poDS->m_gt[5] = 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 : char ** /* 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 24 : return (IdrisiDataset *)GDALOpen(pszFilename, GA_Update);
996 : }
997 :
998 : /************************************************************************/
999 : /* CreateCopy() */
1000 : /************************************************************************/
1001 :
1002 29 : GDALDataset *IdrisiDataset::CreateCopy(const char *pszFilename,
1003 : GDALDataset *poSrcDS, int bStrict,
1004 : char **papszOptions,
1005 : GDALProgressFunc pfnProgress,
1006 : void *pProgressData)
1007 : {
1008 29 : if (!pfnProgress(0.0, nullptr, pProgressData))
1009 0 : return nullptr;
1010 :
1011 : // ------------------------------------------------------------------------
1012 : // Check number of bands
1013 : // ------------------------------------------------------------------------
1014 29 : if (!(poSrcDS->GetRasterCount() == 1) && !(poSrcDS->GetRasterCount() == 3))
1015 : {
1016 4 : CPLError(CE_Failure, CPLE_AppDefined,
1017 : "Attempt to create IDRISI dataset with an illegal number of "
1018 : "bands(%d)."
1019 : " Try again by selecting a specific band if possible.\n",
1020 : poSrcDS->GetRasterCount());
1021 4 : return nullptr;
1022 : }
1023 27 : if ((poSrcDS->GetRasterCount() == 3) &&
1024 2 : ((poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt8) ||
1025 2 : (poSrcDS->GetRasterBand(2)->GetRasterDataType() != GDT_UInt8) ||
1026 2 : (poSrcDS->GetRasterBand(3)->GetRasterDataType() != GDT_UInt8)))
1027 : {
1028 0 : CPLError(
1029 : CE_Failure, CPLE_AppDefined,
1030 : "Attempt to create IDRISI dataset with an unsupported "
1031 : "data type when there are three bands. Only BYTE allowed.\n"
1032 : "Try again by selecting a specific band to convert if possible.\n");
1033 0 : return nullptr;
1034 : }
1035 :
1036 : // ------------------------------------------------------------------------
1037 : // Check Data types
1038 : // ------------------------------------------------------------------------
1039 :
1040 46 : for (int i = 1; i <= poSrcDS->GetRasterCount(); i++)
1041 : {
1042 29 : GDALDataType eType = poSrcDS->GetRasterBand(i)->GetRasterDataType();
1043 :
1044 29 : if (bStrict)
1045 : {
1046 25 : if (eType != GDT_UInt8 && eType != GDT_Int16 &&
1047 : eType != GDT_Float32)
1048 : {
1049 8 : CPLError(CE_Failure, CPLE_AppDefined,
1050 : "Attempt to create IDRISI dataset in strict mode "
1051 : "with an illegal data type(%s).\n",
1052 : GDALGetDataTypeName(eType));
1053 8 : return nullptr;
1054 : }
1055 : }
1056 : else
1057 : {
1058 4 : if (eType != GDT_UInt8 && eType != GDT_Int16 &&
1059 0 : eType != GDT_UInt16 && eType != GDT_UInt32 &&
1060 0 : eType != GDT_Int32 && eType != GDT_Float32 &&
1061 : eType != GDT_Float64)
1062 : {
1063 0 : CPLError(CE_Failure, CPLE_AppDefined,
1064 : "Attempt to create IDRISI dataset with an illegal "
1065 : "data type(%s).\n",
1066 : GDALGetDataTypeName(eType));
1067 0 : return nullptr;
1068 : }
1069 : }
1070 : }
1071 :
1072 : // --------------------------------------------------------------------
1073 : // Define data type
1074 : // --------------------------------------------------------------------
1075 :
1076 17 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
1077 17 : GDALDataType eType = poBand->GetRasterDataType();
1078 :
1079 17 : int bSuccessMin = FALSE;
1080 17 : int bSuccessMax = FALSE;
1081 :
1082 17 : double dfMin = poBand->GetMinimum(&bSuccessMin);
1083 17 : double dfMax = poBand->GetMaximum(&bSuccessMax);
1084 :
1085 17 : if (!(bSuccessMin && bSuccessMax))
1086 : {
1087 6 : poBand->GetStatistics(false, true, &dfMin, &dfMax, nullptr, nullptr);
1088 : }
1089 :
1090 17 : if (!((eType == GDT_UInt8) || (eType == GDT_Int16) ||
1091 : (eType == GDT_Float32)))
1092 : {
1093 0 : if (eType == GDT_Float64)
1094 : {
1095 0 : eType = GDT_Float32;
1096 : }
1097 : else
1098 : {
1099 0 : if ((dfMin < (double)SHRT_MIN) || (dfMax > (double)SHRT_MAX))
1100 : {
1101 0 : eType = GDT_Float32;
1102 : }
1103 : else
1104 : {
1105 0 : eType = GDT_Int16;
1106 : }
1107 : }
1108 : }
1109 :
1110 : // --------------------------------------------------------------------
1111 : // Create the dataset
1112 : // --------------------------------------------------------------------
1113 :
1114 17 : IdrisiDataset *poDS = cpl::down_cast<IdrisiDataset *>(IdrisiDataset::Create(
1115 : pszFilename, poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(),
1116 : poSrcDS->GetRasterCount(), eType, papszOptions));
1117 :
1118 17 : if (poDS == nullptr)
1119 12 : return nullptr;
1120 :
1121 : // --------------------------------------------------------------------
1122 : // Copy information to the dataset
1123 : // --------------------------------------------------------------------
1124 :
1125 5 : GDALGeoTransform gt;
1126 5 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
1127 : {
1128 5 : poDS->SetGeoTransform(gt);
1129 : }
1130 :
1131 5 : if (!EQUAL(poSrcDS->GetProjectionRef(), ""))
1132 : {
1133 5 : poDS->SetProjection(poSrcDS->GetProjectionRef());
1134 : }
1135 :
1136 : // --------------------------------------------------------------------
1137 : // Copy information to the raster band(s)
1138 : // --------------------------------------------------------------------
1139 :
1140 14 : for (int i = 1; i <= poDS->nBands; i++)
1141 : {
1142 9 : GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(i);
1143 : IdrisiRasterBand *poDstBand =
1144 9 : cpl::down_cast<IdrisiRasterBand *>(poDS->GetRasterBand(i));
1145 :
1146 9 : if (poDS->nBands == 1)
1147 : {
1148 3 : poDstBand->SetUnitType(poSrcBand->GetUnitType());
1149 3 : poDstBand->SetColorTable(poSrcBand->GetColorTable());
1150 3 : poDstBand->SetCategoryNames(poSrcBand->GetCategoryNames());
1151 :
1152 3 : const GDALRasterAttributeTable *poRAT = poSrcBand->GetDefaultRAT();
1153 :
1154 3 : if (poRAT != nullptr)
1155 : {
1156 0 : poDstBand->SetDefaultRAT(poRAT);
1157 : }
1158 : }
1159 :
1160 9 : dfMin = poSrcBand->GetMinimum(nullptr);
1161 9 : dfMax = poSrcBand->GetMaximum(nullptr);
1162 9 : poDstBand->SetMinMax(dfMin, dfMax);
1163 : int bHasNoDataValue;
1164 9 : double dfNoDataValue = poSrcBand->GetNoDataValue(&bHasNoDataValue);
1165 9 : if (bHasNoDataValue)
1166 0 : poDstBand->SetNoDataValue(dfNoDataValue);
1167 : }
1168 :
1169 : // --------------------------------------------------------------------
1170 : // Copy image data
1171 : // --------------------------------------------------------------------
1172 :
1173 5 : if (GDALDatasetCopyWholeRaster((GDALDatasetH)poSrcDS, (GDALDatasetH)poDS,
1174 : nullptr, pfnProgress,
1175 5 : pProgressData) != CE_None)
1176 : {
1177 0 : delete poDS;
1178 0 : return nullptr;
1179 : }
1180 :
1181 : // --------------------------------------------------------------------
1182 : // Finalize
1183 : // --------------------------------------------------------------------
1184 :
1185 5 : poDS->FlushCache(false);
1186 :
1187 5 : return poDS;
1188 : }
1189 :
1190 : /************************************************************************/
1191 : /* GetFileList() */
1192 : /************************************************************************/
1193 :
1194 4 : char **IdrisiDataset::GetFileList()
1195 : {
1196 4 : char **papszFileList = GDALPamDataset::GetFileList();
1197 :
1198 : // --------------------------------------------------------------------
1199 : // Symbol table file
1200 : // --------------------------------------------------------------------
1201 :
1202 4 : std::string osAssociated = CPLResetExtensionSafe(pszFilename, extSMP);
1203 :
1204 4 : if (FileExists(osAssociated.c_str()))
1205 : {
1206 0 : papszFileList = CSLAddString(papszFileList, osAssociated.c_str());
1207 : }
1208 : else
1209 : {
1210 4 : osAssociated = CPLResetExtensionSafe(pszFilename, extSMPu);
1211 :
1212 4 : if (FileExists(osAssociated.c_str()))
1213 : {
1214 0 : papszFileList = CSLAddString(papszFileList, osAssociated.c_str());
1215 : }
1216 : }
1217 :
1218 : // --------------------------------------------------------------------
1219 : // Documentation file
1220 : // --------------------------------------------------------------------
1221 :
1222 4 : osAssociated = CPLResetExtensionSafe(pszFilename, extRDC);
1223 :
1224 4 : if (FileExists(osAssociated.c_str()))
1225 : {
1226 4 : papszFileList = CSLAddString(papszFileList, osAssociated.c_str());
1227 : }
1228 : else
1229 : {
1230 0 : osAssociated = CPLResetExtensionSafe(pszFilename, extRDCu);
1231 :
1232 0 : if (FileExists(osAssociated.c_str()))
1233 : {
1234 0 : papszFileList = CSLAddString(papszFileList, osAssociated.c_str());
1235 : }
1236 : }
1237 :
1238 : // --------------------------------------------------------------------
1239 : // Reference file
1240 : // --------------------------------------------------------------------
1241 :
1242 4 : osAssociated = CPLResetExtensionSafe(pszFilename, extREF);
1243 :
1244 4 : if (FileExists(osAssociated.c_str()))
1245 : {
1246 0 : papszFileList = CSLAddString(papszFileList, osAssociated.c_str());
1247 : }
1248 : else
1249 : {
1250 4 : osAssociated = CPLResetExtensionSafe(pszFilename, extREFu);
1251 :
1252 4 : if (FileExists(osAssociated.c_str()))
1253 : {
1254 0 : papszFileList = CSLAddString(papszFileList, osAssociated.c_str());
1255 : }
1256 : }
1257 :
1258 8 : return papszFileList;
1259 : }
1260 :
1261 : /************************************************************************/
1262 : /* GetGeoTransform() */
1263 : /************************************************************************/
1264 :
1265 9 : CPLErr IdrisiDataset::GetGeoTransform(GDALGeoTransform >) const
1266 : {
1267 9 : if (GDALPamDataset::GetGeoTransform(gt) != CE_None)
1268 : {
1269 9 : gt = m_gt;
1270 : }
1271 :
1272 9 : return CE_None;
1273 : }
1274 :
1275 : /************************************************************************/
1276 : /* SetGeoTransform() */
1277 : /************************************************************************/
1278 :
1279 13 : CPLErr IdrisiDataset::SetGeoTransform(const GDALGeoTransform >)
1280 : {
1281 13 : if (gt[2] != 0.0 || gt[4] != 0.0)
1282 : {
1283 0 : CPLError(CE_Failure, CPLE_AppDefined,
1284 : "Attempt to set rotated geotransform on Idrisi Raster file.\n"
1285 : "Idrisi Raster does not support rotation.\n");
1286 0 : return CE_Failure;
1287 : }
1288 :
1289 : // --------------------------------------------------------------------
1290 : // Update the .rdc file
1291 : // --------------------------------------------------------------------
1292 :
1293 13 : double dfXPixSz = gt[1];
1294 13 : double dfYPixSz = gt[5];
1295 13 : double dfMinX = gt[0];
1296 13 : double dfMaxX = (dfXPixSz * nRasterXSize) + dfMinX;
1297 :
1298 : double dfMinY, dfMaxY;
1299 13 : if (dfYPixSz < 0)
1300 : {
1301 13 : dfMaxY = gt[3];
1302 13 : dfMinY = (dfYPixSz * nRasterYSize) + gt[3];
1303 : }
1304 : else
1305 : {
1306 0 : dfMaxY = (dfYPixSz * nRasterYSize) + gt[3];
1307 0 : dfMinY = gt[3];
1308 : }
1309 :
1310 13 : papszRDC = CSLSetNameValue(papszRDC, rdcMIN_X, CPLSPrintf("%.7f", dfMinX));
1311 13 : papszRDC = CSLSetNameValue(papszRDC, rdcMAX_X, CPLSPrintf("%.7f", dfMaxX));
1312 13 : papszRDC = CSLSetNameValue(papszRDC, rdcMIN_Y, CPLSPrintf("%.7f", dfMinY));
1313 13 : papszRDC = CSLSetNameValue(papszRDC, rdcMAX_Y, CPLSPrintf("%.7f", dfMaxY));
1314 13 : papszRDC = CSLSetNameValue(papszRDC, rdcRESOLUTION,
1315 : CPLSPrintf("%.7f", fabs(dfYPixSz)));
1316 :
1317 : // --------------------------------------------------------------------
1318 : // Update the Dataset attribute
1319 : // --------------------------------------------------------------------
1320 :
1321 13 : m_gt = gt;
1322 :
1323 13 : return CE_None;
1324 : }
1325 :
1326 : /************************************************************************/
1327 : /* GetSpatialRef() */
1328 : /************************************************************************/
1329 :
1330 1 : const OGRSpatialReference *IdrisiDataset::GetSpatialRef() const
1331 : {
1332 1 : const auto poSRS = GDALPamDataset::GetSpatialRef();
1333 1 : if (poSRS)
1334 0 : return poSRS;
1335 :
1336 1 : if (m_oSRS.IsEmpty())
1337 : {
1338 1 : const char *pszRefSystem = myCSLFetchNameValue(papszRDC, rdcREF_SYSTEM);
1339 1 : const char *pszRefUnit = myCSLFetchNameValue(papszRDC, rdcREF_UNITS);
1340 1 : if (pszRefSystem != nullptr && pszRefUnit != nullptr)
1341 1 : IdrisiGeoReference2Wkt(pszFilename, pszRefSystem, pszRefUnit,
1342 1 : m_oSRS);
1343 : }
1344 1 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
1345 : }
1346 :
1347 : /************************************************************************/
1348 : /* SetProjection() */
1349 : /************************************************************************/
1350 :
1351 13 : CPLErr IdrisiDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1352 : {
1353 13 : m_oSRS.Clear();
1354 13 : if (poSRS)
1355 13 : m_oSRS = *poSRS;
1356 :
1357 13 : char *pszRefSystem = nullptr;
1358 13 : char *pszRefUnit = nullptr;
1359 :
1360 13 : CPLErr eResult = Wkt2GeoReference(m_oSRS, &pszRefSystem, &pszRefUnit);
1361 :
1362 13 : papszRDC = CSLSetNameValue(papszRDC, rdcREF_SYSTEM, pszRefSystem);
1363 13 : papszRDC = CSLSetNameValue(papszRDC, rdcREF_UNITS, pszRefUnit);
1364 :
1365 13 : CPLFree(pszRefSystem);
1366 13 : CPLFree(pszRefUnit);
1367 :
1368 13 : return eResult;
1369 : }
1370 :
1371 : /************************************************************************/
1372 : /* IdrisiRasterBand() */
1373 : /************************************************************************/
1374 :
1375 42 : IdrisiRasterBand::IdrisiRasterBand(IdrisiDataset *poDSIn, int nBandIn,
1376 42 : GDALDataType eDataTypeIn)
1377 : : poDefaultRAT(nullptr),
1378 126 : nRecordSize(poDSIn->GetRasterXSize() * poDSIn->nBands *
1379 42 : GDALGetDataTypeSizeBytes(eDataTypeIn)),
1380 42 : pabyScanLine(static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
1381 : poDSIn->GetRasterXSize(), GDALGetDataTypeSizeBytes(eDataTypeIn),
1382 : poDSIn->nBands))),
1383 84 : fMaximum(0.0), fMinimum(0.0), bFirstVal(true)
1384 : {
1385 42 : poDS = poDSIn;
1386 42 : nBand = nBandIn;
1387 42 : eDataType = eDataTypeIn;
1388 42 : nBlockYSize = 1;
1389 42 : nBlockXSize = poDS->GetRasterXSize();
1390 42 : }
1391 :
1392 : /************************************************************************/
1393 : /* ~IdrisiRasterBand() */
1394 : /************************************************************************/
1395 :
1396 84 : IdrisiRasterBand::~IdrisiRasterBand()
1397 : {
1398 42 : CPLFree(pabyScanLine);
1399 :
1400 42 : if (poDefaultRAT)
1401 : {
1402 0 : delete poDefaultRAT;
1403 : }
1404 84 : }
1405 :
1406 : /************************************************************************/
1407 : /* IReadBlock() */
1408 : /************************************************************************/
1409 :
1410 1392 : CPLErr IdrisiRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
1411 : void *pImage)
1412 : {
1413 1392 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1414 :
1415 1392 : if (VSIFSeekL(poGDS->fp, vsi_l_offset(nRecordSize) * nBlockYOff, SEEK_SET) <
1416 : 0)
1417 : {
1418 0 : CPLError(CE_Failure, CPLE_FileIO,
1419 : "Can't seek(%s) block with X offset %d and Y offset %d.\n%s",
1420 : poGDS->pszFilename, nBlockXOff, nBlockYOff,
1421 0 : VSIStrerror(errno));
1422 0 : return CE_Failure;
1423 : }
1424 :
1425 1392 : if ((int)VSIFReadL(pabyScanLine, 1, nRecordSize, poGDS->fp) < nRecordSize)
1426 : {
1427 0 : CPLError(CE_Failure, CPLE_FileIO,
1428 : "Can't read(%s) block with X offset %d and Y offset %d.\n%s",
1429 : poGDS->pszFilename, nBlockXOff, nBlockYOff,
1430 0 : VSIStrerror(errno));
1431 0 : return CE_Failure;
1432 : }
1433 :
1434 1392 : if (poGDS->nBands == 3)
1435 : {
1436 43380 : for (int i = 0, j = (3 - nBand); i < nBlockXSize; i++, j += 3)
1437 : {
1438 42800 : ((GByte *)pImage)[i] = pabyScanLine[j];
1439 : }
1440 : }
1441 : else
1442 : {
1443 812 : memcpy(pImage, pabyScanLine, nRecordSize);
1444 : }
1445 :
1446 : #ifdef CPL_MSB
1447 : if (eDataType == GDT_Float32)
1448 : GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
1449 : #endif
1450 :
1451 1392 : return CE_None;
1452 : }
1453 :
1454 : /************************************************************************/
1455 : /* IWriteBlock() */
1456 : /************************************************************************/
1457 :
1458 230 : CPLErr IdrisiRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
1459 : void *pImage)
1460 : {
1461 230 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1462 :
1463 : #ifdef CPL_MSB
1464 : // Swap in input buffer if needed.
1465 : if (eDataType == GDT_Float32)
1466 : GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
1467 : #endif
1468 :
1469 230 : if (poGDS->nBands == 1)
1470 : {
1471 50 : memcpy(pabyScanLine, pImage, nRecordSize);
1472 : }
1473 : else
1474 : {
1475 180 : if (nBand > 1)
1476 : {
1477 120 : VSIFSeekL(poGDS->fp, vsi_l_offset(nRecordSize) * nBlockYOff,
1478 : SEEK_SET);
1479 120 : VSIFReadL(pabyScanLine, 1, nRecordSize, poGDS->fp);
1480 : }
1481 : int i, j;
1482 7980 : for (i = 0, j = (3 - nBand); i < nBlockXSize; i++, j += 3)
1483 : {
1484 7800 : pabyScanLine[j] = ((GByte *)pImage)[i];
1485 : }
1486 : }
1487 :
1488 : #ifdef CPL_MSB
1489 : // Swap input buffer back to original form.
1490 : if (eDataType == GDT_Float32)
1491 : GDALSwapWords(pImage, 4, nBlockXSize * nBlockYSize, 4);
1492 : #endif
1493 :
1494 230 : VSIFSeekL(poGDS->fp, vsi_l_offset(nRecordSize) * nBlockYOff, SEEK_SET);
1495 :
1496 230 : if ((int)VSIFWriteL(pabyScanLine, 1, nRecordSize, poGDS->fp) < nRecordSize)
1497 : {
1498 0 : CPLError(CE_Failure, CPLE_FileIO,
1499 : "Can't write(%s) block with X offset %d and Y offset %d.\n%s",
1500 : poGDS->pszFilename, nBlockXOff, nBlockYOff,
1501 0 : VSIStrerror(errno));
1502 0 : return CE_Failure;
1503 : }
1504 :
1505 230 : int bHasNoDataValue = FALSE;
1506 230 : float fNoDataValue = (float)GetNoDataValue(&bHasNoDataValue);
1507 :
1508 : // --------------------------------------------------------------------
1509 : // Search for the minimum and maximum values
1510 : // --------------------------------------------------------------------
1511 :
1512 230 : if (eDataType == GDT_Float32)
1513 : {
1514 530 : for (int i = 0; i < nBlockXSize; i++)
1515 : {
1516 500 : float fVal =
1517 500 : reinterpret_cast<float *>(pabyScanLine)[i]; // this is fine
1518 500 : if (!bHasNoDataValue || fVal != fNoDataValue)
1519 : {
1520 500 : if (bFirstVal)
1521 : {
1522 2 : fMinimum = fVal;
1523 2 : fMaximum = fVal;
1524 2 : bFirstVal = false;
1525 : }
1526 : else
1527 : {
1528 498 : if (fVal < fMinimum)
1529 3 : fMinimum = fVal;
1530 498 : if (fVal > fMaximum)
1531 9 : fMaximum = fVal;
1532 : }
1533 : }
1534 : }
1535 : }
1536 200 : else if (eDataType == GDT_Int16)
1537 : {
1538 110 : for (int i = 0; i < nBlockXSize; i++)
1539 : {
1540 100 : float fVal = (float)(reinterpret_cast<GInt16 *>(pabyScanLine))[i];
1541 100 : if (!bHasNoDataValue || fVal != fNoDataValue)
1542 : {
1543 100 : if (bFirstVal)
1544 : {
1545 1 : fMinimum = fVal;
1546 1 : fMaximum = fVal;
1547 1 : bFirstVal = false;
1548 : }
1549 : else
1550 : {
1551 99 : if (fVal < fMinimum)
1552 0 : fMinimum = fVal;
1553 99 : if (fVal > fMaximum)
1554 0 : fMaximum = fVal;
1555 : }
1556 : }
1557 : }
1558 : }
1559 190 : else if (poGDS->nBands == 1)
1560 : {
1561 110 : for (int i = 0; i < nBlockXSize; i++)
1562 : {
1563 100 : float fVal = (float)((GByte *)pabyScanLine)[i];
1564 100 : if (!bHasNoDataValue || fVal != fNoDataValue)
1565 : {
1566 100 : if (bFirstVal)
1567 : {
1568 1 : fMinimum = fVal;
1569 1 : fMaximum = fVal;
1570 1 : bFirstVal = false;
1571 : }
1572 : else
1573 : {
1574 99 : if (fVal < fMinimum)
1575 0 : fMinimum =
1576 : fVal; // I don't change this part, keep it as it is
1577 99 : if (fVal > fMaximum)
1578 0 : fMaximum = fVal;
1579 : }
1580 : }
1581 : }
1582 : }
1583 : else
1584 : {
1585 7980 : for (int i = 0, j = (3 - nBand); i < nBlockXSize; i++, j += 3)
1586 : {
1587 7800 : float fVal = (float)((GByte *)pabyScanLine)[j];
1588 7800 : if (!bHasNoDataValue || fVal != fNoDataValue)
1589 : {
1590 7800 : if (bFirstVal)
1591 : {
1592 6 : fMinimum = fVal;
1593 6 : fMaximum = fVal;
1594 6 : bFirstVal = false;
1595 : }
1596 : else
1597 : {
1598 7794 : if (fVal < fMinimum)
1599 0 : fMinimum = fVal;
1600 7794 : if (fVal > fMaximum)
1601 28 : fMaximum = fVal;
1602 : }
1603 : }
1604 : }
1605 : }
1606 :
1607 230 : return CE_None;
1608 : }
1609 :
1610 : /************************************************************************/
1611 : /* GetMinimum() */
1612 : /************************************************************************/
1613 :
1614 0 : double IdrisiRasterBand::GetMinimum(int *pbSuccess)
1615 : {
1616 0 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1617 :
1618 0 : if (myCSLFetchNameValue(poGDS->papszRDC, rdcMIN_VALUE) == nullptr)
1619 0 : return GDALPamRasterBand::GetMinimum(pbSuccess);
1620 :
1621 : double adfMinValue[3];
1622 0 : CPLsscanf(myCSLFetchNameValue(poGDS->papszRDC, rdcMIN_VALUE), "%lf %lf %lf",
1623 : &adfMinValue[0], &adfMinValue[1], &adfMinValue[2]);
1624 :
1625 0 : if (pbSuccess)
1626 : {
1627 0 : *pbSuccess = true;
1628 : }
1629 :
1630 0 : return adfMinValue[this->nBand - 1];
1631 : }
1632 :
1633 : /************************************************************************/
1634 : /* GetMaximum() */
1635 : /************************************************************************/
1636 :
1637 0 : double IdrisiRasterBand::GetMaximum(int *pbSuccess)
1638 : {
1639 0 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1640 :
1641 0 : if (myCSLFetchNameValue(poGDS->papszRDC, rdcMAX_VALUE) == nullptr)
1642 0 : return GDALPamRasterBand::GetMaximum(pbSuccess);
1643 :
1644 : double adfMaxValue[3];
1645 0 : CPLsscanf(myCSLFetchNameValue(poGDS->papszRDC, rdcMAX_VALUE), "%lf %lf %lf",
1646 : &adfMaxValue[0], &adfMaxValue[1], &adfMaxValue[2]);
1647 :
1648 0 : if (pbSuccess)
1649 : {
1650 0 : *pbSuccess = true;
1651 : }
1652 :
1653 0 : return adfMaxValue[this->nBand - 1];
1654 : }
1655 :
1656 : /************************************************************************/
1657 : /* GetNoDataValue() */
1658 : /************************************************************************/
1659 :
1660 278 : double IdrisiRasterBand::GetNoDataValue(int *pbSuccess)
1661 : {
1662 278 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1663 :
1664 278 : const char *pszFlagDefn = nullptr;
1665 :
1666 278 : if (myCSLFetchNameValue(poGDS->papszRDC, rdcFLAG_DEFN) != nullptr)
1667 278 : pszFlagDefn = myCSLFetchNameValue(poGDS->papszRDC, rdcFLAG_DEFN);
1668 0 : else if (myCSLFetchNameValue(poGDS->papszRDC, rdcFLAG_DEFN2) != nullptr)
1669 0 : pszFlagDefn = myCSLFetchNameValue(poGDS->papszRDC, rdcFLAG_DEFN2);
1670 :
1671 : // ------------------------------------------------------------------------
1672 : // If Flag_Def is not "none", Flag_Value means "background"
1673 : // or "missing data"
1674 : // ------------------------------------------------------------------------
1675 :
1676 : double dfNoData;
1677 278 : if (pszFlagDefn != nullptr && !EQUAL(pszFlagDefn, "none"))
1678 : {
1679 0 : dfNoData =
1680 0 : CPLAtof_nz(myCSLFetchNameValue(poGDS->papszRDC, rdcFLAG_VALUE));
1681 0 : if (pbSuccess)
1682 0 : *pbSuccess = TRUE;
1683 : }
1684 : else
1685 : {
1686 278 : dfNoData = -9999.0; /* this value should be ignored */
1687 278 : if (pbSuccess)
1688 278 : *pbSuccess = FALSE;
1689 : }
1690 :
1691 278 : return dfNoData;
1692 : }
1693 :
1694 : /************************************************************************/
1695 : /* SetNoDataValue() */
1696 : /************************************************************************/
1697 :
1698 0 : CPLErr IdrisiRasterBand::SetNoDataValue(double dfNoDataValue)
1699 : {
1700 0 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1701 :
1702 0 : poGDS->papszRDC = CSLSetNameValue(poGDS->papszRDC, rdcFLAG_VALUE,
1703 : CPLSPrintf("%.7g", dfNoDataValue));
1704 0 : poGDS->papszRDC =
1705 0 : CSLSetNameValue(poGDS->papszRDC, rdcFLAG_DEFN, "missing data");
1706 :
1707 0 : return CE_None;
1708 : }
1709 :
1710 : /************************************************************************/
1711 : /* GetColorInterpretation() */
1712 : /************************************************************************/
1713 :
1714 0 : GDALColorInterp IdrisiRasterBand::GetColorInterpretation()
1715 : {
1716 0 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1717 :
1718 0 : if (poGDS->nBands == 3)
1719 : {
1720 0 : switch (nBand)
1721 : {
1722 0 : case 1:
1723 0 : return GCI_BlueBand;
1724 0 : case 2:
1725 0 : return GCI_GreenBand;
1726 0 : case 3:
1727 0 : return GCI_RedBand;
1728 : }
1729 : }
1730 0 : else if (poGDS->poColorTable->GetColorEntryCount() > 0)
1731 : {
1732 0 : return GCI_PaletteIndex;
1733 : }
1734 0 : return GCI_GrayIndex;
1735 : }
1736 :
1737 : /************************************************************************/
1738 : /* GetCategoryNames() */
1739 : /************************************************************************/
1740 :
1741 0 : char **IdrisiRasterBand::GetCategoryNames()
1742 : {
1743 0 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1744 :
1745 0 : return poGDS->papszCategories;
1746 : }
1747 :
1748 : /************************************************************************/
1749 : /* SetCategoryNames() */
1750 : /************************************************************************/
1751 :
1752 3 : CPLErr IdrisiRasterBand::SetCategoryNames(char **papszCategoryNames)
1753 : {
1754 3 : const int nCatCount = CSLCount(papszCategoryNames);
1755 :
1756 3 : if (nCatCount == 0)
1757 3 : return CE_None;
1758 :
1759 0 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1760 :
1761 0 : CSLDestroy(poGDS->papszCategories);
1762 0 : poGDS->papszCategories = CSLDuplicate(papszCategoryNames);
1763 :
1764 : // ------------------------------------------------------
1765 : // Search for the "Legend cats : N" line
1766 : // ------------------------------------------------------
1767 :
1768 0 : int nLine = -1;
1769 0 : for (int i = 0; (i < CSLCount(poGDS->papszRDC)) && (nLine == -1); i++)
1770 0 : if (EQUALN(poGDS->papszRDC[i], rdcLEGEND_CATS, 12))
1771 0 : nLine = i;
1772 :
1773 0 : if (nLine < 0)
1774 0 : return CE_None;
1775 :
1776 0 : int nCount = atoi_nz(myCSLFetchNameValue(poGDS->papszRDC, rdcLEGEND_CATS));
1777 :
1778 : // ------------------------------------------------------
1779 : // Delete old instance of the category names
1780 : // ------------------------------------------------------
1781 :
1782 0 : if (nCount > 0)
1783 0 : poGDS->papszRDC =
1784 0 : CSLRemoveStrings(poGDS->papszRDC, nLine + 1, nCount, nullptr);
1785 :
1786 0 : nCount = 0;
1787 :
1788 0 : for (int i = 0; i < nCatCount; i++)
1789 : {
1790 0 : if ((strlen(papszCategoryNames[i]) > 0))
1791 : {
1792 0 : poGDS->papszRDC =
1793 0 : CSLInsertString(poGDS->papszRDC, (nLine + nCount + 1),
1794 : CPLSPrintf("%s:%s", CPLSPrintf(rdcCODE_N, i),
1795 0 : papszCategoryNames[i]));
1796 0 : nCount++;
1797 : }
1798 : }
1799 :
1800 0 : poGDS->papszRDC =
1801 0 : CSLSetNameValue(poGDS->papszRDC, rdcLEGEND_CATS,
1802 : CPLSPrintf("%d", nCount)); // this is fine
1803 :
1804 0 : return CE_None;
1805 : }
1806 :
1807 : /************************************************************************/
1808 : /* GetColorTable() */
1809 : /************************************************************************/
1810 :
1811 0 : GDALColorTable *IdrisiRasterBand::GetColorTable()
1812 : {
1813 0 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1814 :
1815 0 : if (poGDS->poColorTable->GetColorEntryCount() == 0)
1816 : {
1817 0 : return nullptr;
1818 : }
1819 :
1820 0 : return poGDS->poColorTable;
1821 : }
1822 :
1823 : /************************************************************************/
1824 : /* SetColorTable() */
1825 : /************************************************************************/
1826 :
1827 3 : CPLErr IdrisiRasterBand::SetColorTable(GDALColorTable *poColorTable)
1828 : {
1829 3 : if (poColorTable == nullptr)
1830 : {
1831 3 : return CE_None;
1832 : }
1833 :
1834 0 : if (poColorTable->GetColorEntryCount() == 0)
1835 : {
1836 0 : return CE_None;
1837 : }
1838 :
1839 0 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1840 :
1841 0 : delete poGDS->poColorTable;
1842 :
1843 0 : poGDS->poColorTable = poColorTable->Clone();
1844 :
1845 : const std::string osSMPFilename =
1846 0 : CPLResetExtensionSafe(poGDS->pszFilename, extSMP);
1847 0 : VSILFILE *fpSMP = VSIFOpenL(osSMPFilename.c_str(), "w");
1848 :
1849 0 : if (fpSMP != nullptr)
1850 : {
1851 0 : VSIFWriteL("[Idrisi]", 8, 1, fpSMP);
1852 0 : GByte nPlatform = 1;
1853 0 : VSIFWriteL(&nPlatform, 1, 1, fpSMP);
1854 0 : GByte nVersion = 11;
1855 0 : VSIFWriteL(&nVersion, 1, 1, fpSMP);
1856 0 : GByte nDepth = 8;
1857 0 : VSIFWriteL(&nDepth, 1, 1, fpSMP);
1858 0 : GByte nHeadSz = 18;
1859 0 : VSIFWriteL(&nHeadSz, 1, 1, fpSMP);
1860 0 : GUInt16 nCount = 255;
1861 0 : VSIFWriteL(&nCount, 2, 1, fpSMP);
1862 0 : GUInt16 nMix = 0;
1863 0 : VSIFWriteL(&nMix, 2, 1, fpSMP);
1864 0 : GUInt16 nMax = 255;
1865 0 : VSIFWriteL(&nMax, 2, 1, fpSMP);
1866 :
1867 : GDALColorEntry oEntry;
1868 : GByte aucRGB[3];
1869 :
1870 0 : for (int i = 0; i < poColorTable->GetColorEntryCount(); i++)
1871 : {
1872 0 : poColorTable->GetColorEntryAsRGB(i, &oEntry);
1873 0 : aucRGB[0] = (GByte)oEntry.c1;
1874 0 : aucRGB[1] = (GByte)oEntry.c2;
1875 0 : aucRGB[2] = (GByte)oEntry.c3;
1876 0 : VSIFWriteL(&aucRGB, 3, 1, fpSMP);
1877 : }
1878 : /* smp files always have 256 occurrences. */
1879 0 : for (int i = poColorTable->GetColorEntryCount(); i <= 255; i++)
1880 : {
1881 0 : poColorTable->GetColorEntryAsRGB(i, &oEntry);
1882 0 : aucRGB[0] = (GByte)0;
1883 0 : aucRGB[1] = (GByte)0;
1884 0 : aucRGB[2] = (GByte)0;
1885 0 : VSIFWriteL(&aucRGB, 3, 1, fpSMP);
1886 : }
1887 0 : VSIFCloseL(fpSMP);
1888 : }
1889 :
1890 0 : return CE_None;
1891 : }
1892 :
1893 : /************************************************************************/
1894 : /* GetUnitType() */
1895 : /************************************************************************/
1896 :
1897 0 : const char *IdrisiRasterBand::GetUnitType()
1898 : {
1899 0 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1900 :
1901 0 : return poGDS->pszUnitType;
1902 : }
1903 :
1904 : /************************************************************************/
1905 : /* SetUnitType() */
1906 : /************************************************************************/
1907 :
1908 3 : CPLErr IdrisiRasterBand::SetUnitType(const char *pszUnitType)
1909 : {
1910 3 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1911 :
1912 3 : if (strlen(pszUnitType) == 0)
1913 : {
1914 3 : poGDS->papszRDC =
1915 3 : CSLSetNameValue(poGDS->papszRDC, rdcVALUE_UNITS, "unspecified");
1916 : }
1917 : else
1918 : {
1919 0 : poGDS->papszRDC =
1920 0 : CSLSetNameValue(poGDS->papszRDC, rdcVALUE_UNITS, pszUnitType);
1921 : }
1922 :
1923 3 : return CE_None;
1924 : }
1925 :
1926 : /************************************************************************/
1927 : /* SetMinMax() */
1928 : /************************************************************************/
1929 :
1930 49 : CPLErr IdrisiRasterBand::SetMinMax(double dfMin, double dfMax)
1931 : {
1932 49 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
1933 :
1934 49 : fMinimum = (float)dfMin;
1935 49 : fMaximum = (float)dfMax;
1936 :
1937 49 : double adfMin[3] = {0.0, 0.0, 0.0};
1938 49 : double adfMax[3] = {0.0, 0.0, 0.0};
1939 :
1940 49 : if (myCSLFetchNameValue(poGDS->papszRDC, rdcMIN_VALUE) != nullptr)
1941 49 : CPLsscanf(myCSLFetchNameValue(poGDS->papszRDC, rdcMIN_VALUE),
1942 : "%lf %lf %lf", &adfMin[0], &adfMin[1], &adfMin[2]);
1943 49 : if (myCSLFetchNameValue(poGDS->papszRDC, rdcMAX_VALUE) != nullptr)
1944 49 : CPLsscanf(myCSLFetchNameValue(poGDS->papszRDC, rdcMAX_VALUE),
1945 : "%lf %lf %lf", &adfMax[0], &adfMax[1], &adfMax[2]);
1946 :
1947 49 : adfMin[nBand - 1] = dfMin;
1948 49 : adfMax[nBand - 1] = dfMax;
1949 :
1950 49 : if (poGDS->nBands == 3)
1951 : {
1952 24 : poGDS->papszRDC = CSLSetNameValue(
1953 : poGDS->papszRDC, rdcMIN_VALUE,
1954 : CPLSPrintf("%.8g %.8g %.8g", adfMin[0], adfMin[1], adfMin[2]));
1955 24 : poGDS->papszRDC = CSLSetNameValue(
1956 : poGDS->papszRDC, rdcMAX_VALUE,
1957 : CPLSPrintf("%.8g %.8g %.8g", adfMax[0], adfMax[1], adfMax[2]));
1958 24 : poGDS->papszRDC = CSLSetNameValue(
1959 : poGDS->papszRDC, rdcDISPLAY_MIN,
1960 : CPLSPrintf("%.8g %.8g %.8g", adfMin[0], adfMin[1], adfMin[2]));
1961 24 : poGDS->papszRDC = CSLSetNameValue(
1962 : poGDS->papszRDC, rdcDISPLAY_MAX,
1963 : CPLSPrintf("%.8g %.8g %.8g", adfMax[0], adfMax[1], adfMax[2]));
1964 : }
1965 : else
1966 : {
1967 25 : poGDS->papszRDC = CSLSetNameValue(poGDS->papszRDC, rdcMIN_VALUE,
1968 : CPLSPrintf("%.8g", adfMin[0]));
1969 25 : poGDS->papszRDC = CSLSetNameValue(poGDS->papszRDC, rdcMAX_VALUE,
1970 : CPLSPrintf("%.8g", adfMax[0]));
1971 25 : poGDS->papszRDC = CSLSetNameValue(poGDS->papszRDC, rdcDISPLAY_MIN,
1972 : CPLSPrintf("%.8g", adfMin[0]));
1973 25 : poGDS->papszRDC = CSLSetNameValue(poGDS->papszRDC, rdcDISPLAY_MAX,
1974 : CPLSPrintf("%.8g", adfMax[0]));
1975 : }
1976 :
1977 49 : return CE_None;
1978 : }
1979 :
1980 : /************************************************************************/
1981 : /* SetStatistics() */
1982 : /************************************************************************/
1983 :
1984 20 : CPLErr IdrisiRasterBand::SetStatistics(double dfMin, double dfMax,
1985 : double dfMean, double dfStdDev)
1986 : {
1987 20 : SetMinMax(dfMin, dfMax);
1988 :
1989 20 : return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
1990 : }
1991 :
1992 : /************************************************************************/
1993 : /* SetDefaultRAT() */
1994 : /************************************************************************/
1995 :
1996 0 : CPLErr IdrisiRasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT)
1997 : {
1998 0 : if (!poRAT)
1999 : {
2000 0 : return CE_Failure;
2001 : }
2002 :
2003 : // ----------------------------------------------------------
2004 : // Get field indices
2005 : // ----------------------------------------------------------
2006 :
2007 0 : int iValue = -1;
2008 0 : int iRed = poRAT->GetColOfUsage(GFU_Red);
2009 0 : int iGreen = poRAT->GetColOfUsage(GFU_Green);
2010 0 : int iBlue = poRAT->GetColOfUsage(GFU_Blue);
2011 :
2012 0 : GDALColorTable *poCT = nullptr;
2013 0 : char **papszNames = nullptr;
2014 :
2015 0 : int nFact = 1;
2016 :
2017 : // ----------------------------------------------------------
2018 : // Seek for "Value" field index (AGIS standards field name)
2019 : // ----------------------------------------------------------
2020 :
2021 0 : if (GetColorTable() == nullptr ||
2022 0 : GetColorTable()->GetColorEntryCount() == 0)
2023 : {
2024 0 : for (int i = 0; i < poRAT->GetColumnCount(); i++)
2025 : {
2026 0 : if (STARTS_WITH_CI(poRAT->GetNameOfCol(i), "Value"))
2027 : {
2028 0 : iValue = i;
2029 0 : break;
2030 : }
2031 : }
2032 :
2033 0 : if (iRed != -1 && iGreen != -1 && iBlue != -1)
2034 : {
2035 0 : poCT = new GDALColorTable();
2036 0 : nFact = poRAT->GetTypeOfCol(iRed) == GFT_Real ? 255 : 1;
2037 : }
2038 : }
2039 :
2040 : // ----------------------------------------------------------
2041 : // Seek for Name field index
2042 : // ----------------------------------------------------------
2043 :
2044 0 : int iName = -1;
2045 0 : if (CSLCount(GetCategoryNames()) == 0)
2046 : {
2047 0 : iName = poRAT->GetColOfUsage(GFU_Name);
2048 0 : if (iName == -1)
2049 : {
2050 0 : for (int i = 0; i < poRAT->GetColumnCount(); i++)
2051 : {
2052 0 : if (STARTS_WITH_CI(poRAT->GetNameOfCol(i), "Class_Name"))
2053 : {
2054 0 : iName = i;
2055 0 : break;
2056 : }
2057 0 : else if (STARTS_WITH_CI(poRAT->GetNameOfCol(i), "Categor"))
2058 : {
2059 0 : iName = i;
2060 0 : break;
2061 : }
2062 0 : else if (STARTS_WITH_CI(poRAT->GetNameOfCol(i), "Name"))
2063 : {
2064 0 : iName = i;
2065 0 : break;
2066 : }
2067 : }
2068 : }
2069 :
2070 : /* if still can't find it use the first String column */
2071 :
2072 0 : if (iName == -1)
2073 : {
2074 0 : for (int i = 0; i < poRAT->GetColumnCount(); i++)
2075 : {
2076 0 : if (poRAT->GetTypeOfCol(i) == GFT_String)
2077 : {
2078 0 : iName = i;
2079 0 : break;
2080 : }
2081 : }
2082 : }
2083 :
2084 : // ----------------------------------------------------------
2085 : // Incomplete Attribute Table;
2086 : // ----------------------------------------------------------
2087 :
2088 0 : if (iName == -1)
2089 : {
2090 0 : iName = iValue;
2091 : }
2092 : }
2093 :
2094 : // ----------------------------------------------------------
2095 : // Load values
2096 : // ----------------------------------------------------------
2097 :
2098 : GDALColorEntry sColor;
2099 0 : int iEntry = 0;
2100 0 : int iOut = 0;
2101 0 : int nEntryCount = poRAT->GetRowCount();
2102 0 : int nValue = 0;
2103 :
2104 0 : if (iValue != -1)
2105 : {
2106 0 : nValue = poRAT->GetValueAsInt(iEntry, iValue);
2107 : }
2108 :
2109 0 : for (iOut = 0; iOut < 65535 && (iEntry < nEntryCount); iOut++)
2110 : {
2111 0 : if (iOut == nValue)
2112 : {
2113 0 : if (poCT)
2114 : {
2115 0 : const double dRed = poRAT->GetValueAsDouble(iEntry, iRed);
2116 0 : const double dGreen = poRAT->GetValueAsDouble(iEntry, iGreen);
2117 0 : const double dBlue = poRAT->GetValueAsDouble(iEntry, iBlue);
2118 0 : sColor.c1 = (short)(dRed * nFact);
2119 0 : sColor.c2 = (short)(dGreen * nFact);
2120 0 : sColor.c3 = (short)(dBlue * nFact);
2121 0 : sColor.c4 = (short)(255 / nFact);
2122 0 : poCT->SetColorEntry(iEntry, &sColor);
2123 : }
2124 :
2125 0 : if (iName != -1)
2126 : {
2127 0 : papszNames = CSLAddString(
2128 0 : papszNames, poRAT->GetValueAsString(iEntry, iName));
2129 : }
2130 :
2131 : /* Advance on the table */
2132 :
2133 0 : if ((++iEntry) < nEntryCount)
2134 : {
2135 0 : if (iValue != -1)
2136 0 : nValue = poRAT->GetValueAsInt(iEntry, iValue);
2137 : else
2138 0 : nValue = iEntry;
2139 : }
2140 : }
2141 0 : else if (iOut < nValue)
2142 : {
2143 0 : if (poCT)
2144 : {
2145 0 : sColor.c1 = (short)0;
2146 0 : sColor.c2 = (short)0;
2147 0 : sColor.c3 = (short)0;
2148 0 : sColor.c4 = (short)255;
2149 0 : poCT->SetColorEntry(iEntry, &sColor);
2150 : }
2151 :
2152 0 : if (iName != -1)
2153 0 : papszNames = CSLAddString(papszNames, "");
2154 : }
2155 : }
2156 :
2157 : // ----------------------------------------------------------
2158 : // Set Color Table
2159 : // ----------------------------------------------------------
2160 :
2161 0 : if (poCT)
2162 : {
2163 0 : SetColorTable(poCT);
2164 0 : delete poCT;
2165 : }
2166 :
2167 : // ----------------------------------------------------------
2168 : // Update Category Names
2169 : // ----------------------------------------------------------
2170 :
2171 0 : if (papszNames)
2172 : {
2173 0 : SetCategoryNames(papszNames);
2174 0 : CSLDestroy(papszNames);
2175 : }
2176 :
2177 : // ----------------------------------------------------------
2178 : // Update Attribute Table
2179 : // ----------------------------------------------------------
2180 :
2181 0 : if (poDefaultRAT)
2182 : {
2183 0 : delete poDefaultRAT;
2184 : }
2185 :
2186 0 : poDefaultRAT = poRAT->Clone();
2187 :
2188 0 : return CE_None;
2189 : }
2190 :
2191 : /************************************************************************/
2192 : /* GetDefaultRAT() */
2193 : /************************************************************************/
2194 :
2195 0 : GDALRasterAttributeTable *IdrisiRasterBand::GetDefaultRAT()
2196 : {
2197 0 : IdrisiDataset *poGDS = cpl::down_cast<IdrisiDataset *>(poDS);
2198 :
2199 0 : if (poGDS->papszCategories == nullptr)
2200 : {
2201 0 : return nullptr;
2202 : }
2203 :
2204 0 : bool bHasColorTable = poGDS->poColorTable->GetColorEntryCount() > 0;
2205 :
2206 : // ----------------------------------------------------------
2207 : // Create the bands Attribute Table
2208 : // ----------------------------------------------------------
2209 :
2210 0 : if (poDefaultRAT)
2211 : {
2212 0 : delete poDefaultRAT;
2213 : }
2214 :
2215 0 : poDefaultRAT = new GDALDefaultRasterAttributeTable();
2216 :
2217 : // ----------------------------------------------------------
2218 : // Create (Value, Red, Green, Blue, Alpha, Class_Name) fields
2219 : // ----------------------------------------------------------
2220 :
2221 0 : poDefaultRAT->CreateColumn("Value", GFT_Integer, GFU_Generic);
2222 0 : poDefaultRAT->CreateColumn("Value_1", GFT_Integer, GFU_MinMax);
2223 :
2224 0 : if (bHasColorTable)
2225 : {
2226 0 : poDefaultRAT->CreateColumn("Red", GFT_Integer, GFU_Red);
2227 0 : poDefaultRAT->CreateColumn("Green", GFT_Integer, GFU_Green);
2228 0 : poDefaultRAT->CreateColumn("Blue", GFT_Integer, GFU_Blue);
2229 0 : poDefaultRAT->CreateColumn("Alpha", GFT_Integer, GFU_Alpha);
2230 : }
2231 0 : poDefaultRAT->CreateColumn("Class_name", GFT_String, GFU_Name);
2232 :
2233 : // ----------------------------------------------------------
2234 : // Loop through the Category Names.
2235 : // ----------------------------------------------------------
2236 :
2237 : GDALColorEntry sEntry;
2238 0 : int iName = poDefaultRAT->GetColOfUsage(GFU_Name);
2239 0 : int nEntryCount = CSLCount(poGDS->papszCategories);
2240 0 : int iRows = 0;
2241 :
2242 0 : for (int iEntry = 0; iEntry < nEntryCount; iEntry++)
2243 : {
2244 0 : if (EQUAL(poGDS->papszCategories[iEntry], ""))
2245 : {
2246 0 : continue; // Eliminate the empty ones
2247 : }
2248 0 : poDefaultRAT->SetRowCount(poDefaultRAT->GetRowCount() + 1);
2249 0 : poDefaultRAT->SetValue(iRows, 0, iEntry);
2250 0 : poDefaultRAT->SetValue(iRows, 1, iEntry);
2251 0 : if (bHasColorTable)
2252 : {
2253 0 : poGDS->poColorTable->GetColorEntryAsRGB(iEntry, &sEntry);
2254 0 : poDefaultRAT->SetValue(iRows, 2, sEntry.c1);
2255 0 : poDefaultRAT->SetValue(iRows, 3, sEntry.c2);
2256 0 : poDefaultRAT->SetValue(iRows, 4, sEntry.c3);
2257 0 : poDefaultRAT->SetValue(iRows, 5, sEntry.c4);
2258 : }
2259 0 : poDefaultRAT->SetValue(iRows, iName, poGDS->papszCategories[iEntry]);
2260 0 : iRows++;
2261 : }
2262 :
2263 0 : return poDefaultRAT;
2264 : }
2265 :
2266 : /************************************************************************/
2267 : /* IdrisiGeoReference2Wkt() */
2268 : /************************************************************************/
2269 :
2270 : /***
2271 : * Converts Idrisi geographic reference information to OpenGIS WKT.
2272 : *
2273 : * The Idrisi metadata file contain two fields that describe the
2274 : * geographic reference, RefSystem and RefUnit.
2275 : *
2276 : * RefSystem can contains the world "plane" or the name of a georeference
2277 : * file <refsystem>.ref that details the geographic reference
2278 : * system( coordinate system and projection parameters ). RefUnits
2279 : * indicates the unit of the image bounds.
2280 : *
2281 : * The georeference files are generally located in the product installation
2282 : * folder $IDRISIDIR\\Georef, but they are first looked for in the same
2283 : * folder as the data file.
2284 : *
2285 : * If a Reference system names can be recognized by a name convention
2286 : * it will be interpreted without the need to read the georeference file.
2287 : * That includes "latlong" and all the UTM and State Plane zones.
2288 : *
2289 : * RefSystem "latlong" means that the data is not project and the coordinate
2290 : * system is WGS84. RefSystem "plane" means that the there is no coordinate
2291 : * system but the it is possible to calculate areas and distance by looking
2292 : * at the RefUnits.
2293 : *
2294 : * If the environment variable IDRISIDIR is not set and the georeference file
2295 : * need to be read then the projection string will result as unknown.
2296 : ***/
2297 :
2298 4 : CPLErr IdrisiGeoReference2Wkt(const char *pszFilename, const char *pszRefSystem,
2299 : const char *pszRefUnits,
2300 : OGRSpatialReference &oSRS)
2301 : {
2302 : // ---------------------------------------------------------
2303 : // Plane
2304 : // ---------------------------------------------------------
2305 :
2306 4 : if (EQUAL(pszRefSystem, rstPLANE))
2307 : {
2308 0 : oSRS.SetLocalCS("Plane");
2309 0 : int nUnit = GetUnitIndex(pszRefUnits);
2310 0 : if (nUnit > -1)
2311 : {
2312 0 : int nDeft = aoLinearUnitsConv[nUnit].nDefaultG;
2313 0 : oSRS.SetLinearUnits(aoLinearUnitsConv[nDeft].pszName,
2314 0 : aoLinearUnitsConv[nDeft].dfConv);
2315 : }
2316 0 : return CE_None;
2317 : }
2318 :
2319 : // ---------------------------------------------------------
2320 : // Latlong
2321 : // ---------------------------------------------------------
2322 :
2323 4 : if (EQUAL(pszRefSystem, rstLATLONG) || EQUAL(pszRefSystem, rstLATLONG2))
2324 : {
2325 1 : oSRS.SetWellKnownGeogCS("WGS84");
2326 1 : return CE_None;
2327 : }
2328 :
2329 : // ---------------------------------------------------------
2330 : // Prepare for scanning in lower case
2331 : // ---------------------------------------------------------
2332 :
2333 3 : char *pszRefSystemLower = CPLStrdup(pszRefSystem);
2334 3 : CPLStrlwr(pszRefSystemLower);
2335 :
2336 : // ---------------------------------------------------------
2337 : // UTM naming convention( ex.: utm-30n )
2338 : // ---------------------------------------------------------
2339 :
2340 3 : if (EQUALN(pszRefSystem, rstUTM, 3))
2341 : {
2342 : int nZone;
2343 : char cNorth;
2344 3 : sscanf(pszRefSystemLower, rstUTM, &nZone, &cNorth);
2345 3 : oSRS.SetWellKnownGeogCS("WGS84");
2346 3 : oSRS.SetUTM(nZone, (cNorth == 'n'));
2347 3 : CPLFree(pszRefSystemLower);
2348 3 : return CE_None;
2349 : }
2350 :
2351 : // ---------------------------------------------------------
2352 : // State Plane naming convention( ex.: spc83ma1 )
2353 : // ---------------------------------------------------------
2354 :
2355 0 : if (EQUALN(pszRefSystem, rstSPC, 3))
2356 : {
2357 : int nNAD;
2358 : int nZone;
2359 : char szState[3];
2360 0 : sscanf(pszRefSystemLower, rstSPC, &nNAD, szState, &nZone);
2361 0 : int nSPCode = GetStateCode(szState);
2362 0 : if (nSPCode != -1)
2363 : {
2364 0 : nZone = (nZone == 1 ? nSPCode : nSPCode + nZone - 1);
2365 :
2366 0 : if (oSRS.SetStatePlane(nZone, (nNAD == 83)) != OGRERR_FAILURE)
2367 : {
2368 0 : CPLFree(pszRefSystemLower);
2369 0 : return CE_None;
2370 : }
2371 :
2372 : // ----------------------------------------------------------
2373 : // If SetStatePlane fails, set GeoCS as NAD Datum and let it
2374 : // try to read the projection info from georeference file( * )
2375 : // ----------------------------------------------------------
2376 :
2377 0 : oSRS.SetWellKnownGeogCS(CPLSPrintf("NAD%d", nNAD));
2378 : }
2379 : }
2380 :
2381 0 : CPLFree(pszRefSystemLower);
2382 0 : pszRefSystemLower = nullptr;
2383 :
2384 : // ------------------------------------------------------------------
2385 : // Search for georeference file <RefSystem>.ref
2386 : // ------------------------------------------------------------------
2387 :
2388 : const char *pszFName =
2389 0 : CPLSPrintf("%s%c%s.ref", CPLGetDirnameSafe(pszFilename).c_str(),
2390 : PATHDELIM, pszRefSystem);
2391 :
2392 0 : if (!FileExists(pszFName))
2393 : {
2394 : // ------------------------------------------------------------------
2395 : // Look at $IDRISIDIR\Georef\<RefSystem>.ref
2396 : // ------------------------------------------------------------------
2397 :
2398 0 : const char *pszIdrisiDir = CPLGetConfigOption("IDRISIDIR", nullptr);
2399 :
2400 0 : if ((pszIdrisiDir) != nullptr)
2401 : {
2402 0 : pszFName = CPLSPrintf("%s%cgeoref%c%s.ref", pszIdrisiDir, PATHDELIM,
2403 : PATHDELIM, pszRefSystem);
2404 : }
2405 : }
2406 :
2407 : // ------------------------------------------------------------------
2408 : // Cannot find georeference file
2409 : // ------------------------------------------------------------------
2410 :
2411 0 : if (!FileExists(pszFName))
2412 : {
2413 0 : CPLDebug("RST", "Cannot find Idrisi georeference file %s",
2414 : pszRefSystem);
2415 :
2416 0 : if (oSRS.IsGeographic() == FALSE) /* see State Plane remarks( * ) */
2417 : {
2418 0 : oSRS.SetLocalCS("Unknown");
2419 0 : int nUnit = GetUnitIndex(pszRefUnits);
2420 0 : if (nUnit > -1)
2421 : {
2422 0 : int nDeft = aoLinearUnitsConv[nUnit].nDefaultG;
2423 0 : oSRS.SetLinearUnits(aoLinearUnitsConv[nDeft].pszName,
2424 0 : aoLinearUnitsConv[nDeft].dfConv);
2425 : }
2426 : }
2427 0 : return CE_Failure;
2428 : }
2429 :
2430 : // ------------------------------------------------------------------
2431 : // Read values from georeference file
2432 : // ------------------------------------------------------------------
2433 :
2434 0 : char **papszRef = CSLLoad(pszFName);
2435 0 : myCSLSetNameValueSeparator(papszRef, ":");
2436 :
2437 0 : char *pszGeorefName = nullptr;
2438 :
2439 0 : const char *pszREF_SYSTEM = myCSLFetchNameValue(papszRef, refREF_SYSTEM);
2440 0 : if (pszREF_SYSTEM != nullptr && EQUAL(pszREF_SYSTEM, "") == FALSE)
2441 : {
2442 0 : pszGeorefName = CPLStrdup(pszREF_SYSTEM);
2443 : }
2444 : else
2445 : {
2446 : pszGeorefName =
2447 0 : CPLStrdup(myCSLFetchNameValue(papszRef, refREF_SYSTEM2));
2448 : }
2449 0 : char *pszProjName = CPLStrdup(myCSLFetchNameValue(papszRef, refPROJECTION));
2450 0 : char *pszDatum = CPLStrdup(myCSLFetchNameValue(papszRef, refDATUM));
2451 0 : char *pszEllipsoid = CPLStrdup(myCSLFetchNameValue(papszRef, refELLIPSOID));
2452 : const double dfCenterLat =
2453 0 : CPLAtof_nz(myCSLFetchNameValue(papszRef, refORIGIN_LAT));
2454 : const double dfCenterLong =
2455 0 : CPLAtof_nz(myCSLFetchNameValue(papszRef, refORIGIN_LONG));
2456 : const double dfSemiMajor =
2457 0 : CPLAtof_nz(myCSLFetchNameValue(papszRef, refMAJOR_SAX));
2458 : const double dfSemiMinor =
2459 0 : CPLAtof_nz(myCSLFetchNameValue(papszRef, refMINOR_SAX));
2460 : const double dfFalseEasting =
2461 0 : CPLAtof_nz(myCSLFetchNameValue(papszRef, refORIGIN_X));
2462 : const double dfFalseNorthing =
2463 0 : CPLAtof_nz(myCSLFetchNameValue(papszRef, refORIGIN_Y));
2464 : const double dfStdP1 =
2465 0 : CPLAtof_nz(myCSLFetchNameValue(papszRef, refSTANDL_1));
2466 : const double dfStdP2 =
2467 0 : CPLAtof_nz(myCSLFetchNameValue(papszRef, refSTANDL_2));
2468 : double dfScale;
2469 0 : double adfToWGS84[3] = {0.0, 0.0, 0.0};
2470 :
2471 0 : const char *pszToWGS84 = myCSLFetchNameValue(papszRef, refDELTA_WGS84);
2472 0 : if (pszToWGS84)
2473 0 : CPLsscanf(pszToWGS84, "%lf %lf %lf", &adfToWGS84[0], &adfToWGS84[1],
2474 : &adfToWGS84[2]);
2475 :
2476 0 : const char *pszSCALE_FAC = myCSLFetchNameValue(papszRef, refSCALE_FAC);
2477 0 : if (pszSCALE_FAC == nullptr || EQUAL(pszSCALE_FAC, "na"))
2478 0 : dfScale = 1.0;
2479 : else
2480 0 : dfScale = CPLAtof_nz(pszSCALE_FAC);
2481 :
2482 0 : CSLDestroy(papszRef);
2483 :
2484 : // ----------------------------------------------------------------------
2485 : // Set the Geographic Coordinate System
2486 : // ----------------------------------------------------------------------
2487 :
2488 0 : if (oSRS.IsGeographic() == FALSE) /* see State Plane remarks(*) */
2489 : {
2490 0 : int nEPSG = 0;
2491 :
2492 : // ----------------------------------------------------------------------
2493 : // Is it a WGS84 equivalent?
2494 : // ----------------------------------------------------------------------
2495 :
2496 0 : if ((STARTS_WITH_CI(pszEllipsoid, "WGS")) &&
2497 0 : (strstr(pszEllipsoid, "84")) && (STARTS_WITH_CI(pszDatum, "WGS")) &&
2498 0 : (strstr(pszDatum, "84")) && (adfToWGS84[0] == 0.0) &&
2499 0 : (adfToWGS84[1] == 0.0) && (adfToWGS84[2] == 0.0))
2500 : {
2501 0 : nEPSG = 4326;
2502 : }
2503 :
2504 : // ----------------------------------------------------------------------
2505 : // Match GCS's DATUM_NAME by using 'ApproxString' over Datum
2506 : // ----------------------------------------------------------------------
2507 :
2508 0 : if (nEPSG == 0)
2509 : {
2510 0 : const PJ_TYPE nObjType = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
2511 : auto datumList =
2512 0 : proj_create_from_name(OSRGetProjTLSContext(), "EPSG", pszDatum,
2513 : &nObjType, 1, true, 1, nullptr);
2514 0 : if (datumList && proj_list_get_count(datumList) == 1)
2515 : {
2516 : auto datum =
2517 0 : proj_list_get(OSRGetProjTLSContext(), datumList, 0);
2518 0 : if (datum)
2519 : {
2520 0 : const char *datumCode = proj_get_id_code(datum, 0);
2521 0 : if (datumCode)
2522 : {
2523 0 : auto crsList = proj_query_geodetic_crs_from_datum(
2524 : OSRGetProjTLSContext(), "EPSG", "EPSG", datumCode,
2525 : "geographic 2D");
2526 0 : if (crsList && proj_list_get_count(crsList) != 0)
2527 : {
2528 0 : auto crs = proj_list_get(OSRGetProjTLSContext(),
2529 : crsList, 0);
2530 0 : if (crs)
2531 : {
2532 0 : const char *crsCode = proj_get_id_code(crs, 0);
2533 0 : if (crsCode)
2534 : {
2535 0 : nEPSG = atoi(crsCode);
2536 : }
2537 0 : proj_destroy(crs);
2538 : }
2539 : }
2540 0 : proj_list_destroy(crsList);
2541 : }
2542 0 : proj_destroy(datum);
2543 : }
2544 : }
2545 0 : proj_list_destroy(datumList);
2546 : }
2547 :
2548 : // ----------------------------------------------------------------------
2549 : // Match GCS's COORD_REF_SYS_NAME by using 'ApproxString' over Datum
2550 : // ----------------------------------------------------------------------
2551 :
2552 0 : if (nEPSG == 0)
2553 : {
2554 0 : const PJ_TYPE nObjType = PJ_TYPE_GEOGRAPHIC_2D_CRS;
2555 : auto crsList =
2556 0 : proj_create_from_name(OSRGetProjTLSContext(), "EPSG", pszDatum,
2557 : &nObjType, 1, true, 1, nullptr);
2558 0 : if (crsList && proj_list_get_count(crsList) != 0)
2559 : {
2560 0 : auto crs = proj_list_get(OSRGetProjTLSContext(), crsList, 0);
2561 0 : if (crs)
2562 : {
2563 0 : const char *crsCode = proj_get_id_code(crs, 0);
2564 0 : if (crsCode)
2565 : {
2566 0 : nEPSG = atoi(crsCode);
2567 : }
2568 0 : proj_destroy(crs);
2569 : }
2570 : }
2571 0 : proj_list_destroy(crsList);
2572 : }
2573 :
2574 0 : if (nEPSG != 0)
2575 : {
2576 0 : oSRS.importFromEPSG(nEPSG);
2577 : }
2578 : else
2579 : {
2580 : // --------------------------------------------------
2581 : // Create GeogCS based on the georeference file info
2582 : // --------------------------------------------------
2583 :
2584 0 : oSRS.SetGeogCS(pszRefSystem, pszDatum, pszEllipsoid, dfSemiMajor,
2585 : (dfSemiMinor == dfSemiMajor)
2586 : ? 0.0
2587 0 : : (-1.0 / (dfSemiMinor / dfSemiMajor - 1.0)));
2588 : }
2589 :
2590 : // ----------------------------------------------------------------------
2591 : // Note: That will override EPSG info:
2592 : // ----------------------------------------------------------------------
2593 :
2594 0 : oSRS.SetTOWGS84(adfToWGS84[0], adfToWGS84[1], adfToWGS84[2]);
2595 : }
2596 :
2597 : // ----------------------------------------------------------------------
2598 : // If the georeference file tells that it is a non project system:
2599 : // ----------------------------------------------------------------------
2600 :
2601 0 : if (EQUAL(pszProjName, "none"))
2602 : {
2603 0 : CPLFree(pszGeorefName);
2604 0 : CPLFree(pszProjName);
2605 0 : CPLFree(pszDatum);
2606 0 : CPLFree(pszEllipsoid);
2607 :
2608 0 : return CE_None;
2609 : }
2610 :
2611 : // ----------------------------------------------------------------------
2612 : // Create Projection information based on georeference file info
2613 : // ----------------------------------------------------------------------
2614 :
2615 : // Idrisi user's Manual, Supported Projection:
2616 : //
2617 : // Mercator
2618 : // Transverse Mercator
2619 : // Gauss-Kruger
2620 : // Lambert Conformal Conic
2621 : // Plate Carree
2622 : // Hammer Aitoff
2623 : // Lambert North Polar Azimuthal Equal Area
2624 : // Lambert South Polar Azimuthal Equal Area
2625 : // Lambert Transverse Azimuthal Equal Area
2626 : // Lambert Oblique Polar Azimuthal Equal Area
2627 : // North Polar Stereographic
2628 : // South Polar Stereographic
2629 : // Transverse Stereographic
2630 : // Oblique Stereographic
2631 : // Albers Equal Area Conic
2632 : // Sinusoidal
2633 : // Cylindrical Equal Area
2634 :
2635 0 : if (EQUAL(pszProjName, "Mercator"))
2636 : {
2637 0 : oSRS.SetMercator(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
2638 : dfFalseNorthing);
2639 : }
2640 0 : else if (EQUAL(pszProjName, "Transverse Mercator"))
2641 : {
2642 0 : oSRS.SetTM(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
2643 : dfFalseNorthing);
2644 : }
2645 0 : else if (EQUAL(pszProjName, "Gauss-Kruger"))
2646 : {
2647 0 : oSRS.SetTM(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
2648 : dfFalseNorthing);
2649 : }
2650 0 : else if (EQUAL(pszProjName, "Lambert Conformal Conic"))
2651 : {
2652 0 : oSRS.SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, dfFalseEasting,
2653 : dfFalseNorthing);
2654 : }
2655 0 : else if (EQUAL(pszProjName, "Plate Carr"
2656 : "\xE9"
2657 : "e")) /* 'eacute' in ISO-8859-1 */
2658 : {
2659 0 : oSRS.SetEquirectangular(dfCenterLat, dfCenterLong, dfFalseEasting,
2660 : dfFalseNorthing);
2661 : }
2662 0 : else if (EQUAL(pszProjName, "Hammer Aitoff"))
2663 : {
2664 0 : oSRS.SetProjection(pszProjName);
2665 0 : oSRS.SetProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
2666 0 : oSRS.SetProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
2667 0 : oSRS.SetProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
2668 0 : oSRS.SetProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
2669 : }
2670 0 : else if (EQUAL(pszProjName, "Lambert North Polar Azimuthal Equal Area") ||
2671 0 : EQUAL(pszProjName, "Lambert South Polar Azimuthal Equal Area") ||
2672 0 : EQUAL(pszProjName, "Lambert Transverse Azimuthal Equal Area") ||
2673 0 : EQUAL(pszProjName, "Lambert Oblique Polar Azimuthal Equal Area"))
2674 : {
2675 0 : oSRS.SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
2676 : dfFalseNorthing);
2677 : }
2678 0 : else if (EQUAL(pszProjName, "North Polar Stereographic") ||
2679 0 : EQUAL(pszProjName, "South Polar Stereographic"))
2680 : {
2681 0 : oSRS.SetPS(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
2682 : dfFalseNorthing);
2683 : }
2684 0 : else if (EQUAL(pszProjName, "Transverse Stereographic"))
2685 : {
2686 0 : oSRS.SetStereographic(dfCenterLat, dfCenterLong, dfScale,
2687 : dfFalseEasting, dfFalseNorthing);
2688 : }
2689 0 : else if (EQUAL(pszProjName, "Oblique Stereographic"))
2690 : {
2691 0 : oSRS.SetOS(dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
2692 : dfFalseNorthing);
2693 : }
2694 0 : else if (EQUAL(pszProjName, "Alber's Equal Area Conic") ||
2695 0 : EQUAL(pszProjName, "Albers Equal Area Conic"))
2696 : {
2697 0 : oSRS.SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
2698 : dfFalseEasting, dfFalseNorthing);
2699 : }
2700 0 : else if (EQUAL(pszProjName, "Sinusoidal"))
2701 : {
2702 0 : oSRS.SetSinusoidal(dfCenterLong, dfFalseEasting, dfFalseNorthing);
2703 : }
2704 0 : else if (EQUAL(pszProjName, "CylindricalEA") ||
2705 0 : EQUAL(pszProjName, "Cylindrical Equal Area"))
2706 : {
2707 0 : oSRS.SetCEA(dfStdP1, dfCenterLong, dfFalseEasting, dfFalseNorthing);
2708 : }
2709 : else
2710 : {
2711 0 : CPLError(
2712 : CE_Warning, CPLE_NotSupported,
2713 : "Projection not listed on Idrisi User's Manual( v.15.0/2005 ).\n\t"
2714 : "[\"%s\" in georeference file \"%s\"]",
2715 : pszProjName, pszFName);
2716 0 : oSRS.Clear();
2717 :
2718 0 : CPLFree(pszGeorefName);
2719 0 : CPLFree(pszProjName);
2720 0 : CPLFree(pszDatum);
2721 0 : CPLFree(pszEllipsoid);
2722 :
2723 0 : return CE_Warning;
2724 : }
2725 :
2726 : // ----------------------------------------------------------------------
2727 : // Set the Linear Units
2728 : // ----------------------------------------------------------------------
2729 :
2730 0 : int nUnit = GetUnitIndex(pszRefUnits);
2731 0 : if (nUnit > -1)
2732 : {
2733 0 : int nDeft = aoLinearUnitsConv[nUnit].nDefaultG;
2734 0 : oSRS.SetLinearUnits(aoLinearUnitsConv[nDeft].pszName,
2735 0 : aoLinearUnitsConv[nDeft].dfConv);
2736 : }
2737 : else
2738 : {
2739 0 : oSRS.SetLinearUnits("unknown", 1.0);
2740 : }
2741 :
2742 : // ----------------------------------------------------------------------
2743 : // Name ProjCS with the name on the georeference file
2744 : // ----------------------------------------------------------------------
2745 :
2746 0 : oSRS.SetProjCS(pszGeorefName);
2747 :
2748 0 : CPLFree(pszGeorefName);
2749 0 : CPLFree(pszProjName);
2750 0 : CPLFree(pszDatum);
2751 0 : CPLFree(pszEllipsoid);
2752 :
2753 0 : return CE_None;
2754 : }
2755 :
2756 : /************************************************************************/
2757 : /* Wkt2GeoReference() */
2758 : /************************************************************************/
2759 :
2760 : /***
2761 : * Converts OpenGIS WKT to Idrisi geographic reference information.
2762 : *
2763 : * That function will fill up the two parameters RefSystem and RefUnit
2764 : * that goes into the Idrisi metadata. But it could also create
2765 : * a accompanying georeference file to the output if necessary.
2766 : *
2767 : * First it will try to identify the ProjString as Local, WGS84 or
2768 : * one of the Idrisi name convention reference systems
2769 : * otherwise, if the projection system is supported by Idrisi,
2770 : * it will create a accompanying georeference files.
2771 : ***/
2772 :
2773 13 : CPLErr IdrisiDataset::Wkt2GeoReference(const OGRSpatialReference &oSRS,
2774 : char **pszRefSystem, char **pszRefUnit)
2775 : {
2776 : // -----------------------------------------------------
2777 : // Plane with default "Meters"
2778 : // -----------------------------------------------------
2779 :
2780 13 : if (oSRS.IsEmpty())
2781 : {
2782 0 : *pszRefSystem = CPLStrdup(rstPLANE);
2783 0 : *pszRefUnit = CPLStrdup(rstMETER);
2784 0 : return CE_None;
2785 : }
2786 :
2787 : // -----------------------------------------------------
2788 : // Local => Plane + Linear Unit
2789 : // -----------------------------------------------------
2790 :
2791 13 : if (oSRS.IsLocal())
2792 : {
2793 0 : *pszRefSystem = CPLStrdup(rstPLANE);
2794 0 : *pszRefUnit = GetUnitDefault(oSRS.GetAttrValue("UNIT"),
2795 : CPLSPrintf("%f", oSRS.GetLinearUnits()));
2796 0 : return CE_None;
2797 : }
2798 :
2799 : // -----------------------------------------------------
2800 : // Test to identify WGS84 => Latlong + Angular Unit
2801 : // -----------------------------------------------------
2802 :
2803 13 : if (oSRS.IsGeographic())
2804 : {
2805 13 : char *pszSpheroid = CPLStrdup(oSRS.GetAttrValue("SPHEROID"));
2806 13 : char *pszAuthName = CPLStrdup(oSRS.GetAuthorityName("GEOGCS"));
2807 13 : char *pszDatum = CPLStrdup(oSRS.GetAttrValue("DATUM"));
2808 13 : int nGCSCode = -1;
2809 13 : if (EQUAL(pszAuthName, "EPSG"))
2810 : {
2811 1 : nGCSCode = atoi(oSRS.GetAuthorityCode("GEOGCS"));
2812 : }
2813 13 : if ((nGCSCode == 4326) ||
2814 12 : ((STARTS_WITH_CI(pszSpheroid, "WGS")) &&
2815 12 : (strstr(pszSpheroid, "84")) && (STARTS_WITH_CI(pszDatum, "WGS")) &&
2816 12 : (strstr(pszDatum, "84"))))
2817 : {
2818 13 : *pszRefSystem = CPLStrdup(rstLATLONG);
2819 13 : *pszRefUnit = CPLStrdup(rstDEGREE);
2820 :
2821 13 : CPLFree(pszSpheroid);
2822 13 : CPLFree(pszAuthName);
2823 13 : CPLFree(pszDatum);
2824 :
2825 13 : return CE_None;
2826 : }
2827 :
2828 0 : CPLFree(pszSpheroid);
2829 0 : CPLFree(pszAuthName);
2830 0 : CPLFree(pszDatum);
2831 : }
2832 :
2833 : // -----------------------------------------------------
2834 : // Prepare to match some projections
2835 : // -----------------------------------------------------
2836 :
2837 0 : const char *pszProjName = oSRS.GetAttrValue("PROJECTION");
2838 :
2839 0 : if (pszProjName == nullptr)
2840 : {
2841 0 : pszProjName = "";
2842 : }
2843 :
2844 : // -----------------------------------------------------
2845 : // Check for UTM zones
2846 : // -----------------------------------------------------
2847 :
2848 0 : if (EQUAL(pszProjName, SRS_PT_TRANSVERSE_MERCATOR))
2849 : {
2850 0 : int nZone = oSRS.GetUTMZone();
2851 :
2852 0 : if ((nZone != 0) && (EQUAL(oSRS.GetAttrValue("DATUM"), SRS_DN_WGS84)))
2853 : {
2854 0 : double dfNorth = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING);
2855 0 : *pszRefSystem = CPLStrdup(
2856 : CPLSPrintf(rstUTM, nZone, (dfNorth == 0.0 ? 'n' : 's')));
2857 0 : *pszRefUnit = CPLStrdup(rstMETER);
2858 0 : return CE_None;
2859 : }
2860 : }
2861 :
2862 : // -----------------------------------------------------
2863 : // Check for State Plane
2864 : // -----------------------------------------------------
2865 :
2866 0 : if (EQUAL(pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ||
2867 0 : EQUAL(pszProjName, SRS_PT_TRANSVERSE_MERCATOR))
2868 : {
2869 0 : CPLString osPCSCode;
2870 0 : const char *pszID = oSRS.GetAuthorityCode("PROJCS");
2871 0 : if (pszID != nullptr && strlen(pszID) > 0)
2872 : {
2873 : const char *pszPCSCode =
2874 0 : CSVGetField(CSVFilename("stateplane.csv"), "EPSG_PCS_CODE",
2875 : pszID, CC_Integer, "ID");
2876 0 : osPCSCode = (pszPCSCode) ? pszPCSCode : "";
2877 0 : if (!osPCSCode.empty())
2878 : {
2879 0 : int nZone = osPCSCode.back() - '0';
2880 0 : int nSPCode = atoi(osPCSCode);
2881 :
2882 0 : if (nZone == 0)
2883 0 : nZone = 1;
2884 : else
2885 0 : nSPCode = nSPCode - nZone + 1;
2886 :
2887 0 : int nNADYear = 83;
2888 0 : if (nSPCode > 10000)
2889 : {
2890 0 : nNADYear = 27;
2891 0 : nSPCode -= 10000;
2892 : }
2893 0 : char *pszState = CPLStrdup(GetStateName(nSPCode));
2894 0 : if (!EQUAL(pszState, ""))
2895 : {
2896 0 : *pszRefSystem = CPLStrdup(
2897 : CPLSPrintf(rstSPC, nNADYear, pszState, nZone));
2898 0 : *pszRefUnit =
2899 0 : GetUnitDefault(oSRS.GetAttrValue("UNIT"),
2900 : CPLSPrintf("%f", oSRS.GetLinearUnits()));
2901 0 : CPLFree(pszState);
2902 0 : return CE_None;
2903 : }
2904 0 : CPLFree(pszState);
2905 : }
2906 : } //
2907 :
2908 : // if EPSG code is missing, go to following steps to work with origin
2909 :
2910 0 : const char *pszNAD83 = "83";
2911 0 : const char *pszNAD27 = "27";
2912 0 : bool bIsOldNAD = false;
2913 :
2914 0 : const char *pszDatumValue = oSRS.GetAttrValue("DATUM", 0);
2915 0 : if ((strstr(pszDatumValue, pszNAD83) == nullptr) &&
2916 0 : (strstr(pszDatumValue, pszNAD27) != nullptr))
2917 : // strcpy(pszNAD, "27");
2918 0 : bIsOldNAD = true;
2919 :
2920 0 : if ((oSRS.FindProjParm("central_meridian", nullptr) != -1) &&
2921 0 : (oSRS.FindProjParm("latitude_of_origin", nullptr) != -1))
2922 : {
2923 0 : double dfLon = oSRS.GetProjParm("central_meridian");
2924 0 : double dfLat = oSRS.GetProjParm("latitude_of_origin");
2925 0 : dfLon = (int)(fabs(dfLon) * 100 + 0.5) / 100.0;
2926 0 : dfLat = (int)(fabs(dfLat) * 100 + 0.5) / 100.0;
2927 0 : *pszRefSystem = CPLStrdup(GetSpcs(dfLon, dfLat));
2928 : }
2929 :
2930 0 : if (*pszRefSystem != nullptr)
2931 : {
2932 : // Convert 83 TO 27
2933 0 : if (bIsOldNAD)
2934 : {
2935 : char pszOutRefSystem[9];
2936 0 : NAD83to27(pszOutRefSystem, *pszRefSystem);
2937 0 : *pszRefSystem = CPLStrdup(pszOutRefSystem);
2938 : }
2939 0 : *pszRefUnit =
2940 0 : GetUnitDefault(oSRS.GetAttrValue("UNIT"),
2941 : CPLSPrintf("%f", oSRS.GetLinearUnits()));
2942 0 : return CE_None;
2943 : }
2944 : }
2945 :
2946 0 : const char *pszProjectionOut = nullptr;
2947 :
2948 0 : if (oSRS.IsProjected())
2949 : {
2950 : // ---------------------------------------------------------
2951 : // Check for supported projections
2952 : // ---------------------------------------------------------
2953 :
2954 0 : if (EQUAL(pszProjName, SRS_PT_MERCATOR_1SP))
2955 : {
2956 0 : pszProjectionOut = "Mercator";
2957 : }
2958 0 : else if (EQUAL(pszProjName, SRS_PT_TRANSVERSE_MERCATOR))
2959 : {
2960 0 : pszProjectionOut = "Transverse Mercator";
2961 : }
2962 0 : else if (EQUAL(pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
2963 : {
2964 0 : pszProjectionOut = "Lambert Conformal Conic";
2965 : }
2966 0 : else if (EQUAL(pszProjName, SRS_PT_EQUIRECTANGULAR))
2967 : {
2968 0 : pszProjectionOut = "Plate Carr"
2969 : "\xE9"
2970 : "e"; /* 'eacute' in ISO-8859-1 */
2971 : }
2972 0 : else if (EQUAL(pszProjName, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA))
2973 : {
2974 : double dfCenterLat =
2975 0 : oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0, nullptr);
2976 0 : if (dfCenterLat == 0.0)
2977 0 : pszProjectionOut = "Lambert Transverse Azimuthal Equal Area";
2978 0 : else if (fabs(dfCenterLat) == 90.0)
2979 0 : pszProjectionOut = "Lambert Oblique Polar Azimuthal Equal Area";
2980 0 : else if (dfCenterLat > 0.0)
2981 0 : pszProjectionOut = "Lambert North Oblique Azimuthal Equal Area";
2982 : else
2983 0 : pszProjectionOut = "Lambert South Oblique Azimuthal Equal Area";
2984 : }
2985 0 : else if (EQUAL(pszProjName, SRS_PT_POLAR_STEREOGRAPHIC))
2986 : {
2987 0 : if (oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0, nullptr) > 0)
2988 0 : pszProjectionOut = "North Polar Stereographic";
2989 : else
2990 0 : pszProjectionOut = "South Polar Stereographic";
2991 : }
2992 0 : else if (EQUAL(pszProjName, SRS_PT_STEREOGRAPHIC))
2993 : {
2994 0 : pszProjectionOut = "Transverse Stereographic";
2995 : }
2996 0 : else if (EQUAL(pszProjName, SRS_PT_OBLIQUE_STEREOGRAPHIC))
2997 : {
2998 0 : pszProjectionOut = "Oblique Stereographic";
2999 : }
3000 0 : else if (EQUAL(pszProjName, SRS_PT_SINUSOIDAL))
3001 : {
3002 0 : pszProjectionOut = "Sinusoidal";
3003 : }
3004 0 : else if (EQUAL(pszProjName, SRS_PT_ALBERS_CONIC_EQUAL_AREA))
3005 : {
3006 0 : pszProjectionOut = "Alber's Equal Area Conic";
3007 : }
3008 0 : else if (EQUAL(pszProjName, SRS_PT_CYLINDRICAL_EQUAL_AREA))
3009 : {
3010 0 : pszProjectionOut = "Cylindrical Equal Area";
3011 : }
3012 :
3013 : // ---------------------------------------------------------
3014 : // Failure, Projection system not supported
3015 : // ---------------------------------------------------------
3016 :
3017 0 : if (pszProjectionOut == nullptr)
3018 : {
3019 0 : CPLDebug("RST", "Not supported by RST driver: PROJECTION[\"%s\"]",
3020 : pszProjName);
3021 :
3022 0 : *pszRefSystem = CPLStrdup(rstPLANE);
3023 0 : *pszRefUnit = CPLStrdup(rstMETER);
3024 0 : return CE_Failure;
3025 : }
3026 : }
3027 : else
3028 : {
3029 0 : pszProjectionOut = "none";
3030 : }
3031 :
3032 : // ---------------------------------------------------------
3033 : // Prepare to write ref file
3034 : // ---------------------------------------------------------
3035 :
3036 0 : char *pszGeorefName = CPLStrdup("Unknown");
3037 0 : char *pszDatum = CPLStrdup(oSRS.GetAttrValue("DATUM"));
3038 0 : char *pszEllipsoid = CPLStrdup(oSRS.GetAttrValue("SPHEROID"));
3039 0 : double dfSemiMajor = oSRS.GetSemiMajor();
3040 0 : double dfSemiMinor = oSRS.GetSemiMinor();
3041 : double adfToWGS84[3];
3042 0 : oSRS.GetTOWGS84(adfToWGS84, 3);
3043 :
3044 0 : double dfCenterLat = 0.0;
3045 0 : double dfCenterLong = 0.0;
3046 0 : double dfFalseNorthing = 0.0;
3047 0 : double dfFalseEasting = 0.0;
3048 0 : double dfScale = 1.0;
3049 0 : int nParameters = 0;
3050 0 : double dfStdP1 = 0.0;
3051 0 : double dfStdP2 = 0.0;
3052 0 : char *pszAngularUnit = CPLStrdup(oSRS.GetAttrValue("GEOGCS|UNIT"));
3053 0 : char *pszLinearUnit = nullptr;
3054 :
3055 0 : if (oSRS.IsProjected())
3056 : {
3057 0 : CPLFree(pszGeorefName);
3058 0 : pszGeorefName = CPLStrdup(oSRS.GetAttrValue("PROJCS"));
3059 0 : dfCenterLat = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0, nullptr);
3060 0 : dfCenterLong = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0, nullptr);
3061 0 : dfFalseNorthing = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING, 0.0, nullptr);
3062 0 : dfFalseEasting = oSRS.GetProjParm(SRS_PP_FALSE_EASTING, 0.0, nullptr);
3063 0 : dfScale = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 0.0, nullptr);
3064 0 : dfStdP1 = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1, -0.1, nullptr);
3065 0 : if (EQUAL(pszProjectionOut, "Cylindrical Equal Area"))
3066 : {
3067 0 : dfStdP2 = -dfStdP1;
3068 0 : dfScale = 1.0;
3069 : }
3070 : else
3071 : {
3072 : dfStdP2 =
3073 0 : oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2, -0.1, nullptr);
3074 : }
3075 0 : if (dfStdP1 != -0.1)
3076 : {
3077 0 : nParameters = 1;
3078 0 : if (dfStdP2 != -0.1)
3079 0 : nParameters = 2;
3080 : }
3081 0 : pszLinearUnit = GetUnitDefault(oSRS.GetAttrValue("PROJCS|UNIT"),
3082 : CPLSPrintf("%f", oSRS.GetLinearUnits()));
3083 : }
3084 : else
3085 : {
3086 0 : pszLinearUnit = GetUnitDefault(pszAngularUnit);
3087 : }
3088 :
3089 : // ---------------------------------------------------------
3090 : // Create a companion georeference file for this dataset
3091 : // ---------------------------------------------------------
3092 :
3093 0 : char **papszRef = nullptr;
3094 0 : papszRef = CSLAddNameValue(papszRef, refREF_SYSTEM, pszGeorefName);
3095 0 : papszRef = CSLAddNameValue(papszRef, refPROJECTION, pszProjectionOut);
3096 0 : papszRef = CSLAddNameValue(papszRef, refDATUM, pszDatum);
3097 0 : papszRef = CSLAddNameValue(papszRef, refDELTA_WGS84,
3098 : CPLSPrintf("%.3g %.3g %.3g", adfToWGS84[0],
3099 : adfToWGS84[1], adfToWGS84[2]));
3100 0 : papszRef = CSLAddNameValue(papszRef, refELLIPSOID, pszEllipsoid);
3101 0 : papszRef = CSLAddNameValue(papszRef, refMAJOR_SAX,
3102 : CPLSPrintf("%.3f", dfSemiMajor));
3103 0 : papszRef = CSLAddNameValue(papszRef, refMINOR_SAX,
3104 : CPLSPrintf("%.3f", dfSemiMinor));
3105 0 : papszRef = CSLAddNameValue(papszRef, refORIGIN_LONG,
3106 : CPLSPrintf("%.9g", dfCenterLong));
3107 0 : papszRef = CSLAddNameValue(papszRef, refORIGIN_LAT,
3108 : CPLSPrintf("%.9g", dfCenterLat));
3109 0 : papszRef = CSLAddNameValue(papszRef, refORIGIN_X,
3110 : CPLSPrintf("%.9g", dfFalseEasting));
3111 0 : papszRef = CSLAddNameValue(papszRef, refORIGIN_Y,
3112 : CPLSPrintf("%.9g", dfFalseNorthing));
3113 : papszRef =
3114 0 : CSLAddNameValue(papszRef, refSCALE_FAC, CPLSPrintf("%.9g", dfScale));
3115 0 : papszRef = CSLAddNameValue(papszRef, refUNITS, pszLinearUnit);
3116 0 : papszRef = CSLAddNameValue(papszRef, refPARAMETERS,
3117 : CPLSPrintf("%1d", nParameters));
3118 0 : if (nParameters > 0)
3119 : papszRef =
3120 0 : CSLAddNameValue(papszRef, refSTANDL_1, CPLSPrintf("%.9g", dfStdP1));
3121 0 : if (nParameters > 1)
3122 : papszRef =
3123 0 : CSLAddNameValue(papszRef, refSTANDL_2, CPLSPrintf("%.9g", dfStdP2));
3124 0 : myCSLSetNameValueSeparator(papszRef, ": ");
3125 0 : SaveAsCRLF(papszRef, CPLResetExtensionSafe(pszFilename, extREF).c_str());
3126 0 : CSLDestroy(papszRef);
3127 :
3128 0 : *pszRefSystem = CPLStrdup(CPLGetBasenameSafe(pszFilename).c_str());
3129 0 : *pszRefUnit = CPLStrdup(pszLinearUnit);
3130 :
3131 0 : CPLFree(pszGeorefName);
3132 0 : CPLFree(pszDatum);
3133 0 : CPLFree(pszEllipsoid);
3134 0 : CPLFree(pszLinearUnit);
3135 0 : CPLFree(pszAngularUnit);
3136 :
3137 0 : return CE_None;
3138 : }
3139 :
3140 : /************************************************************************/
3141 : /* FileExists() */
3142 : /************************************************************************/
3143 :
3144 60 : bool FileExists(const char *pszPath)
3145 : {
3146 : VSIStatBufL sStat;
3147 :
3148 60 : return VSIStatL(pszPath, &sStat) == 0;
3149 : }
3150 :
3151 : /************************************************************************/
3152 : /* GetStateCode() */
3153 : /************************************************************************/
3154 :
3155 0 : int GetStateCode(const char *pszState)
3156 : {
3157 0 : for (unsigned int i = 0; i < US_STATE_COUNT; i++)
3158 : {
3159 0 : if (EQUAL(pszState, aoUSStateTable[i].pszName))
3160 : {
3161 0 : return aoUSStateTable[i].nCode;
3162 : }
3163 : }
3164 0 : return -1;
3165 : }
3166 :
3167 : /************************************************************************/
3168 : /* GetStateName() */
3169 : /************************************************************************/
3170 :
3171 0 : const char *GetStateName(int nCode)
3172 : {
3173 0 : for (unsigned int i = 0; i < US_STATE_COUNT; i++)
3174 : {
3175 0 : if (nCode == aoUSStateTable[i].nCode)
3176 : {
3177 0 : return aoUSStateTable[i].pszName;
3178 : }
3179 : }
3180 0 : return nullptr;
3181 : }
3182 :
3183 : /************************************************************************/
3184 : /* GetSpcs() */
3185 : /************************************************************************/
3186 :
3187 0 : char *GetSpcs(double dfLon, double dfLat)
3188 : {
3189 0 : for (int i = 0; i < ORIGIN_COUNT; i++)
3190 : {
3191 0 : if ((dfLon == SPCS83Origin[i].longitude) &&
3192 0 : (dfLat == SPCS83Origin[i].latitude))
3193 : {
3194 0 : return (char *)SPCS83Origin[i].spcs;
3195 : }
3196 : }
3197 0 : return nullptr;
3198 : }
3199 :
3200 : /************************************************************************/
3201 : /* NAD83to27() */
3202 : /************************************************************************/
3203 0 : void NAD83to27(char *pszOutRef, char *pszInRef)
3204 : {
3205 0 : char *pOutput = pszOutRef;
3206 0 : char *pInput = pszInRef;
3207 0 : strncpy(pOutput, pInput, 3);
3208 :
3209 0 : pOutput = pOutput + 3;
3210 0 : pInput = pInput + 3;
3211 :
3212 0 : memcpy(pOutput, "27", 2);
3213 0 : pOutput = pOutput + 2;
3214 0 : pInput = pInput + 2;
3215 0 : strcpy(pOutput, pInput);
3216 0 : }
3217 :
3218 : /************************************************************************/
3219 : /* GetUnitIndex() */
3220 : /************************************************************************/
3221 :
3222 0 : int GetUnitIndex(const char *pszUnitName)
3223 : {
3224 0 : for (int i = 0; i < (int)LINEAR_UNITS_COUNT; i++)
3225 : {
3226 0 : if (EQUAL(pszUnitName, aoLinearUnitsConv[i].pszName))
3227 : {
3228 0 : return i;
3229 : }
3230 : }
3231 0 : return -1;
3232 : }
3233 :
3234 : /************************************************************************/
3235 : /* GetToMeterIndex() */
3236 : /************************************************************************/
3237 :
3238 0 : int GetToMeterIndex(const char *pszToMeter)
3239 : {
3240 0 : const double dfToMeter = CPLAtof_nz(pszToMeter);
3241 :
3242 0 : if (dfToMeter != 0.0)
3243 : {
3244 0 : for (int i = 0; i < (int)LINEAR_UNITS_COUNT; i++)
3245 : {
3246 0 : if (std::abs(aoLinearUnitsConv[i].dfConv - dfToMeter) < 0.00001)
3247 : {
3248 0 : return i;
3249 : }
3250 : }
3251 : }
3252 :
3253 0 : return -1;
3254 : }
3255 :
3256 : /************************************************************************/
3257 : /* GetUnitDefault() */
3258 : /************************************************************************/
3259 :
3260 0 : char *GetUnitDefault(const char *pszUnitName, const char *pszToMeter)
3261 : {
3262 0 : int nIndex = GetUnitIndex(pszUnitName);
3263 :
3264 0 : if (nIndex == -1 && pszToMeter != nullptr)
3265 : {
3266 0 : nIndex = GetToMeterIndex(pszToMeter);
3267 : }
3268 :
3269 0 : if (nIndex == -1)
3270 : {
3271 0 : return CPLStrdup("Unknown");
3272 : }
3273 :
3274 0 : return CPLStrdup(
3275 0 : aoLinearUnitsConv[aoLinearUnitsConv[nIndex].nDefaultI].pszName);
3276 : }
3277 :
3278 : /************************************************************************/
3279 : /* CSLSaveCRLF() */
3280 : /************************************************************************/
3281 :
3282 : /***
3283 : * Write a stringlist to a CR + LF terminated text file.
3284 : *
3285 : * Returns the number of lines written, or 0 if the file could not
3286 : * be written.
3287 : */
3288 :
3289 46 : int SaveAsCRLF(char **papszStrList, const char *pszFname)
3290 : {
3291 46 : int nLines = 0;
3292 :
3293 46 : if (papszStrList)
3294 : {
3295 46 : VSILFILE *fp = VSIFOpenL(pszFname, "wt");
3296 46 : if (fp != nullptr)
3297 : {
3298 800 : while (*papszStrList != nullptr)
3299 : {
3300 766 : if (VSIFPrintfL(fp, "%s\r\n", *papszStrList) < 1)
3301 : {
3302 10 : CPLError(CE_Failure, CPLE_FileIO,
3303 : "CSLSaveCRLF(\"%s\") failed: unable to write to "
3304 : "output file.",
3305 : pszFname);
3306 10 : break;
3307 : }
3308 :
3309 756 : nLines++;
3310 756 : papszStrList++;
3311 : }
3312 :
3313 44 : VSIFCloseL(fp);
3314 : }
3315 : else
3316 : {
3317 2 : CPLError(CE_Failure, CPLE_OpenFailed,
3318 : "CSLSaveCRLF(\"%s\") failed: unable to open output file.",
3319 : pszFname);
3320 : }
3321 : }
3322 :
3323 46 : return nLines;
3324 : }
3325 :
3326 : /************************************************************************/
3327 : /* GDALRegister_IDRISI() */
3328 : /************************************************************************/
3329 :
3330 2058 : void GDALRegister_IDRISI()
3331 : {
3332 2058 : if (GDALGetDriverByName("RST") != nullptr)
3333 283 : return;
3334 :
3335 1775 : GDALDriver *poDriver = new GDALDriver();
3336 :
3337 1775 : poDriver->SetDescription("RST");
3338 1775 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3339 1775 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, rstVERSION);
3340 1775 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/Idrisi.html");
3341 1775 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, extRST);
3342 1775 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte Int16 Float32");
3343 :
3344 1775 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3345 :
3346 1775 : poDriver->pfnOpen = IdrisiDataset::Open;
3347 1775 : poDriver->pfnCreate = IdrisiDataset::Create;
3348 1775 : poDriver->pfnCreateCopy = IdrisiDataset::CreateCopy;
3349 :
3350 1775 : GetGDALDriverManager()->RegisterDriver(poDriver);
3351 : }
|