1 /******************************************************************************
2 *
3 * Project: GDAL Core
4 * Purpose: Read metadata (mainly the remote sensing imagery) from files of
5 * different providers like DigitalGlobe, GeoEye etc.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 * Author: Dmitry Baryshnikov, polimax@mail.ru
8 *
9 ******************************************************************************
10 * Copyright (c) HER MAJESTY THE QUEEN IN RIGHT OF CANADA (2008)
11 * as represented by the Canadian Nuclear Safety Commission
12 * Copyright (c) 2014-2015, NextGIS info@nextgis.ru
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a
15 * copy of this software and associated documentation files (the "Software"),
16 * to deal in the Software without restriction, including without limitation
17 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 * and/or sell copies of the Software, and to permit persons to whom the
19 * Software is furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included
22 * in all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 * DEALINGS IN THE SOFTWARE.
31 ****************************************************************************/
32
33 #include "cpl_port.h"
34 #include "gdal_mdreader.h"
35
36 #include <cctype>
37 #include <cstddef>
38 #include <cstdio>
39 #include <cstring>
40 #include <ctime>
41 #include <string>
42
43 #include "cpl_conv.h"
44 #include "cpl_error.h"
45 #include "cpl_minixml.h"
46 #include "cpl_string.h"
47 #include "cpl_vsi.h"
48 #include "cplkeywordparser.h"
49 #include "gdal_priv.h"
50
51 //readers
52 #include "mdreader/reader_alos.h"
53 #include "mdreader/reader_digital_globe.h"
54 #include "mdreader/reader_eros.h"
55 #include "mdreader/reader_geo_eye.h"
56 #include "mdreader/reader_kompsat.h"
57 #include "mdreader/reader_landsat.h"
58 #include "mdreader/reader_orb_view.h"
59 #include "mdreader/reader_pleiades.h"
60 #include "mdreader/reader_rapid_eye.h"
61 #include "mdreader/reader_rdk1.h"
62 #include "mdreader/reader_spot.h"
63
64 CPL_CVSID("$Id: gdal_mdreader.cpp 5b42715435d8f16fc6d45103cf40879a8c319f9d 2021-07-01 17:21:54 +0200 Even Rouault $")
65
66 /**
67 * The RPC parameters names
68 */
69
70 static const char * const apszRPCTXTSingleValItems[] =
71 {
72 RPC_ERR_BIAS,
73 RPC_ERR_RAND,
74 RPC_LINE_OFF,
75 RPC_SAMP_OFF,
76 RPC_LAT_OFF,
77 RPC_LONG_OFF,
78 RPC_HEIGHT_OFF,
79 RPC_LINE_SCALE,
80 RPC_SAMP_SCALE,
81 RPC_LAT_SCALE,
82 RPC_LONG_SCALE,
83 RPC_HEIGHT_SCALE,
84 nullptr
85 };
86
87 static const char * const apszRPCTXT20ValItems[] =
88 {
89 RPC_LINE_NUM_COEFF,
90 RPC_LINE_DEN_COEFF,
91 RPC_SAMP_NUM_COEFF,
92 RPC_SAMP_DEN_COEFF,
93 nullptr
94 };
95
96 /**
97 * GDALMDReaderManager()
98 */
99 GDALMDReaderManager::GDALMDReaderManager() = default;
100
101 /**
102 * ~GDALMDReaderManager()
103 */
~GDALMDReaderManager()104 GDALMDReaderManager::~GDALMDReaderManager()
105 {
106 if( nullptr != m_pReader )
107 {
108 delete m_pReader;
109 }
110 }
111
112 /**
113 * GetReader()
114 */
115
116 #define INIT_READER(reader) \
117 GDALMDReaderBase* pReaderBase = new reader(pszPath, papszSiblingFiles); \
118 if(pReaderBase->HasRequiredFiles()) { m_pReader = pReaderBase; \
119 return m_pReader; } \
120 delete pReaderBase
121
GetReader(const char * pszPath,char ** papszSiblingFiles,GUInt32 nType)122 GDALMDReaderBase* GDALMDReaderManager::GetReader(const char *pszPath,
123 char **papszSiblingFiles,
124 GUInt32 nType)
125 {
126 if( !GDALCanFileAcceptSidecarFile(pszPath) )
127 return nullptr;
128
129 if(nType & MDR_DG)
130 {
131 INIT_READER(GDALMDReaderDigitalGlobe);
132 }
133
134 // required filename.tif filename.pvl filename_rpc.txt
135 if(nType & MDR_OV)
136 {
137 INIT_READER(GDALMDReaderOrbView);
138 }
139
140 if(nType & MDR_GE)
141 {
142 INIT_READER(GDALMDReaderGeoEye);
143 }
144
145 if(nType & MDR_LS)
146 {
147 INIT_READER(GDALMDReaderLandsat);
148 }
149
150 if(nType & MDR_PLEIADES)
151 {
152 INIT_READER(GDALMDReaderPleiades);
153 }
154
155 if(nType & MDR_SPOT)
156 {
157 INIT_READER(GDALMDReaderSpot);
158 }
159
160 if(nType & MDR_RDK1)
161 {
162 INIT_READER(GDALMDReaderResursDK1);
163 }
164
165 if(nType & MDR_RE)
166 {
167 INIT_READER(GDALMDReaderRapidEye);
168 }
169
170 // required filename.tif filename.rpc filename.txt
171 if(nType & MDR_KOMPSAT)
172 {
173 INIT_READER(GDALMDReaderKompsat);
174 }
175
176 if(nType & MDR_EROS)
177 {
178 INIT_READER(GDALMDReaderEROS);
179 }
180
181 if(nType & MDR_ALOS)
182 {
183 INIT_READER(GDALMDReaderALOS);
184 }
185
186 return nullptr;
187 }
188
189 /**
190 * GDALMDReaderBase()
191 */
GDALMDReaderBase(const char *,char **)192 GDALMDReaderBase::GDALMDReaderBase( const char * /* pszPath */,
193 char ** /* papszSiblingFiles */ )
194 {}
195
196 /**
197 * ~GDALMDReaderBase()
198 */
~GDALMDReaderBase()199 GDALMDReaderBase::~GDALMDReaderBase()
200 {
201 CSLDestroy(m_papszIMDMD);
202 CSLDestroy(m_papszRPCMD);
203 CSLDestroy(m_papszIMAGERYMD);
204 CSLDestroy(m_papszDEFAULTMD);
205 }
206
207 /**
208 * GetMetadataItem()
209 */
GetMetadataDomain(const char * pszDomain)210 char ** GDALMDReaderBase::GetMetadataDomain(const char *pszDomain)
211 {
212 LoadMetadata();
213 if(EQUAL(pszDomain, MD_DOMAIN_DEFAULT))
214 return m_papszDEFAULTMD;
215 else if(EQUAL(pszDomain, MD_DOMAIN_IMD))
216 return m_papszIMDMD;
217 else if(EQUAL(pszDomain, MD_DOMAIN_RPC))
218 return m_papszRPCMD;
219 else if(EQUAL(pszDomain, MD_DOMAIN_IMAGERY))
220 return m_papszIMAGERYMD;
221 return nullptr;
222 }
223
224 /**
225 * LoadMetadata()
226 */
LoadMetadata()227 void GDALMDReaderBase::LoadMetadata()
228 {
229 if(m_bIsMetadataLoad)
230 return;
231 m_bIsMetadataLoad = true;
232 }
233
234 /**
235 * GetAcqisitionTimeFromString()
236 */
GetAcquisitionTimeFromString(const char * pszDateTime)237 time_t GDALMDReaderBase::GetAcquisitionTimeFromString(
238 const char* pszDateTime)
239 {
240 if(nullptr == pszDateTime)
241 return 0;
242
243 int iYear = 0;
244 int iMonth = 0;
245 int iDay = 0;
246 int iHours = 0;
247 int iMin = 0;
248 int iSec = 0;
249
250 const int r = sscanf ( pszDateTime, "%d-%d-%dT%d:%d:%d.%*dZ",
251 &iYear, &iMonth, &iDay, &iHours, &iMin, &iSec);
252
253 if (r != 6)
254 return 0;
255
256 struct tm tmDateTime;
257 tmDateTime.tm_sec = iSec;
258 tmDateTime.tm_min = iMin;
259 tmDateTime.tm_hour = iHours;
260 tmDateTime.tm_mday = iDay;
261 tmDateTime.tm_mon = iMonth - 1;
262 tmDateTime.tm_year = iYear - 1900;
263 tmDateTime.tm_isdst = -1;
264
265 return mktime(&tmDateTime);
266 }
267
268 /**
269 * FillMetadata()
270 */
271
272 #define SETMETADATA(mdmd, md, domain) if(nullptr != md) { \
273 char** papszCurrentMd = CSLDuplicate(mdmd->GetMetadata(domain)); \
274 papszCurrentMd = CSLMerge(papszCurrentMd, md); \
275 mdmd->SetMetadata(papszCurrentMd, domain); \
276 CSLDestroy(papszCurrentMd); }
277
FillMetadata(GDALMultiDomainMetadata * poMDMD)278 bool GDALMDReaderBase::FillMetadata(GDALMultiDomainMetadata* poMDMD)
279 {
280 if(nullptr == poMDMD)
281 return false;
282
283 LoadMetadata();
284
285 SETMETADATA(poMDMD, m_papszIMDMD, MD_DOMAIN_IMD );
286 SETMETADATA(poMDMD, m_papszRPCMD, MD_DOMAIN_RPC );
287 SETMETADATA(poMDMD, m_papszIMAGERYMD, MD_DOMAIN_IMAGERY );
288 SETMETADATA(poMDMD, m_papszDEFAULTMD, MD_DOMAIN_DEFAULT );
289
290 return true;
291 }
292
293 /**
294 * AddXMLNameValueToList()
295 */
AddXMLNameValueToList(char ** papszList,const char * pszName,const char * pszValue)296 char** GDALMDReaderBase::AddXMLNameValueToList(char** papszList,
297 const char *pszName,
298 const char *pszValue)
299 {
300 return CSLAddNameValue(papszList, pszName, pszValue);
301 }
302
303 /**
304 * CPLReadXMLToList()
305 */
ReadXMLToList(CPLXMLNode * psNode,char ** papszList,const char * pszName)306 char** GDALMDReaderBase::ReadXMLToList(CPLXMLNode* psNode, char** papszList,
307 const char* pszName)
308 {
309 if(nullptr == psNode)
310 return papszList;
311
312 if (psNode->eType == CXT_Text)
313 {
314 papszList = AddXMLNameValueToList(papszList, pszName, psNode->pszValue);
315 }
316
317 if (psNode->eType == CXT_Element)
318 {
319
320 int nAddIndex = 0;
321 bool bReset = false;
322 for(CPLXMLNode* psChildNode = psNode->psChild; nullptr != psChildNode;
323 psChildNode = psChildNode->psNext)
324 {
325 if (psChildNode->eType == CXT_Element)
326 {
327 // check name duplicates
328 if(nullptr != psChildNode->psNext)
329 {
330 if(bReset)
331 {
332 bReset = false;
333 nAddIndex = 0;
334 }
335
336 if(EQUAL(psChildNode->pszValue, psChildNode->psNext->pszValue))
337 {
338 nAddIndex++;
339 }
340 else
341 { // the name changed
342
343 if(nAddIndex > 0)
344 {
345 bReset = true;
346 nAddIndex++;
347 }
348 }
349 }
350 else
351 {
352 if(bReset)
353 {
354 bReset = false;
355 nAddIndex = 0;
356 }
357
358 if(nAddIndex > 0)
359 {
360 nAddIndex++;
361 }
362 }
363
364 char szName[512];
365 if(nAddIndex > 0)
366 {
367 CPLsnprintf( szName, 511, "%s_%d", psChildNode->pszValue,
368 nAddIndex);
369 }
370 else
371 {
372 CPLStrlcpy(szName, psChildNode->pszValue, 511);
373 }
374
375 char szNameNew[512];
376 if(CPLStrnlen( pszName, 511 ) > 0) //if no prefix just set name to node name
377 {
378 CPLsnprintf( szNameNew, 511, "%s.%s", pszName, szName );
379 }
380 else
381 {
382 CPLsnprintf( szNameNew, 511, "%s.%s", psNode->pszValue, szName );
383 }
384
385 papszList = ReadXMLToList(psChildNode, papszList, szNameNew);
386 }
387 else if( psChildNode->eType == CXT_Attribute )
388 {
389 papszList = AddXMLNameValueToList(papszList,
390 CPLSPrintf("%s.%s", pszName, psChildNode->pszValue),
391 psChildNode->psChild->pszValue);
392 }
393 else
394 {
395 // Text nodes should always have name
396 if(EQUAL(pszName, ""))
397 {
398 papszList = ReadXMLToList(psChildNode, papszList, psNode->pszValue);
399 }
400 else
401 {
402 papszList = ReadXMLToList(psChildNode, papszList, pszName);
403 }
404 }
405 }
406 }
407
408 // proceed next only on top level
409
410 if(nullptr != psNode->psNext && EQUAL(pszName, ""))
411 {
412 papszList = ReadXMLToList(psNode->psNext, papszList, pszName);
413 }
414
415 return papszList;
416 }
417
418 //------------------------------------------------------------------------------
419 // Miscellaneous functions
420 //------------------------------------------------------------------------------
421
422 /**
423 * GDALCheckFileHeader()
424 */
GDALCheckFileHeader(const CPLString & soFilePath,const char * pszTestString,int nBufferSize)425 bool GDALCheckFileHeader(const CPLString& soFilePath,
426 const char * pszTestString, int nBufferSize)
427 {
428 VSILFILE* fpL = VSIFOpenL( soFilePath, "r" );
429 if( fpL == nullptr )
430 return false;
431 char *pBuffer = new char[nBufferSize + 1];
432 const int nReadBytes = static_cast<int>(
433 VSIFReadL( pBuffer, 1, nBufferSize, fpL ) );
434 CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
435 if(nReadBytes == 0)
436 {
437 delete [] pBuffer;
438 return false;
439 }
440 pBuffer[nReadBytes] = '\0';
441
442 const bool bResult = strstr(pBuffer, pszTestString) != nullptr;
443 delete [] pBuffer;
444
445 return bResult;
446 }
447
448 /**
449 * CPLStrip()
450 */
CPLStrip(const CPLString & sString,const char cChar)451 CPLString CPLStrip(const CPLString& sString, const char cChar)
452 {
453 if(sString.empty())
454 return sString;
455
456 size_t dCopyFrom = 0;
457 size_t dCopyCount = sString.size();
458
459 if (sString[0] == cChar)
460 {
461 dCopyFrom++;
462 dCopyCount--;
463 }
464
465 if (sString.back() == cChar)
466 dCopyCount--;
467
468 if(dCopyCount == 0)
469 return CPLString();
470
471 return sString.substr(dCopyFrom, dCopyCount);
472 }
473
474 /**
475 * CPLStripQuotes()
476 */
CPLStripQuotes(const CPLString & sString)477 CPLString CPLStripQuotes(const CPLString& sString)
478 {
479 return CPLStrip( CPLStrip(sString, '"'), '\'');
480 }
481
482 /************************************************************************/
483 /* GDALLoadRPBFile() */
484 /************************************************************************/
485
486 static const char * const apszRPBMap[] = {
487 apszRPCTXTSingleValItems[0], "IMAGE.errBias",
488 apszRPCTXTSingleValItems[1], "IMAGE.errRand",
489 apszRPCTXTSingleValItems[2], "IMAGE.lineOffset",
490 apszRPCTXTSingleValItems[3], "IMAGE.sampOffset",
491 apszRPCTXTSingleValItems[4], "IMAGE.latOffset",
492 apszRPCTXTSingleValItems[5], "IMAGE.longOffset",
493 apszRPCTXTSingleValItems[6], "IMAGE.heightOffset",
494 apszRPCTXTSingleValItems[7], "IMAGE.lineScale",
495 apszRPCTXTSingleValItems[8], "IMAGE.sampScale",
496 apszRPCTXTSingleValItems[9], "IMAGE.latScale",
497 apszRPCTXTSingleValItems[10], "IMAGE.longScale",
498 apszRPCTXTSingleValItems[11], "IMAGE.heightScale",
499 apszRPCTXT20ValItems[0], "IMAGE.lineNumCoef",
500 apszRPCTXT20ValItems[1], "IMAGE.lineDenCoef",
501 apszRPCTXT20ValItems[2], "IMAGE.sampNumCoef",
502 apszRPCTXT20ValItems[3], "IMAGE.sampDenCoef",
503 nullptr, nullptr };
504
GDALLoadRPBFile(const CPLString & soFilePath)505 char **GDALLoadRPBFile( const CPLString& soFilePath )
506 {
507 if( soFilePath.empty() )
508 return nullptr;
509
510 /* -------------------------------------------------------------------- */
511 /* Read file and parse. */
512 /* -------------------------------------------------------------------- */
513 VSILFILE *fp = VSIFOpenL( soFilePath, "r" );
514
515 if( fp == nullptr )
516 return nullptr;
517
518 CPLKeywordParser oParser;
519 if( !oParser.Ingest( fp ) )
520 {
521 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
522 return nullptr;
523 }
524
525 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
526
527 /* -------------------------------------------------------------------- */
528 /* Extract RPC information, in a GDAL "standard" metadata format. */
529 /* -------------------------------------------------------------------- */
530 char **papszMD = nullptr;
531 for( int i = 0; apszRPBMap[i] != nullptr; i += 2 )
532 {
533 const char *pszRPBVal = oParser.GetKeyword( apszRPBMap[i+1] );
534 CPLString osAdjVal;
535
536 if( pszRPBVal == nullptr )
537 {
538 if (strcmp(apszRPBMap[i], RPC_ERR_RAND) == 0 ||
539 strcmp(apszRPBMap[i], RPC_ERR_BIAS) == 0)
540 {
541 continue;
542 }
543 CPLError( CE_Failure, CPLE_AppDefined,
544 "%s file found, but missing %s field (and possibly others).",
545 soFilePath.c_str(), apszRPBMap[i+1] );
546 CSLDestroy( papszMD );
547 return nullptr;
548 }
549
550 if( strchr(pszRPBVal,',') == nullptr )
551 osAdjVal = pszRPBVal;
552 else
553 {
554 // strip out commas and turn newlines into spaces.
555 for( int j = 0; pszRPBVal[j] != '\0'; j++ )
556 {
557 switch( pszRPBVal[j] )
558 {
559 case ',':
560 case '\n':
561 case '\r':
562 osAdjVal += ' ';
563 break;
564
565 case '(':
566 case ')':
567 break;
568
569 default:
570 osAdjVal += pszRPBVal[j];
571 }
572 }
573 }
574
575 papszMD = CSLSetNameValue( papszMD, apszRPBMap[i], osAdjVal );
576 }
577
578 return papszMD;
579 }
580
581 /************************************************************************/
582 /* GDALLoadRPCFile() */
583 /************************************************************************/
584
585 /* Load a GeoEye _rpc.txt file. See ticket http://trac.osgeo.org/gdal/ticket/3639 */
586
GDALLoadRPCFile(const CPLString & soFilePath)587 char ** GDALLoadRPCFile( const CPLString& soFilePath )
588 {
589 if( soFilePath.empty() )
590 return nullptr;
591
592 /* -------------------------------------------------------------------- */
593 /* Read file and parse. */
594 /* -------------------------------------------------------------------- */
595 // 100 lines would be enough, but some .rpc files have CR CR LF end of
596 // lines, which result in a blank line to be recognized, so accept up
597 // to 200 lines (#6341)
598 char **papszLines = CSLLoad2( soFilePath, 200, 100, nullptr );
599 if(!papszLines)
600 return nullptr;
601
602 char **papszMD = nullptr;
603
604 /* From LINE_OFF to HEIGHT_SCALE */
605 for(size_t i = 0; i < 23; i += 2 )
606 {
607 const char *pszRPBVal = CSLFetchNameValue(papszLines, apszRPBMap[i] );
608
609 if( pszRPBVal == nullptr)
610 {
611 if (strcmp(apszRPBMap[i], RPC_ERR_RAND) == 0 ||
612 strcmp(apszRPBMap[i], RPC_ERR_BIAS) == 0)
613 {
614 continue;
615 }
616 CPLError( CE_Failure, CPLE_AppDefined,
617 "%s file found, but missing %s field (and possibly others).",
618 soFilePath.c_str(), apszRPBMap[i]);
619 CSLDestroy( papszMD );
620 CSLDestroy( papszLines );
621 return nullptr;
622 }
623 else
624 {
625 while( *pszRPBVal == ' ' || *pszRPBVal == '\t' ) pszRPBVal ++;
626 papszMD = CSLSetNameValue( papszMD, apszRPBMap[i], pszRPBVal );
627 }
628 }
629
630 /* For LINE_NUM_COEFF, LINE_DEN_COEFF, SAMP_NUM_COEFF, SAMP_DEN_COEFF */
631 /* parameters that have 20 values each */
632 for(size_t i = 24; apszRPBMap[i] != nullptr; i += 2 )
633 {
634 CPLString soVal;
635 for(int j = 1; j <= 20; j++)
636 {
637 CPLString soRPBMapItem;
638 soRPBMapItem.Printf("%s_%d", apszRPBMap[i], j);
639 const char *pszRPBVal =
640 CSLFetchNameValue(papszLines, soRPBMapItem.c_str() );
641 if( pszRPBVal == nullptr )
642 {
643 CPLError( CE_Failure, CPLE_AppDefined,
644 "%s file found, but missing %s field (and possibly others).",
645 soFilePath.c_str(), soRPBMapItem.c_str() );
646 CSLDestroy( papszMD );
647 CSLDestroy( papszLines );
648 return nullptr;
649 }
650 else
651 {
652 while( *pszRPBVal == ' ' || *pszRPBVal == '\t' ) pszRPBVal ++;
653 soVal += pszRPBVal;
654 soVal += " ";
655 }
656 }
657 papszMD = CSLSetNameValue( papszMD, apszRPBMap[i], soVal.c_str() );
658 }
659
660 CSLDestroy( papszLines );
661 return papszMD;
662 }
663
664 /************************************************************************/
665 /* GDALWriteRPCTXTFile() */
666 /************************************************************************/
667
GDALWriteRPCTXTFile(const char * pszFilename,char ** papszMD)668 CPLErr GDALWriteRPCTXTFile( const char *pszFilename, char **papszMD )
669
670 {
671 CPLString osRPCFilename = pszFilename;
672 CPLString soPt(".");
673 size_t found = osRPCFilename.rfind(soPt);
674 if (found == CPLString::npos)
675 return CE_Failure;
676 osRPCFilename.replace (found, osRPCFilename.size() - found, "_RPC.TXT");
677 if( papszMD == nullptr )
678 {
679 VSIUnlink(osRPCFilename);
680 return CE_None;
681 }
682
683 /* -------------------------------------------------------------------- */
684 /* Read file and parse. */
685 /* -------------------------------------------------------------------- */
686 VSILFILE *fp = VSIFOpenL( osRPCFilename, "w" );
687
688 if( fp == nullptr )
689 {
690 CPLError( CE_Failure, CPLE_OpenFailed,
691 "Unable to create %s for writing.\n%s",
692 osRPCFilename.c_str(), CPLGetLastErrorMsg() );
693 return CE_Failure;
694 }
695
696 /* -------------------------------------------------------------------- */
697 /* Write RPC values from our RPC metadata. */
698 /* -------------------------------------------------------------------- */
699 bool bOK = true;
700 for( int i = 0; apszRPCTXTSingleValItems[i] != nullptr; i ++ )
701 {
702 const char *pszRPCVal = CSLFetchNameValue( papszMD, apszRPCTXTSingleValItems[i] );
703 if( pszRPCVal == nullptr )
704 {
705 if (strcmp(apszRPCTXTSingleValItems[i], RPC_ERR_BIAS) == 0 ||
706 strcmp(apszRPCTXTSingleValItems[i], RPC_ERR_RAND) == 0)
707 {
708 continue;
709 }
710 CPLError( CE_Failure, CPLE_AppDefined,
711 "%s field missing in metadata, %s file not written.",
712 apszRPCTXTSingleValItems[i], osRPCFilename.c_str() );
713 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
714 VSIUnlink( osRPCFilename );
715 return CE_Failure;
716 }
717
718 bOK &= VSIFPrintfL( fp, "%s: %s\n", apszRPCTXTSingleValItems[i], pszRPCVal ) > 0;
719 }
720
721 for( int i = 0; apszRPCTXT20ValItems[i] != nullptr; i ++ )
722 {
723 const char *pszRPCVal = CSLFetchNameValue( papszMD, apszRPCTXT20ValItems[i] );
724 if( pszRPCVal == nullptr )
725 {
726 CPLError( CE_Failure, CPLE_AppDefined,
727 "%s field missing in metadata, %s file not written.",
728 apszRPCTXTSingleValItems[i], osRPCFilename.c_str() );
729 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
730 VSIUnlink( osRPCFilename );
731 return CE_Failure;
732 }
733
734 char **papszItems = CSLTokenizeStringComplex( pszRPCVal, " ,",
735 FALSE, FALSE );
736
737 if( CSLCount(papszItems) != 20 )
738 {
739 CPLError( CE_Failure, CPLE_AppDefined,
740 "%s field is corrupt (not 20 values), %s file not written.\n%s = %s",
741 apszRPCTXT20ValItems[i], osRPCFilename.c_str(),
742 apszRPCTXT20ValItems[i], pszRPCVal );
743 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
744 VSIUnlink( osRPCFilename );
745 CSLDestroy( papszItems );
746 return CE_Failure;
747 }
748
749 for( int j = 0; j < 20; j++ )
750 {
751 bOK &= VSIFPrintfL( fp, "%s_%d: %s\n", apszRPCTXT20ValItems[i], j+1,
752 papszItems[j] ) > 0;
753 }
754 CSLDestroy( papszItems );
755 }
756
757 if( VSIFCloseL( fp ) != 0 )
758 bOK = false;
759
760 return bOK ? CE_None : CE_Failure;
761 }
762
763 /************************************************************************/
764 /* GDALWriteRPBFile() */
765 /************************************************************************/
766
GDALWriteRPBFile(const char * pszFilename,char ** papszMD)767 CPLErr GDALWriteRPBFile( const char *pszFilename, char **papszMD )
768
769 {
770 CPLString osRPBFilename = CPLResetExtension( pszFilename, "RPB" );
771 if( papszMD == nullptr )
772 {
773 VSIUnlink(osRPBFilename);
774 return CE_None;
775 }
776
777 /* -------------------------------------------------------------------- */
778 /* Read file and parse. */
779 /* -------------------------------------------------------------------- */
780 VSILFILE *fp = VSIFOpenL( osRPBFilename, "w" );
781
782 if( fp == nullptr )
783 {
784 CPLError( CE_Failure, CPLE_OpenFailed,
785 "Unable to create %s for writing.\n%s",
786 osRPBFilename.c_str(), CPLGetLastErrorMsg() );
787 return CE_Failure;
788 }
789
790 /* -------------------------------------------------------------------- */
791 /* Write the prefix information. */
792 /* -------------------------------------------------------------------- */
793 bool bOK = VSIFPrintfL( fp, "%s", "satId = \"QB02\";\n" ) > 0;
794 bOK &= VSIFPrintfL( fp, "%s", "bandId = \"P\";\n" ) > 0;
795 bOK &= VSIFPrintfL( fp, "%s", "SpecId = \"RPC00B\";\n" ) > 0;
796 bOK &= VSIFPrintfL( fp, "%s", "BEGIN_GROUP = IMAGE\n" ) > 0;
797
798 /* -------------------------------------------------------------------- */
799 /* Write RPC values from our RPC metadata. */
800 /* -------------------------------------------------------------------- */
801 for( int i = 0; apszRPBMap[i] != nullptr; i += 2 )
802 {
803 const char *pszRPBVal = CSLFetchNameValue( papszMD, apszRPBMap[i] );
804 const char *pszRPBTag;
805
806 if( pszRPBVal == nullptr )
807 {
808 if (strcmp(apszRPBMap[i], RPC_ERR_BIAS) == 0) {
809 bOK &= VSIFPrintfL( fp, "%s", "\terrBias = 0.0;\n" ) > 0;
810 continue;
811 } else if (strcmp(apszRPBMap[i], RPC_ERR_RAND) == 0) {
812 bOK &= VSIFPrintfL( fp, "%s", "\terrRand = 0.0;\n" ) > 0;
813 continue;
814 }
815 CPLError( CE_Failure, CPLE_AppDefined,
816 "%s field missing in metadata, %s file not written.",
817 apszRPBMap[i], osRPBFilename.c_str() );
818 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
819 VSIUnlink( osRPBFilename );
820 return CE_Failure;
821 }
822
823 pszRPBTag = apszRPBMap[i+1];
824 if( STARTS_WITH_CI(pszRPBTag, "IMAGE.") )
825 pszRPBTag += 6;
826
827 if( strstr(apszRPBMap[i], "COEF" ) == nullptr )
828 {
829 bOK &= VSIFPrintfL( fp, "\t%s = %s;\n", pszRPBTag, pszRPBVal ) > 0;
830 }
831 else
832 {
833 // Reformat in brackets with commas over multiple lines.
834
835 bOK &= VSIFPrintfL( fp, "\t%s = (\n", pszRPBTag ) > 0;
836
837 char **papszItems = CSLTokenizeStringComplex( pszRPBVal, " ,",
838 FALSE, FALSE );
839
840 if( CSLCount(papszItems) != 20 )
841 {
842 CPLError( CE_Failure, CPLE_AppDefined,
843 "%s field is corrupt (not 20 values), %s file not "
844 "written.\n%s = %s",
845 apszRPBMap[i], osRPBFilename.c_str(),
846 apszRPBMap[i], pszRPBVal );
847 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
848 VSIUnlink( osRPBFilename );
849 CSLDestroy( papszItems );
850 return CE_Failure;
851 }
852
853 for( int j = 0; j < 20; j++ )
854 {
855 if( j < 19 )
856 bOK &= VSIFPrintfL( fp, "\t\t\t%s,\n", papszItems[j] ) > 0;
857 else
858 bOK &= VSIFPrintfL( fp, "\t\t\t%s);\n", papszItems[j] ) > 0;
859 }
860 CSLDestroy( papszItems );
861 }
862 }
863
864 /* -------------------------------------------------------------------- */
865 /* Write end part */
866 /* -------------------------------------------------------------------- */
867 bOK &= VSIFPrintfL( fp, "%s", "END_GROUP = IMAGE\n" ) > 0;
868 bOK &= VSIFPrintfL( fp, "END;\n" ) > 0;
869 if( VSIFCloseL( fp ) != 0 )
870 bOK = false;
871
872 return bOK ? CE_None : CE_Failure;
873 }
874
875 /************************************************************************/
876 /* GDAL_IMD_AA2R() */
877 /* */
878 /* Translate AA version IMD file to R version. */
879 /************************************************************************/
880
GDAL_IMD_AA2R(char *** ppapszIMD)881 static bool GDAL_IMD_AA2R( char ***ppapszIMD )
882
883 {
884 char **papszIMD = *ppapszIMD;
885
886 /* -------------------------------------------------------------------- */
887 /* Verify that we have a new format file. */
888 /* -------------------------------------------------------------------- */
889 const char *pszValue = CSLFetchNameValue( papszIMD, "version" );
890
891 if( pszValue == nullptr )
892 return false;
893
894 if( EQUAL(pszValue,"\"R\"") )
895 return true;
896
897 if( !EQUAL(pszValue,"\"AA\"") )
898 {
899 CPLDebug( "IMD",
900 "The file is not the expected 'version = \"AA\"' format.\n"
901 "Proceeding, but file may be corrupted." );
902 }
903
904 /* -------------------------------------------------------------------- */
905 /* Fix the version line. */
906 /* -------------------------------------------------------------------- */
907 papszIMD = CSLSetNameValue( papszIMD, "version", "\"R\"" );
908
909 /* -------------------------------------------------------------------- */
910 /* remove a bunch of fields. */
911 /* -------------------------------------------------------------------- */
912 static const char * const apszToRemove[] = {
913 "productCatalogId",
914 "childCatalogId",
915 "productType",
916 "numberOfLooks",
917 "effectiveBandwidth",
918 "mode",
919 "scanDirection",
920 "cloudCover",
921 "productGSD",
922 nullptr };
923
924 for( int iKey = 0; apszToRemove[iKey] != nullptr; iKey++ )
925 {
926 int iTarget = CSLFindName( papszIMD, apszToRemove[iKey] );
927 if( iTarget != -1 )
928 papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, nullptr );
929 }
930
931 /* -------------------------------------------------------------------- */
932 /* Replace various min/mean/max with just the mean. */
933 /* -------------------------------------------------------------------- */
934 static const char * const keylist[] = {
935 "CollectedRowGSD",
936 "CollectedColGSD",
937 "SunAz",
938 "SunEl",
939 "SatAz",
940 "SatEl",
941 "InTrackViewAngle",
942 "CrossTrackViewAngle",
943 "OffNadirViewAngle",
944 nullptr };
945
946 for( int iKey = 0; keylist[iKey] != nullptr; iKey++ )
947 {
948 CPLString osTarget;
949 osTarget.Printf( "IMAGE_1.min%s", keylist[iKey] );
950 int iTarget = CSLFindName( papszIMD, osTarget );
951 if( iTarget != -1 )
952 papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, nullptr );
953
954 osTarget.Printf( "IMAGE_1.max%s", keylist[iKey] );
955 iTarget = CSLFindName( papszIMD, osTarget );
956 if( iTarget != -1 )
957 papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, nullptr );
958
959 osTarget.Printf( "IMAGE_1.mean%s", keylist[iKey] );
960 iTarget = CSLFindName( papszIMD, osTarget );
961 if( iTarget != -1 )
962 {
963 CPLString osValue = CSLFetchNameValue( papszIMD, osTarget );
964 CPLString osLine;
965 osTarget.Printf( "IMAGE_1.%c%s",
966 tolower(keylist[iKey][0]),
967 keylist[iKey]+1 );
968
969 osLine = osTarget + "=" + osValue;
970
971 CPLFree( papszIMD[iTarget] );
972 papszIMD[iTarget] = CPLStrdup(osLine);
973 }
974 }
975
976 *ppapszIMD = papszIMD;
977 return true;
978 }
979
980 /************************************************************************/
981 /* GDALLoadIMDFile() */
982 /************************************************************************/
983
GDALLoadIMDFile(const CPLString & osFilePath)984 char ** GDALLoadIMDFile( const CPLString& osFilePath )
985 {
986 if( osFilePath.empty() )
987 return nullptr;
988
989 /* -------------------------------------------------------------------- */
990 /* Read file and parse. */
991 /* -------------------------------------------------------------------- */
992 CPLKeywordParser oParser;
993
994 VSILFILE *fp = VSIFOpenL( osFilePath, "r" );
995
996 if( fp == nullptr )
997 return nullptr;
998
999 if( !oParser.Ingest( fp ) )
1000 {
1001 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
1002 return nullptr;
1003 }
1004
1005 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
1006
1007 /* -------------------------------------------------------------------- */
1008 /* Consider version changing. */
1009 /* -------------------------------------------------------------------- */
1010 char **papszIMD = CSLDuplicate( oParser.GetAllKeywords() );
1011 const char *pszVersion = CSLFetchNameValue( papszIMD, "version" );
1012
1013 if( pszVersion == nullptr )
1014 {
1015 /* ? */;
1016 }
1017 else if( EQUAL(pszVersion,"\"AA\"") )
1018 {
1019 GDAL_IMD_AA2R( &papszIMD );
1020 }
1021
1022 return papszIMD;
1023 }
1024
1025 /************************************************************************/
1026 /* GDALWriteIMDMultiLine() */
1027 /* */
1028 /* Write a value that is split over multiple lines. */
1029 /************************************************************************/
1030
GDALWriteIMDMultiLine(VSILFILE * fp,const char * pszValue)1031 static void GDALWriteIMDMultiLine( VSILFILE *fp, const char *pszValue )
1032
1033 {
1034 char **papszItems = CSLTokenizeStringComplex( pszValue, "(,) ",
1035 FALSE, FALSE );
1036 const int nItemCount = CSLCount(papszItems);
1037
1038 CPL_IGNORE_RET_VAL(VSIFPrintfL( fp, "(\n" ));
1039
1040 for( int i = 0; i < nItemCount; i++ )
1041 {
1042 if( i == nItemCount-1 )
1043 CPL_IGNORE_RET_VAL(VSIFPrintfL( fp, "\t%s );\n", papszItems[i] ));
1044 else
1045 CPL_IGNORE_RET_VAL(VSIFPrintfL( fp, "\t%s,\n", papszItems[i] ));
1046 }
1047 CSLDestroy( papszItems );
1048 }
1049
1050 /************************************************************************/
1051 /* GDALWriteIMDFile() */
1052 /************************************************************************/
1053
GDALWriteIMDFile(const char * pszFilename,char ** papszMD)1054 CPLErr GDALWriteIMDFile( const char *pszFilename, char **papszMD )
1055
1056 {
1057 CPLString osRPBFilename = CPLResetExtension( pszFilename, "IMD" );
1058
1059 /* -------------------------------------------------------------------- */
1060 /* Read file and parse. */
1061 /* -------------------------------------------------------------------- */
1062 VSILFILE *fp = VSIFOpenL( osRPBFilename, "w" );
1063
1064 if( fp == nullptr )
1065 {
1066 CPLError( CE_Failure, CPLE_OpenFailed,
1067 "Unable to create %s for writing.\n%s",
1068 osRPBFilename.c_str(), CPLGetLastErrorMsg() );
1069 return CE_Failure;
1070 }
1071
1072 /* ==================================================================== */
1073 /* -------------------------------------------------------------------- */
1074 /* Loop through all values writing. */
1075 /* -------------------------------------------------------------------- */
1076 /* ==================================================================== */
1077 CPLString osCurSection;
1078 bool bOK = true;
1079
1080 for( int iKey = 0; papszMD[iKey] != nullptr; iKey++ )
1081 {
1082 char *pszRawKey = nullptr;
1083 const char *pszValue = CPLParseNameValue( papszMD[iKey], &pszRawKey );
1084 if( pszRawKey == nullptr )
1085 continue;
1086 CPLString osKeySection;
1087 CPLString osKeyItem;
1088 char *pszDot = strchr(pszRawKey,'.');
1089
1090 /* -------------------------------------------------------------------- */
1091 /* Split stuff like BAND_P.ULLon into section and item. */
1092 /* -------------------------------------------------------------------- */
1093 if( pszDot == nullptr )
1094 {
1095 osKeyItem = pszRawKey;
1096 }
1097 else
1098 {
1099 osKeyItem = pszDot+1;
1100 *pszDot = '\0';
1101 osKeySection = pszRawKey;
1102 }
1103 CPLFree( pszRawKey );
1104
1105 /* -------------------------------------------------------------------- */
1106 /* Close and/or start sections as needed. */
1107 /* -------------------------------------------------------------------- */
1108 if( !osCurSection.empty() && !EQUAL(osCurSection,osKeySection) )
1109 bOK &= VSIFPrintfL( fp, "END_GROUP = %s\n", osCurSection.c_str() ) > 0;
1110
1111 if( !osKeySection.empty() && !EQUAL(osCurSection,osKeySection) )
1112 bOK &= VSIFPrintfL( fp, "BEGIN_GROUP = %s\n", osKeySection.c_str() ) > 0;
1113
1114 osCurSection = osKeySection;
1115
1116 /* -------------------------------------------------------------------- */
1117 /* Print out simple item. */
1118 /* -------------------------------------------------------------------- */
1119 if( !osCurSection.empty() )
1120 bOK &= VSIFPrintfL( fp, "\t%s = ", osKeyItem.c_str() ) > 0;
1121 else
1122 bOK &= VSIFPrintfL( fp, "%s = ", osKeyItem.c_str() ) > 0;
1123
1124 if( pszValue[0] != '(' )
1125 {
1126 const bool bHasSingleQuote = strchr(pszValue, '\'') != nullptr;
1127 const bool bHasDoubleQuote = strchr(pszValue, '"') != nullptr;
1128 if( strchr(pszValue, ' ') != nullptr ||
1129 strchr(pszValue, ';') != nullptr ||
1130 strchr(pszValue, '\t') != nullptr ||
1131 bHasSingleQuote ||
1132 (bHasDoubleQuote && !(pszValue[0] == '"' && pszValue[strlen(pszValue)-1] == '"')) )
1133 {
1134 if( !bHasDoubleQuote )
1135 bOK &= VSIFPrintfL( fp, "\"%s\";\n", pszValue ) > 0;
1136 else if( !bHasSingleQuote )
1137 bOK &= VSIFPrintfL( fp, "'%s';\n", pszValue ) > 0;
1138 else
1139 {
1140 // There does not seem to have a way to escape double-quotes
1141 // in a double-quoted string (or single-quote in a single-quoted
1142 // string), so we are going to convert double-quotes as
1143 // two single-quotes...
1144 const std::string osVal = CPLString(pszValue).replaceAll('"', "''");
1145 bOK &= VSIFPrintfL( fp, "\"%s\";\n", osVal.c_str() ) > 0;
1146 }
1147 }
1148 else
1149 {
1150 bOK &= VSIFPrintfL( fp, "%s;\n", pszValue ) > 0;
1151 }
1152 }
1153 else
1154 GDALWriteIMDMultiLine( fp, pszValue );
1155 }
1156
1157 /* -------------------------------------------------------------------- */
1158 /* Close off. */
1159 /* -------------------------------------------------------------------- */
1160 if( !osCurSection.empty() )
1161 bOK &= VSIFPrintfL( fp, "END_GROUP = %s\n", osCurSection.c_str() ) > 0;
1162
1163 bOK &= VSIFPrintfL( fp, "END;\n" ) > 0;
1164
1165 if( VSIFCloseL( fp ) != 0 )
1166 bOK = false;
1167
1168 return bOK ? CE_None : CE_Failure;
1169 }
1170