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