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