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 36526 : GDALDataset *ILWISDataset::Open(GDALOpenInfo *poOpenInfo)
675 :
676 : {
677 : /* -------------------------------------------------------------------- */
678 : /* Does this look like an ILWIS file */
679 : /* -------------------------------------------------------------------- */
680 36526 : if (poOpenInfo->nHeaderBytes < 1)
681 30829 : return nullptr;
682 :
683 : {
684 5697 : const std::string &sExt = poOpenInfo->osExtension;
685 5697 : if (!EQUAL(sExt.c_str(), "mpr") && !EQUAL(sExt.c_str(), "mpl"))
686 5687 : 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 = cpl::down_cast<ILWISDataset *>(
1089 : Create(pszFilename, poSrcDS->GetRasterXSize(),
1090 : poSrcDS->GetRasterYSize(), 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 : cpl::down_cast<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 : void *pData = 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 (static_cast<GByte *>(pData)[iCol] ==
1232 : dNoDataValue)
1233 0 : static_cast<GByte *>(pData)[iCol] = 0;
1234 : }
1235 0 : else if (EQUAL(sStoreType.c_str(), "Int"))
1236 : {
1237 0 : if (static_cast<GInt16 *>(pData)[iCol] ==
1238 : dNoDataValue)
1239 0 : static_cast<GInt16 *>(pData)[iCol] = shUNDEF;
1240 : }
1241 0 : else if (EQUAL(sStoreType.c_str(), "Long"))
1242 : {
1243 0 : if (static_cast<GInt32 *>(pData)[iCol] ==
1244 : dNoDataValue)
1245 0 : static_cast<GInt32 *>(pData)[iCol] = iUNDEF;
1246 : }
1247 0 : else if (EQUAL(sStoreType.c_str(), "float"))
1248 : {
1249 0 : if ((static_cast<float *>(pData)[iCol] ==
1250 0 : dNoDataValue) ||
1251 0 : (std::isnan(static_cast<float *>(pData)[iCol])))
1252 0 : static_cast<float *>(pData)[iCol] = flUNDEF;
1253 : }
1254 0 : else if (EQUAL(sStoreType.c_str(), "Real"))
1255 : {
1256 0 : if ((static_cast<double *>(pData)[iCol] ==
1257 0 : dNoDataValue) ||
1258 0 : (std::isnan(
1259 0 : static_cast<double *>(pData)[iCol])))
1260 0 : static_cast<double *>(pData)[iCol] = rUNDEF;
1261 : }
1262 : }
1263 : }
1264 : int iSize = static_cast<int>(
1265 250 : VSIFWriteL(pData, 1, nLineSize, desBand->fpRaw));
1266 250 : if (iSize < 1)
1267 : {
1268 0 : CPLFree(pData);
1269 : // CPLFree( pData32 );
1270 0 : CPLError(CE_Failure, CPLE_FileIO,
1271 : "Write of file failed with fwrite error.");
1272 0 : return nullptr;
1273 : }
1274 : }
1275 250 : if (!pfnProgress(iLine / (nYSize * nBands), nullptr, pProgressData))
1276 0 : return nullptr;
1277 : }
1278 23 : VSIFFlushL(fpData);
1279 23 : CPLFree(pData);
1280 : }
1281 :
1282 13 : poDS->FlushCache(false);
1283 :
1284 13 : if (!pfnProgress(1.0, nullptr, pProgressData))
1285 : {
1286 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
1287 0 : delete poDS;
1288 :
1289 0 : GDALDriver *poILWISDriver = (GDALDriver *)GDALGetDriverByName("ILWIS");
1290 0 : poILWISDriver->Delete(pszFilename);
1291 0 : return nullptr;
1292 : }
1293 :
1294 13 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
1295 :
1296 13 : return poDS;
1297 : }
1298 :
1299 : /************************************************************************/
1300 : /* ILWISRasterBand() */
1301 : /************************************************************************/
1302 :
1303 80 : ILWISRasterBand::ILWISRasterBand(ILWISDataset *poDSIn, int nBandIn,
1304 80 : const std::string &sBandNameIn)
1305 80 : : fpRaw(nullptr), nSizePerPixel(0)
1306 : {
1307 80 : poDS = poDSIn;
1308 80 : nBand = nBandIn;
1309 :
1310 160 : std::string sBandName;
1311 80 : if (EQUAL(poDSIn->pszFileType.c_str(), "Map"))
1312 : {
1313 25 : sBandName = std::string(poDSIn->osFileName);
1314 : }
1315 : else // Map list.
1316 : {
1317 : // Form the band name.
1318 : char cBandName[45];
1319 55 : snprintf(cBandName, sizeof(cBandName), "Map%d", nBand - 1);
1320 55 : if (sBandNameIn.empty())
1321 : {
1322 12 : sBandName = ReadElement("MapList", std::string(cBandName),
1323 18 : std::string(poDSIn->osFileName));
1324 : }
1325 : else
1326 : {
1327 49 : sBandName = sBandNameIn;
1328 : }
1329 : std::string sInputPath =
1330 110 : std::string(CPLGetPathSafe(poDSIn->osFileName));
1331 110 : std::string sBandPath = std::string(CPLGetPathSafe(sBandName.c_str()));
1332 : std::string sBandBaseName =
1333 110 : std::string(CPLGetBasenameSafe(sBandName.c_str()));
1334 55 : if (sBandPath.empty())
1335 110 : sBandName = CPLFormFilenameSafe(sInputPath.c_str(),
1336 55 : sBandBaseName.c_str(), "mpr");
1337 : else
1338 0 : sBandName = CPLFormFilenameSafe(sBandPath.c_str(),
1339 0 : sBandBaseName.c_str(), "mpr");
1340 : }
1341 :
1342 80 : if (poDSIn->bNewDataset)
1343 : {
1344 : // Called from Create():
1345 : // eDataType is defaulted to GDT_Byte by GDALRasterBand::GDALRasterBand
1346 : // Here we set it to match the value of sStoreType (that was set in
1347 : // ILWISDataset::Create) Unfortunately we can't take advantage of the
1348 : // ILWIS "ValueRange" object that would use the most compact storeType
1349 : // possible, without going through all values.
1350 66 : GetStoreType(sBandName, psInfo.stStoreType);
1351 66 : eDataType = ILWIS2GDALType(psInfo.stStoreType);
1352 : }
1353 : else // Called from Open(), thus convert ILWIS type from ODF to eDataType
1354 : {
1355 14 : GetILWISInfo(sBandName);
1356 : }
1357 :
1358 80 : nBlockXSize = poDS->GetRasterXSize();
1359 80 : nBlockYSize = 1;
1360 80 : switch (psInfo.stStoreType)
1361 : {
1362 47 : case stByte:
1363 47 : nSizePerPixel = GDALGetDataTypeSizeBytes(GDT_Byte);
1364 47 : break;
1365 10 : case stInt:
1366 10 : nSizePerPixel = GDALGetDataTypeSizeBytes(GDT_Int16);
1367 10 : break;
1368 10 : case stLong:
1369 10 : nSizePerPixel = GDALGetDataTypeSizeBytes(GDT_Int32);
1370 10 : break;
1371 8 : case stFloat:
1372 8 : nSizePerPixel = GDALGetDataTypeSizeBytes(GDT_Float32);
1373 8 : break;
1374 5 : case stReal:
1375 5 : nSizePerPixel = GDALGetDataTypeSizeBytes(GDT_Float64);
1376 5 : break;
1377 : }
1378 80 : ILWISOpen(sBandName);
1379 80 : }
1380 :
1381 : /************************************************************************/
1382 : /* ~ILWISRasterBand() */
1383 : /************************************************************************/
1384 :
1385 160 : ILWISRasterBand::~ILWISRasterBand()
1386 :
1387 : {
1388 80 : if (fpRaw != nullptr)
1389 : {
1390 79 : VSIFCloseL(fpRaw);
1391 79 : fpRaw = nullptr;
1392 : }
1393 160 : }
1394 :
1395 : /************************************************************************/
1396 : /* ILWISOpen() */
1397 : /************************************************************************/
1398 80 : void ILWISRasterBand::ILWISOpen(const std::string &pszFileName)
1399 : {
1400 80 : ILWISDataset *dataset = cpl::down_cast<ILWISDataset *>(poDS);
1401 : std::string pszDataFile =
1402 80 : std::string(CPLResetExtensionSafe(pszFileName.c_str(), "mp#"));
1403 :
1404 80 : fpRaw = VSIFOpenL(pszDataFile.c_str(),
1405 80 : (dataset->eAccess == GA_Update) ? "rb+" : "rb");
1406 80 : }
1407 :
1408 : /************************************************************************/
1409 : /* ReadValueDomainProperties() */
1410 : /************************************************************************/
1411 : // Helper function for GetILWISInfo, to avoid code-duplication
1412 : // Unfortunately with side-effect (changes members psInfo and eDataType)
1413 12 : void ILWISRasterBand::ReadValueDomainProperties(const std::string &pszFileName)
1414 : {
1415 : std::string rangeString =
1416 36 : ReadElement("BaseMap", "Range", pszFileName.c_str());
1417 12 : psInfo.vr = ValueRange(rangeString);
1418 12 : double rStep = psInfo.vr.get_rStep();
1419 12 : if (rStep != 0)
1420 : {
1421 10 : psInfo.bUseValueRange = true; // use ILWIS ValueRange object to convert
1422 : // from "raw" to "value"
1423 10 : double rMin = psInfo.vr.get_rLo();
1424 10 : double rMax = psInfo.vr.get_rHi();
1425 10 : if (rStep >= INT_MIN && rStep <= INT_MAX &&
1426 10 : rStep - (int)rStep == 0.0) // Integer values
1427 : {
1428 10 : if (rMin >= 0 && rMax <= UCHAR_MAX)
1429 4 : eDataType = GDT_Byte;
1430 6 : else if (rMin >= SHRT_MIN && rMax <= SHRT_MAX)
1431 0 : eDataType = GDT_Int16;
1432 6 : else if (rMin >= 0 && rMax <= USHRT_MAX)
1433 0 : eDataType = GDT_UInt16;
1434 6 : else if (rMin >= INT_MIN && rMax <= INT_MAX)
1435 6 : eDataType = GDT_Int32;
1436 0 : else if (rMin >= 0 && rMax <= UINT_MAX)
1437 0 : eDataType = GDT_UInt32;
1438 : else
1439 0 : eDataType = GDT_Float64;
1440 : }
1441 : else // Floating point values
1442 : {
1443 0 : if ((rMin >= std::numeric_limits<float>::lowest()) &&
1444 0 : (rMax <= std::numeric_limits<float>::max()) &&
1445 0 : (fabs(rStep) >= FLT_EPSILON)) // is "float" good enough?
1446 0 : eDataType = GDT_Float32;
1447 : else
1448 0 : eDataType = GDT_Float64;
1449 : }
1450 : }
1451 : else
1452 : {
1453 2 : if (psInfo.stStoreType == stFloat) // is "float" good enough?
1454 2 : eDataType = GDT_Float32;
1455 : else
1456 0 : eDataType = GDT_Float64;
1457 : }
1458 12 : }
1459 :
1460 : /************************************************************************/
1461 : /* GetILWISInfo() */
1462 : /************************************************************************/
1463 : // Calculates members psInfo and eDataType
1464 14 : CPLErr ILWISRasterBand::GetILWISInfo(const std::string &pszFileName)
1465 : {
1466 : // Fill the psInfo struct with defaults.
1467 : // Get the store type from the ODF
1468 14 : if (GetStoreType(pszFileName, psInfo.stStoreType) != CE_None)
1469 : {
1470 0 : return CE_Failure;
1471 : }
1472 14 : psInfo.bUseValueRange = false;
1473 14 : psInfo.stDomain = "";
1474 :
1475 : // ILWIS has several (currently 22) predefined "system-domains", that
1476 : // influence the data-type The user can also create domains. The possible
1477 : // types for these are "class", "identifier", "bool" and "value" The last
1478 : // one has Type=DomainValue Here we make an effort to determine the
1479 : // most-compact gdal-type (eDataType) that is suitable for the data in the
1480 : // current ILWIS band. First check against all predefined domain names (the
1481 : // "system-domains") If no match is found, read the domain ODF from disk,
1482 : // and get its type We have hardcoded the system domains here, because ILWIS
1483 : // may not be installed, and even if it is, we don't know where (thus it is
1484 : // useless to attempt to read a system-domain-file).
1485 :
1486 : const std::string domName =
1487 42 : ReadElement("BaseMap", "Domain", pszFileName.c_str());
1488 28 : std::string osBaseName = CPLGetBasenameSafe(domName.c_str());
1489 28 : const std::string osPath = CPLGetPathSafe(pszFileName.c_str());
1490 :
1491 : // Check against all "system-domains"
1492 14 : if (EQUAL(osBaseName.c_str(),
1493 : "value") // is it a system domain with Type=DomainValue?
1494 2 : || EQUAL(osBaseName.c_str(), "count") ||
1495 2 : EQUAL(osBaseName.c_str(), "distance") ||
1496 2 : EQUAL(osBaseName.c_str(), "min1to1") ||
1497 2 : EQUAL(osBaseName.c_str(), "nilto1") ||
1498 2 : EQUAL(osBaseName.c_str(), "noaa") ||
1499 16 : EQUAL(osBaseName.c_str(), "perc") || EQUAL(osBaseName.c_str(), "radar"))
1500 : {
1501 12 : ReadValueDomainProperties(pszFileName);
1502 : }
1503 2 : else if (EQUAL(osBaseName.c_str(), "bool") ||
1504 2 : EQUAL(osBaseName.c_str(), "byte") ||
1505 2 : EQUAL(osBaseName.c_str(), "bit") ||
1506 2 : EQUAL(osBaseName.c_str(), "image") ||
1507 2 : EQUAL(osBaseName.c_str(), "colorcmp") ||
1508 2 : EQUAL(osBaseName.c_str(), "flowdirection") ||
1509 6 : EQUAL(osBaseName.c_str(), "hortonratio") ||
1510 2 : EQUAL(osBaseName.c_str(), "yesno"))
1511 : {
1512 0 : eDataType = GDT_Byte;
1513 0 : if (EQUAL(osBaseName.c_str(), "image") ||
1514 0 : EQUAL(osBaseName.c_str(), "colorcmp"))
1515 0 : psInfo.stDomain = std::move(osBaseName);
1516 : }
1517 2 : else if (EQUAL(osBaseName.c_str(), "color") ||
1518 2 : EQUAL(osBaseName.c_str(), "none") ||
1519 2 : EQUAL(osBaseName.c_str(), "coordbuf") ||
1520 6 : EQUAL(osBaseName.c_str(), "binary") ||
1521 2 : EQUAL(osBaseName.c_str(), "string"))
1522 : {
1523 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported ILWIS domain type.");
1524 0 : return CE_Failure;
1525 : }
1526 : else
1527 : {
1528 : // No match found. Assume it is a self-created domain. Read its type and
1529 : // decide the GDAL type.
1530 : const std::string osDomainFileName =
1531 2 : CPLFormFilenameSafe(osPath.c_str(), osBaseName.c_str(), "dom");
1532 : std::string domType =
1533 4 : ReadElement("Domain", "Type", osDomainFileName.c_str());
1534 2 : if (EQUAL(domType.c_str(),
1535 : "domainvalue")) // is it a self-created domain of
1536 : // type=DomainValue?
1537 : {
1538 0 : ReadValueDomainProperties(pszFileName);
1539 : }
1540 2 : else if ((!EQUAL(domType.c_str(), "domainbit")) &&
1541 2 : (!EQUAL(domType.c_str(), "domainstring")) &&
1542 2 : (!EQUAL(domType.c_str(), "domaincolor")) &&
1543 2 : (!EQUAL(domType.c_str(), "domainbinary")) &&
1544 6 : (!EQUAL(domType.c_str(), "domaincoordBuf")) &&
1545 2 : (!EQUAL(domType.c_str(), "domaincoord")))
1546 : {
1547 : // Type is "DomainClass", "DomainBool" or "DomainIdentifier".
1548 : // For now we set the GDAL storeType be the same as the ILWIS
1549 : // storeType The user will have to convert the classes manually.
1550 2 : eDataType = ILWIS2GDALType(psInfo.stStoreType);
1551 : }
1552 : else
1553 : {
1554 0 : CPLError(CE_Failure, CPLE_AppDefined,
1555 : "Unsupported ILWIS domain type.");
1556 0 : return CE_Failure;
1557 : }
1558 : }
1559 :
1560 14 : return CE_None;
1561 : }
1562 :
1563 : /** This driver defines a Block to be the entire raster; The method reads
1564 : each line as a block. it reads the data into pImage.
1565 :
1566 : @param nBlockXOff This must be zero for this driver
1567 : @param pImage Dump the data here
1568 :
1569 : @return A CPLErr code. This implementation returns a CE_Failure if the
1570 : block offsets are non-zero, If successful, returns CE_None. */
1571 : /************************************************************************/
1572 : /* IReadBlock() */
1573 : /************************************************************************/
1574 506 : CPLErr ILWISRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
1575 : void *pImage)
1576 : {
1577 : // pImage is empty; this function fills it with data from fpRaw
1578 : // (ILWIS data to foreign data)
1579 :
1580 : // If the x block offset is non-zero, something is wrong.
1581 506 : CPLAssert(nBlockXOff == 0);
1582 :
1583 506 : int nBlockSize = nBlockXSize * nBlockYSize * nSizePerPixel;
1584 506 : if (fpRaw == nullptr)
1585 : {
1586 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1587 : "Failed to open ILWIS data file.");
1588 0 : return CE_Failure;
1589 : }
1590 :
1591 : /* -------------------------------------------------------------------- */
1592 : /* Handle the case of a strip in a writable file that doesn't */
1593 : /* exist yet, but that we want to read. Just set to zeros and */
1594 : /* return. */
1595 : /* -------------------------------------------------------------------- */
1596 506 : ILWISDataset *poIDS = cpl::down_cast<ILWISDataset *>(poDS);
1597 :
1598 : #ifdef notdef
1599 : if (poIDS->bNewDataset && (poIDS->eAccess == GA_Update))
1600 : {
1601 : FillWithNoData(pImage);
1602 : return CE_None;
1603 : }
1604 : #endif
1605 :
1606 506 : VSIFSeekL(fpRaw, static_cast<vsi_l_offset>(nBlockSize) * nBlockYOff,
1607 : SEEK_SET);
1608 506 : void *pData = (char *)CPLMalloc(nBlockSize);
1609 506 : if (VSIFReadL(pData, 1, nBlockSize, fpRaw) < 1)
1610 : {
1611 0 : if (poIDS->bNewDataset)
1612 : {
1613 0 : FillWithNoData(pImage);
1614 0 : return CE_None;
1615 : }
1616 : else
1617 : {
1618 0 : CPLFree(pData);
1619 0 : CPLError(CE_Failure, CPLE_FileIO,
1620 : "Read of file failed with fread error.");
1621 0 : return CE_Failure;
1622 : }
1623 : }
1624 :
1625 : // Copy the data from pData to pImage, and convert the store-type
1626 : // The data in pData has store-type = psInfo.stStoreType
1627 : // The data in pImage has store-type = eDataType
1628 : // They may not match, because we have chosen the most compact store-type,
1629 : // and for GDAL this may be different than for ILWIS.
1630 :
1631 506 : switch (psInfo.stStoreType)
1632 : {
1633 305 : case stByte:
1634 15030 : for (int iCol = 0; iCol < nBlockXSize; iCol++)
1635 : {
1636 : double rV =
1637 14725 : psInfo.bUseValueRange
1638 14725 : ? psInfo.vr.rValue(static_cast<GByte *>(pData)[iCol])
1639 6425 : : static_cast<GByte *>(pData)[iCol];
1640 14725 : SetValue(pImage, iCol, rV);
1641 : }
1642 305 : break;
1643 0 : case stInt:
1644 0 : for (int iCol = 0; iCol < nBlockXSize; iCol++)
1645 : {
1646 : double rV =
1647 0 : psInfo.bUseValueRange
1648 0 : ? psInfo.vr.rValue(static_cast<GInt16 *>(pData)[iCol])
1649 0 : : static_cast<GInt16 *>(pData)[iCol];
1650 0 : SetValue(pImage, iCol, rV);
1651 : }
1652 0 : break;
1653 0 : case stLong:
1654 0 : for (int iCol = 0; iCol < nBlockXSize; iCol++)
1655 : {
1656 : double rV =
1657 0 : psInfo.bUseValueRange
1658 0 : ? psInfo.vr.rValue(static_cast<GInt32 *>(pData)[iCol])
1659 0 : : static_cast<GInt32 *>(pData)[iCol];
1660 0 : SetValue(pImage, iCol, rV);
1661 : }
1662 0 : break;
1663 201 : case stFloat:
1664 40602 : for (int iCol = 0; iCol < nBlockXSize; iCol++)
1665 40401 : ((float *)pImage)[iCol] = static_cast<float *>(pData)[iCol];
1666 201 : break;
1667 0 : case stReal:
1668 0 : for (int iCol = 0; iCol < nBlockXSize; iCol++)
1669 0 : ((double *)pImage)[iCol] = static_cast<double *>(pData)[iCol];
1670 0 : break;
1671 0 : default:
1672 0 : CPLAssert(false);
1673 : }
1674 :
1675 : // Officially we should also translate "nodata" values, but at this point
1676 : // we can't tell what's the "nodata" value of the destination (foreign)
1677 : // dataset
1678 :
1679 506 : CPLFree(pData);
1680 :
1681 506 : return CE_None;
1682 : }
1683 :
1684 14725 : void ILWISRasterBand::SetValue(void *pImage, int i, double rV)
1685 : {
1686 14725 : switch (eDataType)
1687 : {
1688 7225 : case GDT_Byte:
1689 7225 : ((GByte *)pImage)[i] = (GByte)rV;
1690 7225 : break;
1691 0 : case GDT_UInt16:
1692 0 : ((GUInt16 *)pImage)[i] = (GUInt16)rV;
1693 0 : break;
1694 0 : case GDT_Int16:
1695 0 : ((GInt16 *)pImage)[i] = (GInt16)rV;
1696 0 : break;
1697 0 : case GDT_UInt32:
1698 0 : ((GUInt32 *)pImage)[i] = (GUInt32)rV;
1699 0 : break;
1700 7500 : case GDT_Int32:
1701 7500 : ((GInt32 *)pImage)[i] = (GInt32)rV;
1702 7500 : break;
1703 0 : case GDT_Float32:
1704 0 : ((float *)pImage)[i] = (float)rV;
1705 0 : break;
1706 0 : case GDT_Float64:
1707 0 : ((double *)pImage)[i] = rV;
1708 0 : break;
1709 0 : default:
1710 0 : CPLAssert(false);
1711 : }
1712 14725 : }
1713 :
1714 7500 : double ILWISRasterBand::GetValue(void *pImage, int i)
1715 : {
1716 7500 : double rV = 0; // Does GDAL have an official nodata value?
1717 7500 : switch (eDataType)
1718 : {
1719 7500 : case GDT_Byte:
1720 7500 : rV = ((GByte *)pImage)[i];
1721 7500 : break;
1722 0 : case GDT_UInt16:
1723 0 : rV = ((GUInt16 *)pImage)[i];
1724 0 : break;
1725 0 : case GDT_Int16:
1726 0 : rV = ((GInt16 *)pImage)[i];
1727 0 : break;
1728 0 : case GDT_UInt32:
1729 0 : rV = ((GUInt32 *)pImage)[i];
1730 0 : break;
1731 0 : case GDT_Int32:
1732 0 : rV = ((GInt32 *)pImage)[i];
1733 0 : break;
1734 0 : case GDT_Float32:
1735 0 : rV = ((float *)pImage)[i];
1736 0 : break;
1737 0 : case GDT_Float64:
1738 0 : rV = ((double *)pImage)[i];
1739 0 : break;
1740 0 : default:
1741 0 : CPLAssert(false);
1742 : }
1743 7500 : return rV;
1744 : }
1745 :
1746 0 : void ILWISRasterBand::FillWithNoData(void *pImage)
1747 : {
1748 0 : if (psInfo.stStoreType == stByte)
1749 0 : memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
1750 : else
1751 : {
1752 0 : switch (psInfo.stStoreType)
1753 : {
1754 0 : case stInt:
1755 0 : ((GInt16 *)pImage)[0] = shUNDEF;
1756 0 : break;
1757 0 : case stLong:
1758 0 : ((GInt32 *)pImage)[0] = iUNDEF;
1759 0 : break;
1760 0 : case stFloat:
1761 0 : ((float *)pImage)[0] = flUNDEF;
1762 0 : break;
1763 0 : case stReal:
1764 0 : ((double *)pImage)[0] = rUNDEF;
1765 0 : break;
1766 0 : default: // should there be handling for stByte?
1767 0 : break;
1768 : }
1769 0 : int iItemSize = GDALGetDataTypeSize(eDataType) / 8;
1770 0 : for (int i = 1; i < nBlockXSize * nBlockYSize; ++i)
1771 0 : memcpy(((char *)pImage) + iItemSize * i,
1772 0 : (char *)pImage + iItemSize * (i - 1), iItemSize);
1773 : }
1774 0 : }
1775 :
1776 : /************************************************************************/
1777 : /* IWriteBlock() */
1778 : /* */
1779 : /************************************************************************/
1780 :
1781 351 : CPLErr ILWISRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
1782 : void *pImage)
1783 : {
1784 : // pImage has data; this function reads this data and stores it to fpRaw
1785 : // (foreign data to ILWIS data)
1786 :
1787 : // Note that this function will not overwrite existing data in fpRaw, but
1788 : // it will "fill gaps" marked by "nodata" values
1789 :
1790 351 : ILWISDataset *dataset = cpl::down_cast<ILWISDataset *>(poDS);
1791 :
1792 351 : CPLAssert(dataset != nullptr && nBlockXOff == 0 && nBlockYOff >= 0 &&
1793 : pImage != nullptr);
1794 :
1795 351 : int nXSize = dataset->GetRasterXSize();
1796 351 : int nBlockSize = nBlockXSize * nBlockYSize * nSizePerPixel;
1797 351 : void *pData = CPLMalloc(nBlockSize);
1798 :
1799 351 : VSIFSeekL(fpRaw, static_cast<vsi_l_offset>(nBlockSize) * nBlockYOff,
1800 : SEEK_SET);
1801 :
1802 351 : bool fDataExists = (VSIFReadL(pData, 1, nBlockSize, fpRaw) >= 1);
1803 :
1804 : // Copy the data from pImage to pData, and convert the store-type
1805 : // The data in pData has store-type = psInfo.stStoreType
1806 : // The data in pImage has store-type = eDataType
1807 : // They may not match, because we have chosen the most compact store-type,
1808 : // and for GDAL this may be different than for ILWIS.
1809 :
1810 351 : if (fDataExists)
1811 : {
1812 : // fpRaw (thus pData) already has data
1813 : // Take care to not overwrite it
1814 : // thus only fill in gaps (nodata values)
1815 0 : switch (psInfo.stStoreType)
1816 : {
1817 0 : case stByte:
1818 0 : for (int iCol = 0; iCol < nXSize; iCol++)
1819 0 : if (static_cast<GByte *>(pData)[iCol] == 0)
1820 : {
1821 0 : double rV = GetValue(pImage, iCol);
1822 0 : static_cast<GByte *>(pData)[iCol] =
1823 0 : (GByte)(psInfo.bUseValueRange ? psInfo.vr.iRaw(rV)
1824 : : rV);
1825 : }
1826 0 : break;
1827 0 : case stInt:
1828 0 : for (int iCol = 0; iCol < nXSize; iCol++)
1829 0 : if (static_cast<GInt16 *>(pData)[iCol] == shUNDEF)
1830 : {
1831 0 : double rV = GetValue(pImage, iCol);
1832 0 : static_cast<GInt16 *>(pData)[iCol] =
1833 0 : (GInt16)(psInfo.bUseValueRange ? psInfo.vr.iRaw(rV)
1834 : : rV);
1835 : }
1836 0 : break;
1837 0 : case stLong:
1838 0 : for (int iCol = 0; iCol < nXSize; iCol++)
1839 0 : if (static_cast<GInt32 *>(pData)[iCol] == iUNDEF)
1840 : {
1841 0 : double rV = GetValue(pImage, iCol);
1842 0 : static_cast<GInt32 *>(pData)[iCol] =
1843 0 : (GInt32)(psInfo.bUseValueRange ? psInfo.vr.iRaw(rV)
1844 : : rV);
1845 : }
1846 0 : break;
1847 0 : case stFloat:
1848 0 : for (int iCol = 0; iCol < nXSize; iCol++)
1849 0 : if (static_cast<float *>(pData)[iCol] == flUNDEF)
1850 0 : static_cast<float *>(pData)[iCol] =
1851 0 : ((float *)pImage)[iCol];
1852 0 : break;
1853 0 : case stReal:
1854 0 : for (int iCol = 0; iCol < nXSize; iCol++)
1855 0 : if (static_cast<double *>(pData)[iCol] == rUNDEF)
1856 0 : static_cast<double *>(pData)[iCol] =
1857 0 : ((double *)pImage)[iCol];
1858 0 : break;
1859 : }
1860 : }
1861 : else
1862 : {
1863 : // fpRaw (thus pData) is still empty, just write the data
1864 351 : switch (psInfo.stStoreType)
1865 : {
1866 150 : case stByte:
1867 7650 : for (int iCol = 0; iCol < nXSize; iCol++)
1868 : {
1869 7500 : double rV = GetValue(pImage, iCol);
1870 7500 : static_cast<GByte *>(pData)[iCol] =
1871 7500 : (GByte)(psInfo.bUseValueRange ? psInfo.vr.iRaw(rV)
1872 : : rV);
1873 : }
1874 150 : break;
1875 0 : case stInt:
1876 0 : for (int iCol = 0; iCol < nXSize; iCol++)
1877 : {
1878 0 : double rV = GetValue(pImage, iCol);
1879 0 : static_cast<GInt16 *>(pData)[iCol] =
1880 0 : (GInt16)(psInfo.bUseValueRange ? psInfo.vr.iRaw(rV)
1881 : : rV);
1882 : }
1883 0 : break;
1884 0 : case stLong:
1885 0 : for (int iCol = 0; iCol < nXSize; iCol++)
1886 : {
1887 0 : double rV = GetValue(pImage, iCol);
1888 0 : static_cast<GInt32 *>(pData)[iCol] =
1889 0 : (GInt32)(psInfo.bUseValueRange ? psInfo.vr.iRaw(rV)
1890 : : rV);
1891 : }
1892 0 : break;
1893 201 : case stFloat:
1894 40602 : for (int iCol = 0; iCol < nXSize; iCol++)
1895 40401 : static_cast<float *>(pData)[iCol] = ((float *)pImage)[iCol];
1896 201 : break;
1897 0 : case stReal:
1898 0 : for (int iCol = 0; iCol < nXSize; iCol++)
1899 0 : static_cast<double *>(pData)[iCol] =
1900 0 : ((double *)pImage)[iCol];
1901 0 : break;
1902 : }
1903 : }
1904 :
1905 : // Officially we should also translate "nodata" values, but at this point
1906 : // we can't tell what's the "nodata" value of the source (foreign) dataset
1907 :
1908 351 : VSIFSeekL(fpRaw, static_cast<vsi_l_offset>(nBlockSize) * nBlockYOff,
1909 : SEEK_SET);
1910 :
1911 351 : if (VSIFWriteL(pData, 1, nBlockSize, fpRaw) < 1)
1912 : {
1913 0 : CPLFree(pData);
1914 0 : CPLError(CE_Failure, CPLE_FileIO,
1915 : "Write of file failed with fwrite error.");
1916 0 : return CE_Failure;
1917 : }
1918 :
1919 351 : CPLFree(pData);
1920 351 : return CE_None;
1921 : }
1922 :
1923 : /************************************************************************/
1924 : /* GetNoDataValue() */
1925 : /************************************************************************/
1926 16 : double ILWISRasterBand::GetNoDataValue(int *pbSuccess)
1927 :
1928 : {
1929 16 : if (pbSuccess)
1930 14 : *pbSuccess = TRUE;
1931 :
1932 16 : if (eDataType == GDT_Float64)
1933 0 : return rUNDEF;
1934 16 : if (eDataType == GDT_Int32)
1935 3 : return iUNDEF;
1936 13 : if (eDataType == GDT_Int16)
1937 0 : return shUNDEF;
1938 13 : if (eDataType == GDT_Float32)
1939 6 : return flUNDEF;
1940 14 : if (pbSuccess && (EQUAL(psInfo.stDomain.c_str(), "image") ||
1941 7 : EQUAL(psInfo.stDomain.c_str(), "colorcmp")))
1942 : {
1943 0 : *pbSuccess = FALSE;
1944 : }
1945 :
1946 : // TODO: Defaults to pbSuccess TRUE. Is the unhandled case really success?
1947 7 : return 0.0;
1948 : }
1949 :
1950 : /************************************************************************/
1951 : /* ValueRange() */
1952 : /************************************************************************/
1953 :
1954 24 : static double doubleConv(const char *s)
1955 : {
1956 24 : if (s == nullptr)
1957 0 : return rUNDEF;
1958 24 : char *begin = const_cast<char *>(s);
1959 :
1960 : // skip leading spaces; strtol will return 0 on a std::string with only
1961 : // spaces which is not what we want
1962 24 : while (isspace((unsigned char)*begin))
1963 0 : ++begin;
1964 :
1965 24 : if (strlen(begin) == 0)
1966 0 : return rUNDEF;
1967 24 : errno = 0;
1968 24 : char *endptr = nullptr;
1969 24 : const double r = CPLStrtod(begin, &endptr);
1970 24 : if ((0 == *endptr) && (errno == 0))
1971 24 : return r;
1972 0 : while (*endptr != 0)
1973 : { // check trailing spaces
1974 0 : if (*endptr != ' ')
1975 0 : return rUNDEF;
1976 0 : endptr++;
1977 : }
1978 0 : return r;
1979 : }
1980 :
1981 12 : ValueRange::ValueRange(const std::string &sRng)
1982 : : _rLo(0.0), _rHi(0.0), _rStep(0.0), _iDec(0), _r0(0.0), iRawUndef(0),
1983 12 : _iWidth(0), st(stByte)
1984 : {
1985 12 : char *sRange = new char[sRng.length() + 1];
1986 476 : for (unsigned int i = 0; i < sRng.length(); ++i)
1987 464 : sRange[i] = sRng[i];
1988 12 : sRange[sRng.length()] = 0;
1989 :
1990 12 : char *p1 = strchr(sRange, ':');
1991 12 : if (nullptr == p1)
1992 : {
1993 0 : delete[] sRange;
1994 0 : init();
1995 0 : return;
1996 : }
1997 :
1998 12 : char *p3 = strstr(sRange, ",offset=");
1999 12 : if (nullptr == p3)
2000 12 : p3 = strstr(sRange, ":offset=");
2001 12 : _r0 = rUNDEF;
2002 12 : if (nullptr != p3)
2003 : {
2004 12 : _r0 = doubleConv(p3 + 8);
2005 12 : *p3 = 0;
2006 : }
2007 12 : char *p2 = strrchr(sRange, ':');
2008 12 : _rStep = 1;
2009 12 : if (p1 != p2)
2010 : { // step
2011 12 : _rStep = doubleConv(p2 + 1);
2012 12 : *p2 = 0;
2013 : }
2014 :
2015 12 : p2 = strchr(sRange, ':');
2016 12 : if (p2 != nullptr)
2017 : {
2018 12 : *p2 = 0;
2019 12 : _rLo = CPLAtof(sRange);
2020 12 : _rHi = CPLAtof(p2 + 1);
2021 : }
2022 : else
2023 : {
2024 0 : _rLo = CPLAtof(sRange);
2025 0 : _rHi = _rLo;
2026 : }
2027 12 : init(_r0);
2028 :
2029 12 : delete[] sRange;
2030 : }
2031 :
2032 80 : ValueRange::ValueRange(double min, double max) // step = 1
2033 : {
2034 80 : _rLo = min;
2035 80 : _rHi = max;
2036 80 : _rStep = 1;
2037 80 : init();
2038 80 : }
2039 :
2040 0 : ValueRange::ValueRange(double min, double max, double step)
2041 : {
2042 0 : _rLo = min;
2043 0 : _rHi = max;
2044 0 : _rStep = step;
2045 0 : init();
2046 0 : }
2047 :
2048 90 : static ilwisStoreType stNeeded(unsigned int iNr)
2049 : {
2050 90 : if (iNr <= 256)
2051 84 : return stByte;
2052 6 : if (iNr <= SHRT_MAX)
2053 0 : return stInt;
2054 6 : return stLong;
2055 : }
2056 :
2057 80 : void ValueRange::init()
2058 : {
2059 80 : init(rUNDEF);
2060 80 : }
2061 :
2062 92 : void ValueRange::init(double rRaw0)
2063 : {
2064 92 : _iDec = 0;
2065 92 : if (_rStep < 0)
2066 0 : _rStep = 0;
2067 92 : double r = _rStep;
2068 92 : if (r <= 1e-20)
2069 2 : _iDec = 3;
2070 : else
2071 90 : while (r - floor(r) > 1e-20)
2072 : {
2073 0 : r *= 10;
2074 0 : _iDec++;
2075 0 : if (_iDec > 10)
2076 0 : break;
2077 : }
2078 :
2079 92 : short iBeforeDec = 1;
2080 92 : double rMax = std::max(fabs(get_rLo()), fabs(get_rHi()));
2081 92 : if (rMax != 0)
2082 12 : iBeforeDec = (short)floor(log10(rMax)) + 1;
2083 92 : if (get_rLo() < 0)
2084 8 : iBeforeDec++;
2085 92 : _iWidth = (short)(iBeforeDec + _iDec);
2086 92 : if (_iDec > 0)
2087 2 : _iWidth++;
2088 92 : if (_iWidth > 12)
2089 0 : _iWidth = 12;
2090 92 : if (_rStep < 1e-06)
2091 : {
2092 2 : st = stReal;
2093 2 : _rStep = 0;
2094 : }
2095 : else
2096 : {
2097 90 : r = get_rHi() - get_rLo();
2098 90 : if (r <= UINT_MAX)
2099 : {
2100 90 : r /= _rStep;
2101 90 : r += 1;
2102 : }
2103 90 : r += 1;
2104 90 : if (r > INT_MAX)
2105 0 : st = stReal;
2106 : else
2107 : {
2108 90 : st = stNeeded((unsigned int)floor(r + 0.5));
2109 90 : if (st < stByte)
2110 0 : st = stByte;
2111 : }
2112 : }
2113 92 : if (rUNDEF != rRaw0)
2114 12 : _r0 = rRaw0;
2115 : else
2116 : {
2117 80 : _r0 = 0;
2118 80 : if (st <= stByte)
2119 80 : _r0 = -1;
2120 : }
2121 92 : if (st > stInt)
2122 8 : iRawUndef = iUNDEF;
2123 84 : else if (st == stInt)
2124 0 : iRawUndef = shUNDEF;
2125 : else
2126 84 : iRawUndef = 0;
2127 92 : }
2128 :
2129 0 : std::string ValueRange::ToString() const
2130 : {
2131 : char buffer[200];
2132 0 : if (fabs(get_rLo()) > 1.0e20 || fabs(get_rHi()) > 1.0e20)
2133 0 : CPLsnprintf(buffer, sizeof(buffer), "%g:%g:%f:offset=%g", get_rLo(),
2134 : get_rHi(), get_rStep(), get_rRaw0());
2135 0 : else if (get_iDec() >= 0)
2136 0 : CPLsnprintf(buffer, sizeof(buffer), "%.*f:%.*f:%.*f:offset=%.0f",
2137 : get_iDec(), get_rLo(), get_iDec(), get_rHi(), get_iDec(),
2138 : get_rStep(), get_rRaw0());
2139 : else
2140 0 : CPLsnprintf(buffer, sizeof(buffer), "%f:%f:%f:offset=%.0f", get_rLo(),
2141 : get_rHi(), get_rStep(), get_rRaw0());
2142 0 : return std::string(buffer);
2143 : }
2144 :
2145 8300 : double ValueRange::rValue(int iRawIn) const
2146 : {
2147 8300 : if (iRawIn == iUNDEF || iRawIn == iRawUndef)
2148 0 : return rUNDEF;
2149 8300 : double rVal = iRawIn + _r0;
2150 8300 : rVal *= _rStep;
2151 8300 : if (get_rLo() == get_rHi())
2152 0 : return rVal;
2153 8300 : const double rEpsilon =
2154 8300 : _rStep == 0.0 ? 1e-6
2155 8300 : : _rStep / 3.0; // avoid any rounding problems with an
2156 : // epsilon directly based on the
2157 : // the stepsize
2158 8300 : if ((rVal - get_rLo() < -rEpsilon) || (rVal - get_rHi() > rEpsilon))
2159 0 : return rUNDEF;
2160 8300 : return rVal;
2161 : }
2162 :
2163 0 : int ValueRange::iRaw(double rValueIn) const
2164 : {
2165 0 : if (rValueIn == rUNDEF) // || !fContains(rValue))
2166 0 : return iUNDEF;
2167 0 : if (_rStep == 0.0)
2168 0 : return iUNDEF;
2169 0 : const double rEpsilon = _rStep / 3.0;
2170 0 : if (rValueIn - get_rLo() < -rEpsilon) // take a little rounding tolerance
2171 0 : return iUNDEF;
2172 0 : else if (rValueIn - get_rHi() >
2173 : rEpsilon) // take a little rounding tolerance
2174 0 : return iUNDEF;
2175 0 : rValueIn /= _rStep;
2176 0 : double rVal = floor(rValueIn + 0.5);
2177 0 : rVal -= _r0;
2178 0 : return intConv(rVal);
2179 : }
2180 :
2181 : } // namespace GDAL
2182 :
2183 : /************************************************************************/
2184 : /* GDALRegister_ILWIS() */
2185 : /************************************************************************/
2186 :
2187 1889 : void GDALRegister_ILWIS()
2188 :
2189 : {
2190 1889 : if (GDALGetDriverByName("ILWIS") != nullptr)
2191 282 : return;
2192 :
2193 1607 : GDALDriver *poDriver = new GDALDriver();
2194 :
2195 1607 : poDriver->SetDescription("ILWIS");
2196 1607 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
2197 1607 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ILWIS Raster Map");
2198 1607 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "mpr mpl");
2199 1607 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
2200 1607 : "Byte Int16 Int32 Float64");
2201 1607 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
2202 :
2203 1607 : poDriver->pfnOpen = GDAL::ILWISDataset::Open;
2204 1607 : poDriver->pfnCreate = GDAL::ILWISDataset::Create;
2205 1607 : poDriver->pfnCreateCopy = GDAL::ILWISDataset::CreateCopy;
2206 :
2207 1607 : GetGDALDriverManager()->RegisterDriver(poDriver);
2208 : }
|