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