Skip to content

Commit

Permalink
ISIS3: preserve label in PAM .aux.xml when copying to other formats (f…
Browse files Browse the repository at this point in the history
…ixes #1854)
  • Loading branch information
rouault committed Sep 27, 2019
1 parent 86a6532 commit 5dea6d2
Show file tree
Hide file tree
Showing 6 changed files with 316 additions and 24 deletions.
149 changes: 149 additions & 0 deletions autotest/gdrivers/isis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1538,3 +1538,152 @@ def test_isis3_bandbin_multiple_bands():
}
ds = None
gdal.Unlink('/vsimem/test.lbl')


###############################################################################
# Test that when converting from ISIS3 to other formats (PAM-enabled), the
# json:ISIS3 metadata domain is preserved.

def test_isis3_preserve_label_across_format():

gdal.FileFromMemBuffer('/vsimem/multiband.lbl', """Object = IsisCube
Object = Core
Format = BandSequential
Group = Dimensions
Samples = 1
Lines = 1
Bands = 2
End_Group
Group = Pixels
Type = UnsignedByte
ByteOrder = Lsb
Base = 0.0
Multiplier = 1.0
End_Group
End_Object
Group = BandBin
BandSuffixName = ("first band", "second band")
BandSuffixUnit = (DEGREE, DEGREE)
BandBinCenter = (1.0, 2.0)
BandBinUnit = MICROMETER
Width = (0.5, 1.0) <um>
End_Group
End_Object
End""")
src_ds = gdal.Open('/vsimem/multiband.lbl')

# Copy ISIS3 to GeoTIFF
gdal.GetDriverByName('GTiff').CreateCopy('/vsimem/out.tif', src_ds)

# Check GeoTIFF
ds = gdal.Open('/vsimem/out.tif')
assert len(ds.GetMetadataDomainList()) == 3
assert set(ds.GetMetadataDomainList()) == set(['IMAGE_STRUCTURE', 'json:ISIS3', 'DERIVED_SUBDATASETS'])
lbl = ds.GetMetadata_List('json:ISIS3')[0]
assert lbl
ds = None
assert gdal.VSIStatL('/vsimem/out.tif.aux.xml')

# Check that the label is in PAM, and not internal to GTiff
with gdaltest.config_option('GDAL_PAM_ENABLED', 'NO'):
ds = gdal.Open('/vsimem/out.tif')
assert not ds.GetMetadata_List('json:ISIS3')

# Copy back from GeoTIFF to ISIS3
src_ds_gtiff = gdal.Open('/vsimem/out.tif')
gdal.GetDriverByName('ISIS3').CreateCopy('/vsimem/out.cub', src_ds_gtiff)
assert not gdal.VSIStatL('/vsimem/out.cub.aux.xml')
ds = gdal.Open('/vsimem/out.cub')
lbl = ds.GetMetadata_List('json:ISIS3')[0]
# Check label preservation
assert 'BandBin' in lbl
ds = None

gdal.GetDriverByName('GTiff').Delete('/vsimem/out.tif')
gdal.GetDriverByName('ISIS3').Delete('/vsimem/out.cub')

# Copy ISIS3 to PDS4
with gdaltest.error_handler():
gdal.GetDriverByName('PDS4').CreateCopy('/vsimem/out.xml', src_ds)
ds = gdal.Open('/vsimem/out.xml')
lbl = ds.GetMetadata_List('json:ISIS3')[0]
assert lbl
ds = None
assert gdal.VSIStatL('/vsimem/out.xml.aux.xml')
gdal.GetDriverByName('PDS4').Delete('/vsimem/out.xml')

# Copy ISIS3 to PNG
gdal.GetDriverByName('PNG').CreateCopy('/vsimem/out.png', src_ds)
ds = gdal.Open('/vsimem/out.png')
lbl = ds.GetMetadata_List('json:ISIS3')[0]
assert lbl
ds = None
assert gdal.VSIStatL('/vsimem/out.png.aux.xml')
gdal.GetDriverByName('PNG').Delete('/vsimem/out.png')

# Check GeoTIFF with non pure copy mode (test gdal_translate_lib)
gdal.Translate('/vsimem/out.tif', src_ds, options = '-mo FOO=BAR')
ds = gdal.Open('/vsimem/out.tif')
lbl = ds.GetMetadata_List('json:ISIS3')[0]
assert lbl
ds = None
gdal.GetDriverByName('GTiff').Delete('/vsimem/out.tif')

# Test converting a subset of bands
gdal.Translate('/vsimem/out.tif', src_ds, options = '-b 2 -mo FOO=BAR')
ds = gdal.Open('/vsimem/out.tif')
lbl = ds.GetMetadata_List('json:ISIS3')[0]
lbl = json.loads(lbl)

assert lbl['IsisCube']['BandBin'] == json.loads("""{
"_type":"group",
"BandBinUnit":"MICROMETER",
"Width":{
"unit":"um",
"value":[
1.000000
]
},
"BandSuffixName":[
"second band"
],
"BandSuffixUnit":[
"DEGREE"
],
"BandBinCenter":[
2.000000
]
}""")

assert 'OriginalBandBin' in lbl['IsisCube']
assert lbl['IsisCube']['OriginalBandBin'] == json.loads("""{
"_type":"group",
"BandSuffixName":[
"first band",
"second band"
],
"BandSuffixUnit":[
"DEGREE",
"DEGREE"
],
"BandBinCenter":[
1.000000,
2.000000
],
"BandBinUnit":"MICROMETER",
"Width":{
"value":[
0.500000,
1.000000
],
"unit":"um"
}
}""")

ds = None
gdal.GetDriverByName('GTiff').Delete('/vsimem/out.tif')

src_ds = None
gdal.Unlink('/vsimem/multiband.lbl')
104 changes: 99 additions & 5 deletions gdal/apps/gdal_translate_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "commonutils.h"
#include "cpl_conv.h"
#include "cpl_error.h"
#include "cpl_json.h"
#include "cpl_progress.h"
#include "cpl_string.h"
#include "cpl_vsi.h"
Expand Down Expand Up @@ -506,6 +507,85 @@ static GDALDatasetH GDALTranslateFlush(GDALDatasetH hOutDS)
return hOutDS;
}

/************************************************************************/
/* EditISIS3MetadataForBandChange() */
/************************************************************************/

static CPLJSONObject Clone(const CPLJSONObject& obj)
{
auto serialized = obj.Format(CPLJSONObject::Plain);
CPLJSONDocument oJSONDocument;
const GByte *pabyData = reinterpret_cast<const GByte *>(serialized.c_str());
oJSONDocument.LoadMemory( pabyData );
return oJSONDocument.GetRoot();
}

static void ReworkArray(CPLJSONObject& container, const CPLJSONObject& obj,
int nSrcBandCount,
const GDALTranslateOptions *psOptions)
{
auto oArray = obj.ToArray();
if( oArray.Size() == nSrcBandCount )
{
CPLJSONArray oNewArray;
for( int i = 0; i < psOptions->nBandCount; i++ )
{
const int iSrcIdx = psOptions->panBandList[i]-1;
oNewArray.Add(oArray[iSrcIdx]);
}
const auto childName(obj.GetName());
container.Delete(childName);
container.Add(childName, oNewArray);
}
}

static CPLString EditISIS3MetadataForBandChange(const char* pszJSON,
int nSrcBandCount,
const GDALTranslateOptions *psOptions)
{
CPLJSONDocument oJSONDocument;
const GByte *pabyData = reinterpret_cast<const GByte *>(pszJSON);
if( !oJSONDocument.LoadMemory( pabyData ) )
{
return CPLString();
}

auto oRoot = oJSONDocument.GetRoot();
if( !oRoot.IsValid() )
{
return CPLString();
}

auto oBandBin = oRoot.GetObj( "IsisCube/BandBin" );
if( oBandBin.IsValid() && oBandBin.GetType() == CPLJSONObject::Object )
{
// Backup original BandBin object
oRoot.GetObj("IsisCube").Add("OriginalBandBin", Clone(oBandBin));

// Iterate over BandBin members and reorder/resize its arrays that
// have the same number of elements than the number of bands of the
// source dataset.
for( auto& child: oBandBin.GetChildren() )
{
if( child.GetType() == CPLJSONObject::Array )
{
ReworkArray(oBandBin, child, nSrcBandCount, psOptions);
}
else if( child.GetType() == CPLJSONObject::Object )
{
auto oValue = child.GetObj("value");
auto oUnit = child.GetObj("unit");
if( oValue.GetType() == CPLJSONObject::Array )
{
ReworkArray(child, oValue, nSrcBandCount, psOptions);
}
}
}
}

return oRoot.Format(CPLJSONObject::Pretty);
}

/************************************************************************/
/* GDALTranslate() */
/************************************************************************/
Expand Down Expand Up @@ -1349,14 +1429,28 @@ GDALDatasetH GDALTranslate( const char *pszDest, GDALDatasetH hSrcDataset,
if (pszInterleave)
poVDS->SetMetadataItem("INTERLEAVE", pszInterleave, "IMAGE_STRUCTURE");

/* ISIS3 -> ISIS3 special case */
if( EQUAL(psOptions->pszFormat, "ISIS3") )
/* ISIS3 metadata preservation */
char** papszMD_ISIS3 = poSrcDS->GetMetadata("json:ISIS3");
if( papszMD_ISIS3 != nullptr)
{
char** papszMD_ISIS3 = poSrcDS->GetMetadata("json:ISIS3");
if( papszMD_ISIS3 != nullptr)
if( !bAllBandsInOrder )
{
CPLString osJSON = EditISIS3MetadataForBandChange(
papszMD_ISIS3[0], GDALGetRasterCount( hSrcDataset ), psOptions);
if( !osJSON.empty() )
{
char* apszMD[] = { &osJSON[0], nullptr };
poVDS->SetMetadata( apszMD, "json:ISIS3" );
}
}
else
{
poVDS->SetMetadata( papszMD_ISIS3, "json:ISIS3" );
}
}
else if( EQUAL(psOptions->pszFormat, "PDS4") )

// PDS4 -> PDS4 special case
if( EQUAL(psOptions->pszFormat, "PDS4") )
{
char** papszMD_PDS4 = poSrcDS->GetMetadata("xml:PDS4");
if( papszMD_PDS4 != nullptr)
Expand Down
19 changes: 18 additions & 1 deletion gdal/frmts/gtiff/geotiff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17830,17 +17830,29 @@ GTiffDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
/* to write metadata that we could not write as a TIFF tag. */
/* -------------------------------------------------------------------- */
if( !bHasWrittenMDInGeotiffTAG && !bStreaming )
{
GTiffDataset::WriteMetadata(
poDS, l_hTIFF, true, eProfile,
pszFilename, papszOptions,
true /* don't write RPC and IMD file again */ );
}

if( !bStreaming )
GTiffDataset::WriteRPC(
poDS, l_hTIFF, true, eProfile,
pszFilename, papszOptions,
true /* write only in PAM AND if needed */ );

// Propagate ISIS3 metadata, but only as PAM metadata.
{
char **papszISIS3MD = poSrcDS->GetMetadata("json:ISIS3");
if( papszISIS3MD )
{
poDS->SetMetadata( papszISIS3MD, "json:ISIS3");
poDS->PushMetadataToPam();
}
}

poDS->m_bWriteCOGLayout = bCopySrcOverviews;

// To avoid unnecessary directory rewriting.
Expand Down Expand Up @@ -18687,7 +18699,12 @@ char **GTiffDataset::GetMetadataDomainList()
const int nbBaseDomains = CSLCount(papszBaseList);

for( int domainId = 0; domainId < nbBaseDomains; ++domainId )
papszDomainList = CSLAddString(papszDomainList,papszBaseList[domainId]);
{
if( CSLFindString(papszDomainList, papszBaseList[domainId]) < 0 )
{
papszDomainList = CSLAddString(papszDomainList,papszBaseList[domainId]);
}
}

CSLDestroy(papszBaseList);

Expand Down
6 changes: 6 additions & 0 deletions gdal/frmts/pds/pds4dataset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4326,6 +4326,12 @@ GDALDataset* PDS4Dataset::CreateCopy( const char *pszFilename,
delete poDS;
return nullptr;
}

char **papszISIS3MD = poSrcDS->GetMetadata("json:ISIS3");
if( papszISIS3MD )
{
poDS->SetMetadata( papszISIS3MD, "json:ISIS3");
}
}

return poDS;
Expand Down
Loading

0 comments on commit 5dea6d2

Please sign in to comment.