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