1 : /******************************************************************************
2 : * $Id: gdal_rpcimdio.cpp 18063 2009-11-21 21:11:49Z warmerdam $
3 : *
4 : * Project: GDAL Core
5 : * Purpose: Functions for reading RPC and IMD formats, and normalizing.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) HER MAJESTY THE QUEEN IN RIGHT OF CANADA (2008)
10 : * as represented by the Canadian Nuclear Safety Commission
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "gdal.h"
32 : #include "cpl_string.h"
33 : #include "cplkeywordparser.h"
34 :
35 : CPL_CVSID("$Id: gdal_rpcimdio.cpp 18063 2009-11-21 21:11:49Z warmerdam $");
36 :
37 : /************************************************************************/
38 : /* GDALLoadRPBFile() */
39 : /************************************************************************/
40 :
41 : static const char *apszRPBMap[] = {
42 : "LINE_OFF", "IMAGE.lineOffset",
43 : "SAMP_OFF", "IMAGE.sampOffset",
44 : "LAT_OFF", "IMAGE.latOffset",
45 : "LONG_OFF", "IMAGE.longOffset",
46 : "HEIGHT_OFF", "IMAGE.heightOffset",
47 : "LINE_SCALE", "IMAGE.lineScale",
48 : "SAMP_SCALE", "IMAGE.sampScale",
49 : "LAT_SCALE", "IMAGE.latScale",
50 : "LONG_SCALE", "IMAGE.longScale",
51 : "HEIGHT_SCALE", "IMAGE.heightScale",
52 : "LINE_NUM_COEFF", "IMAGE.lineNumCoef",
53 : "LINE_DEN_COEFF", "IMAGE.lineDenCoef",
54 : "SAMP_NUM_COEFF", "IMAGE.sampNumCoef",
55 : "SAMP_DEN_COEFF", "IMAGE.sampDenCoef",
56 : NULL, NULL };
57 :
58 2850 : char **CPL_STDCALL GDALLoadRPBFile( const char *pszFilename,
59 : char **papszSiblingFiles )
60 :
61 : {
62 : /* -------------------------------------------------------------------- */
63 : /* Try to identify the RPB file in upper or lower case. */
64 : /* -------------------------------------------------------------------- */
65 2850 : CPLString osTarget = CPLResetExtension( pszFilename, "RPB" );
66 :
67 2850 : if( papszSiblingFiles == NULL )
68 : {
69 : VSIStatBufL sStatBuf;
70 :
71 2850 : if( VSIStatL( osTarget, &sStatBuf ) != 0 )
72 : {
73 2847 : osTarget = CPLResetExtension( pszFilename, "rpb" );
74 :
75 2847 : if( VSIStatL( osTarget, &sStatBuf ) != 0 )
76 2847 : return NULL;
77 : }
78 : }
79 : else
80 : {
81 : int iSibling = CSLFindString( papszSiblingFiles,
82 0 : CPLGetFilename(osTarget) );
83 0 : if( iSibling < 0 )
84 0 : return NULL;
85 :
86 0 : osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
87 0 : osTarget += papszSiblingFiles[iSibling];
88 : }
89 :
90 : /* -------------------------------------------------------------------- */
91 : /* Read file and parse. */
92 : /* -------------------------------------------------------------------- */
93 3 : CPLKeywordParser oParser;
94 :
95 3 : FILE *fp = VSIFOpenL( osTarget, "r" );
96 :
97 3 : if( fp == NULL )
98 0 : return NULL;
99 :
100 3 : if( !oParser.Ingest( fp ) )
101 : {
102 0 : VSIFCloseL( fp );
103 0 : return NULL;
104 : }
105 :
106 3 : VSIFCloseL( fp );
107 :
108 : /* -------------------------------------------------------------------- */
109 : /* Extract RPC information, in a GDAL "standard" metadata format. */
110 : /* -------------------------------------------------------------------- */
111 : int i;
112 3 : char **papszMD = NULL;
113 3 : for( i = 0; apszRPBMap[i] != NULL; i += 2 )
114 : {
115 42 : const char *pszRPBVal = oParser.GetKeyword( apszRPBMap[i+1] );
116 42 : CPLString osAdjVal;
117 :
118 42 : if( pszRPBVal == NULL )
119 : {
120 : CPLError( CE_Failure, CPLE_AppDefined,
121 : "%s file found, but missing %s field (and possibly others).",
122 0 : osTarget.c_str(), apszRPBMap[i+1] );
123 0 : CSLDestroy( papszMD );
124 0 : return NULL;
125 : }
126 :
127 42 : if( strchr(pszRPBVal,',') == NULL )
128 30 : osAdjVal = pszRPBVal;
129 : else
130 : {
131 : // strip out commas and turn newlines into spaces.
132 : int j;
133 :
134 3384 : for( j = 0; pszRPBVal[j] != '\0'; j++ )
135 : {
136 3372 : switch( pszRPBVal[j] )
137 : {
138 : case ',':
139 : case '\n':
140 : case '\r':
141 228 : osAdjVal += ' ';
142 228 : break;
143 :
144 : case '(':
145 : case ')':
146 24 : break;
147 :
148 : default:
149 3120 : osAdjVal += pszRPBVal[j];
150 : }
151 : }
152 : }
153 :
154 42 : papszMD = CSLSetNameValue( papszMD, apszRPBMap[i], osAdjVal );
155 : }
156 :
157 3 : return papszMD;
158 : }
159 :
160 : /************************************************************************/
161 : /* GDALWriteRPBFile() */
162 : /************************************************************************/
163 :
164 1 : CPLErr CPL_STDCALL GDALWriteRPBFile( const char *pszFilename, char **papszMD )
165 :
166 : {
167 1 : CPLString osRPBFilename = CPLResetExtension( pszFilename, "RPB" );
168 :
169 :
170 : /* -------------------------------------------------------------------- */
171 : /* Read file and parse. */
172 : /* -------------------------------------------------------------------- */
173 1 : FILE *fp = VSIFOpenL( osRPBFilename, "w" );
174 :
175 1 : if( fp == NULL )
176 : {
177 : CPLError( CE_Failure, CPLE_OpenFailed,
178 : "Unable to create %s for writing.\n%s",
179 0 : osRPBFilename.c_str(), CPLGetLastErrorMsg() );
180 0 : return CE_Failure;
181 : }
182 :
183 : /* -------------------------------------------------------------------- */
184 : /* Write the prefix information. */
185 : /* -------------------------------------------------------------------- */
186 1 : VSIFPrintfL( fp, "%s", "satId = \"QB02\";\n" );
187 1 : VSIFPrintfL( fp, "%s", "bandId = \"P\";\n" );
188 1 : VSIFPrintfL( fp, "%s", "SpecId = \"RPC00B\";\n" );
189 1 : VSIFPrintfL( fp, "%s", "BEGIN_GROUP = IMAGE\n" );
190 1 : VSIFPrintfL( fp, "%s", "\terrBias = 0.0;\n" );
191 1 : VSIFPrintfL( fp, "%s", "\terrRand = 0.0;\n" );
192 :
193 : /* -------------------------------------------------------------------- */
194 : /* Write RPC values from our RPC metadata. */
195 : /* -------------------------------------------------------------------- */
196 : int i;
197 :
198 15 : for( i = 0; apszRPBMap[i] != NULL; i += 2 )
199 : {
200 14 : const char *pszRPBVal = CSLFetchNameValue( papszMD, apszRPBMap[i] );
201 : const char *pszRPBTag;
202 :
203 14 : if( pszRPBVal == NULL )
204 : {
205 : CPLError( CE_Failure, CPLE_AppDefined,
206 : "%s field missing in metadata, %s file not written.",
207 0 : apszRPBMap[i], osRPBFilename.c_str() );
208 0 : VSIFCloseL( fp );
209 0 : VSIUnlink( osRPBFilename );
210 0 : return CE_Failure;
211 : }
212 :
213 14 : pszRPBTag = apszRPBMap[i+1];
214 14 : if( EQUALN(pszRPBTag,"IMAGE.",6) )
215 14 : pszRPBTag += 6;
216 :
217 14 : if( strstr(apszRPBMap[i], "COEF" ) == NULL )
218 : {
219 10 : VSIFPrintfL( fp, "\t%s = %s;\n", pszRPBTag, pszRPBVal );
220 : }
221 : else
222 : {
223 : // Reformat in brackets with commas over multiple lines.
224 :
225 4 : VSIFPrintfL( fp, "\t%s = (\n", pszRPBTag );
226 :
227 : char **papszItems = CSLTokenizeStringComplex( pszRPBVal, " ,",
228 4 : FALSE, FALSE );
229 :
230 4 : if( CSLCount(papszItems) != 20 )
231 : {
232 : CPLError( CE_Failure, CPLE_AppDefined,
233 : "%s field is corrupt (not 20 values), %s file not written.\n%s = %s",
234 : apszRPBMap[i], osRPBFilename.c_str(),
235 0 : apszRPBMap[i], pszRPBVal );
236 0 : VSIFCloseL( fp );
237 0 : VSIUnlink( osRPBFilename );
238 0 : return CE_Failure;
239 : }
240 :
241 : int j;
242 :
243 84 : for( j = 0; j < 20; j++ )
244 : {
245 80 : if( j < 19 )
246 76 : VSIFPrintfL( fp, "\t\t\t%s,\n", papszItems[j] );
247 : else
248 4 : VSIFPrintfL( fp, "\t\t\t%s);\n", papszItems[j] );
249 : }
250 4 : CSLDestroy( papszItems );
251 : }
252 : }
253 :
254 : /* -------------------------------------------------------------------- */
255 : /* Write end part */
256 : /* -------------------------------------------------------------------- */
257 1 : VSIFPrintfL( fp, "%s", "END_GROUP = IMAGE\n" );
258 1 : VSIFPrintfL( fp, "END;\n" );
259 1 : VSIFCloseL( fp );
260 :
261 1 : return CE_None;
262 : }
263 :
264 : /************************************************************************/
265 : /* GDAL_IMD_AA2R() */
266 : /* */
267 : /* Translate AA version IMD file to R version. */
268 : /************************************************************************/
269 :
270 0 : static int GDAL_IMD_AA2R( char ***ppapszIMD )
271 :
272 : {
273 0 : char **papszIMD = *ppapszIMD;
274 :
275 : /* -------------------------------------------------------------------- */
276 : /* Verify that we have a new format file. */
277 : /* -------------------------------------------------------------------- */
278 0 : const char *pszValue = CSLFetchNameValue( papszIMD, "version" );
279 :
280 0 : if( pszValue == NULL )
281 0 : return FALSE;
282 :
283 0 : if( EQUAL(pszValue,"\"R\"") )
284 0 : return TRUE;
285 :
286 0 : if( !EQUAL(pszValue,"\"AA\"") )
287 : {
288 0 : CPLDebug( "IMD", "The file is not the expected 'version = \"AA\"' format.\nProceeding, but file may be corrupted." );
289 : }
290 :
291 : /* -------------------------------------------------------------------- */
292 : /* Fix the version line. */
293 : /* -------------------------------------------------------------------- */
294 0 : papszIMD = CSLSetNameValue( papszIMD, "version", "\"R\"" );
295 :
296 : /* -------------------------------------------------------------------- */
297 : /* remove a bunch of fields. */
298 : /* -------------------------------------------------------------------- */
299 : int iKey;
300 :
301 : static const char *apszToRemove[] = {
302 : "productCatalogId",
303 : "childCatalogId",
304 : "productType",
305 : "numberOfLooks",
306 : "effectiveBandwidth",
307 : "mode",
308 : "scanDirection",
309 : "cloudCover",
310 : "productGSD",
311 : NULL };
312 :
313 0 : for( iKey = 0; apszToRemove[iKey] != NULL; iKey++ )
314 : {
315 0 : int iTarget = CSLFindName( papszIMD, apszToRemove[iKey] );
316 0 : if( iTarget != -1 )
317 0 : papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, NULL );
318 : }
319 :
320 : /* -------------------------------------------------------------------- */
321 : /* Replace various min/mean/max with just the mean. */
322 : /* -------------------------------------------------------------------- */
323 : static const char *keylist[] = {
324 : "CollectedRowGSD",
325 : "CollectedColGSD",
326 : "SunAz",
327 : "SunEl",
328 : "SatAz",
329 : "SatEl",
330 : "InTrackViewAngle",
331 : "CrossTrackViewAngle",
332 : "OffNadirViewAngle",
333 : NULL };
334 :
335 0 : for( iKey = 0; keylist[iKey] != NULL; iKey++ )
336 : {
337 0 : CPLString osTarget;
338 : int iTarget;
339 :
340 0 : osTarget.Printf( "IMAGE_1.min%s", keylist[iKey] );
341 0 : iTarget = CSLFindName( papszIMD, osTarget );
342 0 : if( iTarget != -1 )
343 0 : papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, NULL );
344 :
345 0 : osTarget.Printf( "IMAGE_1.max%s", keylist[iKey] );
346 0 : iTarget = CSLFindName( papszIMD, osTarget );
347 0 : if( iTarget != -1 )
348 0 : papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, NULL );
349 :
350 0 : osTarget.Printf( "IMAGE_1.mean%s", keylist[iKey] );
351 0 : iTarget = CSLFindName( papszIMD, osTarget );
352 0 : if( iTarget != -1 )
353 : {
354 0 : CPLString osValue = CSLFetchNameValue( papszIMD, osTarget );
355 0 : CPLString osLine;
356 :
357 : osTarget.Printf( "IMAGE_1.%c%s",
358 0 : tolower(keylist[iKey][0]),
359 0 : keylist[iKey]+1 );
360 :
361 0 : osLine = osTarget + "=" + osValue;
362 :
363 0 : CPLFree( papszIMD[iTarget] );
364 0 : papszIMD[iTarget] = CPLStrdup(osLine);
365 : }
366 : }
367 :
368 0 : *ppapszIMD = papszIMD;
369 0 : return TRUE;
370 : }
371 :
372 : /************************************************************************/
373 : /* GDALLoadIMDFile() */
374 : /************************************************************************/
375 :
376 2850 : char ** CPL_STDCALL GDALLoadIMDFile( const char *pszFilename,
377 : char **papszSiblingFiles )
378 :
379 : {
380 : /* -------------------------------------------------------------------- */
381 : /* Try to identify the RPB file in upper or lower case. */
382 : /* -------------------------------------------------------------------- */
383 2850 : CPLString osTarget = CPLResetExtension( pszFilename, "IMD" );
384 :
385 2850 : if( papszSiblingFiles == NULL )
386 : {
387 : VSIStatBufL sStatBuf;
388 :
389 2850 : if( VSIStatL( osTarget, &sStatBuf ) != 0 )
390 : {
391 2844 : osTarget = CPLResetExtension( pszFilename, "imd" );
392 :
393 2844 : if( VSIStatL( osTarget, &sStatBuf ) != 0 )
394 2844 : return NULL;
395 : }
396 : }
397 : else
398 : {
399 : int iSibling = CSLFindString( papszSiblingFiles,
400 0 : CPLGetFilename(osTarget) );
401 0 : if( iSibling < 0 )
402 0 : return NULL;
403 :
404 0 : osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
405 0 : osTarget += papszSiblingFiles[iSibling];
406 : }
407 :
408 : /* -------------------------------------------------------------------- */
409 : /* Read file and parse. */
410 : /* -------------------------------------------------------------------- */
411 6 : CPLKeywordParser oParser;
412 :
413 6 : FILE *fp = VSIFOpenL( osTarget, "r" );
414 :
415 6 : if( fp == NULL )
416 0 : return NULL;
417 :
418 6 : if( !oParser.Ingest( fp ) )
419 : {
420 0 : VSIFCloseL( fp );
421 0 : return NULL;
422 : }
423 :
424 6 : VSIFCloseL( fp );
425 :
426 : /* -------------------------------------------------------------------- */
427 : /* Consider version changing. */
428 : /* -------------------------------------------------------------------- */
429 6 : char **papszIMD = CSLDuplicate( oParser.GetAllKeywords() );
430 6 : const char *pszVersion = CSLFetchNameValue( papszIMD, "version" );
431 :
432 6 : if( pszVersion == NULL )
433 : {
434 : /* ? */;
435 : }
436 6 : else if( EQUAL(pszVersion,"\"AA\"") )
437 : {
438 0 : GDAL_IMD_AA2R( &papszIMD );
439 : }
440 :
441 6 : return papszIMD;
442 : }
443 :
444 : /************************************************************************/
445 : /* GDALWriteIMDMultiLine() */
446 : /* */
447 : /* Write a value that is split over multiple lines. */
448 : /************************************************************************/
449 :
450 4 : static void GDALWriteIMDMultiLine( FILE *fp, const char *pszValue )
451 :
452 : {
453 : char **papszItems = CSLTokenizeStringComplex( pszValue, "(,) ",
454 4 : FALSE, FALSE );
455 4 : int nItemCount = CSLCount(papszItems);
456 : int i;
457 :
458 4 : VSIFPrintfL( fp, "(\n" );
459 :
460 40 : for( i = 0; i < nItemCount; i++ )
461 : {
462 36 : if( i == nItemCount-1 )
463 4 : VSIFPrintfL( fp, "\t%s );\n", papszItems[i] );
464 : else
465 32 : VSIFPrintfL( fp, "\t%s,\n", papszItems[i] );
466 : }
467 4 : CSLDestroy( papszItems );
468 4 : }
469 :
470 : /************************************************************************/
471 : /* GDALWriteIMDFile() */
472 : /************************************************************************/
473 :
474 2 : CPLErr CPL_STDCALL GDALWriteIMDFile( const char *pszFilename, char **papszMD )
475 :
476 : {
477 2 : CPLString osRPBFilename = CPLResetExtension( pszFilename, "IMD" );
478 :
479 :
480 : /* -------------------------------------------------------------------- */
481 : /* Read file and parse. */
482 : /* -------------------------------------------------------------------- */
483 2 : FILE *fp = VSIFOpenL( osRPBFilename, "w" );
484 :
485 2 : if( fp == NULL )
486 : {
487 : CPLError( CE_Failure, CPLE_OpenFailed,
488 : "Unable to create %s for writing.\n%s",
489 0 : osRPBFilename.c_str(), CPLGetLastErrorMsg() );
490 0 : return CE_Failure;
491 : }
492 :
493 : /* ==================================================================== */
494 : /* -------------------------------------------------------------------- */
495 : /* Loop through all values writing. */
496 : /* -------------------------------------------------------------------- */
497 : /* ==================================================================== */
498 : int iKey;
499 2 : CPLString osCurSection;
500 :
501 176 : for( iKey = 0; papszMD[iKey] != NULL; iKey++ )
502 : {
503 174 : char *pszRawKey = NULL;
504 174 : const char *pszValue = CPLParseNameValue( papszMD[iKey], &pszRawKey );
505 174 : CPLString osKeySection, osKeyItem;
506 174 : char *pszDot = strchr(pszRawKey,'.');
507 :
508 : /* -------------------------------------------------------------------- */
509 : /* Split stuff like BAND_P.ULLon into section and item. */
510 : /* -------------------------------------------------------------------- */
511 174 : if( pszDot == NULL )
512 : {
513 28 : osKeyItem = pszRawKey;
514 : }
515 : else
516 : {
517 146 : osKeyItem = pszDot+1;
518 146 : *pszDot = '\0';
519 146 : osKeySection = pszRawKey;
520 : }
521 174 : CPLFree( pszRawKey );
522 :
523 : /* -------------------------------------------------------------------- */
524 : /* Close and/or start sections as needed. */
525 : /* -------------------------------------------------------------------- */
526 174 : if( osCurSection.size() && !EQUAL(osCurSection,osKeySection) )
527 4 : VSIFPrintfL( fp, "END_GROUP = %s\n", osCurSection.c_str() );
528 :
529 174 : if( osKeySection.size() && !EQUAL(osCurSection,osKeySection) )
530 6 : VSIFPrintfL( fp, "BEGIN_GROUP = %s\n", osKeySection.c_str() );
531 :
532 174 : osCurSection = osKeySection;
533 :
534 : /* -------------------------------------------------------------------- */
535 : /* Print out simple item. */
536 : /* -------------------------------------------------------------------- */
537 174 : if( osCurSection.size() )
538 146 : VSIFPrintfL( fp, "\t%s = ", osKeyItem.c_str() );
539 : else
540 28 : VSIFPrintfL( fp, "%s = ", osKeyItem.c_str() );
541 :
542 174 : if( pszValue[0] != '(' )
543 170 : VSIFPrintfL( fp, "%s;\n", pszValue );
544 : else
545 4 : GDALWriteIMDMultiLine( fp, pszValue );
546 : }
547 :
548 : /* -------------------------------------------------------------------- */
549 : /* Close off. */
550 : /* -------------------------------------------------------------------- */
551 2 : if( osCurSection.size() )
552 2 : VSIFPrintfL( fp, "END_GROUP = %s\n", osCurSection.c_str() );
553 :
554 2 : VSIFPrintfL( fp, "END;\n" );
555 :
556 2 : VSIFCloseL( fp );
557 :
558 2 : return CE_None;
559 : }
|