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