1 : /******************************************************************************
2 : * $Id: sagadataset.cpp 18389 2009-12-26 21:41:55Z rouault $
3 : * Project: SAGA GIS Binary Driver
4 : * Purpose: Implements the SAGA GIS Binary Grid Format.
5 : * Author: Volker Wichmann, wichmann@laserdata.at
6 : * (Based on gsbgdataset.cpp by Kevin Locke and Frank Warmerdam)
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2009, Volker Wichmann <wichmann@laserdata.at>
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 "cpl_conv.h"
31 :
32 : #include <float.h>
33 : #include <limits.h>
34 : #include <assert.h>
35 :
36 : #include "gdal_pam.h"
37 :
38 : CPL_CVSID("$Id: sagadataset.cpp 18389 2009-12-26 21:41:55Z rouault $");
39 :
40 : #ifndef INT_MAX
41 : # define INT_MAX 2147483647
42 : #endif /* INT_MAX */
43 :
44 : /* NODATA Values */
45 : //#define SG_NODATA_GDT_Bit 0.0
46 : #define SG_NODATA_GDT_Byte 255
47 : #define SG_NODATA_GDT_UInt16 65535
48 : #define SG_NODATA_GDT_Int16 -32767
49 : #define SG_NODATA_GDT_UInt32 4294967295U
50 : #define SG_NODATA_GDT_Int32 -2147483647
51 : #define SG_NODATA_GDT_Float32 -99999.0
52 : #define SG_NODATA_GDT_Float64 -99999.0
53 :
54 :
55 : CPL_C_START
56 : void GDALRegister_SAGA(void);
57 : CPL_C_END
58 :
59 :
60 : /************************************************************************/
61 : /* ==================================================================== */
62 : /* SAGADataset */
63 : /* ==================================================================== */
64 : /************************************************************************/
65 :
66 : class SAGARasterBand;
67 :
68 : class SAGADataset : public GDALPamDataset
69 81 : {
70 : friend class SAGARasterBand;
71 :
72 : static CPLErr WriteHeader( CPLString osHDRFilename, GDALDataType eType,
73 : GInt16 nXSize, GInt16 nYSize,
74 : double dfMinX, double dfMinY,
75 : double dfCellsize, double dfNoData,
76 : double dfZFactor, bool bTopToBottom );
77 : FILE *fp;
78 :
79 : public:
80 : ~SAGADataset();
81 :
82 : static GDALDataset *Open( GDALOpenInfo * );
83 : static GDALDataset *Create( const char * pszFilename,
84 : int nXSize, int nYSize, int nBands,
85 : GDALDataType eType,
86 : char **papszParmList );
87 : static GDALDataset *CreateCopy( const char *pszFilename,
88 : GDALDataset *poSrcDS,
89 : int bStrict, char **papszOptions,
90 : GDALProgressFunc pfnProgress,
91 : void *pProgressData );
92 :
93 : virtual char **GetFileList();
94 :
95 : CPLErr GetGeoTransform( double *padfGeoTransform );
96 : CPLErr SetGeoTransform( double *padfGeoTransform );
97 : };
98 :
99 :
100 : /************************************************************************/
101 : /* ==================================================================== */
102 : /* SAGARasterBand */
103 : /* ==================================================================== */
104 : /************************************************************************/
105 :
106 : class SAGARasterBand : public GDALPamRasterBand
107 : {
108 : friend class SAGADataset;
109 :
110 : int m_Cols;
111 : int m_Rows;
112 : double m_Xmin;
113 : double m_Ymin;
114 : double m_Cellsize;
115 : double m_NoData;
116 : int m_ByteOrder;
117 : int m_nBits;
118 :
119 : void SetDataType( GDALDataType eType );
120 : void SwapBuffer(void* pImage);
121 : public:
122 :
123 : SAGARasterBand( SAGADataset *, int );
124 : ~SAGARasterBand();
125 :
126 : CPLErr IReadBlock( int, int, void * );
127 : CPLErr IWriteBlock( int, int, void * );
128 :
129 : double GetNoDataValue( int *pbSuccess = NULL );
130 : };
131 :
132 : /************************************************************************/
133 : /* SAGARasterBand() */
134 : /************************************************************************/
135 :
136 81 : SAGARasterBand::SAGARasterBand( SAGADataset *poDS, int nBand )
137 :
138 : {
139 81 : this->poDS = poDS;
140 81 : nBand = nBand;
141 :
142 81 : eDataType = GDT_Float32;
143 :
144 81 : nBlockXSize = poDS->GetRasterXSize();
145 81 : nBlockYSize = 1;
146 81 : }
147 :
148 : /************************************************************************/
149 : /* ~SAGARasterBand() */
150 : /************************************************************************/
151 :
152 162 : SAGARasterBand::~SAGARasterBand( )
153 :
154 : {
155 162 : }
156 :
157 : /************************************************************************/
158 : /* SetDataType() */
159 : /************************************************************************/
160 :
161 81 : void SAGARasterBand::SetDataType( GDALDataType eType )
162 :
163 : {
164 81 : eDataType = eType;
165 : return;
166 : }
167 :
168 : /************************************************************************/
169 : /* SwapBuffer() */
170 : /************************************************************************/
171 :
172 977 : void SAGARasterBand::SwapBuffer(void* pImage)
173 : {
174 :
175 : #ifdef CPL_LSB
176 977 : int bSwap = ( m_ByteOrder == 1);
177 : #else
178 : int bSwap = ( m_ByteOrder == 0);
179 : #endif
180 :
181 977 : if (bSwap)
182 : {
183 0 : if ( m_nBits == 16 )
184 : {
185 0 : short* pImage16 = (short*) pImage;
186 0 : for( int iPixel=0; iPixel<nBlockXSize; iPixel++ )
187 : {
188 0 : CPL_SWAP16PTR( pImage16 + iPixel );
189 : }
190 : }
191 0 : else if ( m_nBits == 32 )
192 : {
193 0 : int* pImage32 = (int*) pImage;
194 0 : for( int iPixel=0; iPixel<nBlockXSize; iPixel++ )
195 : {
196 0 : CPL_SWAP32PTR( pImage32 + iPixel );
197 : }
198 : }
199 0 : else if ( m_nBits == 64 )
200 : {
201 0 : double* pImage64 = (double*) pImage;
202 0 : for( int iPixel=0; iPixel<nBlockXSize; iPixel++ )
203 : {
204 0 : CPL_SWAP64PTR( pImage64 + iPixel );
205 : }
206 : }
207 : }
208 :
209 977 : }
210 :
211 : /************************************************************************/
212 : /* IReadBlock() */
213 : /************************************************************************/
214 :
215 357 : CPLErr SAGARasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
216 : void * pImage )
217 :
218 : {
219 357 : if( nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0 )
220 0 : return CE_Failure;
221 :
222 357 : SAGADataset *poGDS = dynamic_cast<SAGADataset *>(poDS);
223 :
224 357 : if( VSIFSeekL( poGDS->fp, (m_nBits / 8) * nRasterXSize * (nRasterYSize - nBlockYOff - 1), SEEK_SET ) != 0 )
225 : {
226 : CPLError( CE_Failure, CPLE_FileIO,
227 0 : "Unable to seek to beginning of grid row.\n" );
228 0 : return CE_Failure;
229 : }
230 357 : if( VSIFReadL( pImage, m_nBits / 8, nBlockXSize,
231 : poGDS->fp ) != static_cast<unsigned>(nBlockXSize) )
232 : {
233 : CPLError( CE_Failure, CPLE_FileIO,
234 0 : "Unable to read block from grid file.\n" );
235 0 : return CE_Failure;
236 : }
237 :
238 357 : SwapBuffer(pImage);
239 :
240 357 : return CE_None;
241 : }
242 :
243 : /************************************************************************/
244 : /* IWriteBlock() */
245 : /************************************************************************/
246 :
247 310 : CPLErr SAGARasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
248 : void *pImage )
249 :
250 : {
251 310 : if( eAccess == GA_ReadOnly )
252 : {
253 : CPLError( CE_Failure, CPLE_NoWriteAccess,
254 0 : "Unable to write block, dataset opened read only.\n" );
255 0 : return CE_Failure;
256 : }
257 :
258 310 : if( nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0 )
259 0 : return CE_Failure;
260 :
261 310 : SAGADataset *poGDS = dynamic_cast<SAGADataset *>(poDS);
262 310 : assert( poGDS != NULL );
263 :
264 310 : if( VSIFSeekL( poGDS->fp, (m_nBits / 8) * nRasterXSize * (nRasterYSize - nBlockYOff - 1), SEEK_SET ) != 0 )
265 : {
266 : CPLError( CE_Failure, CPLE_FileIO,
267 0 : "Unable to seek to beginning of grid row.\n" );
268 0 : return CE_Failure;
269 : }
270 :
271 310 : SwapBuffer(pImage);
272 :
273 : int bSuccess = ( VSIFWriteL( pImage, m_nBits / 8, nBlockXSize,
274 310 : poGDS->fp ) == static_cast<unsigned>(nBlockXSize) );
275 :
276 310 : SwapBuffer(pImage);
277 :
278 310 : if (!bSuccess)
279 : {
280 : CPLError( CE_Failure, CPLE_FileIO,
281 0 : "Unable to write block to grid file.\n" );
282 0 : return CE_Failure;
283 : }
284 :
285 310 : return CE_None;
286 : }
287 :
288 : /************************************************************************/
289 : /* GetNoDataValue() */
290 : /************************************************************************/
291 :
292 44 : double SAGARasterBand::GetNoDataValue( int * pbSuccess )
293 : {
294 44 : if( pbSuccess )
295 44 : *pbSuccess = TRUE;
296 :
297 44 : return m_NoData;
298 : }
299 :
300 :
301 : /************************************************************************/
302 : /* ==================================================================== */
303 : /* SAGADataset */
304 : /* ==================================================================== */
305 : /************************************************************************/
306 :
307 162 : SAGADataset::~SAGADataset()
308 :
309 : {
310 81 : FlushCache();
311 81 : if( fp != NULL )
312 81 : VSIFCloseL( fp );
313 162 : }
314 :
315 :
316 : /************************************************************************/
317 : /* GetFileList() */
318 : /************************************************************************/
319 :
320 23 : char** SAGADataset::GetFileList()
321 : {
322 23 : CPLString osPath = CPLGetPath( GetDescription() );
323 23 : CPLString osName = CPLGetBasename( GetDescription() );
324 23 : CPLString osHDRFilename;
325 :
326 23 : char **papszFileList = NULL;
327 :
328 : // Main data file, etc.
329 23 : papszFileList = GDALPamDataset::GetFileList();
330 :
331 : // Header file.
332 23 : osHDRFilename = CPLFormCIFilename( osPath, osName, ".sgrd" );
333 23 : papszFileList = CSLAddString( papszFileList, osHDRFilename );
334 :
335 23 : return papszFileList;
336 : }
337 :
338 : /************************************************************************/
339 : /* Open() */
340 : /************************************************************************/
341 :
342 8416 : GDALDataset *SAGADataset::Open( GDALOpenInfo * poOpenInfo )
343 :
344 : {
345 : /* -------------------------------------------------------------------- */
346 : /* We assume the user is pointing to the binary (ie. .sdat) file. */
347 : /* -------------------------------------------------------------------- */
348 8416 : if( !EQUAL(CPLGetExtension( poOpenInfo->pszFilename ), "sdat"))
349 : {
350 8317 : return NULL;
351 : }
352 :
353 99 : CPLString osPath = CPLGetPath( poOpenInfo->pszFilename );
354 99 : CPLString osName = CPLGetBasename( poOpenInfo->pszFilename );
355 99 : CPLString osHDRFilename;
356 :
357 99 : osHDRFilename = CPLFormCIFilename( osPath, osName, ".sgrd" );
358 :
359 :
360 : FILE *fp;
361 :
362 99 : fp = VSIFOpenL( osHDRFilename, "r" );
363 :
364 99 : if( fp == NULL )
365 : {
366 18 : return NULL;
367 : }
368 :
369 : /* -------------------------------------------------------------------- */
370 : /* Is this file a SAGA header file? Read a few lines of text */
371 : /* searching for something starting with nrows or ncols. */
372 : /* -------------------------------------------------------------------- */
373 : const char *pszLine;
374 81 : int nRows = -1, nCols = -1;
375 81 : double dXmin = 0.0, dYmin = 0.0, dCellsize = 0.0, dNoData = 0.0, dZFactor = 0.0;
376 81 : int nLineCount = 0;
377 81 : char szDataFormat[20] = "DOUBLE";
378 81 : char szByteOrderBig[10] = "FALSE";
379 81 : char szTopToBottom[10] = "FALSE";
380 81 : char **papszHDR = NULL;
381 :
382 :
383 1296 : while( (pszLine = CPLReadLineL( fp )) )
384 : {
385 : char **papszTokens;
386 :
387 1134 : nLineCount++;
388 :
389 1134 : if( nLineCount > 50 || strlen(pszLine) > 1000 )
390 0 : break;
391 :
392 1134 : papszHDR = CSLAddString( papszHDR, pszLine );
393 :
394 1134 : papszTokens = CSLTokenizeStringComplex( pszLine, " =", TRUE, FALSE );
395 1134 : if( CSLCount( papszTokens ) < 2 )
396 : {
397 162 : CSLDestroy( papszTokens );
398 162 : continue;
399 : }
400 :
401 972 : if( EQUALN(papszTokens[0],"CELLCOUNT_X",strlen("CELLCOUNT_X")) )
402 81 : nCols = atoi(papszTokens[1]);
403 891 : else if( EQUALN(papszTokens[0],"CELLCOUNT_Y",strlen("CELLCOUNT_Y")) )
404 81 : nRows = atoi(papszTokens[1]);
405 810 : else if( EQUALN(papszTokens[0],"POSITION_XMIN",strlen("POSITION_XMIN")) )
406 81 : dXmin = atof(papszTokens[1]);
407 729 : else if( EQUALN(papszTokens[0],"POSITION_YMIN",strlen("POSITION_YMIN")) )
408 81 : dYmin = atof(papszTokens[1]);
409 648 : else if( EQUALN(papszTokens[0],"CELLSIZE",strlen("CELLSIZE")) )
410 81 : dCellsize = atof(papszTokens[1]);
411 567 : else if( EQUALN(papszTokens[0],"NODATA_VALUE",strlen("NODATA_VALUE")) )
412 81 : dNoData = atof(papszTokens[1]);
413 486 : else if( EQUALN(papszTokens[0],"DATAFORMAT",strlen("DATAFORMAT")) )
414 81 : strncpy( szDataFormat, papszTokens[1], sizeof(szDataFormat)-1 );
415 405 : else if( EQUALN(papszTokens[0],"BYTEORDER_BIG",strlen("BYTEORDER_BIG")) )
416 81 : strncpy( szByteOrderBig, papszTokens[1], sizeof(szByteOrderBig)-1 );
417 324 : else if( EQUALN(papszTokens[0],"TOPTOBOTTOM",strlen("TOPTOBOTTOM")) )
418 81 : strncpy( szTopToBottom, papszTokens[1], sizeof(szTopToBottom)-1 );
419 243 : else if( EQUALN(papszTokens[0],"Z_FACTOR",strlen("Z_FACTOR")) )
420 81 : dZFactor = atof(papszTokens[1]);
421 :
422 972 : CSLDestroy( papszTokens );
423 : }
424 :
425 81 : VSIFCloseL( fp );
426 :
427 81 : CSLDestroy( papszHDR );
428 :
429 : /* -------------------------------------------------------------------- */
430 : /* Did we get the required keywords? If not we return with */
431 : /* this never having been considered to be a match. This isn't */
432 : /* an error! */
433 : /* -------------------------------------------------------------------- */
434 81 : if( nRows == -1 || nCols == -1 )
435 : {
436 0 : return NULL;
437 : }
438 :
439 81 : if (!GDALCheckDatasetDimensions(nCols, nRows))
440 : {
441 0 : return NULL;
442 : }
443 :
444 81 : if( EQUALN(szTopToBottom,"TRUE",strlen("TRUE")) )
445 : {
446 : CPLError( CE_Failure, CPLE_AppDefined,
447 : "Currently the SAGA Binary Grid driver does not support\n"
448 0 : "SAGA grids written TOPTOBOTTOM.\n");
449 0 : return NULL;
450 : }
451 81 : if( dZFactor != 1.0)
452 : {
453 : CPLError( CE_Warning, CPLE_AppDefined,
454 : "Currently the SAGA Binary Grid driver does not support\n"
455 0 : "ZFACTORs other than 1.\n");
456 : }
457 :
458 :
459 :
460 : /* -------------------------------------------------------------------- */
461 : /* Create a corresponding GDALDataset. */
462 : /* -------------------------------------------------------------------- */
463 81 : SAGADataset *poDS = new SAGADataset();
464 :
465 81 : poDS->eAccess = poOpenInfo->eAccess;
466 81 : if( poOpenInfo->eAccess == GA_ReadOnly )
467 57 : poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
468 : else
469 24 : poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" );
470 :
471 81 : if( poDS->fp == NULL )
472 : {
473 0 : delete poDS;
474 : CPLError( CE_Failure, CPLE_OpenFailed,
475 : "VSIFOpenL(%s) failed unexpectedly.",
476 0 : poOpenInfo->pszFilename );
477 0 : return NULL;
478 : }
479 :
480 81 : poDS->nRasterXSize = nCols;
481 81 : poDS->nRasterYSize = nRows;
482 :
483 81 : SAGARasterBand *poBand = new SAGARasterBand( poDS, 1 );
484 :
485 :
486 : /* -------------------------------------------------------------------- */
487 : /* Figure out the byte order. */
488 : /* -------------------------------------------------------------------- */
489 81 : if( EQUALN(szByteOrderBig,"TRUE",strlen("TRUE")) )
490 0 : poBand->m_ByteOrder = 1;
491 81 : else if( EQUALN(szByteOrderBig,"FALSE",strlen("FALSE")) )
492 81 : poBand->m_ByteOrder = 0;
493 :
494 :
495 : /* -------------------------------------------------------------------- */
496 : /* Figure out the data type. */
497 : /* -------------------------------------------------------------------- */
498 81 : if( EQUAL(szDataFormat,"BIT") )
499 : {
500 0 : poBand->SetDataType(GDT_Byte);
501 0 : poBand->m_nBits = 8;
502 : }
503 81 : else if( EQUAL(szDataFormat,"BYTE_UNSIGNED") )
504 : {
505 10 : poBand->SetDataType(GDT_Byte);
506 10 : poBand->m_nBits = 8;
507 : }
508 71 : else if( EQUAL(szDataFormat,"BYTE") )
509 : {
510 0 : poBand->SetDataType(GDT_Byte);
511 0 : poBand->m_nBits = 8;
512 : }
513 71 : else if( EQUAL(szDataFormat,"SHORTINT_UNSIGNED") )
514 : {
515 10 : poBand->SetDataType(GDT_UInt16);
516 10 : poBand->m_nBits = 16;
517 : }
518 61 : else if( EQUAL(szDataFormat,"SHORTINT") )
519 : {
520 10 : poBand->SetDataType(GDT_Int16);
521 10 : poBand->m_nBits = 16;
522 : }
523 51 : else if( EQUAL(szDataFormat,"INTEGER_UNSIGNED") )
524 : {
525 10 : poBand->SetDataType(GDT_UInt32);
526 10 : poBand->m_nBits = 32;
527 : }
528 41 : else if( EQUAL(szDataFormat,"INTEGER") )
529 : {
530 10 : poBand->SetDataType(GDT_Int32);
531 10 : poBand->m_nBits = 32;
532 : }
533 31 : else if( EQUAL(szDataFormat,"FLOAT") )
534 : {
535 23 : poBand->SetDataType(GDT_Float32);
536 23 : poBand->m_nBits = 32;
537 : }
538 8 : else if( EQUAL(szDataFormat,"DOUBLE") )
539 : {
540 8 : poBand->SetDataType(GDT_Float64);
541 8 : poBand->m_nBits = 64;
542 : }
543 : else
544 : {
545 : CPLError( CE_Failure, CPLE_NotSupported,
546 : "SAGA driver does not support the dataformat %s.",
547 0 : szDataFormat );
548 0 : delete poBand;
549 0 : delete poDS;
550 0 : return NULL;
551 : }
552 :
553 : /* -------------------------------------------------------------------- */
554 : /* Save band information */
555 : /* -------------------------------------------------------------------- */
556 81 : poBand->m_Xmin = dXmin;
557 81 : poBand->m_Ymin = dYmin;
558 81 : poBand->m_NoData = dNoData;
559 81 : poBand->m_Cellsize = dCellsize;
560 81 : poBand->m_Rows = nRows;
561 81 : poBand->m_Cols = nCols;
562 :
563 81 : poDS->SetBand( 1, poBand );
564 81 : poDS->SetDescription( poOpenInfo->pszFilename );
565 :
566 81 : return poDS;
567 : }
568 :
569 : /************************************************************************/
570 : /* GetGeoTransform() */
571 : /************************************************************************/
572 :
573 4 : CPLErr SAGADataset::GetGeoTransform( double *padfGeoTransform )
574 : {
575 4 : if( padfGeoTransform == NULL )
576 0 : return CE_Failure;
577 :
578 4 : SAGARasterBand *poGRB = dynamic_cast<SAGARasterBand *>(GetRasterBand( 1 ));
579 :
580 4 : if( poGRB == NULL )
581 : {
582 0 : padfGeoTransform[0] = 0;
583 0 : padfGeoTransform[1] = 1;
584 0 : padfGeoTransform[2] = 0;
585 0 : padfGeoTransform[3] = 0;
586 0 : padfGeoTransform[4] = 0;
587 0 : padfGeoTransform[5] = 1;
588 0 : return CE_Failure;
589 : }
590 :
591 : /* check if we have a PAM GeoTransform stored */
592 4 : CPLPushErrorHandler( CPLQuietErrorHandler );
593 4 : CPLErr eErr = GDALPamDataset::GetGeoTransform( padfGeoTransform );
594 4 : CPLPopErrorHandler();
595 :
596 4 : if( eErr == CE_None )
597 0 : return CE_None;
598 :
599 4 : padfGeoTransform[1] = poGRB->m_Cellsize;
600 4 : padfGeoTransform[5] = poGRB->m_Cellsize * -1.0;
601 4 : padfGeoTransform[0] = poGRB->m_Xmin - poGRB->m_Cellsize / 2;
602 4 : padfGeoTransform[3] = poGRB->m_Ymin + (nRasterYSize - 1) * poGRB->m_Cellsize + poGRB->m_Cellsize / 2;
603 :
604 : /* tilt/rotation is not supported by SAGA grids */
605 4 : padfGeoTransform[4] = 0.0;
606 4 : padfGeoTransform[2] = 0.0;
607 :
608 4 : return CE_None;
609 : }
610 :
611 : /************************************************************************/
612 : /* SetGeoTransform() */
613 : /************************************************************************/
614 :
615 9 : CPLErr SAGADataset::SetGeoTransform( double *padfGeoTransform )
616 : {
617 :
618 9 : if( eAccess == GA_ReadOnly )
619 : {
620 : CPLError( CE_Failure, CPLE_NoWriteAccess,
621 0 : "Unable to set GeoTransform, dataset opened read only.\n" );
622 0 : return CE_Failure;
623 : }
624 :
625 9 : SAGARasterBand *poGRB = dynamic_cast<SAGARasterBand *>(GetRasterBand( 1 ));
626 :
627 9 : if( poGRB == NULL || padfGeoTransform == NULL)
628 0 : return CE_Failure;
629 :
630 9 : if( padfGeoTransform[1] != padfGeoTransform[5] * -1.0 )
631 : {
632 : CPLError( CE_Failure, CPLE_NotSupported,
633 : "Unable to set GeoTransform, SAGA binary grids only support "
634 0 : "the same cellsize in x-y.\n" );
635 0 : return CE_Failure;
636 : }
637 :
638 9 : double dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2;
639 : double dfMinY =
640 9 : padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3];
641 :
642 9 : CPLString osPath = CPLGetPath( GetDescription() );
643 9 : CPLString osName = CPLGetBasename( GetDescription() );
644 9 : CPLString osHDRFilename = CPLFormCIFilename( osPath, osName, ".sgrd" );
645 :
646 : CPLErr eErr = WriteHeader( osHDRFilename, poGRB->GetRasterDataType(),
647 : poGRB->nRasterXSize, poGRB->nRasterYSize,
648 : dfMinX, dfMinY, padfGeoTransform[1],
649 9 : poGRB->m_NoData, 1.0, false );
650 :
651 :
652 9 : if( eErr == CE_None )
653 : {
654 9 : poGRB->m_Xmin = dfMinX;
655 9 : poGRB->m_Ymin = dfMinY;
656 9 : poGRB->m_Cellsize = padfGeoTransform[1];
657 9 : poGRB->m_Cols = nRasterXSize;
658 9 : poGRB->m_Rows = nRasterYSize;
659 : }
660 :
661 9 : return eErr;
662 : }
663 :
664 : /************************************************************************/
665 : /* WriteHeader() */
666 : /************************************************************************/
667 :
668 47 : CPLErr SAGADataset::WriteHeader( CPLString osHDRFilename, GDALDataType eType,
669 : GInt16 nXSize, GInt16 nYSize,
670 : double dfMinX, double dfMinY,
671 : double dfCellsize, double dfNoData,
672 : double dfZFactor, bool bTopToBottom )
673 :
674 : {
675 : FILE *fp;
676 :
677 47 : fp = VSIFOpenL( osHDRFilename, "wt" );
678 :
679 47 : if( fp == NULL )
680 : {
681 : CPLError( CE_Failure, CPLE_OpenFailed,
682 : "Failed to write .sgrd file %s.",
683 0 : osHDRFilename.c_str() );
684 0 : return CE_Failure;
685 : }
686 :
687 47 : VSIFPrintfL( fp, "NAME\t= %s\n", CPLGetBasename( osHDRFilename ) );
688 47 : VSIFPrintfL( fp, "DESCRIPTION\t=\n" );
689 47 : VSIFPrintfL( fp, "UNIT\t=\n" );
690 47 : VSIFPrintfL( fp, "DATAFILE_OFFSET\t= 0\n" );
691 :
692 47 : if( eType == GDT_Int32 )
693 6 : VSIFPrintfL( fp, "DATAFORMAT\t= INTEGER\n" );
694 41 : else if( eType == GDT_UInt32 )
695 6 : VSIFPrintfL( fp, "DATAFORMAT\t= INTEGER_UNSIGNED\n" );
696 35 : else if( eType == GDT_Int16 )
697 6 : VSIFPrintfL( fp, "DATAFORMAT\t= SHORTINT\n" );
698 29 : else if( eType == GDT_UInt16 )
699 6 : VSIFPrintfL( fp, "DATAFORMAT\t= SHORTINT_UNSIGNED\n" );
700 23 : else if( eType == GDT_Byte )
701 6 : VSIFPrintfL( fp, "DATAFORMAT\t= BYTE_UNSIGNED\n" );
702 17 : else if( eType == GDT_Float32 )
703 11 : VSIFPrintfL( fp, "DATAFORMAT\t= FLOAT\n" );
704 : else //if( eType == GDT_Float64 )
705 6 : VSIFPrintfL( fp, "DATAFORMAT\t= DOUBLE\n" );
706 : #ifdef CPL_LSB
707 47 : VSIFPrintfL( fp, "BYTEORDER_BIG\t= FALSE\n" );
708 : #else
709 : VSIFPrintfL( fp, "BYTEORDER_BIG\t= TRUE\n" );
710 : #endif
711 :
712 47 : VSIFPrintfL( fp, "POSITION_XMIN\t= %.10f\n", dfMinX );
713 47 : VSIFPrintfL( fp, "POSITION_YMIN\t= %.10f\n", dfMinY );
714 47 : VSIFPrintfL( fp, "CELLCOUNT_X\t= %d\n", nXSize );
715 47 : VSIFPrintfL( fp, "CELLCOUNT_Y\t= %d\n", nYSize );
716 47 : VSIFPrintfL( fp, "CELLSIZE\t= %.10f\n", dfCellsize );
717 47 : VSIFPrintfL( fp, "Z_FACTOR\t= %f\n", dfZFactor );
718 47 : VSIFPrintfL( fp, "NODATA_VALUE\t= %f\n", dfNoData );
719 47 : if (bTopToBottom)
720 0 : VSIFPrintfL( fp, "TOPTOBOTTOM\t= TRUE\n" );
721 : else
722 47 : VSIFPrintfL( fp, "TOPTOBOTTOM\t= FALSE\n" );
723 :
724 :
725 47 : VSIFCloseL( fp );
726 :
727 47 : return CE_None;
728 : }
729 :
730 :
731 : /************************************************************************/
732 : /* Create() */
733 : /************************************************************************/
734 :
735 51 : GDALDataset *SAGADataset::Create( const char * pszFilename,
736 : int nXSize, int nYSize, int nBands,
737 : GDALDataType eType,
738 : char **papszParmList )
739 :
740 : {
741 51 : if( nXSize <= 0 || nYSize <= 0 )
742 : {
743 : CPLError( CE_Failure, CPLE_IllegalArg,
744 : "Unable to create grid, both X and Y size must be "
745 0 : "non-negative.\n" );
746 :
747 0 : return NULL;
748 : }
749 :
750 51 : if( nBands != 1 )
751 : {
752 : CPLError( CE_Failure, CPLE_IllegalArg,
753 5 : "SAGA Binary Grid only supports 1 band" );
754 5 : return NULL;
755 : }
756 :
757 46 : if( eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Int16
758 : && eType != GDT_UInt32 && eType != GDT_Int32 && eType != GDT_Float32
759 : && eType != GDT_Float64 )
760 : {
761 : CPLError( CE_Failure, CPLE_AppDefined,
762 : "SAGA Binary Grid only supports Byte, UInt16, Int16, "
763 : "UInt32, Int32, Float32 and Float64 datatypes. Unable to "
764 8 : "create with type %s.\n", GDALGetDataTypeName( eType ) );
765 :
766 8 : return NULL;
767 : }
768 :
769 38 : FILE *fp = VSIFOpenL( pszFilename, "w+b" );
770 :
771 38 : if( fp == NULL )
772 : {
773 : CPLError( CE_Failure, CPLE_OpenFailed,
774 : "Attempt to create file '%s' failed.\n",
775 0 : pszFilename );
776 0 : return NULL;
777 : }
778 :
779 : char abyNoData[8];
780 38 : double dfNoDataVal = 0.0;
781 :
782 38 : switch (eType) /* GDT_Byte, GDT_UInt16, GDT_Int16, GDT_UInt32 */
783 : { /* GDT_Int32, GDT_Float32, GDT_Float64 */
784 : case (GDT_Byte):
785 : {
786 5 : GByte nodata = SG_NODATA_GDT_Byte;
787 5 : dfNoDataVal = nodata;
788 5 : memcpy(abyNoData, &nodata, 1);
789 5 : break;
790 : }
791 : case (GDT_UInt16):
792 : {
793 5 : GUInt16 nodata = SG_NODATA_GDT_UInt16;
794 5 : dfNoDataVal = nodata;
795 5 : memcpy(abyNoData, &nodata, 2);
796 5 : break;
797 : }
798 : case (GDT_Int16):
799 : {
800 5 : GInt16 nodata = SG_NODATA_GDT_Int16;
801 5 : dfNoDataVal = nodata;
802 5 : memcpy(abyNoData, &nodata, 2);
803 5 : break;
804 : }
805 : case (GDT_UInt32):
806 : {
807 5 : GUInt32 nodata = SG_NODATA_GDT_UInt32;
808 5 : dfNoDataVal = nodata;
809 5 : memcpy(abyNoData, &nodata, 4);
810 5 : break;
811 : }
812 : case (GDT_Int32):
813 : {
814 5 : GInt32 nodata = SG_NODATA_GDT_Int32;
815 5 : dfNoDataVal = nodata;
816 5 : memcpy(abyNoData, &nodata, 4);
817 5 : break;
818 : }
819 : default:
820 : case (GDT_Float32):
821 : {
822 8 : float nodata = SG_NODATA_GDT_Float32;
823 8 : dfNoDataVal = nodata;
824 8 : memcpy(abyNoData, &nodata, 4);
825 8 : break;
826 : }
827 : case (GDT_Float64):
828 : {
829 5 : double nodata = SG_NODATA_GDT_Float64;
830 5 : dfNoDataVal = nodata;
831 5 : memcpy(abyNoData, &nodata, 8);
832 : break;
833 : }
834 : }
835 :
836 38 : CPLString osHdrFilename = CPLResetExtension( pszFilename, "sgrd" );
837 : CPLErr eErr = WriteHeader( osHdrFilename, eType,
838 : nXSize, nYSize,
839 : 0.0, 0.0, 1.0,
840 38 : dfNoDataVal, 1.0, false );
841 :
842 38 : if( eErr != CE_None )
843 : {
844 0 : VSIFCloseL( fp );
845 0 : return NULL;
846 : }
847 :
848 38 : if (CSLFetchBoolean( papszParmList , "FILL_NODATA", TRUE ))
849 : {
850 22 : int nDataTypeSize = GDALGetDataTypeSize(eType) / 8;
851 22 : GByte* pabyNoDataBuf = (GByte*) VSIMalloc2(nDataTypeSize, nXSize);
852 22 : if (pabyNoDataBuf == NULL)
853 : {
854 0 : VSIFCloseL( fp );
855 0 : return NULL;
856 : }
857 :
858 886 : for( int iCol = 0; iCol < nXSize; iCol++)
859 : {
860 864 : memcpy(pabyNoDataBuf + iCol * nDataTypeSize, abyNoData, nDataTypeSize);
861 : }
862 :
863 886 : for( int iRow = 0; iRow < nYSize; iRow++ )
864 : {
865 864 : if( VSIFWriteL( pabyNoDataBuf, nDataTypeSize, nXSize, fp ) != (unsigned)nXSize )
866 : {
867 0 : VSIFCloseL( fp );
868 0 : VSIFree(pabyNoDataBuf);
869 : CPLError( CE_Failure, CPLE_FileIO,
870 0 : "Unable to write grid cell. Disk full?\n" );
871 0 : return NULL;
872 : }
873 : }
874 :
875 22 : VSIFree(pabyNoDataBuf);
876 : }
877 :
878 38 : VSIFCloseL( fp );
879 :
880 38 : return (GDALDataset *)GDALOpen( pszFilename, GA_Update );
881 : }
882 :
883 : /************************************************************************/
884 : /* CreateCopy() */
885 : /************************************************************************/
886 :
887 25 : GDALDataset *SAGADataset::CreateCopy( const char *pszFilename,
888 : GDALDataset *poSrcDS,
889 : int bStrict, char **papszOptions,
890 : GDALProgressFunc pfnProgress,
891 : void *pProgressData )
892 : {
893 25 : if( pfnProgress == NULL )
894 0 : pfnProgress = GDALDummyProgress;
895 :
896 25 : int nBands = poSrcDS->GetRasterCount();
897 25 : if (nBands == 0)
898 : {
899 : CPLError( CE_Failure, CPLE_NotSupported,
900 1 : "SAGA driver does not support source dataset with zero band.\n");
901 1 : return NULL;
902 : }
903 24 : else if (nBands > 1)
904 : {
905 4 : if( bStrict )
906 : {
907 : CPLError( CE_Failure, CPLE_NotSupported,
908 : "Unable to create copy, SAGA Binary Grid "
909 4 : "format only supports one raster band.\n" );
910 4 : return NULL;
911 : }
912 : else
913 : CPLError( CE_Warning, CPLE_NotSupported,
914 : "SAGA Binary Grid format only supports one "
915 0 : "raster band, first band will be copied.\n" );
916 : }
917 :
918 20 : GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( 1 );
919 :
920 20 : char** papszCreateOptions = NULL;
921 20 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "FILL_NODATA", "NO");
922 : GDALDataset* poDstDS =
923 : Create(pszFilename, poSrcBand->GetXSize(), poSrcBand->GetYSize(),
924 20 : 1, poSrcBand->GetRasterDataType(), papszCreateOptions);
925 20 : CSLDestroy(papszCreateOptions);
926 :
927 20 : if (poDstDS == NULL)
928 11 : return NULL;
929 :
930 : /* -------------------------------------------------------------------- */
931 : /* Copy band data. */
932 : /* -------------------------------------------------------------------- */
933 :
934 : CPLErr eErr;
935 :
936 : eErr = GDALDatasetCopyWholeRaster( (GDALDatasetH) poSrcDS,
937 : (GDALDatasetH) poDstDS,
938 : NULL,
939 9 : pfnProgress, pProgressData );
940 :
941 9 : if (eErr == CE_Failure)
942 : {
943 0 : delete poDstDS;
944 0 : return NULL;
945 : }
946 :
947 : double adfGeoTransform[6];
948 :
949 9 : poSrcDS->GetGeoTransform( adfGeoTransform );
950 9 : poDstDS->SetGeoTransform( adfGeoTransform );
951 :
952 9 : return poDstDS;
953 : }
954 :
955 : /************************************************************************/
956 : /* GDALRegister_SAGA() */
957 : /************************************************************************/
958 :
959 338 : void GDALRegister_SAGA()
960 :
961 : {
962 : GDALDriver *poDriver;
963 :
964 338 : if( GDALGetDriverByName( "SAGA" ) == NULL )
965 : {
966 336 : poDriver = new GDALDriver();
967 :
968 336 : poDriver->SetDescription( "SAGA" );
969 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
970 336 : "SAGA GIS Binary Grid (.sdat)" );
971 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
972 336 : "frmt_various.html#SAGA" );
973 336 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "sdat" );
974 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
975 336 : "Byte Int16 UInt16 Int32 UInt32 Float32 Float64" );
976 :
977 336 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
978 :
979 336 : poDriver->pfnOpen = SAGADataset::Open;
980 336 : poDriver->pfnCreate = SAGADataset::Create;
981 336 : poDriver->pfnCreateCopy = SAGADataset::CreateCopy;
982 :
983 336 : GetGDALDriverManager()->RegisterDriver( poDriver );
984 : }
985 338 : }
|