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