1 : /******************************************************************************
2 : * $Id: gdal_rpcimdio.cpp 22057 2011-03-28 15:20:21Z 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 "gdal_priv.h"
33 : #include "cpl_string.h"
34 : #include "cplkeywordparser.h"
35 :
36 : CPL_CVSID("$Id: gdal_rpcimdio.cpp 22057 2011-03-28 15:20:21Z warmerdam $");
37 :
38 : /************************************************************************/
39 : /* GDALLoadRPBFile() */
40 : /************************************************************************/
41 :
42 : static const char *apszRPBMap[] = {
43 : "LINE_OFF", "IMAGE.lineOffset",
44 : "SAMP_OFF", "IMAGE.sampOffset",
45 : "LAT_OFF", "IMAGE.latOffset",
46 : "LONG_OFF", "IMAGE.longOffset",
47 : "HEIGHT_OFF", "IMAGE.heightOffset",
48 : "LINE_SCALE", "IMAGE.lineScale",
49 : "SAMP_SCALE", "IMAGE.sampScale",
50 : "LAT_SCALE", "IMAGE.latScale",
51 : "LONG_SCALE", "IMAGE.longScale",
52 : "HEIGHT_SCALE", "IMAGE.heightScale",
53 : "LINE_NUM_COEFF", "IMAGE.lineNumCoef",
54 : "LINE_DEN_COEFF", "IMAGE.lineDenCoef",
55 : "SAMP_NUM_COEFF", "IMAGE.sampNumCoef",
56 : "SAMP_DEN_COEFF", "IMAGE.sampDenCoef",
57 : NULL, NULL };
58 :
59 8 : char **CPL_STDCALL GDALLoadRPBFile( const char *pszFilename,
60 : char **papszSiblingFiles )
61 :
62 : {
63 : /* -------------------------------------------------------------------- */
64 : /* Try to identify the RPB file in upper or lower case. */
65 : /* -------------------------------------------------------------------- */
66 : CPLString osTarget = GDALFindAssociatedFile( pszFilename, "RPB",
67 8 : papszSiblingFiles, 0 );
68 :
69 8 : if( osTarget == "" )
70 0 : return NULL;
71 :
72 : /* -------------------------------------------------------------------- */
73 : /* Read file and parse. */
74 : /* -------------------------------------------------------------------- */
75 8 : CPLKeywordParser oParser;
76 :
77 8 : VSILFILE *fp = VSIFOpenL( osTarget, "r" );
78 :
79 8 : if( fp == NULL )
80 0 : return NULL;
81 :
82 8 : if( !oParser.Ingest( fp ) )
83 : {
84 0 : VSIFCloseL( fp );
85 0 : return NULL;
86 : }
87 :
88 8 : VSIFCloseL( fp );
89 :
90 : /* -------------------------------------------------------------------- */
91 : /* Extract RPC information, in a GDAL "standard" metadata format. */
92 : /* -------------------------------------------------------------------- */
93 : int i;
94 8 : char **papszMD = NULL;
95 8 : for( i = 0; apszRPBMap[i] != NULL; i += 2 )
96 : {
97 112 : const char *pszRPBVal = oParser.GetKeyword( apszRPBMap[i+1] );
98 112 : CPLString osAdjVal;
99 :
100 112 : if( pszRPBVal == NULL )
101 : {
102 : CPLError( CE_Failure, CPLE_AppDefined,
103 : "%s file found, but missing %s field (and possibly others).",
104 0 : osTarget.c_str(), apszRPBMap[i+1] );
105 0 : CSLDestroy( papszMD );
106 0 : return NULL;
107 : }
108 :
109 112 : if( strchr(pszRPBVal,',') == NULL )
110 80 : osAdjVal = pszRPBVal;
111 : else
112 : {
113 : // strip out commas and turn newlines into spaces.
114 : int j;
115 :
116 9024 : for( j = 0; pszRPBVal[j] != '\0'; j++ )
117 : {
118 8992 : switch( pszRPBVal[j] )
119 : {
120 : case ',':
121 : case '\n':
122 : case '\r':
123 608 : osAdjVal += ' ';
124 608 : break;
125 :
126 : case '(':
127 : case ')':
128 64 : break;
129 :
130 : default:
131 8320 : osAdjVal += pszRPBVal[j];
132 : }
133 : }
134 : }
135 :
136 112 : papszMD = CSLSetNameValue( papszMD, apszRPBMap[i], osAdjVal );
137 : }
138 :
139 8 : return papszMD;
140 : }
141 :
142 : /************************************************************************/
143 : /* GDALLoadRPCFile() */
144 : /************************************************************************/
145 :
146 : /* Load a GeoEye _rpc.txt file. See ticket http://trac.osgeo.org/gdal/ticket/3639 */
147 :
148 1 : char **CPL_STDCALL GDALLoadRPCFile( const char *pszFilename,
149 : char **papszSiblingFiles )
150 :
151 : {
152 : /* -------------------------------------------------------------------- */
153 : /* Try to identify the RPC file in upper or lower case. */
154 : /* -------------------------------------------------------------------- */
155 1 : CPLString osTarget;
156 :
157 : /* Is this already a _RPC.TXT file ? */
158 2 : if (strlen(pszFilename) > 8 && EQUAL(pszFilename + strlen(pszFilename) - 8, "_RPC.TXT"))
159 1 : osTarget = pszFilename;
160 : else
161 : {
162 0 : CPLString osSrcPath = pszFilename;
163 0 : CPLString soPt(".");
164 0 : size_t found = osSrcPath.rfind(soPt);
165 0 : if (found == CPLString::npos)
166 0 : return NULL;
167 0 : osSrcPath.replace (found, osSrcPath.size() - found, "_rpc.txt");
168 0 : CPLString osTarget = osSrcPath;
169 :
170 0 : if( papszSiblingFiles == NULL )
171 : {
172 : VSIStatBufL sStatBuf;
173 :
174 0 : if( VSIStatL( osTarget, &sStatBuf ) != 0 )
175 : {
176 0 : osSrcPath = pszFilename;
177 0 : osSrcPath.replace (found, osSrcPath.size() - found, "_RPC.TXT");
178 0 : osTarget = osSrcPath;
179 :
180 0 : if( VSIStatL( osTarget, &sStatBuf ) != 0 )
181 : {
182 0 : osSrcPath = pszFilename;
183 0 : osSrcPath.replace (found, osSrcPath.size() - found, "_rpc.TXT");
184 0 : osTarget = osSrcPath;
185 :
186 0 : if( VSIStatL( osTarget, &sStatBuf ) != 0 )
187 : {
188 0 : return NULL;
189 : }
190 : }
191 : }
192 : }
193 : else
194 : {
195 : int iSibling = CSLFindString( papszSiblingFiles,
196 0 : CPLGetFilename(osTarget) );
197 0 : if( iSibling < 0 )
198 0 : return NULL;
199 :
200 0 : osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
201 0 : osTarget += papszSiblingFiles[iSibling];
202 0 : }
203 : }
204 :
205 : /* -------------------------------------------------------------------- */
206 : /* Read file and parse. */
207 : /* -------------------------------------------------------------------- */
208 1 : char **papszLines = CSLLoad2( osTarget, 100, 100, NULL );
209 1 : if(!papszLines)
210 0 : return NULL;
211 :
212 1 : char **papszMD = NULL;
213 :
214 : /* From LINE_OFF to HEIGHT_SCALE */
215 11 : for(size_t i = 0; i < 19; i += 2 )
216 : {
217 10 : const char *pszRPBVal = CSLFetchNameValue(papszLines, apszRPBMap[i] );
218 10 : if( pszRPBVal == NULL )
219 : {
220 : CPLError( CE_Failure, CPLE_AppDefined,
221 : "%s file found, but missing %s field (and possibly others).",
222 0 : osTarget.c_str(), apszRPBMap[i]);
223 0 : CSLDestroy( papszMD );
224 0 : CSLDestroy( papszLines );
225 0 : return NULL;
226 : }
227 : else
228 : {
229 10 : papszMD = CSLSetNameValue( papszMD, apszRPBMap[i], pszRPBVal );
230 : }
231 : }
232 :
233 : /* For LINE_NUM_COEFF, LINE_DEN_COEFF, SAMP_NUM_COEFF, SAMP_DEN_COEFF */
234 : /* parameters that have 20 values each */
235 1 : for(size_t i = 20; apszRPBMap[i] != NULL; i += 2 )
236 : {
237 4 : CPLString soVal;
238 4 : for(int j = 1; j <= 20; j++)
239 : {
240 80 : CPLString soRPBMapItem;
241 80 : soRPBMapItem.Printf("%s_%d", apszRPBMap[i], j);
242 80 : const char *pszRPBVal = CSLFetchNameValue(papszLines, soRPBMapItem.c_str() );
243 80 : if( pszRPBVal == NULL )
244 : {
245 : CPLError( CE_Failure, CPLE_AppDefined,
246 : "%s file found, but missing %s field (and possibly others).",
247 0 : osTarget.c_str(), soRPBMapItem.c_str() );
248 0 : CSLDestroy( papszMD );
249 0 : CSLDestroy( papszLines );
250 0 : return NULL;
251 : }
252 : else
253 : {
254 80 : soVal += pszRPBVal;
255 80 : soVal += " ";
256 : }
257 : }
258 4 : papszMD = CSLSetNameValue( papszMD, apszRPBMap[i], soVal.c_str() );
259 : }
260 :
261 1 : CSLDestroy( papszLines );
262 1 : return papszMD;
263 : }
264 :
265 : /************************************************************************/
266 : /* GDALWriteRPBFile() */
267 : /************************************************************************/
268 :
269 2 : CPLErr CPL_STDCALL GDALWriteRPBFile( const char *pszFilename, char **papszMD )
270 :
271 : {
272 2 : CPLString osRPBFilename = CPLResetExtension( pszFilename, "RPB" );
273 :
274 :
275 : /* -------------------------------------------------------------------- */
276 : /* Read file and parse. */
277 : /* -------------------------------------------------------------------- */
278 2 : VSILFILE *fp = VSIFOpenL( osRPBFilename, "w" );
279 :
280 2 : if( fp == NULL )
281 : {
282 : CPLError( CE_Failure, CPLE_OpenFailed,
283 : "Unable to create %s for writing.\n%s",
284 0 : osRPBFilename.c_str(), CPLGetLastErrorMsg() );
285 0 : return CE_Failure;
286 : }
287 :
288 : /* -------------------------------------------------------------------- */
289 : /* Write the prefix information. */
290 : /* -------------------------------------------------------------------- */
291 2 : VSIFPrintfL( fp, "%s", "satId = \"QB02\";\n" );
292 2 : VSIFPrintfL( fp, "%s", "bandId = \"P\";\n" );
293 2 : VSIFPrintfL( fp, "%s", "SpecId = \"RPC00B\";\n" );
294 2 : VSIFPrintfL( fp, "%s", "BEGIN_GROUP = IMAGE\n" );
295 2 : VSIFPrintfL( fp, "%s", "\terrBias = 0.0;\n" );
296 2 : VSIFPrintfL( fp, "%s", "\terrRand = 0.0;\n" );
297 :
298 : /* -------------------------------------------------------------------- */
299 : /* Write RPC values from our RPC metadata. */
300 : /* -------------------------------------------------------------------- */
301 : int i;
302 :
303 30 : for( i = 0; apszRPBMap[i] != NULL; i += 2 )
304 : {
305 28 : const char *pszRPBVal = CSLFetchNameValue( papszMD, apszRPBMap[i] );
306 : const char *pszRPBTag;
307 :
308 28 : if( pszRPBVal == NULL )
309 : {
310 : CPLError( CE_Failure, CPLE_AppDefined,
311 : "%s field missing in metadata, %s file not written.",
312 0 : apszRPBMap[i], osRPBFilename.c_str() );
313 0 : VSIFCloseL( fp );
314 0 : VSIUnlink( osRPBFilename );
315 0 : return CE_Failure;
316 : }
317 :
318 28 : pszRPBTag = apszRPBMap[i+1];
319 28 : if( EQUALN(pszRPBTag,"IMAGE.",6) )
320 28 : pszRPBTag += 6;
321 :
322 28 : if( strstr(apszRPBMap[i], "COEF" ) == NULL )
323 : {
324 20 : VSIFPrintfL( fp, "\t%s = %s;\n", pszRPBTag, pszRPBVal );
325 : }
326 : else
327 : {
328 : // Reformat in brackets with commas over multiple lines.
329 :
330 8 : VSIFPrintfL( fp, "\t%s = (\n", pszRPBTag );
331 :
332 : char **papszItems = CSLTokenizeStringComplex( pszRPBVal, " ,",
333 8 : FALSE, FALSE );
334 :
335 8 : if( CSLCount(papszItems) != 20 )
336 : {
337 : CPLError( CE_Failure, CPLE_AppDefined,
338 : "%s field is corrupt (not 20 values), %s file not written.\n%s = %s",
339 : apszRPBMap[i], osRPBFilename.c_str(),
340 0 : apszRPBMap[i], pszRPBVal );
341 0 : VSIFCloseL( fp );
342 0 : VSIUnlink( osRPBFilename );
343 0 : return CE_Failure;
344 : }
345 :
346 : int j;
347 :
348 168 : for( j = 0; j < 20; j++ )
349 : {
350 160 : if( j < 19 )
351 152 : VSIFPrintfL( fp, "\t\t\t%s,\n", papszItems[j] );
352 : else
353 8 : VSIFPrintfL( fp, "\t\t\t%s);\n", papszItems[j] );
354 : }
355 8 : CSLDestroy( papszItems );
356 : }
357 : }
358 :
359 : /* -------------------------------------------------------------------- */
360 : /* Write end part */
361 : /* -------------------------------------------------------------------- */
362 2 : VSIFPrintfL( fp, "%s", "END_GROUP = IMAGE\n" );
363 2 : VSIFPrintfL( fp, "END;\n" );
364 2 : VSIFCloseL( fp );
365 :
366 2 : return CE_None;
367 : }
368 :
369 : /************************************************************************/
370 : /* GDAL_IMD_AA2R() */
371 : /* */
372 : /* Translate AA version IMD file to R version. */
373 : /************************************************************************/
374 :
375 0 : static int GDAL_IMD_AA2R( char ***ppapszIMD )
376 :
377 : {
378 0 : char **papszIMD = *ppapszIMD;
379 :
380 : /* -------------------------------------------------------------------- */
381 : /* Verify that we have a new format file. */
382 : /* -------------------------------------------------------------------- */
383 0 : const char *pszValue = CSLFetchNameValue( papszIMD, "version" );
384 :
385 0 : if( pszValue == NULL )
386 0 : return FALSE;
387 :
388 0 : if( EQUAL(pszValue,"\"R\"") )
389 0 : return TRUE;
390 :
391 0 : if( !EQUAL(pszValue,"\"AA\"") )
392 : {
393 0 : CPLDebug( "IMD", "The file is not the expected 'version = \"AA\"' format.\nProceeding, but file may be corrupted." );
394 : }
395 :
396 : /* -------------------------------------------------------------------- */
397 : /* Fix the version line. */
398 : /* -------------------------------------------------------------------- */
399 0 : papszIMD = CSLSetNameValue( papszIMD, "version", "\"R\"" );
400 :
401 : /* -------------------------------------------------------------------- */
402 : /* remove a bunch of fields. */
403 : /* -------------------------------------------------------------------- */
404 : int iKey;
405 :
406 : static const char *apszToRemove[] = {
407 : "productCatalogId",
408 : "childCatalogId",
409 : "productType",
410 : "numberOfLooks",
411 : "effectiveBandwidth",
412 : "mode",
413 : "scanDirection",
414 : "cloudCover",
415 : "productGSD",
416 : NULL };
417 :
418 0 : for( iKey = 0; apszToRemove[iKey] != NULL; iKey++ )
419 : {
420 0 : int iTarget = CSLFindName( papszIMD, apszToRemove[iKey] );
421 0 : if( iTarget != -1 )
422 0 : papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, NULL );
423 : }
424 :
425 : /* -------------------------------------------------------------------- */
426 : /* Replace various min/mean/max with just the mean. */
427 : /* -------------------------------------------------------------------- */
428 : static const char *keylist[] = {
429 : "CollectedRowGSD",
430 : "CollectedColGSD",
431 : "SunAz",
432 : "SunEl",
433 : "SatAz",
434 : "SatEl",
435 : "InTrackViewAngle",
436 : "CrossTrackViewAngle",
437 : "OffNadirViewAngle",
438 : NULL };
439 :
440 0 : for( iKey = 0; keylist[iKey] != NULL; iKey++ )
441 : {
442 0 : CPLString osTarget;
443 : int iTarget;
444 :
445 0 : osTarget.Printf( "IMAGE_1.min%s", keylist[iKey] );
446 0 : iTarget = CSLFindName( papszIMD, osTarget );
447 0 : if( iTarget != -1 )
448 0 : papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, NULL );
449 :
450 0 : osTarget.Printf( "IMAGE_1.max%s", keylist[iKey] );
451 0 : iTarget = CSLFindName( papszIMD, osTarget );
452 0 : if( iTarget != -1 )
453 0 : papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, NULL );
454 :
455 0 : osTarget.Printf( "IMAGE_1.mean%s", keylist[iKey] );
456 0 : iTarget = CSLFindName( papszIMD, osTarget );
457 0 : if( iTarget != -1 )
458 : {
459 0 : CPLString osValue = CSLFetchNameValue( papszIMD, osTarget );
460 0 : CPLString osLine;
461 :
462 : osTarget.Printf( "IMAGE_1.%c%s",
463 0 : tolower(keylist[iKey][0]),
464 0 : keylist[iKey]+1 );
465 :
466 0 : osLine = osTarget + "=" + osValue;
467 :
468 0 : CPLFree( papszIMD[iTarget] );
469 0 : papszIMD[iTarget] = CPLStrdup(osLine);
470 : }
471 : }
472 :
473 0 : *ppapszIMD = papszIMD;
474 0 : return TRUE;
475 : }
476 :
477 : /************************************************************************/
478 : /* GDALLoadIMDFile() */
479 : /************************************************************************/
480 :
481 10 : char ** CPL_STDCALL GDALLoadIMDFile( const char *pszFilename,
482 : char **papszSiblingFiles )
483 :
484 : {
485 : /* -------------------------------------------------------------------- */
486 : /* Try to identify the IMD file in upper or lower case. */
487 : /* -------------------------------------------------------------------- */
488 : CPLString osTarget = GDALFindAssociatedFile( pszFilename, "IMD",
489 10 : papszSiblingFiles, 0 );
490 :
491 10 : if( osTarget == "" )
492 0 : return NULL;
493 :
494 : /* -------------------------------------------------------------------- */
495 : /* Read file and parse. */
496 : /* -------------------------------------------------------------------- */
497 10 : CPLKeywordParser oParser;
498 :
499 10 : VSILFILE *fp = VSIFOpenL( osTarget, "r" );
500 :
501 10 : if( fp == NULL )
502 0 : return NULL;
503 :
504 10 : if( !oParser.Ingest( fp ) )
505 : {
506 0 : VSIFCloseL( fp );
507 0 : return NULL;
508 : }
509 :
510 10 : VSIFCloseL( fp );
511 :
512 : /* -------------------------------------------------------------------- */
513 : /* Consider version changing. */
514 : /* -------------------------------------------------------------------- */
515 10 : char **papszIMD = CSLDuplicate( oParser.GetAllKeywords() );
516 10 : const char *pszVersion = CSLFetchNameValue( papszIMD, "version" );
517 :
518 10 : if( pszVersion == NULL )
519 : {
520 : /* ? */;
521 : }
522 8 : else if( EQUAL(pszVersion,"\"AA\"") )
523 : {
524 0 : GDAL_IMD_AA2R( &papszIMD );
525 : }
526 :
527 10 : return papszIMD;
528 : }
529 :
530 : /************************************************************************/
531 : /* GDALWriteIMDMultiLine() */
532 : /* */
533 : /* Write a value that is split over multiple lines. */
534 : /************************************************************************/
535 :
536 8 : static void GDALWriteIMDMultiLine( VSILFILE *fp, const char *pszValue )
537 :
538 : {
539 : char **papszItems = CSLTokenizeStringComplex( pszValue, "(,) ",
540 8 : FALSE, FALSE );
541 8 : int nItemCount = CSLCount(papszItems);
542 : int i;
543 :
544 8 : VSIFPrintfL( fp, "(\n" );
545 :
546 80 : for( i = 0; i < nItemCount; i++ )
547 : {
548 72 : if( i == nItemCount-1 )
549 8 : VSIFPrintfL( fp, "\t%s );\n", papszItems[i] );
550 : else
551 64 : VSIFPrintfL( fp, "\t%s,\n", papszItems[i] );
552 : }
553 8 : CSLDestroy( papszItems );
554 8 : }
555 :
556 : /************************************************************************/
557 : /* GDALWriteIMDFile() */
558 : /************************************************************************/
559 :
560 4 : CPLErr CPL_STDCALL GDALWriteIMDFile( const char *pszFilename, char **papszMD )
561 :
562 : {
563 4 : CPLString osRPBFilename = CPLResetExtension( pszFilename, "IMD" );
564 :
565 : /* -------------------------------------------------------------------- */
566 : /* Read file and parse. */
567 : /* -------------------------------------------------------------------- */
568 4 : VSILFILE *fp = VSIFOpenL( osRPBFilename, "w" );
569 :
570 4 : if( fp == NULL )
571 : {
572 : CPLError( CE_Failure, CPLE_OpenFailed,
573 : "Unable to create %s for writing.\n%s",
574 0 : osRPBFilename.c_str(), CPLGetLastErrorMsg() );
575 0 : return CE_Failure;
576 : }
577 :
578 : /* ==================================================================== */
579 : /* -------------------------------------------------------------------- */
580 : /* Loop through all values writing. */
581 : /* -------------------------------------------------------------------- */
582 : /* ==================================================================== */
583 : int iKey;
584 4 : CPLString osCurSection;
585 :
586 352 : for( iKey = 0; papszMD[iKey] != NULL; iKey++ )
587 : {
588 348 : char *pszRawKey = NULL;
589 348 : const char *pszValue = CPLParseNameValue( papszMD[iKey], &pszRawKey );
590 348 : CPLString osKeySection, osKeyItem;
591 348 : char *pszDot = strchr(pszRawKey,'.');
592 :
593 : /* -------------------------------------------------------------------- */
594 : /* Split stuff like BAND_P.ULLon into section and item. */
595 : /* -------------------------------------------------------------------- */
596 348 : if( pszDot == NULL )
597 : {
598 56 : osKeyItem = pszRawKey;
599 : }
600 : else
601 : {
602 292 : osKeyItem = pszDot+1;
603 292 : *pszDot = '\0';
604 292 : osKeySection = pszRawKey;
605 : }
606 348 : CPLFree( pszRawKey );
607 :
608 : /* -------------------------------------------------------------------- */
609 : /* Close and/or start sections as needed. */
610 : /* -------------------------------------------------------------------- */
611 348 : if( osCurSection.size() && !EQUAL(osCurSection,osKeySection) )
612 12 : VSIFPrintfL( fp, "END_GROUP = %s\n", osCurSection.c_str() );
613 :
614 348 : if( osKeySection.size() && !EQUAL(osCurSection,osKeySection) )
615 12 : VSIFPrintfL( fp, "BEGIN_GROUP = %s\n", osKeySection.c_str() );
616 :
617 348 : osCurSection = osKeySection;
618 :
619 : /* -------------------------------------------------------------------- */
620 : /* Print out simple item. */
621 : /* -------------------------------------------------------------------- */
622 348 : if( osCurSection.size() )
623 292 : VSIFPrintfL( fp, "\t%s = ", osKeyItem.c_str() );
624 : else
625 56 : VSIFPrintfL( fp, "%s = ", osKeyItem.c_str() );
626 :
627 348 : if( pszValue[0] != '(' )
628 340 : VSIFPrintfL( fp, "%s;\n", pszValue );
629 : else
630 8 : GDALWriteIMDMultiLine( fp, pszValue );
631 : }
632 :
633 : /* -------------------------------------------------------------------- */
634 : /* Close off. */
635 : /* -------------------------------------------------------------------- */
636 4 : if( osCurSection.size() )
637 0 : VSIFPrintfL( fp, "END_GROUP = %s\n", osCurSection.c_str() );
638 :
639 4 : VSIFPrintfL( fp, "END;\n" );
640 :
641 4 : VSIFCloseL( fp );
642 :
643 4 : return CE_None;
644 : }
|