Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f4b5c11
Fixes apps that fail when passing a GeoTiff with ISIS metadata
acpaquette May 30, 2025
ea373fb
Fix spiceserver issues when format is GTiff
acpaquette Jun 18, 2025
fd524c8
Fixed serial number composition to handle Gtiffs
acpaquette Jun 19, 2025
3e4f8c8
Fix csminit on GeoTiff in ISIS
acpaquette Sep 15, 2025
9bd9141
Add try catch around check for json blob
acpaquette Sep 15, 2025
98bf500
Remove error throw from gdalDataset and change json blob detection
acpaquette Sep 15, 2025
0ed7e7b
Move gdal label reading from Cube into the Pvl class
acpaquette Nov 17, 2025
6e89633
Revert autoseed and serialnumber label read changes
acpaquette Nov 19, 2025
570db5e
Revert other apps that read pvl labels
acpaquette Nov 19, 2025
ef3f33c
Remove try catch around read from debugging
acpaquette Nov 19, 2025
b77887d
Reverted serialnumber compose to access the label directly
acpaquette Nov 19, 2025
3289213
Revert more label reads
acpaquette Nov 19, 2025
3ba7252
Fixed throw in PVL class at the end contructor from file path
acpaquette Dec 2, 2025
45f4ff6
Update opencv to use the cmake config
acpaquette Dec 3, 2025
9b2d4b8
Fix using opencv config for find_package
acpaquette Dec 4, 2025
73b72c8
Fixed error throwing in Pvl contruction
acpaquette Dec 4, 2025
d1228e5
Fixed other tests with new errors and label size change
acpaquette Dec 4, 2025
b73568c
Decide to open a cube using gdal or ISIS open logic based on the GDAL…
acpaquette Dec 30, 2025
00777c0
Added changelog entry
acpaquette Dec 30, 2025
382d527
Fix tests after changing how cube decides to open files as geotiffs v…
acpaquette Dec 31, 2025
2de9a13
Handle gdal metadata pointer comparison change
acpaquette Jan 6, 2026
dd609bd
Change geotiff creation to default to .tif
acpaquette Jan 6, 2026
bfa49b2
Fix ecub creation
acpaquette Jan 6, 2026
da4b734
Changed check for using gdal to open images if the driver used is not…
acpaquette Jan 6, 2026
65882a2
Fixed gdal metadata check to read metadata if it is NOT a nullptr
acpaquette Jan 6, 2026
7cde20c
Handle ISIS2 and ISIS3 gdal drivers when opening images
acpaquette Jan 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ ctest FunctionalTestJigsawApollo to validate this output. [#5710](https://github
- Changed 'jigsaw' attribute type to account for Linux compiler changes [#5904](https://github.com/DOI-USGS/ISIS3/pull/5904)
- Updated GDAL to 3.12 and QT to 6.X [#5909](https://github.com/DOI-USGS/ISIS3/pull/5909)
- Updated cmake configs to accommodate new cspice release [#5886](https://github.com/DOI-USGS/ISIS3/pull/5886)
- Changed `PVL` class to read data from GDAL metadata directly [#5824](https://github.com/DOI-USGS/ISIS3/pull/5824)

### Fixed
- Fixed Chandrayaan-2 TMC2 serial numbers by setting InstrumentId to CH2_TMC_FORE/NADIR/AFT based on the filename sensor [#5871](https://github.com/DOI-USGS/ISIS3/issues/5871)
Expand Down
5 changes: 4 additions & 1 deletion isis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ find_package(GSL 2.2.1 REQUIRED)
find_package(HDF5 1.8.15 REQUIRED)
find_package(Jama 125 REQUIRED)
find_package(NN REQUIRED)
find_package(OpenCV 4.12.0 REQUIRED)
find_package(OpenCV 4.9.0 REQUIRED CONFIG)
find_package(PCL REQUIRED)
find_package(Protobuf REQUIRED CONFIG)
find_package(PROJ REQUIRED CONFIG)
Expand Down Expand Up @@ -396,6 +396,9 @@ endforeach()
# add target based linkages to ALLLIBS variable
list(APPEND ALLLIBS pantor::inja sensorutilities protobuf::libprotobuf embree GDAL::GDAL Bullet::Bullet_double CSPICE::cspice)

list(APPEND ALLLIBS ${OpenCV_LIBS})
list(APPEND ALLINCDIRS ${OpenCV_INCLUDE_DIRS})

# Sometimes we add the same lib more than once (especially with LIBDIRS)
list(REMOVE_DUPLICATES ALLLIBDIRS)
list(REMOVE_DUPLICATES ALLLIBS)
Expand Down
9 changes: 1 addition & 8 deletions isis/src/base/apps/catlab/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,7 @@ void IsisMain() {
QString file = ui.GetCubeName("FROM");

// Extract label from file
Pvl label;
try {
label = Pvl(file);
}
catch (...) {
Cube cube(file);
label = *cube.label();
}
Pvl label(file);

// Output to file if entered
if(ui.WasEntered("TO")) {
Expand Down
6 changes: 6 additions & 0 deletions isis/src/base/apps/spiceinit/SpiceClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ namespace Isis {

raw += " <label>\n";
stringstream str;
// Force BandSequential format as a hack to avoid issues
// opening/operating on GTiff formated data
PvlObject& core = cubeLabel.findObject("IsisCube").findObject("Core");
if (core.findKeyword("Format")[0] == "GTiff") {
core.findKeyword("Format") = "BandSequential";
}
str << cubeLabel;
raw += QString(QByteArray(str.str().c_str()).toHex().constData()) + "\n";

Expand Down
9 changes: 1 addition & 8 deletions isis/src/base/apps/tabledump/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,7 @@ void helperButtonGetTableList() {

UserInterface &ui = Application::GetUserInterface();
QString currentFile = ui.GetCubeName("FROM");
Pvl label;
try {
label = Pvl(FileName(currentFile).expanded());
}
catch (...) {
Cube cube(FileName(currentFile).expanded());
label = *cube.label();
}
const Pvl label(FileName(currentFile).expanded());

// Check to see if the "FILE" parameter has changed since last press
if (currentFile != g_previousFile) {
Expand Down
206 changes: 40 additions & 166 deletions isis/src/base/objs/Cube/Cube.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,10 @@ namespace Isis {
imageFile = imageFile.addExtension("cub");
}
else if (format() == Format::GTiff) {
imageFile = imageFile.addExtension("tiff");
if (imageFile.extension() != "tif" &&
imageFile.extension() != "tiff") {
imageFile = imageFile.addExtension("tif");
}
}
else {
QString msg = "Unknown format type [" + toString(format()) + "]";
Expand Down Expand Up @@ -533,14 +536,15 @@ namespace Isis {
core.addGroup(ptype);
}
else if (labelsAttached() == LabelAttachment::ExternalLabel) {
imageFile = imageFile.addExtension("ecub");
if (!m_dataFileName) {
if (format() == Bsq || format() == Tile) {
imageFile = imageFile.addExtension("cub");
imageFile = imageFile.setExtension("cub");

Pvl dnLabel;
PvlObject isiscube("IsisCube");
PvlObject dnCore(core);
PvlKeyword fileFormat("Format", toString(format()));
PvlKeyword fileFormat("Format", CubeAttributeOutput::toString(format()));

dnCore.addKeyword(fileFormat);
dnCore.addGroup(dims);
Expand All @@ -552,13 +556,11 @@ namespace Isis {
Cube dnCube;
dnCube.fromLabel(imageFile, dnLabel, "rw");
dnCube.close();

m_dataFileName = new FileName(imageFile);
}
else if (format() == Format::GTiff) {
imageFile = imageFile.setExtension("tiff");
m_dataFileName = new FileName(imageFile);
imageFile = imageFile.setExtension("tif");
}
m_dataFileName = new FileName(imageFile);

imageFile = imageFile.setExtension("ecub");
FileName labelFileName(imageFile);
Expand Down Expand Up @@ -808,27 +810,26 @@ namespace Isis {

QString msg = "Failed to open [" + cubeFileName + "]";
IException exceptions(IException::Io, msg, _FILEINFO_);
if (!isOpen()) {
try {
openCube(cubeFileName, access);
}
catch (IException &e) {
cleanUp(false);
exceptions.append(e);
}

GDALDriverH hDriver = GDALIdentifyDriver(FileName(cubeFileName).expanded().toStdString().c_str(), nullptr);
bool openWithGdal = false;
if (hDriver != nullptr) {
GDALDriver* poDriver = (GDALDriver*)hDriver;
QString driverDescription = poDriver->GetDescription();
openWithGdal = (!driverDescription.contains("ISIS"));
}

if (!isOpen()) {
try {
try{
if (openWithGdal) {
openGdal(cubeFileName, access);
}
catch(IException &e) {
cleanUp(false);
exceptions.append(e);
else {
openCube(cubeFileName, access);
}
}

if (!isOpen()) {
catch(IException &e) {
cleanUp(false);
exceptions.append(e);
throw exceptions;
}

Expand Down Expand Up @@ -942,7 +943,9 @@ namespace Isis {
realDataFileLabel(), true);
}
else if (m_format == GTiff) {
m_dataFile->close();
if (m_dataFile) {
m_dataFile->close();
}
m_geodataSet = GDALDataset::FromHandle(GDALOpen(m_dataFileName->expanded().toStdString().c_str(), eAccess));
if (!m_geodataSet) {
QString msg = "Opening GDALDataset from [" + m_dataFileName->name() + "] failed with access [" + QString::number(eAccess) +"]";
Expand Down Expand Up @@ -975,145 +978,8 @@ namespace Isis {
m_dataFileName = new FileName(*m_labelFileName);

initCoreFromGdal(m_labelFileName->expanded());
GDALDataset *dataset = GDALDataset::FromHandle(GDALOpen(m_dataFileName->expanded().toStdString().c_str(), GA_ReadOnly));
if (!dataset) {
QString msg = "Failed opening GDALDataset from [" + m_dataFileName->name() + "]";
cleanUp(false);
throw IException(IException::Programmer, msg, _FILEINFO_);
}

CPLStringList metadata = CPLStringList(dataset->GetMetadata("USGS"), false);

m_label = new Pvl();
if (metadata[0] != nullptr) {
for (int i = 0; i < metadata.size(); i++) {
const char *metadataItem = CPLParseNameValue(metadata[i], nullptr);
nlohmann::ordered_json metadataAsJson = nlohmann::ordered_json::parse(metadataItem);
Pvl pvl;
Pvl::readObject(pvl, metadataAsJson);
for (int i = 0; i < pvl.objects(); i++) {
m_label->addObject(pvl.object(i));
}
for (int i = 0; i < pvl.groups(); i++) {
m_label->addGroup(pvl.group(i));
}
}
}
else {
// Setup the PVL
PvlObject isiscube("IsisCube");
PvlObject core("Core");

// Create the size of the core
PvlGroup dims("Dimensions");
dims += PvlKeyword("Samples", toString(m_samples));
dims += PvlKeyword("Lines", toString(m_lines));
dims += PvlKeyword("Bands", toString(m_bands));

// Create the pixel type
PvlGroup ptype("Pixels");
ptype += PvlKeyword("Type", PixelTypeName(m_pixelType));

// And the byte ordering
ptype += PvlKeyword("ByteOrder", ByteOrderName(m_byteOrder));
ptype += PvlKeyword("Base", toString(m_base));
ptype += PvlKeyword("Multiplier", toString(m_multiplier));

core += PvlKeyword("StartByte", toString(m_labelBytes + 1));

core.addGroup(dims);
core.addGroup(ptype);

isiscube.addObject(core);

m_label->addObject(isiscube);
}

if (dataset->GetSpatialRef() && !(m_label->findObject("IsisCube").hasGroup("Mapping"))) {
char ** projStr = new char*[1];
const OGRSpatialReference &oSRS = *dataset->GetSpatialRef();
oSRS.exportToProj4(projStr);
QString qProjStr = QString::fromStdString(std::string(projStr[0]) + " +type=crs");
delete[] projStr[0];
delete[] projStr;

char ** projJsonStr = new char*[1];
oSRS.exportToPROJJSON(projJsonStr, nullptr);
nlohmann::json projJson = nlohmann::json::parse(projJsonStr[0]);
CPLFree(projJsonStr);

PvlGroup mappingGroup("Mapping");
mappingGroup.addKeyword(PvlKeyword("ProjectionName", "IProj"));
mappingGroup.addKeyword(PvlKeyword("EquatorialRadius", toString(oSRS.GetSemiMajor()), "meters"));
mappingGroup.addKeyword(PvlKeyword("PolarRadius", toString(oSRS.GetSemiMinor()), "meters"));

if (projJson.contains("base_crs")) {
projJson = projJson["base_crs"];
}

std::string direction = projJson["coordinate_system"]["axis"][1]["direction"];
if (direction == "east") {
mappingGroup.addKeyword(PvlKeyword("LongitudeDirection", "PositiveEast"));
}
else if (direction == "west") {
mappingGroup.addKeyword(PvlKeyword("LongitudeDirection", "PositiveWest"));
}
else {
QString msg = "Unknown direction [" + QString::fromStdString(direction) + "]";
throw IException(IException::Programmer, msg, _FILEINFO_);
}

if (oSRS.GetSemiMajor() == oSRS.GetSemiMinor()) {
mappingGroup.addKeyword(PvlKeyword("LatitudeType", "Planetocentric"));
}
else {
mappingGroup.addKeyword(PvlKeyword("LatitudeType", "Planetographic"));
}

mappingGroup.addKeyword(PvlKeyword("LongitudeDomain", "180"));
mappingGroup.addKeyword(PvlKeyword("ProjStr", qProjStr));

// Read the GeoTransform and get the elements we care about
double *padfTransform = new double[6];
dataset->GetGeoTransform(padfTransform);
if (abs(padfTransform[1]) != abs(padfTransform[5])) {
delete[] padfTransform;
QString msg = "Vertical and horizontal resolution do not match";
throw IException(IException::Io, msg, _FILEINFO_);
}

double dfScale;
double dfRes;
double upperLeftX;
double upperLeftY;
dfRes = padfTransform[1] * oSRS.GetLinearUnits();
upperLeftX = padfTransform[0];
upperLeftY = padfTransform[3];
if (oSRS.IsProjected()) {
const double dfDegToMeter = oSRS.GetSemiMajor() * M_PI / 180.0;
dfScale = dfDegToMeter / dfRes;
mappingGroup.addKeyword(PvlKeyword("PixelResolution", toString(dfRes), "meters/pixel"));
}
else if (oSRS.IsGeographic()) {
dfScale = 1.0 / dfRes;
mappingGroup.addKeyword(PvlKeyword("PixelResolution", toString(dfRes), "degrees/pixel"));
}
else {
QString msg = "Gdal spatial reference is not Geographic or Projected";
throw IException(IException::Io, msg, _FILEINFO_);
}
mappingGroup.addKeyword(PvlKeyword("Scale", toString(dfScale), "pixels/degree"));
mappingGroup.addKeyword(PvlKeyword("UpperLeftCornerX", toString(upperLeftX)));
mappingGroup.addKeyword(PvlKeyword("UpperLeftCornerY", toString(upperLeftY)));
delete[] padfTransform;

PvlObject &isiscube = m_label->findObject("IsisCube");
if (isiscube.hasGroup("Mapping")) {
isiscube.deleteGroup("Mapping");
}
isiscube.addGroup(mappingGroup);
}
GDALClose(dataset);
m_label = new Pvl(m_dataFileName->expanded());

GDALAccess eAccess = GA_ReadOnly;
if (access == "rw") {
Expand Down Expand Up @@ -1374,7 +1240,9 @@ namespace Isis {
// maxbyte = position after the cube DN data and labels
streampos maxbyte = (streampos) m_labelBytes;

maxbyte += (streampos) m_ioHandler->getDataSize();
if (labelsAttached() != ExternalLabel) {
maxbyte += (streampos) m_ioHandler->getDataSize();
}

// If EOF is too early, allocate space up to where we want the blob
if (endByte < maxbyte) {
Expand Down Expand Up @@ -2406,6 +2274,16 @@ namespace Isis {
* @return bool True if the BLOB was found
*/
bool Cube::hasBlob(const QString &name, const QString &type) {
if (gdalDataset()) {
string key = type.toStdString() + "_" + name.toStdString();
const char *jsonblobStr = gdalDataset()->GetMetadataItem(key.c_str(), "USGS");

if (jsonblobStr) {
return true;
}
return false;
}

for(int o = 0; o < label()->objects(); o++) {
PvlObject &obj = label()->object(o);
if (obj.isNamed(type)) {
Expand Down Expand Up @@ -2585,10 +2463,6 @@ namespace Isis {


GDALDataset *Cube::gdalDataset() const {
if (!m_geodataSet) {
QString msg = "No GDALDataset has been constructed";
throw IException(IException::Programmer, msg, _FILEINFO_);
}
return m_geodataSet;
}

Expand Down
3 changes: 1 addition & 2 deletions isis/src/base/objs/CubeAttribute/CubeAttribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ namespace Isis {

using CubeAttribute<CubeAttributeOutput>::toString;

static QString toString(Cube::Format);

private:
bool isByteOrder(QString attribute) const;
Expand All @@ -535,8 +536,6 @@ namespace Isis {
bool isPixelType(QString attribute) const;
bool isRange(QString attribute) const;

static QString toString(Cube::Format);

/**
* @brief Output cube range tracker
*
Expand Down
1 change: 0 additions & 1 deletion isis/src/base/objs/CubeManager/CubeManager.truth
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,4 @@ Setting number of open cubes > 60 percent of system open file limit

Attempting to open a file that does not exist:
**I/O ERROR** Failed to open [dne.cub].
**PROGRAMMER ERROR** Gdal failed to open [dne.cub].
**I/O ERROR** Unable to open [dne.cub].
3 changes: 1 addition & 2 deletions isis/src/base/objs/ProcessMapMosaic/ProcessMapMosaic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,7 @@ namespace Isis {
double xmin, double xmax, double ymin, double ymax,
double slat, double elat, double slon, double elon, int nbands,
CubeAttributeOutput &oAtt, const QString &mosaicFile, bool latlonflag) {
Cube cube(inputFile);
Pvl fileLab = *cube.label();
Pvl fileLab(inputFile);
PvlGroup &mapping = fileLab.findGroup("Mapping", Pvl::Traverse);

// All mosaicking programs use only the upper left x and y to determine where to
Expand Down
Loading