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