1 /******************************************************************************
2 *
3 * Project: SDTS Translator
4 * Purpose: Implementation of SDTSRasterReader class.
5 * Author: Frank Warmerdam, warmerdam@pobox.com
6 *
7 ******************************************************************************
8 * Copyright (c) 1999, Frank Warmerdam
9 * Copyright (c) 2008-2013, 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 "sdts_al.h"
31
32 #include <algorithm>
33
34 CPL_CVSID("$Id: sdtsrasterreader.cpp b1c9c12ad373e40b955162b45d704070d4ebf7b0 2019-06-19 16:50:15 +0200 Even Rouault $")
35
36 /************************************************************************/
37 /* SDTSRasterReader() */
38 /************************************************************************/
39
SDTSRasterReader()40 SDTSRasterReader::SDTSRasterReader() :
41 nXSize(0),
42 nYSize(0),
43 nXBlockSize(0),
44 nYBlockSize(0),
45 nXStart(0),
46 nYStart(0)
47 {
48 strcpy( szINTR, "CE" );
49 memset( szModule, 0, sizeof(szModule) );
50 memset( adfTransform, 0, sizeof(adfTransform) );
51 memset( szFMT, 0, sizeof(szFMT) );
52 memset( szUNITS, 0, sizeof(szUNITS) );
53 memset( szLabel, 0, sizeof(szLabel) );
54 }
55
56 /************************************************************************/
57 /* ~SDTSRasterReader() */
58 /************************************************************************/
59
~SDTSRasterReader()60 SDTSRasterReader::~SDTSRasterReader() {}
61
62 /************************************************************************/
63 /* Close() */
64 /************************************************************************/
65
Close()66 void SDTSRasterReader::Close()
67
68 {
69 oDDFModule.Close();
70 }
71
72 /************************************************************************/
73 /* Open() */
74 /* */
75 /* Open the requested cell file, and collect required */
76 /* information. */
77 /************************************************************************/
78
Open(SDTS_CATD * poCATD,SDTS_IREF * poIREF,const char * pszModule)79 int SDTSRasterReader::Open( SDTS_CATD * poCATD, SDTS_IREF * poIREF,
80 const char * pszModule )
81
82 {
83 snprintf( szModule, sizeof(szModule), "%s", pszModule );
84
85 /* ==================================================================== */
86 /* Search the LDEF module for the requested cell module. */
87 /* ==================================================================== */
88
89 /* -------------------------------------------------------------------- */
90 /* Open the LDEF module, and report failure if it is missing. */
91 /* -------------------------------------------------------------------- */
92 if( poCATD->GetModuleFilePath("LDEF") == nullptr )
93 {
94 CPLError( CE_Failure, CPLE_AppDefined,
95 "Can't find LDEF entry in CATD module ... "
96 "can't treat as raster.\n" );
97 return FALSE;
98 }
99
100 DDFModule oLDEF;
101 if( !oLDEF.Open( poCATD->GetModuleFilePath("LDEF") ) )
102 return FALSE;
103
104 /* -------------------------------------------------------------------- */
105 /* Read each record, till we find what we want. */
106 /* -------------------------------------------------------------------- */
107 DDFRecord *poRecord = nullptr;
108 while( (poRecord = oLDEF.ReadRecord() ) != nullptr )
109 {
110 const char* pszCandidateModule = poRecord->GetStringSubfield("LDEF",0,"CMNM",0);
111 if( pszCandidateModule == nullptr )
112 {
113 poRecord = nullptr;
114 break;
115 }
116 if( EQUAL(pszCandidateModule, pszModule) )
117 break;
118 }
119
120 if( poRecord == nullptr )
121 {
122 CPLError( CE_Failure, CPLE_AppDefined,
123 "Can't find module `%s' in LDEF file.\n",
124 pszModule );
125 return FALSE;
126 }
127
128 /* -------------------------------------------------------------------- */
129 /* Extract raster dimensions, and origin offset (0/1). */
130 /* -------------------------------------------------------------------- */
131 nXSize = poRecord->GetIntSubfield( "LDEF", 0, "NCOL", 0 );
132 nYSize = poRecord->GetIntSubfield( "LDEF", 0, "NROW", 0 );
133
134 nXStart = poRecord->GetIntSubfield( "LDEF", 0, "SOCI", 0 );
135 nYStart = poRecord->GetIntSubfield( "LDEF", 0, "SORI", 0 );
136
137 /* -------------------------------------------------------------------- */
138 /* Get the point in the pixel that the origin defines. We only */
139 /* support top left and center. */
140 /* -------------------------------------------------------------------- */
141 const char* pszINTR = poRecord->GetStringSubfield( "LDEF", 0, "INTR", 0 );
142 if( pszINTR == nullptr )
143 {
144 CPLError( CE_Failure, CPLE_AppDefined, "Can't find INTR subfield of LDEF field" );
145 return FALSE;
146 }
147 snprintf( szINTR, sizeof(szINTR), "%s", pszINTR );
148 if( EQUAL(szINTR,"") )
149 snprintf( szINTR, sizeof(szINTR), "%s", "CE" );
150
151 if( !EQUAL(szINTR,"CE") && !EQUAL(szINTR,"TL") )
152 {
153 CPLError( CE_Warning, CPLE_AppDefined,
154 "Unsupported INTR value of `%s', assume CE.\n"
155 "Positions may be off by one pixel.\n",
156 szINTR );
157 snprintf( szINTR, sizeof(szINTR), "%s", "CE" );
158 }
159
160 /* -------------------------------------------------------------------- */
161 /* Record the LDEF record number we used so we can find the */
162 /* corresponding RSDF record. */
163 /* -------------------------------------------------------------------- */
164 int nLDEF_RCID = poRecord->GetIntSubfield( "LDEF", 0, "RCID", 0 );
165
166 oLDEF.Close();
167
168 /* ==================================================================== */
169 /* Search the RSDF module for the requested cell module. */
170 /* ==================================================================== */
171
172 /* -------------------------------------------------------------------- */
173 /* Open the RSDF module, and report failure if it is missing. */
174 /* -------------------------------------------------------------------- */
175 if( poCATD->GetModuleFilePath("RSDF") == nullptr )
176 {
177 CPLError( CE_Failure, CPLE_AppDefined,
178 "Can't find RSDF entry in CATD module ... "
179 "can't treat as raster.\n" );
180 return FALSE;
181 }
182
183 DDFModule oRSDF;
184 if( !oRSDF.Open( poCATD->GetModuleFilePath("RSDF") ) )
185 return FALSE;
186
187 /* -------------------------------------------------------------------- */
188 /* Read each record, till we find what we want. */
189 /* -------------------------------------------------------------------- */
190 while( (poRecord = oRSDF.ReadRecord() ) != nullptr )
191 {
192 if( poRecord->GetIntSubfield("LYID",0,"RCID",0) == nLDEF_RCID )
193 break;
194 }
195
196 if( poRecord == nullptr )
197 {
198 CPLError( CE_Failure, CPLE_AppDefined,
199 "Can't find LDEF:%d record in RSDF file.\n",
200 nLDEF_RCID );
201 return FALSE;
202 }
203
204 /* -------------------------------------------------------------------- */
205 /* Establish the raster pixel/line to georef transformation. */
206 /* -------------------------------------------------------------------- */
207
208 if( poRecord->FindField( "SADR" ) == nullptr )
209 {
210 CPLError( CE_Failure, CPLE_AppDefined,
211 "Can't find SADR field in RSDF record.\n" );
212 return FALSE;
213 }
214
215 double dfZ;
216 poIREF->GetSADR( poRecord->FindField( "SADR" ), 1,
217 adfTransform + 0, adfTransform + 3, &dfZ );
218
219 adfTransform[1] = poIREF->dfXRes;
220 adfTransform[2] = 0.0;
221 adfTransform[4] = 0.0;
222 adfTransform[5] = -1 * poIREF->dfYRes;
223
224 /* -------------------------------------------------------------------- */
225 /* If the origin is the center of the pixel, then shift it back */
226 /* half a pixel to the top left of the top left. */
227 /* -------------------------------------------------------------------- */
228 if( EQUAL(szINTR,"CE") )
229 {
230 adfTransform[0] -= adfTransform[1] * 0.5;
231 adfTransform[3] -= adfTransform[5] * 0.5;
232 }
233
234 /* -------------------------------------------------------------------- */
235 /* Verify some other assumptions. */
236 /* -------------------------------------------------------------------- */
237 const char *pszString = poRecord->GetStringSubfield( "RSDF", 0, "OBRP", 0);
238 if( pszString == nullptr )
239 pszString = "";
240 if( !EQUAL(pszString, "G2") )
241 {
242 CPLError( CE_Failure, CPLE_AppDefined,
243 "OBRP value of `%s' not expected 2D raster code (G2).\n",
244 pszString );
245 return FALSE;
246 }
247
248 pszString = poRecord->GetStringSubfield( "RSDF", 0, "SCOR", 0);
249 if( pszString == nullptr )
250 pszString = "";
251 if( !EQUAL(pszString,"TL") )
252 {
253 CPLError( CE_Warning, CPLE_AppDefined,
254 "SCOR (origin) is `%s' instead of expected top left.\n"
255 "Georef coordinates will likely be incorrect.\n",
256 pszString );
257 }
258
259 oRSDF.Close();
260
261 /* -------------------------------------------------------------------- */
262 /* For now we will assume that the block size is one scanline. */
263 /* We will blow a gasket later while reading the cell file if */
264 /* this isn't the case. */
265 /* */
266 /* This isn't a very flexible raster implementation! */
267 /* -------------------------------------------------------------------- */
268 nXBlockSize = nXSize;
269 nYBlockSize = 1;
270
271 /* ==================================================================== */
272 /* Fetch the data type used for the raster, and the units from */
273 /* the data dictionary/schema record (DDSH). */
274 /* ==================================================================== */
275
276 /* -------------------------------------------------------------------- */
277 /* Open the DDSH module, and report failure if it is missing. */
278 /* -------------------------------------------------------------------- */
279 if( poCATD->GetModuleFilePath("DDSH") == nullptr )
280 {
281 CPLError( CE_Failure, CPLE_AppDefined,
282 "Can't find DDSH entry in CATD module ... "
283 "can't treat as raster.\n" );
284 return FALSE;
285 }
286
287 DDFModule oDDSH;
288 if( !oDDSH.Open( poCATD->GetModuleFilePath("DDSH") ) )
289 return FALSE;
290
291 /* -------------------------------------------------------------------- */
292 /* Read each record, till we find what we want. */
293 /* -------------------------------------------------------------------- */
294 while( (poRecord = oDDSH.ReadRecord() ) != nullptr )
295 {
296 const char* pszName = poRecord->GetStringSubfield("DDSH",0,"NAME",0);
297 if( pszName == nullptr )
298 {
299 poRecord = nullptr;
300 break;
301 }
302 if( EQUAL(pszName,pszModule) )
303 break;
304 }
305
306 if( poRecord == nullptr )
307 {
308 CPLError( CE_Failure, CPLE_AppDefined,
309 "Can't find DDSH record for %s.\n",
310 pszModule );
311 return FALSE;
312 }
313
314 /* -------------------------------------------------------------------- */
315 /* Get some values we are interested in. */
316 /* -------------------------------------------------------------------- */
317 if( poRecord->GetStringSubfield("DDSH",0,"FMT",0) != nullptr )
318 snprintf( szFMT, sizeof(szFMT), "%s", poRecord->GetStringSubfield("DDSH",0,"FMT",0) );
319 else
320 snprintf( szFMT, sizeof(szFMT), "%s", "BI16" );
321 if( !EQUAL(szFMT, "BI16") && !EQUAL(szFMT, "BFP32") )
322 {
323 CPLError( CE_Failure, CPLE_AppDefined,
324 "Unhandled FMT=%s", szFMT );
325 return FALSE;
326 }
327
328 if( poRecord->GetStringSubfield("DDSH",0,"UNIT",0) != nullptr )
329 snprintf( szUNITS, sizeof(szUNITS), "%s", poRecord->GetStringSubfield("DDSH",0,"UNIT",0) );
330 else
331 snprintf( szUNITS, sizeof(szUNITS), "%s", "METERS" );
332
333 if( poRecord->GetStringSubfield("DDSH",0,"ATLB",0) != nullptr )
334 snprintf( szLabel, sizeof(szLabel), "%s", poRecord->GetStringSubfield("DDSH",0,"ATLB",0) );
335 else
336 strcpy( szLabel, "" );
337
338 /* -------------------------------------------------------------------- */
339 /* Open the cell file. */
340 /* -------------------------------------------------------------------- */
341 return oDDFModule.Open( poCATD->GetModuleFilePath(pszModule) );
342 }
343
344 /************************************************************************/
345 /* GetBlock() */
346 /* */
347 /* Read a requested block of raster data from the file. */
348 /* */
349 /* Currently we will always use sequential access. In the */
350 /* future we should modify the iso8211 library to support */
351 /* seeking, and modify this to seek directly to the right */
352 /* record once its location is known. */
353 /************************************************************************/
354
355 /**
356 Read a block of raster data from the file.
357
358 @param nXOffset X block offset into the file. Normally zero for scanline
359 organized raster files.
360
361 @param nYOffset Y block offset into the file. Normally the scanline offset
362 from top of raster for scanline organized raster files.
363
364 @param pData pointer to GInt16 (signed short) buffer of data into which to
365 read the raster.
366
367 @return TRUE on success and FALSE on error.
368
369 */
370
GetBlock(CPL_UNUSED int nXOffset,int nYOffset,void * pData)371 int SDTSRasterReader::GetBlock( CPL_UNUSED int nXOffset,
372 int nYOffset,
373 void * pData )
374 {
375 CPLAssert( nXOffset == 0 );
376
377 /* -------------------------------------------------------------------- */
378 /* Analyse the datatype. */
379 /* -------------------------------------------------------------------- */
380 CPLAssert( EQUAL(szFMT,"BI16") || EQUAL(szFMT,"BFP32") );
381
382 int nBytesPerValue;
383 if( EQUAL(szFMT,"BI16") )
384 nBytesPerValue = 2;
385 else
386 nBytesPerValue = 4;
387
388 DDFRecord *poRecord = nullptr;
389
390 for( int iTry = 0; iTry < 2; iTry++ )
391 {
392 /* -------------------------------------------------------------------- */
393 /* Read through till we find the desired record. */
394 /* -------------------------------------------------------------------- */
395 CPLErrorReset();
396 while( (poRecord = oDDFModule.ReadRecord()) != nullptr )
397 {
398 if( poRecord->GetIntSubfield( "CELL", 0, "ROWI", 0 )
399 == nYOffset + nYStart )
400 {
401 break;
402 }
403 }
404
405 if( CPLGetLastErrorType() == CE_Failure )
406 return FALSE;
407
408 /* -------------------------------------------------------------------- */
409 /* If we didn't get what we needed just start over. */
410 /* -------------------------------------------------------------------- */
411 if( poRecord == nullptr )
412 {
413 if (iTry == 0)
414 oDDFModule.Rewind();
415 else
416 {
417 CPLError( CE_Failure, CPLE_AppDefined,
418 "Cannot read scanline %d. Raster access failed.\n",
419 nYOffset );
420 return FALSE;
421 }
422 }
423 else
424 {
425 break;
426 }
427 }
428
429 /* -------------------------------------------------------------------- */
430 /* Validate the records size. Does it represent exactly one */
431 /* scanline? */
432 /* -------------------------------------------------------------------- */
433 DDFField *poCVLS = poRecord->FindField( "CVLS" );
434 if( poCVLS == nullptr )
435 return FALSE;
436
437 if( poCVLS->GetRepeatCount() != nXSize )
438 {
439 CPLError( CE_Failure, CPLE_AppDefined,
440 "Cell record is %d long, but we expected %d, the number\n"
441 "of pixels in a scanline. Raster access failed.\n",
442 poCVLS->GetRepeatCount(), nXSize );
443 return FALSE;
444 }
445
446 /* -------------------------------------------------------------------- */
447 /* Does the CVLS field consist of exactly 1 B(16) field? */
448 /* -------------------------------------------------------------------- */
449 if( poCVLS->GetDataSize() < nBytesPerValue * nXSize
450 || poCVLS->GetDataSize() > nBytesPerValue * nXSize + 1 )
451 {
452 CPLError( CE_Failure, CPLE_AppDefined,
453 "Cell record is not of expected format. Raster access "
454 "failed.\n" );
455
456 return FALSE;
457 }
458
459 /* -------------------------------------------------------------------- */
460 /* Copy the data to the application buffer, and byte swap if */
461 /* required. */
462 /* -------------------------------------------------------------------- */
463 memcpy( pData, poCVLS->GetData(), nXSize * nBytesPerValue );
464
465 #ifdef CPL_LSB
466 if( nBytesPerValue == 2 )
467 {
468 for( int i = 0; i < nXSize; i++ )
469 {
470 reinterpret_cast<GInt16 *>( pData )[i] = CPL_MSBWORD16(
471 reinterpret_cast<GInt16 *>( pData )[i] );
472 }
473 }
474 else
475 {
476 for( int i = 0; i < nXSize; i++ )
477 {
478 CPL_MSBPTR32( reinterpret_cast<GByte *>( pData ) + i*4 );
479 }
480 }
481 #endif
482
483 return TRUE;
484 }
485
486 /************************************************************************/
487 /* GetTransform() */
488 /************************************************************************/
489
490 /**
491 Fetch the transformation between pixel/line coordinates and georeferenced
492 coordinates.
493
494 @param padfTransformOut pointer to an array of six doubles which will be
495 filled with the georeferencing transform.
496
497 @return TRUE is returned, indicating success.
498
499 The padfTransformOut array consists of six values. The pixel/line coordinate
500 (Xp,Yp) can be related to a georeferenced coordinate (Xg,Yg) or (Easting,
501 Northing).
502
503 <pre>
504 Xg = padfTransformOut[0] + Xp * padfTransform[1] + Yp * padfTransform[2]
505 Yg = padfTransformOut[3] + Xp * padfTransform[4] + Yp * padfTransform[5]
506 </pre>
507
508 In other words, for a north up image the top left corner of the top left
509 pixel is at georeferenced coordinate (padfTransform[0],padfTransform[3])
510 the pixel width is padfTransform[1], the pixel height is padfTransform[5]
511 and padfTransform[2] and padfTransform[4] will be zero.
512
513 */
514
GetTransform(double * padfTransformOut)515 int SDTSRasterReader::GetTransform( double * padfTransformOut )
516
517 {
518 memcpy( padfTransformOut, adfTransform, sizeof(double)*6 );
519
520 return TRUE;
521 }
522
523 /************************************************************************/
524 /* GetRasterType() */
525 /************************************************************************/
526
527 /**
528 * Fetch the pixel data type.
529 *
530 * Returns one of SDTS_RT_INT16 (1) or SDTS_RT_FLOAT32 (6) indicating the
531 * type of buffer that should be passed to GetBlock().
532 */
533
GetRasterType()534 int SDTSRasterReader::GetRasterType()
535
536 {
537 if( EQUAL(szFMT,"BFP32") )
538 return SDTS_RT_FLOAT32;
539
540 return SDTS_RT_INT16;
541 }
542
543 /************************************************************************/
544 /* GetMinMax() */
545 /************************************************************************/
546
547 /**
548 * Fetch the minimum and maximum raster values that occur in the file.
549 *
550 * Note this operation current results in a scan of the entire file.
551 *
552 * @param pdfMin variable in which the minimum value encountered is returned.
553 * @param pdfMax variable in which the maximum value encountered is returned.
554 * @param dfNoData a value to ignore when computing min/max, defaults to
555 * -32766.
556 *
557 * @return TRUE on success, or FALSE if an error occurs.
558 */
559
GetMinMax(double * pdfMin,double * pdfMax,double dfNoData)560 int SDTSRasterReader::GetMinMax( double * pdfMin, double * pdfMax,
561 double dfNoData )
562
563 {
564 CPLAssert( GetBlockXSize() == GetXSize() && GetBlockYSize() == 1 );
565
566 bool bFirst = true;
567 const bool b32Bit = GetRasterType() == SDTS_RT_FLOAT32;
568 void *pBuffer = CPLMalloc(sizeof(float) * GetXSize());
569
570 for( int iLine = 0; iLine < GetYSize(); iLine++ )
571 {
572 if( !GetBlock( 0, iLine, pBuffer ) )
573 {
574 CPLFree( pBuffer );
575 return FALSE;
576 }
577
578 for( int iPixel = 0; iPixel < GetXSize(); iPixel++ )
579 {
580 double dfValue;
581
582 if( b32Bit )
583 dfValue = reinterpret_cast<float *>( pBuffer )[iPixel];
584 else
585 dfValue = reinterpret_cast<short *>( pBuffer )[iPixel];
586
587 if( dfValue != dfNoData )
588 {
589 if( bFirst )
590 {
591 *pdfMin = dfValue;
592 *pdfMax = dfValue;
593 bFirst = false;
594 }
595 else
596 {
597 *pdfMin = std::min( *pdfMin, dfValue );
598 *pdfMax = std::max( *pdfMax, dfValue );
599 }
600 }
601 }
602 }
603
604 CPLFree( pBuffer );
605
606 return !bFirst;
607 }
608