Skip to content

Commit

Permalink
GDALMDReader::ReadXMLToList(): make it suffix repeated elements with …
Browse files Browse the repository at this point in the history
…a occurence counter, even when they are not consecutive (fixes OSGeo#6047)
  • Loading branch information
rouault committed Jul 11, 2022
1 parent 42b30a4 commit 8557b99
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 73 deletions.
5 changes: 3 additions & 2 deletions autotest/gdrivers/data/jpeg2000/DIM_md_pneo.XML
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<METADATA_SUBPROFILE>PRODUCT</METADATA_SUBPROFILE>
<METADATA_LANGUAGE>en</METADATA_LANGUAGE>
</Metadata_Identification>
<Dataset_Content>
<Dataset_Content>
<Dataset_Extent>
<EXTENT_TYPE>Bounding_Polygon</EXTENT_TYPE>
<Vertex>
Expand Down Expand Up @@ -41,7 +41,7 @@
<ROW>3066</ROW>
</Center>
</Dataset_Extent>
</Dataset_Content>
</Dataset_Content>
<Coordinate_Reference_System>
<Geodetic_CRS>
<CRS_TABLES version="0.0">EPSG</CRS_TABLES>
Expand All @@ -58,6 +58,7 @@
<GSD_ALONG_TRACK unit="m">0.7145258524056943</GSD_ALONG_TRACK>
</Ground_Sample_Distance>
</Located_Geometric_Values>
<XXXXXXXXXXXX_added_just_to_test_issue_6047_XXXXXXXXXXXXXX/>
<Located_Geometric_Values>
<Ground_Sample_Distance>
<GSD_ACROSS_TRACK unit="m">0.7059011471512184</GSD_ACROSS_TRACK>
Expand Down
4 changes: 4 additions & 0 deletions autotest/gdrivers/jp2metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ def test_jp2metadata_2b():
}
_test_jp2metadata(file_path, rpc_values_to_check=rpc_values_to_check)

ds = gdal.Open(file_path)
md = ds.GetMetadata_Dict("IMD")
assert md['Geometric_Data.Use_Area.Located_Geometric_Values_1.Ground_Sample_Distance.GSD_ACROSS_TRACK'] == '0.7054510257833884'
assert md['Geometric_Data.Use_Area.Located_Geometric_Values_2.Ground_Sample_Distance.GSD_ACROSS_TRACK'] == '0.7059011471512184'

###############################################################################
# Test reading GMLJP2 file with srsName only on the Envelope, and lots of other
Expand Down
176 changes: 105 additions & 71 deletions gcore/gdal_mdreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,120 +302,154 @@ char** GDALMDReaderBase::AddXMLNameValueToList(char** papszList,
}

/**
* CPLReadXMLToList()
* ReadXMLToListFirstPass()
*/
char** GDALMDReaderBase::ReadXMLToList(CPLXMLNode* psNode, char** papszList,
const char* pszName)
void GDALMDReaderBase::ReadXMLToListFirstPass(const CPLXMLNode* psNode,
std::map<std::string, int>& oMapCountKeysFull,
const std::string& osPrefixFull)
{
if(nullptr == psNode)
return;
if (psNode->eType == CXT_Element)
{
std::string osNewPrefixFull;
for(const CPLXMLNode* psChildNode = psNode->psChild;
nullptr != psChildNode;
psChildNode = psChildNode->psNext)
{
if (psChildNode->eType == CXT_Element)
{
osNewPrefixFull = !osPrefixFull.empty() ? osPrefixFull : std::string(psNode->pszValue);
osNewPrefixFull += '.';
osNewPrefixFull += psChildNode->pszValue;
osNewPrefixFull += CPLSPrintf("_%d", ++oMapCountKeysFull[osNewPrefixFull]);

ReadXMLToListFirstPass(psChildNode, oMapCountKeysFull, osNewPrefixFull);
}
}
}

// proceed next only on top level

if(nullptr != psNode->psNext && osPrefixFull.empty())
{
ReadXMLToListFirstPass(psNode->psNext, oMapCountKeysFull, osPrefixFull);
}
}

/**
* ReadXMLToList()
*/
char** GDALMDReaderBase::ReadXMLToList(const CPLXMLNode* psNode,
char** papszList,
const std::map<std::string, int>& oMapCountKeysFullRef,
std::map<std::string, int>& oMapCountKeysFull,
std::map<std::string, int>& oMapCountKeys,
const std::string& osPrefix,
const std::string& osPrefixFull)
{
if(nullptr == psNode)
return papszList;

if (psNode->eType == CXT_Text)
{
papszList = AddXMLNameValueToList(papszList, pszName, psNode->pszValue);
papszList = AddXMLNameValueToList(papszList, osPrefix.c_str(), psNode->pszValue);
}

if (psNode->eType == CXT_Element)
{

int nAddIndex = 0;
bool bReset = false;
for(CPLXMLNode* psChildNode = psNode->psChild; nullptr != psChildNode;
std::string osNewPrefix;
std::string osNewPrefixFull;
for(const CPLXMLNode* psChildNode = psNode->psChild;
nullptr != psChildNode;
psChildNode = psChildNode->psNext)
{
if (psChildNode->eType == CXT_Element)
{
// check name duplicates
if(nullptr != psChildNode->psNext)
osNewPrefixFull = !osPrefixFull.empty() ? osPrefixFull : std::string(psNode->pszValue);
osNewPrefixFull += '.';
osNewPrefixFull += psChildNode->pszValue;

const auto oIter = oMapCountKeysFullRef.find(osNewPrefixFull);
CPLAssert(oIter != oMapCountKeysFullRef.end());
osNewPrefixFull += CPLSPrintf("_%d", ++oMapCountKeysFull[osNewPrefixFull]);

osNewPrefix = !osPrefix.empty() ? osPrefix : std::string(psNode->pszValue);
osNewPrefix += '.';
osNewPrefix += psChildNode->pszValue;
const int nIndex = ++oMapCountKeys[osNewPrefix];
const bool bMultipleInstances = oIter->second >= 2;
if( bMultipleInstances )
{
if(bReset)
{
bReset = false;
nAddIndex = 0;
}

if(EQUAL(psChildNode->pszValue, psChildNode->psNext->pszValue))
{
nAddIndex++;
}
else
{ // the name changed

if(nAddIndex > 0)
{
bReset = true;
nAddIndex++;
}
}
osNewPrefix += CPLSPrintf("_%d", nIndex);
}
else
{
if(bReset)
{
bReset = false;
nAddIndex = 0;
}

if(nAddIndex > 0)
{
nAddIndex++;
}
}

char szName[512];
if(nAddIndex > 0)
{
CPLsnprintf( szName, 511, "%s_%d", psChildNode->pszValue,
nAddIndex);
}
else
{
CPLStrlcpy(szName, psChildNode->pszValue, 511);
}

char szNameNew[512];
if(CPLStrnlen( pszName, 511 ) > 0) //if no prefix just set name to node name
{
CPLsnprintf( szNameNew, 511, "%s.%s", pszName, szName );
}
else
{
CPLsnprintf( szNameNew, 511, "%s.%s", psNode->pszValue, szName );
}

papszList = ReadXMLToList(psChildNode, papszList, szNameNew);
papszList = ReadXMLToList(psChildNode, papszList,
oMapCountKeysFullRef,
oMapCountKeysFull,
oMapCountKeys,
osNewPrefix, osNewPrefixFull);
}
else if( psChildNode->eType == CXT_Attribute )
{
papszList = AddXMLNameValueToList(papszList,
CPLSPrintf("%s.%s", pszName, psChildNode->pszValue),
CPLSPrintf("%s.%s", osPrefix.c_str(), psChildNode->pszValue),
psChildNode->psChild->pszValue);
}
else
{
// Text nodes should always have name
if(EQUAL(pszName, ""))
if(osPrefix.empty())
{
papszList = ReadXMLToList(psChildNode, papszList, psNode->pszValue);
papszList = ReadXMLToList(psChildNode, papszList,
oMapCountKeysFullRef,
oMapCountKeysFull,
oMapCountKeys,
psNode->pszValue, psNode->pszValue);
}
else
{
papszList = ReadXMLToList(psChildNode, papszList, pszName);
papszList = ReadXMLToList(psChildNode, papszList,
oMapCountKeysFullRef,
oMapCountKeysFull,
oMapCountKeys,
osPrefix.c_str(), osNewPrefixFull.c_str());
}
}
}
}

// proceed next only on top level

if(nullptr != psNode->psNext && EQUAL(pszName, ""))
if(nullptr != psNode->psNext && osPrefix.empty())
{
papszList = ReadXMLToList(psNode->psNext, papszList, pszName);
papszList = ReadXMLToList(psNode->psNext, papszList,
oMapCountKeysFullRef,
oMapCountKeysFull,
oMapCountKeys,
osPrefix, osPrefixFull);
}

return papszList;
}


/**
* ReadXMLToList()
*/
char** GDALMDReaderBase::ReadXMLToList(CPLXMLNode* psNode, char** papszList,
const char* pszName)
{
std::map<std::string, int> oMapCountKeysFullRef;
ReadXMLToListFirstPass(psNode, oMapCountKeysFullRef, pszName);
std::map<std::string, int> oMapCountKeysFull;
std::map<std::string, int> oMapCountKeys;
return ReadXMLToList(psNode, papszList,
oMapCountKeysFullRef,
oMapCountKeysFull,
oMapCountKeys,
pszName, pszName);
}

//------------------------------------------------------------------------------
// Miscellaneous functions
//------------------------------------------------------------------------------
Expand Down
14 changes: 14 additions & 0 deletions gcore/gdal_mdreader.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#include "cpl_port.h"
#include "gdal_priv.h"

#include <map>

#define MD_DOMAIN_IMD "IMD" /**< image metadata section */
#define MD_DOMAIN_RPC "RPC" /**< rpc metadata section */
#define MD_DOMAIN_IMAGERY "IMAGERY" /**< imagery metadata section */
Expand Down Expand Up @@ -108,6 +110,18 @@ class CPL_DLL GDALMDReaderBase{

CPL_DISALLOW_COPY_ASSIGN(GDALMDReaderBase)

void ReadXMLToListFirstPass(const CPLXMLNode* psNode,
std::map<std::string, int>& oMapCountKeysFull,
const std::string& osPrefixFull);

char** ReadXMLToList(const CPLXMLNode* psNode,
char** papszList,
const std::map<std::string, int>& oMapCountKeysFullRef,
std::map<std::string, int>& oMapCountKeysFull,
std::map<std::string, int>& oMapCountKeys,
const std::string& osPrefix,
const std::string& osPrefixFull);

public:
GDALMDReaderBase(const char *pszPath, char **papszSiblingFiles);
virtual ~GDALMDReaderBase();
Expand Down

0 comments on commit 8557b99

Please sign in to comment.