diff --git a/environment.yml b/environment.yml index d0441833c7..bdbf5cc5fd 100644 --- a/environment.yml +++ b/environment.yml @@ -5,8 +5,8 @@ dependencies: - ale =0.10.0,<1 - aom - armadillo - - boost >=1.78.0,<1.79 - - boost-cpp >=1.78.0,<1.79 + - boost >=1.76.0,<1.82 + - boost-cpp >=1.76.0,<1.82 - blas - usgs-astrogeology::bullet - bz2file @@ -22,6 +22,7 @@ dependencies: - ffmpeg - geos >=3.12, <3.13 - geotiff + - gdal >=3.5 ,<3.9.0a0 - gmp - graphviz - conda-forge::gsl >=2.6, <2.7 @@ -34,10 +35,10 @@ dependencies: - kalasiris - krb5 - libgdal >=3.7,<3.8 - - libopencv >=4.7,<4.8 + - libopencv >=4.5,<4.8 - libpng >=1.6.34,<1.7 - libprotobuf - - libtiff >=4.5,<4.6 + - libtiff >=4.5,<4.8 - libxml2 - make - mesalib @@ -47,17 +48,17 @@ dependencies: - nlohmann_json=3.11.2 - ninja >=1.7.2,<1.8 - nn - - opencv >=4.7,<4.8 + - opencv >=4.5,<4.8 - openssl >=3.1.2,<3.2 - pcl >=1.13,<1.14 - plio - protobuf - - python >=3.10 - - python_abi >=3.10 + - python >=3.9 + - python_abi >=3.9 - pytest - rclone - qhull - - qt-main>=5.15.8, <5.16 + - qt-main>=5.15, <5.16 - qwt <6.3.0 - sqlite >=3.46.0,<3.47 - suitesparse <7.7.0 diff --git a/gtest b/gtest index b10fad38c4..f8d7d77c06 160000 --- a/gtest +++ b/gtest @@ -1 +1 @@ -Subproject commit b10fad38c4026a29ea6561ab15fc4818170d1c10 +Subproject commit f8d7d77c06936315286eb55f8de22cd23c188571 diff --git a/isis/CMakeLists.txt b/isis/CMakeLists.txt index 8584042726..8bdf20b301 100644 --- a/isis/CMakeLists.txt +++ b/isis/CMakeLists.txt @@ -267,6 +267,7 @@ find_package(CSM 3.0.3.3 REQUIRED) find_package(CSPICE 65 REQUIRED) find_package(Eigen REQUIRED) find_package(Embree 3.13.0 REQUIRED) +find_package(GDAL REQUIRED CONFIG) find_package(GeoTIFF 2 REQUIRED) SET(GSL_ROOT_DIR $ENV{CONDA_PREFIX}) find_package(GSL 2.2.1 REQUIRED) @@ -353,7 +354,7 @@ foreach (_variableName ${_variableNames}) endforeach() # add target based linkages to ALLLIBS variable -list(APPEND ALLLIBS pantor::inja sensorutilities protobuf::libprotobuf embree) +list(APPEND ALLLIBS pantor::inja sensorutilities protobuf::libprotobuf embree GDAL::GDAL) # Sometimes we add the same lib more than once (especially with LIBDIRS) list(REMOVE_DUPLICATES ALLLIBDIRS) diff --git a/isis/src/apollo/apps/apollopanstitcher/apollopanstitcher.cpp b/isis/src/apollo/apps/apollopanstitcher/apollopanstitcher.cpp index 4a4ad3a414..de48692a9a 100644 --- a/isis/src/apollo/apps/apollopanstitcher/apollopanstitcher.cpp +++ b/isis/src/apollo/apps/apollopanstitcher/apollopanstitcher.cpp @@ -715,11 +715,11 @@ namespace Isis { att.setFileFormat( panC[0]->format() ); att.setByteOrder( panC[0]->byteOrder() ); att.setPixelType( panC[0]->pixelType() ); - if (panC[0]->labelsAttached()) { - att.setLabelAttachment(AttachedLabel); + if (panC[0]->labelsAttached() == Cube::AttachedLabel) { + att.setLabelAttachment(Cube::AttachedLabel); } else { - att.setLabelAttachment(DetachedLabel); + att.setLabelAttachment(Cube::DetachedLabel); } //define an output cube diff --git a/isis/src/base/apps/cathist/main.cpp b/isis/src/base/apps/cathist/main.cpp index 4280d3ad27..ccdd311f9d 100644 --- a/isis/src/base/apps/cathist/main.cpp +++ b/isis/src/base/apps/cathist/main.cpp @@ -26,9 +26,17 @@ void IsisMain() { } // Extract history from file - Blob historyBlob("IsisCube", "History", fromfile.expanded()); - History hist(historyBlob); - Pvl pvl = hist.ReturnHist(); + Pvl pvl; + try { + Blob historyBlob("IsisCube", "History", fromfile.expanded()); + History hist(historyBlob); + pvl = hist.ReturnHist(); + } + catch (...) { + Cube cube(fromfile); + History hist = cube.readHistory(); + pvl = hist.ReturnHist(); + } // Print full history if(mode == "FULL") { diff --git a/isis/src/base/apps/catlab/main.cpp b/isis/src/base/apps/catlab/main.cpp index f48d3a0525..a9595e3054 100644 --- a/isis/src/base/apps/catlab/main.cpp +++ b/isis/src/base/apps/catlab/main.cpp @@ -17,7 +17,14 @@ void IsisMain() { QString file = ui.GetCubeName("FROM"); // Extract label from file - Pvl label(file); + Pvl label; + try { + label = Pvl(file); + } + catch (...) { + Cube cube(file); + label = *cube.label(); + } // Output to file if entered if(ui.WasEntered("TO")) { diff --git a/isis/src/base/apps/catoriglab/main.cpp b/isis/src/base/apps/catoriglab/main.cpp index 6702a19274..2d14832746 100644 --- a/isis/src/base/apps/catoriglab/main.cpp +++ b/isis/src/base/apps/catoriglab/main.cpp @@ -22,9 +22,10 @@ void IsisMain() { UserInterface &ui = Application::GetUserInterface(); QString file = ui.GetCubeName("FROM"); - Pvl fromLabel(file); + Cube fromCube(file); + Pvl fromLabel = *(fromCube.label()); if ( fromLabel.hasObject("OriginalLabel") ) { - OriginalLabel origLab(file); + OriginalLabel origLab = fromCube.readOriginalLabel(); Pvl pvl = origLab.ReturnLabels(); if (ui.IsInteractive()) { Application::GuiLog(pvl); diff --git a/isis/src/base/apps/crop/crop.cpp b/isis/src/base/apps/crop/crop.cpp index 6943e20c3a..b7d0f3a970 100644 --- a/isis/src/base/apps/crop/crop.cpp +++ b/isis/src/base/apps/crop/crop.cpp @@ -140,7 +140,7 @@ namespace Isis { } // Read the table into a table object - Table table(obj["Name"], from); + Table table = cube->readTable(obj["Name"]); // We are not going to bother with line/sample associations; they apply // only to the alpha cube at this time. I'm leaving this code here for the diff --git a/isis/src/base/apps/noproj/noproj.cpp b/isis/src/base/apps/noproj/noproj.cpp index 021792ec28..76a44d56ab 100644 --- a/isis/src/base/apps/noproj/noproj.cpp +++ b/isis/src/base/apps/noproj/noproj.cpp @@ -191,7 +191,7 @@ namespace Isis { CubeAttributeOutput cao; // Can we do a regular label? Didn't work on 12-15-2006 - cao.setLabelAttachment(Isis::DetachedLabel); + cao.setLabelAttachment(Cube::DetachedLabel); FileName matchCubeFile = FileName::createTempFile("$Temporary/match.cub"); QString matchCubeFileNoExt = matchCubeFile.path() + "/" + matchCubeFile.baseName(); diff --git a/isis/src/base/apps/shadow/ShadowFunctor.cpp b/isis/src/base/apps/shadow/ShadowFunctor.cpp index 02447dbfae..8bdf79e031 100644 --- a/isis/src/base/apps/shadow/ShadowFunctor.cpp +++ b/isis/src/base/apps/shadow/ShadowFunctor.cpp @@ -394,7 +394,7 @@ namespace Isis { Portal portal(3, 3, m_inputDem->pixelType(), -0.5, -0.5); - portal.SetPosition(sample, line, input.Band()); + portal.SetPosition(sample + 1, line + 1, input.Band()); if (!portal.CopyOverlapFrom(input)) { m_inputDem->read(portal); diff --git a/isis/src/base/apps/spiceinit/spiceinit.cpp b/isis/src/base/apps/spiceinit/spiceinit.cpp index cd9f23d493..611a0b96cc 100644 --- a/isis/src/base/apps/spiceinit/spiceinit.cpp +++ b/isis/src/base/apps/spiceinit/spiceinit.cpp @@ -263,7 +263,6 @@ namespace Isis { } } icube->deleteGroup("CsmInfo"); - p.WriteHistory(*icube); p.EndProcess(); } @@ -592,7 +591,6 @@ namespace Isis { icube->putGroup(originalKernels); return false; } - return true; } diff --git a/isis/src/base/apps/tabledump/main.cpp b/isis/src/base/apps/tabledump/main.cpp index 1880572f58..898fc4ada1 100644 --- a/isis/src/base/apps/tabledump/main.cpp +++ b/isis/src/base/apps/tabledump/main.cpp @@ -27,7 +27,14 @@ void IsisMain() { UserInterface &ui = Application::GetUserInterface(); FileName file = ui.GetCubeName("FROM"); QString tableName = ui.GetString("NAME"); - Table table(tableName, file.expanded()); + Table table(tableName); + try { + table = Table(tableName, file.expanded()); + } + catch (...) { + Cube fromCube(file); + table = fromCube.readTable(tableName); + } // Set the character to separate the entries QString delimit; @@ -147,7 +154,14 @@ void helperButtonGetTableList() { UserInterface &ui = Application::GetUserInterface(); QString currentFile = ui.GetCubeName("FROM"); - const Pvl label(FileName(currentFile).expanded()); + Pvl label; + try { + label = Pvl(FileName(currentFile).expanded()); + } + catch (...) { + Cube cube(FileName(currentFile).expanded()); + label = *cube.label(); + } // Check to see if the "FILE" parameter has changed since last press if (currentFile != g_previousFile) { diff --git a/isis/src/base/objs/Blob/Blob.cpp b/isis/src/base/objs/Blob/Blob.cpp index 42791b03d9..5dfd0d38c0 100644 --- a/isis/src/base/objs/Blob/Blob.cpp +++ b/isis/src/base/objs/Blob/Blob.cpp @@ -9,6 +9,8 @@ find files of those names at the top level of this repository. **/ #include #include #include +#include +#include #include @@ -17,7 +19,11 @@ find files of those names at the top level of this repository. **/ #include "Message.h" #include "Pvl.h" +namespace fs = std::filesystem; +using json = nlohmann::json; +using ordered_json = nlohmann::ordered_json; using namespace std; + namespace Isis { /** * Constructs a Blob object using a name and type. @@ -244,6 +250,33 @@ namespace Isis { } } + void Blob::ReadGdal(GDALDataset *dataset) { + try { + std::string key = QString(p_type + "_" + p_blobName).toStdString(); + const char *jsonblobStr = dataset->GetMetadataItem(key.c_str(), "USGS"); + if (jsonblobStr == nullptr) { + QString msg = "The key [" + QString::fromStdString(key) + "] does not exist on the geodata set."; + throw IException( IException::Io, msg, _FILEINFO_); + } + ordered_json jsonblob = ordered_json::parse(jsonblobStr); + std::string blobData = jsonblob[key]["Data"]; + jsonblob[key].erase("Data"); + + Pvl pvl; + Pvl::readObject(pvl, jsonblob); + + p_blobName = QString::fromStdString(jsonblob[key]["Name"]); + p_type = QString::fromStdString(jsonblob[key]["_container_name"]); + + Find(pvl); + ReadData(blobData); + } + catch(exception &e) { + QString msg = "Failed to read blob [" + p_blobName + "]: " + e.what(); + throw IException(IException::Io, msg, _FILEINFO_); + } + } + /** * This method reads Pvl values from a specified file. * @@ -255,7 +288,7 @@ namespace Isis { void Blob::Read(const QString &file, const std::vector keywords) { // Expand the filename QString temp(FileName(file).expanded()); - + // Get the pvl Pvl pvl; try { @@ -351,7 +384,8 @@ namespace Isis { void Blob::ReadData(std::istream &stream) { // Read the binary data if (p_buffer != NULL) delete [] p_buffer; - p_buffer = new char[p_nbytes]; + p_buffer = new char[p_nbytes + 1]; + p_buffer[p_nbytes] = '\0'; streampos sbyte = p_startByte - 1; stream.seekg(sbyte, std::ios::beg); @@ -380,8 +414,16 @@ namespace Isis { * @param nbytes The amount of data in the buffer */ void Blob::setData(const char *buffer, int nbytes) { - char *buf = new char[nbytes]; - memcpy(buf, buffer, nbytes); + char *buf; + if (buffer[nbytes - 1] != '\0') { + buf = new char[nbytes + 1]; + memcpy(buf, buffer, nbytes); + buf[nbytes] = '\0'; + } + else { + buf = new char[nbytes]; + memcpy(buf, buffer, nbytes); + } takeData(buf, nbytes); } @@ -405,6 +447,46 @@ namespace Isis { p_buffer = buffer; } + void Blob::WriteGdal(GDALDataset *dataset) { + try { + WriteInit(); + Pvl pvl; + pvl.addObject(p_blobPvl); + ostringstream os; + os << pvl << endl; + os.seekp(0, std::ios::end); + + stringstream stream; + WriteData(stream); + PvlObject &blobObj = pvl.findObject(p_type); + + blobObj["Bytes"] = toString(p_nbytes); + blobObj["StartByte"] = toString(1); + if(blobObj.hasKeyword("Data")) { + blobObj["Data"] = QString::fromStdString(stream.str()); + } + else { + blobObj += PvlKeyword("Data", QString::fromStdString(stream.str())); + } + + if(blobObj.hasKeyword("Name")) { + blobObj["Name"] = p_blobName; + } + else { + blobObj += PvlKeyword("Name", p_blobName); + } + + // update metadata + string jsonblobstr = pvl.toJson().dump(); + string key = this->Type().toStdString() + "_" + this->Name().toStdString(); + dataset->SetMetadataItem(key.c_str(), jsonblobstr.c_str(), "USGS"); + } + catch(exception &e) { + cout << "Failed to write blob [" + p_blobName + "]: " << e.what() << endl; + } + } + + /** * Write the blob data out to a file. * @@ -415,7 +497,7 @@ namespace Isis { * @throws IException::Io - Error creating file */ void Blob::Write(const QString &file) { - // Determine the size of the label and write it out + // Determine the size of the label and write it out try { WriteInit(); Pvl pvl; @@ -456,6 +538,7 @@ namespace Isis { } } + /** * Write the blob data out to a Pvl object. * @param pvl The pvl object to update @@ -464,10 +547,10 @@ namespace Isis { * the name of the file */ void Blob::Write(Pvl &pvl, std::fstream &stm, - const QString &detachedFileName, bool overwrite) { + const QString &detachedFileName, bool overwrite, bool inline_data) { + // Handle 64-bit I/O WriteInit(); - // Find out where they wanted to write the blob streampos sbyte = stm.tellp(); sbyte += 1; @@ -482,11 +565,9 @@ namespace Isis { p_blobPvl += PvlKeyword("^" + p_type, detachedFileName); } - p_blobPvl["StartByte"] = toString((BigInt)sbyte); p_blobPvl["Bytes"] = toString(p_nbytes); - // See if the blob is already in the file bool found = false; if (overwrite) { @@ -570,6 +651,55 @@ namespace Isis { } + /** + * Writes blob data to a stream + * + * @param stream Output stream blob data will be written to + * + * @throws IException::Io - Error writing data to stream + */ + void Blob::WriteData(std::stringstream &stream) { + stream << std::hex << std::setfill('0'); + int copy = 0; + for (int i = 0; i < p_nbytes; ++i) { + memcpy(©, &(p_buffer[i]), 1); + stream << std::setw(2) << copy; + } + + if (!stream.good()) { + QString msg = "Error writing data to " + p_type + " [" + p_blobName + "]"; + throw IException(IException::Io, msg, _FILEINFO_); + } + } + + + /** + * Writes blob data to a stream + * + * @param stream Output steam blob data will be written to + * + * @throws IException::Io - Error writing data to stream + */ + void Blob::ReadData(string &hexdata) { + // Read the binary data + if (p_buffer != NULL) delete [] p_buffer; + p_buffer = new char[p_nbytes + 1]; + p_buffer[p_nbytes] = '\0'; + + // Loop through the hex string and bytes, hex is two characters at a time + for (size_t i=0,j=0; i < p_nbytes; i++,j+=2) { + string byteString = hexdata.substr(j, 2); + + char byteValue = static_cast( + stoi(byteString, nullptr, 16)); + + // Add the byte to the byte array + p_buffer[i] = byteValue; + } + } + + + /** * Checks pvl object and returns whether or not it is a Blob * diff --git a/isis/src/base/objs/Blob/Blob.h b/isis/src/base/objs/Blob/Blob.h index 1abf7828e4..f431171d03 100644 --- a/isis/src/base/objs/Blob/Blob.h +++ b/isis/src/base/objs/Blob/Blob.h @@ -11,6 +11,8 @@ find files of those names at the top level of this repository. **/ #include #include +#include "gdal_priv.h" + #include "PvlObject.h" namespace Isis { @@ -70,11 +72,13 @@ namespace Isis { const std::vector keywords = std::vector()); virtual void Read(const Pvl &pvl, std::istream &is, const std::vector keywords = std::vector()); + void ReadData(std::string &hexdata); void Write(const QString &file); void Write(Pvl &pvl, std::fstream &stm, - const QString &detachedFileName = "", bool overwrite=true); - + const QString &detachedFileName = "", bool overwrite=true, bool inline_data=true); + void WriteGdal(GDALDataset *dataset); + void ReadGdal(GDALDataset *dataset); char *getBuffer(); void setData(const char *buffer, int nbytes); @@ -86,6 +90,7 @@ namespace Isis { virtual void ReadData(std::istream &is); virtual void WriteInit(); virtual void WriteData(std::fstream &os); + virtual void WriteData(std::stringstream &os); PvlObject p_blobPvl; //!< Pvl Blob object QString p_blobName; //!< Name of the Blob object diff --git a/isis/src/base/objs/Blob/Blob.truth b/isis/src/base/objs/Blob/Blob.truth deleted file mode 100644 index 0079631fb3..0000000000 --- a/isis/src/base/objs/Blob/Blob.truth +++ /dev/null @@ -1,27 +0,0 @@ -Testing Blob(name, type) constructor and Write(filename) method... -Name = UnitTest -Number of Bytes = 4 -StartByte = 150 -Type = Blob - -Testing Blob(name, type, file) constructor... -Name = UNITtest -Number of Bytes = 4 -StartByte = 150 -Type = Blob -ABCD - -Testing writing into existing space... -Testing Write(pvl, stream)... -Name = UNITtest -Number of Bytes = 3 -StartByte = 150 -Type = Blob - -Testing writing over existing space at the end of file... -Testing Write(pvl, stream)... -Name = UNITtest -Number of Bytes = 4 -StartByte = 150 -Type = Blob - diff --git a/isis/src/base/objs/Blob/unitTest.cpp b/isis/src/base/objs/Blob/unitTest.cpp deleted file mode 100644 index 56113ace38..0000000000 --- a/isis/src/base/objs/Blob/unitTest.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/** This is free and unencumbered software released into the public domain. -The authors of ISIS do not claim copyright on the contents of this file. -For more details about the LICENSE terms and the AUTHORS, you will -find files of those names at the top level of this repository. **/ - -/* SPDX-License-Identifier: CC0-1.0 */ -#include -#include "Blob.h" -#include "IException.h" -#include "IString.h" -#include "Preference.h" - -using namespace Isis; -using namespace std; - -class MyBlob : public Isis::Blob { - public: - MyBlob(const QString &name) : Isis::Blob(name, "Blob") {}; - MyBlob(const QString &name, const QString &file) : Isis::Blob(name, "Blob", file) {}; - void MyBuf(char *buf, int size) { - p_buffer = new char[size]; - p_nbytes = size; - memcpy(p_buffer, buf, size); - }; - int StartByte() { - return p_startByte; - }; - void GetBuf(char *buf) { - memcpy(buf, p_buffer, p_nbytes); - } -}; - -int main(int argc, char *argv[]) { - Isis::Preference::Preferences(true); - try { - cout << "Testing Blob(name, type) constructor and Write(filename) method..." << endl; - MyBlob b("UnitTest"); - char buf[] = {"ABCD"}; - b.MyBuf(buf, 4); - b.Write("junk"); - cout << "Name = " << b.Name() << endl; - cout << "Number of Bytes = " << b.Size() << endl; - cout << "StartByte = " << b.StartByte() << endl; - cout << "Type = " << b.Type() << endl; - cout << endl; - - cout << "Testing Blob(name, type, file) constructor..." << endl; - MyBlob c("UNITtest", "junk"); - char buf2[5]; - c.GetBuf(buf2); - buf2[4] = 0; - cout << "Name = " << c.Name() << endl; - cout << "Number of Bytes = " << c.Size() << endl; - cout << "StartByte = " << c.StartByte() << endl; - cout << "Type = " << c.Type() << endl; - cout << buf2 << endl; - cout << endl; - - // Test writing into existing space - cout << "Testing writing into existing space..." << endl; - cout << "Testing Write(pvl, stream)..." << endl; - Isis::Pvl pvl("junk"); - fstream strm; - strm.open("junk", std::ios::binary | std::ios::out); - c.MyBuf(buf, 3); - c.Write(pvl, strm); - strm.seekp(0, std::ios::beg); - strm << pvl; - cout << "Name = " << c.Name() << endl; - cout << "Number of Bytes = " << c.Size() << endl; - cout << "StartByte = " << c.StartByte() << endl; - cout << "Type = " << c.Type() << endl; - cout << endl; - strm.close(); - - // Test writing over existing space at the end of file - cout << "Testing writing over existing space at the end of file..." << endl; - cout << "Testing Write(pvl, stream)..." << endl; - Isis::Pvl pvl2("junk"); - fstream strm2; - strm2.open("junk", std::ios::binary | std::ios::out); - c.MyBuf(buf, 4); - c.Write(pvl2, strm2); - strm2.seekp(0, std::ios::beg); - strm2 << pvl2; - cout << "Name = " << c.Name() << endl; - cout << "Number of Bytes = " << c.Size() << endl; - cout << "StartByte = " << c.StartByte() << endl; - cout << "Type = " << c.Type() << endl; - cout << endl; - strm2.close(); - - remove("junk"); - - } - catch(Isis::IException &e) { - e.print(); - } -} - diff --git a/isis/src/base/objs/Brick/Brick.cpp b/isis/src/base/objs/Brick/Brick.cpp index 630458f0d0..c0bb72a675 100644 --- a/isis/src/base/objs/Brick/Brick.cpp +++ b/isis/src/base/objs/Brick/Brick.cpp @@ -22,7 +22,27 @@ namespace Isis { p_nsamps = nsamps; p_nlines = nlines; p_nbands = nbands; - p_npixels = p_nsamps * p_nlines * p_nbands; + if(p_nsamps <= 0) { + std::string message = "Invalid value for sample dimensions (nsamps)"; + throw IException(IException::Programmer, message, _FILEINFO_); + } + if(p_nlines <= 0) { + std::string message = "Invalid value for line dimensions (nlines)"; + throw IException(IException::Programmer, message, _FILEINFO_); + } + if(p_nbands <= 0) { + std::string message = "Invalid value for band dimensions (nbands)"; + throw IException(IException::Programmer, message, _FILEINFO_); + } + if (int(p_nsamps * p_scale) <= 0) { + std::string message = "Scaleing [" + std::to_string(p_nsamps) + "] by [" + std::to_string(p_scale) + "] resulted in 0 samples in the buffer"; + throw IException(IException::Programmer, message, _FILEINFO_); + } + if (int(p_nlines * p_scale) <= 0) { + std::string message = "Scaleing [" + std::to_string(p_nlines) + "] by [" + std::to_string(p_scale) + "] resulted in 0 lines in the buffer"; + throw IException(IException::Programmer, message, _FILEINFO_); + } + p_npixels = (int(p_nsamps * p_scale) * int(p_nlines * p_scale)) * p_nbands; Allocate(); } diff --git a/isis/src/base/objs/Brick/Brick.h b/isis/src/base/objs/Brick/Brick.h index 29ce6c54cc..25494657e4 100644 --- a/isis/src/base/objs/Brick/Brick.h +++ b/isis/src/base/objs/Brick/Brick.h @@ -58,9 +58,9 @@ namespace Isis { * bands first, then lines, then samples. */ Brick(const int nsamps, const int nlines, const int nbands, - const Isis::PixelType type, bool reverse=false) : + const Isis::PixelType type, bool reverse=false, double scale=1.0) : Isis::BufferManager(nsamps, nlines, nbands, - nsamps, nlines, nbands, type, reverse) { + nsamps, nlines, nbands, type, reverse, scale) { }; /** @@ -78,10 +78,10 @@ namespace Isis { */ Brick(const Isis::Cube &cube, const int &bufNumSamples, const int &bufNumLines, const int &bufNumBands, - bool reverse=false) : + bool reverse=false, double scale=1.0) : Isis::BufferManager(cube.sampleCount(), cube.lineCount(), cube.bandCount(), bufNumSamples, bufNumLines, - bufNumBands, cube.pixelType(), reverse) { + bufNumBands, cube.pixelType(), reverse, scale) { }; /** @@ -103,9 +103,9 @@ namespace Isis { */ Brick(int maxSamples, int maxLines, int maxBands, int bufNumSamples, int bufNumLines, int bufNumBands, Isis::PixelType type, - bool reverse=false) : + bool reverse=false, double scale=1.0) : Isis::BufferManager(maxSamples, maxLines, maxBands, bufNumSamples, - bufNumLines, bufNumBands, type, reverse) { + bufNumLines, bufNumBands, type, reverse, scale) { }; public: diff --git a/isis/src/base/objs/Buffer/Buffer.cpp b/isis/src/base/objs/Buffer/Buffer.cpp index 0757c99bc3..cf00d406e7 100644 --- a/isis/src/base/objs/Buffer/Buffer.cpp +++ b/isis/src/base/objs/Buffer/Buffer.cpp @@ -5,10 +5,11 @@ find files of those names at the top level of this repository. **/ /* SPDX-License-Identifier: CC0-1.0 */ -#include "PixelType.h" #include "Buffer.h" #include "IException.h" #include "Message.h" +#include "PixelType.h" +#include "SpecialPixel.h" #include @@ -24,7 +25,7 @@ namespace Isis { */ Buffer::Buffer() : p_sample(0), p_nsamps(0), p_line(0), p_nlines(0), p_band(0), p_nbands(0), p_npixels(0), p_buf(0), - p_pixelType(None), p_rawbuf(0) { } + p_pixelType(None), p_rawbuf(0), p_scale(1) { } /** @@ -39,9 +40,11 @@ namespace Isis { * @throws Isis::iException::Programmer - Invalid value for a dimension */ Buffer::Buffer(const int nsamps, const int nlines, - const int nbands, const Isis::PixelType type) : + const int nbands, const Isis::PixelType type, + const double scale) : p_nsamps(nsamps), p_nlines(nlines), - p_nbands(nbands), p_pixelType(type) { + p_nbands(nbands), p_pixelType(type), + p_scale(scale) { p_sample = p_line = p_band = 0; @@ -57,8 +60,20 @@ namespace Isis { string message = "Invalid value for band dimensions (nbands)"; throw IException(IException::Programmer, message, _FILEINFO_); } + if(p_scale <= 0 || scale > 1) { + string message = "Invalid value for scale (scale)"; + throw IException(IException::Programmer, message, _FILEINFO_); + } + p_nsampsScaled = int(p_nsamps * p_scale); + if (p_nsampsScaled <= 0) { + p_nsampsScaled = 1; + } + p_nlinesScaled = int(p_nlines * p_scale); + if (p_nlinesScaled <= 0) { + p_nlinesScaled = 1; + } - p_npixels = p_nsamps * p_nlines * p_nbands; + p_npixels = (p_nsampsScaled * p_nlinesScaled) * p_nbands; Allocate(); } @@ -111,6 +126,29 @@ namespace Isis { SetBaseBand(start_band); } + /** + * This method is used to print out a buffer + * + * @param os Output stream to write to + * @param buffer The buffer to write to the output stream + * + * @return The updated output stream + */ + std::ostream &operator<<(std::ostream &os, Buffer &buffer) { + for(int i = 1; i < buffer.size() + 1; i++) { + os << buffer[i - 1]; + + if ((i % buffer.SampleDimensionScaled()) == 0) { + os << std::endl; + } + if ((i % (buffer.SampleDimensionScaled() * buffer.LineDimensionScaled())) == 0) { + os << std::endl; + } + } + + return os; + } + /** * Returns the sample position associated with a shape buffer index. The @@ -212,9 +250,10 @@ namespace Isis { } // Got a valid reference location so compute the index and return - int index = (i_band - p_band) * (p_nlines * p_nsamps) + - (i_line - p_line) * (p_nsamps) + - (i_samp - p_sample); + int bandOffset = (i_band - p_band) * (p_nsampsScaled * p_nlinesScaled); + int lineOffset = (int((i_line - p_line) * p_scale) % p_nlinesScaled) * p_nsampsScaled; + int sampleOffset = (int((i_samp - p_sample) * p_scale)); + int index = bandOffset + lineOffset + sampleOffset; return (index); } @@ -283,7 +322,45 @@ namespace Isis { * @return The operation was successful (the buffers overlapped) */ bool Buffer::CopyOverlapFrom(const Buffer &in) { - bool isSubareaOfIn = (p_npixels <= in.size()); + bool isSubareaOfIn = true; + + // If one rectangle is on left side of other + // if (l1.x > r2.x || l2.x > r1.x) + if (p_line > in.p_line + in.p_nlines || + in.p_line > p_line + p_nlines) + isSubareaOfIn = false; + + // If one rectangle is above other + // if (r1.y > l2.y || r2.y > l1.y) + if (p_sample + p_nsamps < in.p_sample || + in.p_sample + in.p_nsamps < p_sample) + isSubareaOfIn = false; + + if (isSubareaOfIn) { + int topLine = max(in.p_line, p_line); + int topSamp = max(in.p_sample, p_sample); + + int bottomLine = min(in.p_line + in.p_nlines, p_line + p_nlines); + int bottomSamp = min(in.p_sample + in.p_nsamps, p_sample + p_nsamps); + + int firstBand = max(in.p_band, p_band); + int lastBand = min(in.p_band + in.p_nbands, p_band + p_nbands); + + for (int b = firstBand; b < lastBand; b++) { + for (int i = topLine; i < bottomLine; i++) { + for (int j = topSamp; j < bottomSamp; j++) { + try { + (*this)[Index(j, i, b)] = in[in.Index(j, i, b)]; + } + catch(...) { + (*this)[Index(j, i, b)] = NULL8; + } + } + } + } + } + + isSubareaOfIn = (p_npixels <= in.size()); isSubareaOfIn &= (p_sample >= in.p_sample); isSubareaOfIn &= (p_line >= in.p_line); isSubareaOfIn &= (p_band >= in.p_band); @@ -301,12 +378,6 @@ namespace Isis { isSubareaOfIn &= (endLine <= otherEndLine); isSubareaOfIn &= (endBand <= otherEndBand); - if (isSubareaOfIn) { - for (int i = 0; i < size(); i++) { - (*this)[i] = in[in.Index(Sample(i), Line(i), Band(i))]; - } - } - return isSubareaOfIn; } @@ -322,10 +393,13 @@ namespace Isis { p_nbands(rhs.p_nbands), p_pixelType(rhs.p_pixelType) { p_sample = rhs.p_sample; + p_nsampsScaled = rhs.p_nsampsScaled; p_line = rhs.p_line; + p_nlinesScaled = rhs.p_nlinesScaled; p_band = rhs.p_band; p_npixels = rhs.p_npixels; + p_scale = rhs.p_scale; Allocate(); Copy(rhs); @@ -364,5 +438,6 @@ namespace Isis { QString message = Message::MemoryAllocationFailed(); throw IException(IException::Unknown, message, _FILEINFO_); } + (*this) = NULL8; } } diff --git a/isis/src/base/objs/Buffer/Buffer.h b/isis/src/base/objs/Buffer/Buffer.h index f0e5298602..90f2d0918c 100644 --- a/isis/src/base/objs/Buffer/Buffer.h +++ b/isis/src/base/objs/Buffer/Buffer.h @@ -54,7 +54,7 @@ namespace Isis { public: Buffer(); Buffer(const int nsamps, const int nlines, const int nbands, - const Isis::PixelType type); + const Isis::PixelType type, const double scale = 1); ~Buffer(); @@ -80,6 +80,24 @@ namespace Isis { return (p_nlines); } + /** + * Returns the number of samples in the shape buffer + * + * @return int + */ + inline int SampleDimensionScaled() const { + return (p_nsampsScaled); + } + + /** + * Returns the number of lines in the shape buffer + * + * @return int + */ + inline int LineDimensionScaled() const { + return (p_nlinesScaled); + } + /** * Returns the number of bands in the shape buffer * @@ -89,6 +107,15 @@ namespace Isis { return (p_nbands); } + /** + * Returns the scale of the shape buffer + * + * @return double + */ + inline double scale() const { + return (p_scale); + } + /** * Returns the total number of pixels in the shape buffer * @@ -161,10 +188,12 @@ namespace Isis { return p_pixelType; }; - protected: void SetBasePosition(const int start_sample, const int start_line, const int start_band); + friend std::ostream &operator<<(std::ostream &os, Buffer &buffer); + + protected: /** * This method is used to set the base sample position of the shape buffer. * @@ -197,20 +226,25 @@ namespace Isis { int p_sample; //!< Starting sample to read/write int p_nsamps; //!< Number of samples to read/write + int p_nsampsScaled; int p_line; //!< Starting line to read/write int p_nlines; //!< Number of lines to read/write + int p_nlinesScaled; int p_band; //!< Starting band to read/write int p_nbands; //!< Number of bands to read/write - int p_npixels; //!< Number of pixels (nsamps * nlines * nbands) + int p_npixels; //!< Number of pixels (nsamps * nlines * scale ** 2) * bands double *p_buf; /**< Shape buffer allocated to the size of npixels for handling reads/writes*/ const Isis::PixelType p_pixelType; //!< The pixel type of the raw buffer void *p_rawbuf; //!< The raw dm read from the disk + // Might need x and y scale + double p_scale; //!< Amount to scale the buffers lines and samples, defaults to 1 + void Allocate(); /** diff --git a/isis/src/base/objs/Buffer/Buffer.truth b/isis/src/base/objs/Buffer/Buffer.truth deleted file mode 100644 index 29652d2314..0000000000 --- a/isis/src/base/objs/Buffer/Buffer.truth +++ /dev/null @@ -1,53 +0,0 @@ -Isis::Buffer Unit Test - -SampleDimension: 4 -LineDimension: 3 -BandDimension: 2 -Size: 24 - -Sample(): 0 -Line(): 0 -Band(): 0 -Position: 0 0 0 -Index: 0 - -at(0): 0 -at(10): 10 -at(23): 23 -b[0]: 0 -b[10]: 10 -b[23]: 23 - -Sample(): 3 -Line(): 2 -Band(): 1 -Position: 3 2 1 -Index: 0 - -Sample(16): 3 -Line(16): 3 -Band(16): 2 -Position: 3 3 2 -Index: 16 - -Copy constructor: Worked -SampleDimension: 4 -LineDimension: 3 -BandDimension: 2 -Size: 24 -a[0]: 0 -a[23]: 23 - -PixelType = SignedInteger - -Null Buffer size: 0 - -Test assignment operator for a constant... -d.size(): 8 -d[0]: 999 -d[2]: 999 -d[n]: 999 - -**PROGRAMMER ERROR** Array subscript [-1] is out of array bounds. -**PROGRAMMER ERROR** Array subscript [24] is out of array bounds. -**PROGRAMMER ERROR** Input and output buffers are not the same pixel type. diff --git a/isis/src/base/objs/Buffer/unitTest.cpp b/isis/src/base/objs/Buffer/unitTest.cpp deleted file mode 100644 index 2babf6da8e..0000000000 --- a/isis/src/base/objs/Buffer/unitTest.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/** This is free and unencumbered software released into the public domain. -The authors of ISIS do not claim copyright on the contents of this file. -For more details about the LICENSE terms and the AUTHORS, you will -find files of those names at the top level of this repository. **/ - -/* SPDX-License-Identifier: CC0-1.0 */ -#include -#include "Buffer.h" -#include "IException.h" -#include "Preference.h" - -using namespace std; -using namespace Isis; - -class Test : public Buffer { - public: - Test(int s, int l, int b) : Isis::Buffer(s, l, b, Isis::SignedInteger) {}; - ~Test() {}; - void print(); -}; - -void Test::print() { - SetBasePosition(3, 2, 1); - cout << "Sample(): " << Sample() << endl; - cout << "Line(): " << Line() << endl; - cout << "Band(): " << Band() << endl; - - int samp, line, band; - Position(0, samp, line, band); - cout << "Position: " << samp << " " << line << " " << band - << endl; - cout << "Index: " << Index(samp, line, band) << endl << endl; - - cout << "Sample(16): " << Sample(16) << endl; - cout << "Line(16): " << Line(16) << endl; - cout << "Band(16): " << Band(16) << endl; - - Position(16, samp, line, band); - cout << "Position: " << samp << " " << line << " " << band - << endl; - cout << "Index: " << Index(samp, line, band) << endl << endl; -} - -int main(int argc, char *argv[]) { - Isis::Preference::Preferences(true); - cout << "Isis::Buffer Unit Test" << endl << endl; - - Test b(4, 3, 2); - for(int i = 0; i < b.size(); i++) { - b[i] = i; - } - - cout << "SampleDimension: " << b.SampleDimension() << endl; - cout << "LineDimension: " << b.LineDimension() << endl; - cout << "BandDimension: " << b.BandDimension() << endl; - cout << "Size: " << b.size() << endl << endl; - - cout << "Sample(): " << b.Sample() << endl; - cout << "Line(): " << b.Line() << endl; - cout << "Band(): " << b.Band() << endl; - - int samp, line, band; - b.Position(0, samp, line, band); - cout << "Position: " << samp << " " << line << " " << band - << endl; - cout << "Index: " << b.Index(samp, line, band) << endl << endl; - - cout << "at(0): " << b.at(0) << endl; - cout << "at(10): " << b.at(10) << endl; - cout << "at(23): " << b.at(23) << endl; - cout << "b[0]: " << b[0] << endl; - cout << "b[10]: " << b[10] << endl; - cout << "b[23]: " << b[23] << endl << endl; - - b.print(); - - Test a = b; - if(a.DoubleBuffer() != b.DoubleBuffer()) { - cout << "Copy constructor: Worked" << endl; - } - else { - cout << "Copy constructor: Failed" << endl; - } - cout << "SampleDimension: " << a.SampleDimension() << endl; - cout << "LineDimension: " << a.LineDimension() << endl; - cout << "BandDimension: " << a.BandDimension() << endl; - cout << "Size: " << a.size() << endl ; - cout << "a[0]: " << a[0] << endl; - cout << "a[23]: " << a[23] << endl << endl; - - cout << "PixelType = " << Isis::PixelTypeName(a.PixelType()) << endl; - cout << endl; - - // Test new default constructor. Enclose in braces so destructor is tested - { - Buffer nullbuf; - cout << "Null Buffer size: " << nullbuf.size() << endl << endl; - } - - // Test assignment operator - cout << "Test assignment operator for a constant...\n"; - Buffer d(2, 2, 2, Double); - d = 999.0; - cout << "d.size(): " << d.size() << endl; - cout << "d[0]: " << d[0] << endl; - cout << "d[2]: " << d[2] << endl; - cout << "d[n]: " << d[d.size()-1] << endl << endl; - - try { - a.at(-1); - } - catch(Isis::IException &e) { - e.print(); - } - - try { - a.at(24); - } - catch(Isis::IException &e) { - e.print(); - } - - try { - Buffer a2(2, 2, 2, Isis::SignedWord); - Buffer b2(2, 2, 2, Isis::SignedByte); - - a2.Copy(b2); - } - catch(Isis::IException &e) { - e.print(); - } - - return 0; -} diff --git a/isis/src/base/objs/BufferManager/BufferManager.cpp b/isis/src/base/objs/BufferManager/BufferManager.cpp index 7b2ff2fb24..3da87df37f 100644 --- a/isis/src/base/objs/BufferManager/BufferManager.cpp +++ b/isis/src/base/objs/BufferManager/BufferManager.cpp @@ -44,8 +44,9 @@ namespace Isis { BufferManager::BufferManager(int maxsamps, int maxlines, int maxbands, int bufsamps, int buflines, int bufbands, - Isis::PixelType type, bool reverse) : - Isis::Buffer(bufsamps, buflines, bufbands, type), + Isis::PixelType type, bool reverse, + double scale) : + Isis::Buffer(bufsamps, buflines, bufbands, type, scale), p_maxSamps(maxsamps), p_maxLines(maxlines), p_maxBands(maxbands) { SetIncrements(bufsamps, buflines, bufbands); diff --git a/isis/src/base/objs/BufferManager/BufferManager.h b/isis/src/base/objs/BufferManager/BufferManager.h index b034f146e2..746599704c 100644 --- a/isis/src/base/objs/BufferManager/BufferManager.h +++ b/isis/src/base/objs/BufferManager/BufferManager.h @@ -56,7 +56,8 @@ namespace Isis { BufferManager(); BufferManager(int maxsamps, int maxlines, int maxbands, int bufsamps, int buflines, int bufbands, - Isis::PixelType type, bool reverse = false); + Isis::PixelType type, bool reverse = false, + double scale = 1.0); BufferManager(const BufferManager &other); //! Destroys the BufferManager object diff --git a/isis/src/base/objs/Cube/Cube.cpp b/isis/src/base/objs/Cube/Cube.cpp index c4206bf0c1..1c8a5cd813 100644 --- a/isis/src/base/objs/Cube/Cube.cpp +++ b/isis/src/base/objs/Cube/Cube.cpp @@ -25,6 +25,7 @@ find files of those names at the top level of this repository. **/ #include "CubeStretch.h" #include "Endian.h" #include "FileName.h" +#include "GdalIoHandler.h" #include "History.h" #include "ImageHistogram.h" #include "ImagePolygon.h" @@ -42,12 +43,13 @@ find files of those names at the top level of this repository. **/ #include "TProjection.h" #include "Longitude.h" - using namespace std; namespace Isis { //! Constructs a Cube object. Cube::Cube() { + GDALAllRegister(); + CPLSetErrorHandler(CPLQuietErrorHandler); construct(); } @@ -60,6 +62,8 @@ namespace Isis { * "r" or read-write "rw". */ Cube::Cube(const FileName &fileName, QString access) { + GDALAllRegister(); + CPLSetErrorHandler(CPLQuietErrorHandler); construct(); open(fileName.toString(), access); } @@ -74,6 +78,8 @@ namespace Isis { * "r" or read-write "rw". */ void Cube::fromLabel(const FileName &fileName, Pvl &label, QString access) { + GDALAllRegister(); + CPLSetErrorHandler(CPLQuietErrorHandler); initCoreFromLabel(label); create(fileName.expanded()); @@ -97,6 +103,8 @@ namespace Isis { * "r" or read-write "rw". */ void Cube::fromIsd(const FileName &fileName, Pvl &label, nlohmann::json &isd, QString access) { + GDALAllRegister(); + CPLSetErrorHandler(CPLQuietErrorHandler); fromLabel(fileName, label, access); PvlGroup &instGrp = label.findGroup("Instrument", Pvl::Traverse); @@ -220,8 +228,18 @@ namespace Isis { throw IException(IException::Programmer, msg, _FILEINFO_); } - if ((m_labelFile->openMode() & QIODevice::ReadWrite) != QIODevice::ReadWrite) - readOnly = true; + if (m_labelFile) { + if ((m_labelFile->openMode() & QIODevice::ReadWrite) != QIODevice::ReadWrite) + readOnly = true; + } + else if (m_geodataSet) { + if (m_geodataSet->GetAccess() == GA_ReadOnly) + readOnly = true; + } + else { + QString msg = "No source files open"; + throw IException(IException::Programmer, msg, _FILEINFO_); + } return readOnly; } @@ -247,7 +265,7 @@ namespace Isis { * * @returns True for attached labels, false for detached */ - bool Cube::labelsAttached() const { + Cube::LabelAttachment Cube::labelsAttached() const { return m_attached; } @@ -284,15 +302,15 @@ namespace Isis { Cube *result = new Cube; + result->setLabelsAttached(newFileAttributes.labelAttachment()); - if (newFileAttributes.labelAttachment() != ExternalLabel) { + if (result->labelsAttached() != ExternalLabel) { result->setDimensions(sampleCount(), lineCount(), bandCount()); result->setByteOrder(newFileAttributes.byteOrder()); result->setFormat(newFileAttributes.fileFormat()); - if (newFileAttributes.labelAttachment() == DetachedLabel) { - result->setLabelsAttached(false); - } + // if (newFileAttributes.labelAttachment() == DetachedLabel) { + // } if (newFileAttributes.propagatePixelType()) { result->setPixelType(pixelType()); @@ -413,7 +431,7 @@ namespace Isis { * is forced). Environment variables in the filename will be * automatically expanded as well. */ - void Cube::create(const QString &cubeFileName) { + void Cube::create(const QString &imageFileName) { // Already opened? if (isOpen()) { string msg = "You already have a cube opened"; @@ -430,11 +448,11 @@ namespace Isis { if (m_pixelType == None) { throw IException(IException::Unknown, QString("Cannot create the cube [%1] with a pixel type set to None") - .arg(cubeFileName), + .arg(imageFileName), _FILEINFO_); } - if (m_storesDnData) { + if (labelsAttached() == LabelAttachment::AttachedLabel) { // Make sure the cube is not going to exceed the maximum size preference BigInt size = (BigInt)m_samples * m_lines * (BigInt)m_bands * (BigInt)SizeOf(m_pixelType); @@ -450,7 +468,7 @@ namespace Isis { if (size > maxSizePreference) { QString msg; - msg += "The cube you are attempting to create [" + cubeFileName + "] is [" + msg += "The cube you are attempting to create [" + imageFileName + "] is [" + toString(size) + "GB]. This is larger than the current allowed " "size of [" + toString(maxSizePreference) + "GB]. The cube " "dimensions were (S,L,B) [" + toString(m_samples) + ", " + @@ -465,59 +483,106 @@ namespace Isis { } // Expand output name - FileName cubFile(cubeFileName); + FileName imageFile(imageFileName); PvlObject isiscube("IsisCube"); PvlObject core("Core"); - if (m_storesDnData) { - cubFile = cubFile.addExtension("cub"); + // 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)); + + if (labelsAttached() != LabelAttachment::ExternalLabel) { + if (format() != Format::GTiff) { + imageFile = imageFile.addExtension("cub"); + } + else if (format() == Format::GTiff) { + imageFile = imageFile.addExtension("tiff"); + } + else { + QString msg = "Unknown format type [" + toString(format()) + "]"; + throw IException(IException::Programmer, msg, _FILEINFO_); + } + + m_dataFileName = new FileName(imageFile); // See if we have attached or detached labels - if (m_attached) { + if (labelsAttached() == LabelAttachment::AttachedLabel) { // StartByte is 1-based (why!!) so we need to do + 1 core += PvlKeyword("StartByte", toString(m_labelBytes + 1)); - m_labelFileName = new FileName(cubFile); - m_dataFileName = new FileName(cubFile); + m_labelFileName = new FileName(imageFile); m_labelFile = new QFile(m_labelFileName->expanded()); } - else { + else if (labelsAttached() == LabelAttachment::DetachedLabel) { core += PvlKeyword("StartByte", toString(1)); - core += PvlKeyword("^Core", cubFile.name()); - m_dataFileName = new FileName(cubFile); + core += PvlKeyword("^Core", imageFile.name()); m_dataFile = new QFile(realDataFileName().expanded()); - FileName labelFileName(cubFile); + FileName labelFileName(imageFile); labelFileName = labelFileName.setExtension("lbl"); m_labelFileName = new FileName(labelFileName); m_labelFile = new QFile(m_labelFileName->expanded()); } - - // 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)); + else { + QString msg = "Label type [" + LabelAttachmentName(labelsAttached()) + "] not supported"; + throw IException(IException::Programmer, msg, _FILEINFO_); + } core.addGroup(dims); - - // 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.addGroup(ptype); } - else { - cubFile = cubFile.addExtension("ecub"); + else if (labelsAttached() == LabelAttachment::ExternalLabel) { + if (!m_dataFileName) { + if (format() == Bsq || format() == Tile) { + imageFile = imageFile.addExtension("cub"); + + Pvl dnLabel; + PvlObject isiscube("IsisCube"); + PvlObject dnCore(core); + PvlKeyword fileFormat("Format", toString(format())); + + dnCore.addKeyword(fileFormat); + dnCore.addGroup(dims); + dnCore.addGroup(ptype); - core += PvlKeyword("^DnFile", m_dataFileName->original()); -// m_dataFileName = new FileName(cubFile); - m_dataFile = new QFile(realDataFileName().expanded()); + isiscube.addObject(dnCore); + dnLabel.addObject(isiscube); - m_labelFileName = new FileName(cubFile); - m_labelFile = new QFile(cubFile.expanded()); + 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("ecub"); + FileName labelFileName(imageFile); + m_labelFileName = new FileName(labelFileName); + m_labelFile = new QFile(m_labelFileName->expanded()); + } + + core += PvlKeyword("^DnFile", m_dataFileName->name()); + m_dataFile = new QFile(m_dataFileName->expanded()); + + m_labelFileName = new FileName(imageFile); + m_labelFile = new QFile(m_labelFileName->expanded()); + } + else { + QString msg = "Label type [" + LabelAttachmentName(labelsAttached()) + "] not supported"; + throw IException(IException::Programmer, msg, _FILEINFO_); } isiscube.addObject(core); @@ -545,15 +610,15 @@ namespace Isis { cleanUp(false); throw IException(IException::Io, msg, _FILEINFO_); } - + if (m_dataFile) { - if (m_storesDnData && !m_dataFile->open(QIODevice::Truncate | QIODevice::ReadWrite)) { + if (labelsAttached() != ExternalLabel && !m_dataFile->open(QIODevice::Truncate | QIODevice::ReadWrite)) { QString msg = "Failed to create [" + m_dataFile->fileName() + "]. "; msg += "Verify the output path exists and you have permission to write to the path."; cleanUp(false); throw IException(IException::Io, msg, _FILEINFO_); } - else if (!m_storesDnData && !m_dataFile->open(QIODevice::ReadOnly)) { + else if (labelsAttached() == ExternalLabel && !m_dataFile->open(QIODevice::ReadWrite)) { QString msg = "Failed to open [" + m_dataFile->fileName() + "] for reading. "; msg += "Verify the output path exists and you have permission to read from the path."; cleanUp(false); @@ -561,24 +626,103 @@ namespace Isis { } } - bool dataAlreadyOnDisk = m_storesDnData ? false : true; + bool dataAlreadyOnDisk = (labelsAttached() == ExternalLabel); - if (m_format == Bsq) { - m_ioHandler = new CubeBsqHandler(dataFile(), m_virtualBandList, realDataFileLabel(), - dataAlreadyOnDisk); - } - else { - m_ioHandler = new CubeTileHandler(dataFile(), m_virtualBandList, realDataFileLabel(), + if (format() != GTiff) { + if (format() == Bsq) { + m_ioHandler = new CubeBsqHandler(dataFile(), m_virtualBandList, realDataFileLabel(), dataAlreadyOnDisk); + } + else if(format() == Tile) { + m_ioHandler = new CubeTileHandler(dataFile(), m_virtualBandList, realDataFileLabel(), + dataAlreadyOnDisk); + } + + if (!dataAlreadyOnDisk) { + m_ioHandler->updateLabels(*m_label); + } } + else if(format() == Format::GTiff) { + char **papszOptions = NULL; + papszOptions = CSLSetNameValue(papszOptions, "COMPRESS", "DEFLATE"); + papszOptions = CSLSetNameValue(papszOptions, "PREDICTOR", "2"); + QString datafile = m_dataFileName->expanded(); + QString format = "GTiff"; + createGdal(datafile, format, papszOptions); + + m_geodataSet = GDALDataset::FromHandle(GDALOpen(datafile.toStdString().c_str(), GA_Update)); + if (!m_geodataSet) { + QString msg = "Failed opening GDALDataset from [" + m_dataFileName->name() + "]"; + cleanUp(false); + throw IException(IException::Programmer, msg, _FILEINFO_); + } - if (m_storesDnData) + m_ioHandler = new GdalIoHandler(gdalDataset(), m_virtualBandList, IsisPixelToGdal(pixelType())); m_ioHandler->updateLabels(*m_label); + } + else { + QString msg = "Format [" + toString(format()) + "] not supported"; + throw IException(IException::Programmer, msg, _FILEINFO_); + } // Write the labels writeLabels(); } + void Cube::createGdal(QString &dataFileName, QString &driverName, char **papszOptions) { + GDALAllRegister(); + CPLSetErrorHandler(CPLQuietErrorHandler); + GDALDriver* driver = GetGDALDriverManager()->GetDriverByName(driverName.toStdString().c_str()); + if (driver) { + double noDataValue; + switch (pixelType()) { + case UnsignedByte: + noDataValue = (double) NULL1; + break; + case SignedByte: + noDataValue = (double) NULLS1; + break; + case UnsignedWord: + noDataValue = (double) NULLU2; + break; + case SignedWord: + noDataValue = (double) NULL2; + break; + case UnsignedInteger: + noDataValue = (double) NULLUI4; + break; + case SignedInteger: + noDataValue = (double) NULLI4; + break; + case Real: + noDataValue = (double) NULL4; + break; + + default: + noDataValue = NULL8; + break; + } + GDALDataset *dataset = driver->Create(dataFileName.toStdString().c_str(), sampleCount(), lineCount(), bandCount(), IsisPixelToGdal(pixelType()), papszOptions); + if (!dataset) { + QString msg = "Could not create file [" + dataFileName + "] from [" + driverName + "] Driver."; + throw IException(IException::Io, msg, _FILEINFO_); + } + for (int i = 1; i <= bandCount(); i++) { + GDALRasterBand *band = dataset->GetRasterBand(i); + band->SetScale(multiplier()); + band->SetOffset(base()); + band->SetNoDataValue(noDataValue); + band->Fill(noDataValue); + } + GDALClose(dataset); + CSLDestroy( papszOptions ); + } + else { + QString msg = "No Driver found for name [" + driverName + "]"; + throw IException(IException::Io, msg, _FILEINFO_); + } + } + /** * This method will create an isis cube for writing. The programmer should @@ -606,7 +750,7 @@ namespace Isis { setByteOrder(att.byteOrder()); setFormat(att.fileFormat()); - setLabelsAttached(att.labelAttachment() == AttachedLabel); + setLabelsAttached(att.labelAttachment()); if (!att.propagatePixelType()) setPixelType(att.pixelType()); setMinMax(att.minimum(), att.maximum()); @@ -615,11 +759,20 @@ namespace Isis { create(cubeFileName); } + void Cube::checkAccess(QString access) { + if (!(access == "r" || access == "rw")) { + QString msg = "Unknown value for access [" + access + "]. Expected 'r' " + " or 'rw'"; + cleanUp(false); + throw IException(IException::Programmer, msg, _FILEINFO_); + } + } + /** - * This method will open an existing isis cube for reading or - * reading/writing. Any input cube attributes following the file - * name will be applied. + * This method will try to open a file as either a cube or + * geotiff in either read or read/write. Any input cube + * attributes following the file name will be applied. * * @param[in] cubeFileName Name of the cube file to open. Environment * variables in the filename will be automatically expanded. @@ -634,7 +787,7 @@ namespace Isis { throw IException(IException::Programmer, msg, _FILEINFO_); } - initLabelFromFile(cubeFileName, (access == "rw")); + checkAccess(access); try { Isis::CubeAttributeInput att(cubeFileName); @@ -647,6 +800,49 @@ namespace Isis { // there is an exception parsing and adding an attribute } + 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); + } + } + + if (!isOpen()) { + try { + openGdal(cubeFileName, access); + } + catch(IException &e) { + cleanUp(false); + exceptions.append(e); + } + } + + if (!isOpen()) { + throw exceptions; + } + + applyVirtualBandsToLabel(); + } + + /** + * This method will open an existing isis cube for reading or + * reading/writing. + * + * @param[in] cubeFileName Name of the cube file to open. Environment + * variables in the filename will be automatically expanded. + * @param[in] access (Default value of "r") Defines how the cube will be + * accessed. Either read-only "r" or read-write "rw". + */ + void Cube::openCube(const QString &cubeFileName, QString access) { + checkAccess(access); + + initLabelFromFile(cubeFileName, (access == "rw")); + // Figure out the name of the data file try { PvlObject &core = m_label->findObject("IsisCube").findObject("Core"); @@ -660,11 +856,13 @@ namespace Isis { else { m_dataFileName = new FileName(temp); } + setLabelsAttached(LabelAttachment::DetachedLabel); - m_attached = false; - m_storesDnData = true; + FileName realDataFile = realDataFileName(); + delete m_dataFileName; + m_dataFileName = new FileName(realDataFile.expanded()); - m_dataFile = new QFile(realDataFileName().expanded()); + m_dataFile = new QFile(m_dataFileName->expanded()); } // External cube files (ecub), ecub contains all labels and SPICE blobs, history else if (core.hasKeyword("^DnFile")) { @@ -676,103 +874,242 @@ namespace Isis { else { m_dataFileName = new FileName(dataFileName); } + setLabelsAttached(LabelAttachment::ExternalLabel); - m_attached = true; - m_storesDnData = false; - *m_dataFileName = FileName(realDataFileName().expanded()); - m_dataFile = new QFile(realDataFileName().expanded()); + FileName realDataFile = realDataFileName(); + delete m_dataFileName; + m_dataFileName = new FileName(realDataFile.expanded()); + + m_dataFile = new QFile(m_dataFileName->expanded()); } // Typical cube containing labels, SPICE, history and dn data else { + setLabelsAttached(LabelAttachment::AttachedLabel); m_dataFileName = new FileName(*m_labelFileName); - m_attached = true; - m_storesDnData = true; } } catch (IException &e) { cleanUp(false); - throw; + throw e; } - if (access == "r") { - if (!m_labelFile->open(QIODevice::ReadOnly)) { - QString msg = "Failed to open [" + m_labelFile->fileName() + "] with " - "read only access"; - cleanUp(false); - throw IException(IException::Io, msg, _FILEINFO_); - } - - if (m_dataFile) { - if (!m_dataFile->open(QIODevice::ReadOnly)) { - QString msg = "Failed to open [" + m_dataFile->fileName() + "] with " - "read only access"; - cleanUp(false); - throw IException(IException::Io, msg, _FILEINFO_); - } - } + QIODevice::OpenModeFlag qtAccess = QIODevice::ReadOnly; + GDALAccess eAccess = GA_ReadOnly; + if (access == "rw") { + qtAccess = QIODevice::ReadWrite; + eAccess = GA_Update; } - else if (access == "rw") { - if (!m_labelFile->open(QIODevice::ReadWrite)) { + if (!m_labelFile->open(qtAccess)) { QString msg = "Failed to open [" + m_labelFile->fileName() + "] with " - "read/write access"; + "access [" + access + "]"; cleanUp(false); throw IException(IException::Io, msg, _FILEINFO_); } if (m_dataFile) { - if (m_storesDnData && !m_dataFile->open(QIODevice::ReadWrite)) { - QString msg = "Failed to open [" + m_dataFile->fileName() + "] with " - "read/write access"; - cleanUp(false); - throw IException(IException::Io, msg, _FILEINFO_); + if (!m_dataFile->open(qtAccess)) { + QString msg = "Failed to open [" + m_dataFile->fileName() + "] with " + "access [" + access + "]"; + cleanUp(false); + throw IException(IException::Io, msg, _FILEINFO_); } - else if (!m_storesDnData && !m_dataFile->open(QIODevice::ReadOnly)) { - QString msg = "Failed to open [" + m_dataFile->fileName() + "] with " - "read access"; - cleanUp(false); - throw IException(IException::Io, msg, _FILEINFO_); - } - } - } - else { - QString msg = "Unknown value for access [" + access + "]. Expected 'r' " - " or 'rw'"; - cleanUp(false); - throw IException(IException::Programmer, msg, _FILEINFO_); } initCoreFromLabel(*m_label); // Determine the number of bytes in the label - if (m_attached) { + if (labelsAttached() != LabelAttachment::DetachedLabel) { m_labelBytes = m_label->findObject("Label")["Bytes"]; } else { m_labelBytes = labelSize(true); } - QPair dataLabel = qMakePair(false, m_label); - if (!m_storesDnData) { - dataLabel = qMakePair(true, new Pvl(m_dataFileName->expanded())); - } - // Now examine the format to see which type of handler to create if (m_format == Bsq) { m_ioHandler = new CubeBsqHandler(dataFile(), m_virtualBandList, realDataFileLabel(), true); } - else { + else if (m_format == Tile) { m_ioHandler = new CubeTileHandler(dataFile(), m_virtualBandList, realDataFileLabel(), true); } + else if (m_format == GTiff) { + 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 [" + eAccess +"]"; + cleanUp(false); + throw IException(IException::Io, msg, _FILEINFO_); + } + m_ioHandler = new GdalIoHandler(gdalDataset(), m_virtualBandList, IsisPixelToGdal(pixelType())); + } + else { + QString msg = "Unknown format [" + toString(m_format) + "]"; + throw IException(IException::Programmer, msg, _FILEINFO_); + } + } + + /** + * This method will open an existing geotiff for reading or + * reading/writing. + * + * @param[in] cubeFileName Name of the file to open. Environment + * variables in the filename will be automatically expanded. + * @param[in] access (Default value of "r") Defines how the cube will be + * accessed. Either read-only "r" or read-write "rw". + */ + void Cube::openGdal(const QString &cubeFileName, QString access) { + checkAccess(access); + + m_labelFileName = new FileName(cubeFileName); + + setLabelsAttached(LabelAttachment::AttachedLabel); + 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); - if (dataLabel.first) { - delete dataLabel.second; - dataLabel.second = NULL; + m_label = new Pvl(); + if (metadata) { + 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"); - applyVirtualBandsToLabel(); + // 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); + + if (dataset->GetSpatialRef()) { + 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_); + } + mappingGroup.addKeyword(PvlKeyword("LongitudeDomain", "180")); + mappingGroup.addKeyword(PvlKeyword("LatitudeType", "Planetocentric")); + 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; + + isiscube.addGroup(mappingGroup); + } + m_label->addObject(isiscube); + } + GDALClose(dataset); + + GDALAccess eAccess = GA_ReadOnly; + if (access == "rw") { + eAccess = GA_Update; + } + 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 [" + eAccess +"]"; + cleanUp(false); + throw IException(IException::Io, msg, _FILEINFO_); + } + + m_ioHandler = new GdalIoHandler(gdalDataset(), m_virtualBandList, IsisPixelToGdal(pixelType())); + m_ioHandler->updateLabels(*m_label); } @@ -827,9 +1164,14 @@ namespace Isis { if (m_tempCube) cubeFile = *m_tempCube; - QMutexLocker locker(m_mutex); - QMutexLocker locker2(m_ioHandler->dataFileMutex()); - blob.Read(cubeFile.toString(), *label(), keywords); + if (format() == Format::GTiff && labelsAttached() == LabelAttachment::AttachedLabel) { + blob.ReadGdal(gdalDataset()); + } + else { + QMutexLocker locker(m_mutex); + QMutexLocker locker2(m_ioHandler->dataFileMutex()); + blob.Read(cubeFile.toString(), *label(), keywords); + } } @@ -987,13 +1329,19 @@ namespace Isis { throw IException(IException::Programmer, msg, _FILEINFO_); } + if (m_format == Format::GTiff && labelsAttached() == LabelAttachment::AttachedLabel) { + // write new type of blob + blob.WriteGdal(gdalDataset()); + return; // nothing else to do + } + if (!m_labelFile->isWritable()) { string msg = "The cube must be opened in read/write mode, not readOnly"; throw IException(IException::Programmer, msg, _FILEINFO_); } // Write an attached blob - if (m_attached) { + if (labelsAttached() != LabelAttachment::DetachedLabel) { QMutexLocker locker(m_mutex); QMutexLocker locker2(m_ioHandler->dataFileMutex()); @@ -1008,9 +1356,7 @@ namespace Isis { // maxbyte = position after the cube DN data and labels streampos maxbyte = (streampos) m_labelBytes; - if (m_storesDnData) { - maxbyte += (streampos) m_ioHandler->getDataSize(); - } + maxbyte += (streampos) m_ioHandler->getDataSize(); // If EOF is too early, allocate space up to where we want the blob if (endByte < maxbyte) { @@ -1021,7 +1367,7 @@ namespace Isis { blob.Write(*m_label, stream, "", overwrite); } - // Write a detached blob + // Write a detached else { FileName blobFileName = fileName(); blobFileName = blobFileName.removeExtension(); @@ -1140,11 +1486,11 @@ namespace Isis { throw IException(IException::Programmer, msg, _FILEINFO_); } - if (!m_storesDnData) { - QString msg = "The cube [" + QFileInfo(fileName()).fileName() + - "] does not support storing DN data because it is using an external file for DNs"; - throw IException(IException::Unknown, msg, _FILEINFO_); - } + // if (labelsAttached() == ExternalLabel) { + // QString msg = "The cube [" + QFileInfo(fileName()).fileName() + + // "] does not support storing DN data because it is using an external file for DNs"; + // throw IException(IException::Unknown, msg, _FILEINFO_); + // } QMutexLocker locker(m_mutex); m_ioHandler->write(bufferToWrite); @@ -1184,7 +1530,7 @@ namespace Isis { m_base = 0.0; m_multiplier = 1.0; - double x1, x2; + double x1 = 0.0, x2 = 0.0; if (m_pixelType == UnsignedByte) { x1 = VALID_MIN1; x2 = VALID_MAX1; @@ -1252,14 +1598,17 @@ namespace Isis { delete m_label; m_label = NULL; } - catch (IException &) { + catch (IException &e) { delete m_label; m_label = NULL; - throw; + throw e; } - m_storesDnData = false; + setLabelsAttached(Cube::ExternalLabel); m_dataFileName = new FileName(cubeFileWithDnData); + FileName realDataFile = realDataFileName(); + delete m_dataFileName; + m_dataFileName = new FileName(realDataFile.expanded()); delete m_labelFile; m_labelFile = NULL; @@ -1288,7 +1637,7 @@ namespace Isis { * * @param attach If false, the labels and data will be in separate files. */ - void Cube::setLabelsAttached(bool attach) { + void Cube::setLabelsAttached(LabelAttachment attach) { openCheck(); m_attached = attach; } @@ -1386,7 +1735,7 @@ namespace Isis { } - if (m_storesDnData) { + if (labelsAttached() != ExternalLabel) { throw IException(IException::Unknown, QString("The cube [%1] stores DN data. It cannot be relocated to [%2] - " "this is only supported for external cube label files.") @@ -1573,7 +1922,7 @@ namespace Isis { _FILEINFO_); } - if (storesDnData()) { + if (labelsAttached() == ExternalLabel) { throw IException(IException::Unknown, "Cube::getExternalCubeFileName can only be called on an external cube label " "file", @@ -1581,8 +1930,7 @@ namespace Isis { } - PvlObject &core = m_label->findObject("IsisCube").findObject("Core"); - + PvlObject &core = m_label->findObject("IsisCube").findObject("Core"); return core["^DnFile"][0]; } @@ -1929,16 +2277,6 @@ namespace Isis { } - /** - * This method returns a boolean value - * - * @return bool - */ - bool Cube::storesDnData() const { - return m_storesDnData; - } - - /** * This will add the given caching algorithm to the list of attempted caching * algorithms. The algorithms are tried in the opposite order that they @@ -2164,6 +2502,11 @@ namespace Isis { delete m_dataFile; m_dataFile = NULL; + if (m_geodataSet) { + GDALClose(m_geodataSet); + } + m_geodataSet = NULL; + delete m_labelFileName; m_labelFileName = NULL; @@ -2223,6 +2566,14 @@ 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; + } + /** * This gets the file name of the file which actually contains the DN data. With ecub's, our * data file name could be another ecub or a detached label, so using m_dataFileName is @@ -2234,15 +2585,15 @@ namespace Isis { FileName result; // Attached, stores DN data - normal cube - if (m_attached && m_storesDnData) { + if (labelsAttached() == LabelAttachment::AttachedLabel) { result = *m_labelFileName; } // Detached, stores DN data - standard detached cube - else if (!m_attached && m_storesDnData) { + else if (labelsAttached() == LabelAttachment::DetachedLabel) { result = *m_dataFileName; } // External cube - go look at our external file - else if (!m_storesDnData) { + else if (labelsAttached() == LabelAttachment::ExternalLabel) { FileName guess = *m_dataFileName; QDir dir(guess.toString()); @@ -2255,20 +2606,26 @@ namespace Isis { guess = dir2.absolutePath() + "/" + guess.name(); } do { - Pvl guessLabel(guess.expanded()); + Cube dataCube(guess.expanded()); + Pvl guessLabel = *(dataCube.label()); - PvlObject &core = guessLabel.findObject("IsisCube").findObject("Core"); + if (guessLabel.hasObject("IsisCube")) { + PvlObject &core = guessLabel.findObject("IsisCube").findObject("Core"); - if (core.hasKeyword("^DnFile")) { - FileName currentGuess = guess; - guess = core["^DnFile"][0]; + if (core.hasKeyword("^DnFile")) { + FileName currentGuess = guess; + guess = core["^DnFile"][0]; - if (!guess.path().startsWith("/")) { - guess = currentGuess.path() + "/" + guess.original(); + if (!guess.path().startsWith("/")) { + guess = currentGuess.path() + "/" + guess.original(); + } + } + else if (core.hasKeyword("^Core")) { + result = core["^Core"][0]; + } + else { + result = guess; } - } - else if (core.hasKeyword("^Core")) { - result = core["^Core"][0]; } else { result = guess; @@ -2299,8 +2656,7 @@ namespace Isis { m_format = Tile; m_pixelType = Real; - m_attached = true; - m_storesDnData = true; + m_attached = LabelAttachment::AttachedLabel; m_labelBytes = 65536; m_samples = 0; @@ -2318,9 +2674,11 @@ namespace Isis { * @param label Pvl label to initialize from */ void Cube::initCoreFromLabel(const Pvl &label) { + initialize(); + initLabelState(label); const PvlObject &core = label.findObject("IsisCube").findObject("Core"); - if (!core.hasKeyword("^DnFile")) { + if (labelsAttached() != ExternalLabel) { // Dimensions const PvlGroup &dims = core.findGroup("Dimensions"); m_samples = dims["Samples"]; @@ -2336,10 +2694,17 @@ namespace Isis { // Now examine the format to see which type of handler to create if ((QString) core["Format"] == "BandSequential") { - m_format = Bsq; + m_format = Format::Bsq; + } + else if ((QString) core["Format"] == "Tile") { + m_format = Format::Tile; + } + else if ((QString) core["Format"] == "GTiff") { + m_format = Format::GTiff; } else { - m_format = Tile; + QString msg = "Unknown format [" + (QString)core["Format"] + "]"; + throw IException(IException::Io, msg, _FILEINFO_); } } else { @@ -2347,12 +2712,42 @@ namespace Isis { if (!temp.expanded().startsWith("/")) { temp = FileName(m_labelFileName->path() + "/" + temp.original()); } - - initCoreFromLabel(Pvl(temp.toString())); + try { + initCoreFromLabel(Pvl(temp.toString())); + } + catch (IException &e) { + initCoreFromGdal(temp.toString()); + } + // Reset the label state as we recurse back out from initCoreFromLabel calls + initLabelState(label); } } + void Cube::initCoreFromGdal(const QString &labelFile) { + GDALDataset *geodataSet = GDALDataset::FromHandle(GDALOpen(labelFile.toStdString().c_str(), GA_ReadOnly)); + if (!geodataSet) { + QString msg = "Gdal failed to open [" + labelFile + "]"; + throw IException(IException::Programmer, msg, _FILEINFO_); + } + + std::string format = std::string(m_geodataSet->GetDriverName()); + if (format == "GTiff") { + setFormat(GTiff); + } + else { + QString msg = "Unsupported GDAL format [" + QString::fromStdString(format) + "]"; + throw IException(IException::Io, msg, _FILEINFO_); + } + + setDimensions(geodataSet->GetRasterXSize(), geodataSet->GetRasterYSize(), geodataSet->GetRasterCount()); + + GDALRasterBand *band = geodataSet->GetRasterBand(1); + setPixelType(GdalPixelToIsis(band->GetRasterDataType())); + setBaseMultiplier(band->GetOffset(), band->GetScale()); + GDALClose(geodataSet); + } + /** * This function initializes the Cube label from a file passed as a parameter * @@ -2632,19 +3027,35 @@ namespace Isis { string msg = "Cube must be opened first before writing labels"; throw IException(IException::Programmer, msg, _FILEINFO_); } + + if (m_format == Format::GTiff && labelsAttached() == LabelAttachment::AttachedLabel) { + // update metadata + nlohmann::ordered_json jsonblob = this->label()->toJson()["Root"]; + nlohmann::ordered_json jsonOut; + // std::cout << jsonblob << std::endl; + for (auto& [key, val] : jsonblob.items()) { + if (!val.contains("Bytes") || key == "Label") { + jsonOut[key] = val; + } + } + std::string jsonblobstr = jsonOut.dump(); + std::string name = "CubeLabel"; + gdalDataset()->SetMetadataItem(name.c_str(), jsonblobstr.c_str(), "USGS"); + return; + } // Set the pvl's format template m_label->setFormatTemplate(m_formatTemplateFile->original()); // Write them with attached data - if (m_attached) { + if (labelsAttached() != LabelAttachment::DetachedLabel) { QMutexLocker locker(m_mutex); QMutexLocker locker2(m_ioHandler->dataFileMutex()); ostringstream temp; temp << *m_label << endl; string tempstr = temp.str(); - if ((int) tempstr.length() < m_labelBytes) { + if ((int) tempstr.length() <= m_labelBytes) { QByteArray labelArea(m_labelBytes, '\0'); QByteArray labelUnpaddedContents(tempstr.c_str(), tempstr.length()); labelArea.replace(0, labelUnpaddedContents.size(), labelUnpaddedContents); @@ -2667,4 +3078,17 @@ namespace Isis { m_label->write(m_labelFileName->expanded()); } } + + void Cube::initLabelState(const Pvl &label) { + const PvlObject &core = label.findObject("IsisCube").findObject("Core"); + LabelAttachment attachmentStyle = AttachedLabel; + if (core.hasKeyword("^DnFile")) { + attachmentStyle = ExternalLabel; + } + else if (core.hasKeyword("^Core")) { + attachmentStyle = DetachedLabel; + } + + setLabelsAttached(attachmentStyle); + } } diff --git a/isis/src/base/objs/Cube/Cube.h b/isis/src/base/objs/Cube/Cube.h index 32d234367c..6e28110aa4 100644 --- a/isis/src/base/objs/Cube/Cube.h +++ b/isis/src/base/objs/Cube/Cube.h @@ -30,9 +30,9 @@ namespace Isis { class Camera; class CubeAttributeOutput; class CubeCachingAlgorithm; - class CubeIoHandler; class CubeStretch; class FileName; + class ImageIoHandler; class Projection; class Pvl; class PvlGroup; @@ -230,7 +230,29 @@ namespace Isis { * The symbol '*' denotes tile boundaries. * The symbols '-' and '|' denote cube boundaries. */ - Tile + Tile, + GTiff + }; + + /** + * @brief Input cube label type tracker + * + * This enumeration and its functions are for the label + * type of an input cube. The enum defines the type of labels (i.e., + * Both the label and cube are in the same file and the label is in a + * separate file from the cube. + */ + enum LabelAttachment { + AttachedLabel, //!< The input label is embedded in the image file + DetachedLabel, //!< The input label is in a separate data file from the image + /** + * The label is pointing to an external DN file - the label is also external to the data. + * + * This format implies that the output is a cube that contains everything except DN data + * (more similar to attached than detached). + */ + ExternalLabel, + GdalLabel }; void fromIsd(const FileName &fileName, Pvl &label, nlohmann::json &isd, QString access); @@ -242,7 +264,7 @@ namespace Isis { bool isProjected() const; bool isReadOnly() const; bool isReadWrite() const; - bool labelsAttached() const; + LabelAttachment labelsAttached() const; void attachSpiceFromIsd(nlohmann::json Isd); void attachLineScanTableFromIsd(nlohmann::json isd); @@ -251,7 +273,11 @@ namespace Isis { Cube *copy(FileName newFile, const CubeAttributeOutput &newFileAttributes); void create(const QString &cfile); void create(const QString &cfile, const CubeAttributeOutput &att); + void createGdal(QString &dataFileName, QString &, char **papszOptions); + void checkAccess(QString access); void open(const QString &cfile, QString access = "r"); + void openCube(const QString &cubeFileName, QString access); + void openGdal(const QString &cubeFileName, QString access); void reopen(QString access = "r"); void read(Blob &blob, @@ -279,7 +305,8 @@ namespace Isis { void setDimensions(int ns, int nl, int nb); void setExternalDnData(FileName cubeFileWithDnData); void setFormat(Format format); - void setLabelsAttached(bool attached); + void setLabelsAttached(LabelAttachment attached); + // void setLabelsExternal(bool external); void setLabelSize(int labelBytes); void setPixelType(PixelType pixelType); void setVirtualBands(const QList &vbands); @@ -313,7 +340,6 @@ namespace Isis { Statistics *statistics(const int &band, const double &validMin, const double &validMax, QString msg = "Gathering statistics"); - bool storesDnData() const; void addCachingAlgorithm(CubeCachingAlgorithm *); void clearIoCache(); @@ -334,9 +360,12 @@ namespace Isis { void construct(); QFile *dataFile() const; + GDALDataset *gdalDataset() const; void initialize(); void initCoreFromLabel(const Pvl &label); + void initCoreFromGdal(const QString &labelFile); + void initLabelState(const Pvl &label); void initLabelFromFile(FileName labelFileName, bool readWrite); void openCheck(); Pvl realDataFileLabel() const; @@ -353,6 +382,7 @@ namespace Isis { * If isOpen() is true, then this is allocated. */ QFile *m_labelFile; + /** * This is only sometimes allocated. This is used for when IsOpen is true * and labels are detached so the QFile for the labels does not give @@ -360,11 +390,16 @@ namespace Isis { */ QFile *m_dataFile; + /** + * TODO: Write description + */ + GDALDataset *m_geodataSet = nullptr; + /** * This does the heavy lifting for cube DN IO and is always allocated * when isOpen() is true. */ - CubeIoHandler *m_ioHandler; + ImageIoHandler *m_ioHandler; /** * The byte order of the opened cube; if there is no open cube then @@ -413,14 +448,7 @@ namespace Isis { FileName *m_formatTemplateFile; //! True if labels are attached - bool m_attached; - - /** - * True (most common case) when the cube DN data is inside the file we're writing to. False - * means we're referencing another cube's internal DN data for reading, and writing buffers - * is disallowed. - */ - bool m_storesDnData; + LabelAttachment m_attached; //! The label if IsOpen(), otherwise NULL Pvl *m_label; diff --git a/isis/src/base/objs/Cube/CubeIoHandler.cpp b/isis/src/base/objs/Cube/CubeIoHandler.cpp index f94d075553..de8c20c97c 100644 --- a/isis/src/base/objs/Cube/CubeIoHandler.cpp +++ b/isis/src/base/objs/Cube/CubeIoHandler.cpp @@ -59,17 +59,16 @@ namespace Isis { * initialized into the file before this object is destructed. */ CubeIoHandler::CubeIoHandler(QFile * dataFile, - const QList *virtualBandList, const Pvl &label, bool alreadyOnDisk) { + const QList *virtualBandList, const Pvl &label, bool alreadyOnDisk) : + ImageIoHandler(virtualBandList) { m_byteSwapper = NULL; m_cachingAlgorithms = NULL; m_dataIsOnDiskMap = NULL; m_rawData = NULL; - m_virtualBands = NULL; m_nullChunkData = NULL; m_lastProcessByLineChunks = NULL; m_writeCache = NULL; m_ioThreadPool = NULL; - m_writeThreadMutex = NULL; try { if (!dataFile) { @@ -134,8 +133,6 @@ namespace Isis { if(!alreadyOnDisk) { m_dataIsOnDiskMap = new QMap; } - - setVirtualBands(virtualBandList); } catch(IException &e) { IString msg = "Constructing CubeIoHandler failed"; @@ -201,17 +198,11 @@ namespace Isis { delete m_byteSwapper; m_byteSwapper = NULL; - delete m_virtualBands; - m_virtualBands = NULL; - delete m_nullChunkData; m_nullChunkData = NULL; delete m_lastProcessByLineChunks; m_lastProcessByLineChunks = NULL; - - delete m_writeThreadMutex; - m_writeThreadMutex = NULL; } @@ -425,37 +416,6 @@ namespace Isis { (BigInt)getBytesPerChunk(); } - - /** - * This changes the virtual band list. - * - * @param virtualBandList A list where the indices are the vbands and the - * values are the physical bands. The values are 1-based. This can - * be specified as NULL, in which case the vbands are the physical - * bands. The virtual band list is copied - * (the pointer provided isn't remembered). - */ - void CubeIoHandler::setVirtualBands(const QList *virtualBandList) { - if(m_virtualBands) { - delete m_virtualBands; - m_virtualBands = NULL; - } - - if(virtualBandList && !virtualBandList->empty()) - m_virtualBands = new QList(*virtualBandList); - } - - /** - * Get the mutex that this IO handler is using around I/Os on the given - * data file. A lock should be acquired before doing any reads/writes on - * the data file externally. - * - * @return A mutex that can guarantee exclusive access to the data file - */ - QMutex *CubeIoHandler::dataFileMutex() { - return m_writeThreadMutex; - } - /** * @return the number of physical bands in the cube. */ @@ -920,10 +880,10 @@ namespace Isis { startX = max(cube1.getStartSample(), cube2.Sample()); startY = max(cube1.getStartLine(), cube2.Line()); startZ = max(cube1.getStartBand(), startPhysicalBand); - endX = min(cube1.getStartSample() + cube1.sampleCount() - 1, - cube2.Sample() + cube2.SampleDimension() - 1); - endY = min(cube1.getStartLine() + cube1.lineCount() - 1, - cube2.Line() + cube2.LineDimension() - 1); + endX = min(cube1.getStartSample() + cube1.sampleCount(), + cube2.Sample() + cube2.SampleDimension()); + endY = min(cube1.getStartLine() + cube1.lineCount(), + cube2.Line() + cube2.LineDimension()); endZ = min(cube1.getStartBand() + cube1.bandCount() - 1, endPhysicalBand); } @@ -1379,6 +1339,11 @@ namespace Isis { const char *chunkBuf = chunk.getRawData().data(); char *buffersRawBuf = (char *)output.RawBuffer(); + + // Scale the sampleand line increment, only necessary in read for Q apps + int lineIncrement = (double)output.LineDimension() / (double)output.LineDimensionScaled(); + int sampleIncrement = (double)output.SampleDimension() / (double)output.SampleDimensionScaled(); + for(int z = startZ; z <= endZ; z++) { const int &bandIntoChunk = z - chunkStartBand; int virtualBand = z; @@ -1388,12 +1353,16 @@ namespace Isis { if(virtualBand != 0 && virtualBand >= bufferBand && virtualBand <= bufferBand + bufferBands - 1) { - for(int y = startY; y <= endY; y++) { + for(int y = startY; y < endY; y = y + lineIncrement) { const int &lineIntoChunk = y - chunkStartLine; - int bufferIndex = output.Index(startX, y, virtualBand); - for(int x = startX; x <= endX; x++) { + for(int x = startX; x < endX; x = x + sampleIncrement) { const int &sampleIntoChunk = x - chunkStartSample; + int bufferIndex = output.Index(x, y, virtualBand); + // Avoid rolling back onto your buffer + if (bufferIndex >= output.size()) { + bufferIndex = output.size() - 1; + } const int &chunkIndex = sampleIntoChunk + (chunkLineSize * lineIntoChunk) + @@ -1534,7 +1503,7 @@ namespace Isis { ((unsigned char *)buffersRawBuf)[bufferIndex] = raw; } - bufferIndex ++; + bufferIndex++; } } } @@ -1590,11 +1559,11 @@ namespace Isis { if(virtualBand != 0 && virtualBand >= bufferBand && virtualBand <= bufferBand + bufferBands - 1) { - for(int y = startY; y <= endY; y++) { + for(int y = startY; y < endY; y++) { const int &lineIntoChunk = y - outputStartLine; int bufferIndex = buffer.Index(startX, y, virtualBand); - for(int x = startX; x <= endX; x++) { + for(int x = startX; x < endX; x++) { const int &sampleIntoChunk = x - outputStartSample; const int &chunkIndex = sampleIntoChunk + diff --git a/isis/src/base/objs/Cube/CubeIoHandler.h b/isis/src/base/objs/Cube/CubeIoHandler.h index a5114ec4fe..69ce20435e 100644 --- a/isis/src/base/objs/Cube/CubeIoHandler.h +++ b/isis/src/base/objs/Cube/CubeIoHandler.h @@ -13,6 +13,7 @@ find files of those names at the top level of this repository. **/ #include "Constants.h" #include "Endian.h" +#include "ImageIoHandler.h" #include "PixelType.h" class QFile; @@ -106,19 +107,18 @@ namespace Isis { * @history 2018-08-13 Summer Stapleton - Fixed incoming buffer comparison values for * unsigned int type in writeIntoRaw(...). */ - class CubeIoHandler { + class CubeIoHandler : public ImageIoHandler { public: CubeIoHandler(QFile * dataFile, const QList *virtualBandList, const Pvl &label, bool alreadyOnDisk); virtual ~CubeIoHandler(); - void read(Buffer &bufferToFill) const; - void write(const Buffer &bufferToWrite); + virtual void read(Buffer &bufferToFill) const; + virtual void write(const Buffer &bufferToWrite); - void addCachingAlgorithm(CubeCachingAlgorithm *algorithm); - void clearCache(bool blockForWriteCache = true) const; - BigInt getDataSize() const; - void setVirtualBands(const QList *virtualBandList); + virtual void addCachingAlgorithm(CubeCachingAlgorithm *algorithm); + virtual void clearCache(bool blockForWriteCache = true) const; + virtual BigInt getDataSize() const; /** * Function to update the labels with a Pvl object * @@ -126,7 +126,6 @@ namespace Isis { */ virtual void updateLabels(Pvl &labels) = 0; - QMutex *dataFileMutex(); int bandCount() const; int getBandCountInChunk() const; BigInt getBytesPerChunk() const; @@ -310,8 +309,6 @@ namespace Isis { //! The map from chunk index to on-disk status, all true if not allocated. mutable QMap * m_dataIsOnDiskMap; - //! Converts from virtual band to physical band. - QList * m_virtualBands; //! The number of samples in a cube chunk. int m_samplesInChunk; @@ -352,9 +349,6 @@ namespace Isis { */ bool m_useOptimizedCubeWrite; - //! This enables us to block while the write thread is working - QMutex *m_writeThreadMutex; - //! Ideal write cache flush size mutable volatile int m_idealFlushSize; diff --git a/isis/src/base/objs/CubeAttribute/CubeAttribute.cpp b/isis/src/base/objs/CubeAttribute/CubeAttribute.cpp index 1402990f61..f1d8b400db 100644 --- a/isis/src/base/objs/CubeAttribute/CubeAttribute.cpp +++ b/isis/src/base/objs/CubeAttribute/CubeAttribute.cpp @@ -274,6 +274,9 @@ namespace Isis { if (formatString == "BSQ" || formatString == "BANDSEQUENTIAL") result = Cube::Bsq; + else if(formatString == "GTIFF") { + result = Cube::GTiff; + } } return result; @@ -286,8 +289,19 @@ namespace Isis { void CubeAttributeOutput::setFileFormat(Cube::Format fmt) { - setAttribute((fmt == Cube::Tile)? "Tile" : "BandSequential", - &CubeAttributeOutput::isFileFormat); + if (fmt == Cube::Tile) { + setAttribute("Tile", &CubeAttributeOutput::isFileFormat); + } + else if (fmt == Cube::Bsq) { + setAttribute("BandSequential", &CubeAttributeOutput::isFileFormat); + } + else if (fmt == Cube::GTiff) { + setAttribute("GTiff", &CubeAttributeOutput::isFileFormat); + } + else { + QString msg = "Unsupported format [" + toString(fmt) + "]"; + IException(IException::Programmer, msg, _FILEINFO_); + } } @@ -392,22 +406,22 @@ namespace Isis { } - void CubeAttributeOutput::setLabelAttachment(LabelAttachment attachment) { + void CubeAttributeOutput::setLabelAttachment(Cube::LabelAttachment attachment) { setAttribute(LabelAttachmentName(attachment), &CubeAttributeOutput::isLabelAttachment); } - LabelAttachment CubeAttributeOutput::labelAttachment() const { - LabelAttachment result = AttachedLabel; + Cube::LabelAttachment CubeAttributeOutput::labelAttachment() const { + Cube::LabelAttachment result = Cube::AttachedLabel; QStringList labelAttachmentAtts = attributeList(&CubeAttributeOutput::isLabelAttachment); if (!labelAttachmentAtts.isEmpty()) { QString labelAttachmentAtt = labelAttachmentAtts.last(); if (labelAttachmentAtt == "DETACHED") - result = DetachedLabel; + result = Cube::DetachedLabel; else if (labelAttachmentAtt == "EXTERNAL") - result = ExternalLabel; + result = Cube::ExternalLabel; } return result; @@ -420,7 +434,7 @@ namespace Isis { bool CubeAttributeOutput::isFileFormat(QString attribute) const { - return QRegExp("(BANDSEQUENTIAL|BSQ|TILE)").exactMatch(attribute); + return QRegExp("(BANDSEQUENTIAL|BSQ|TILE|GTIFF)").exactMatch(attribute); } @@ -443,10 +457,21 @@ namespace Isis { QString CubeAttributeOutput::toString(Cube::Format format) { - QString result = "Tile"; + QString result; - if (format == Cube::Bsq) + if (format == Cube::Tile) { + result = "Tile"; + } + else if (format == Cube::Bsq) { result = "BandSequential"; + } + else if (format == Cube::GTiff) { + result = "GTiff"; + } + else { + QString msg = "Format [" + QString::number(format) + "] cannot be translated to string"; + throw IException(IException::Programmer, msg, _FILEINFO_); + } return result; } diff --git a/isis/src/base/objs/CubeAttribute/CubeAttribute.h b/isis/src/base/objs/CubeAttribute/CubeAttribute.h index 28684c4a5a..5736d1ac21 100644 --- a/isis/src/base/objs/CubeAttribute/CubeAttribute.h +++ b/isis/src/base/objs/CubeAttribute/CubeAttribute.h @@ -20,27 +20,6 @@ find files of those names at the top level of this repository. **/ namespace Isis { - /** - * @brief Input cube label type tracker - * - * This enumeration and its functions are for the label - * type of an input cube. The enum defines the type of labels (i.e., - * Both the label and cube are in the same file and the label is in a - * separate file from the cube. - */ - enum LabelAttachment { - AttachedLabel, //!< The input label is embedded in the image file - DetachedLabel, //!< The input label is in a separate data file from the image - /** - * The label is pointing to an external DN file - the label is also external to the data. - * - * This format implies that the output is a cube that contains everything except DN data - * (more similar to attached than detached). - */ - ExternalLabel - }; - - /** * Return the string representation of the contents of a * variable of type LabelAttachment @@ -49,10 +28,11 @@ namespace Isis { * * @return A string representation of the parameter */ - inline QString LabelAttachmentName(LabelAttachment labelType) { - if(labelType == AttachedLabel) return "Attached"; - if(labelType == DetachedLabel) return "Detached"; - if(labelType == ExternalLabel) return "External"; + inline QString LabelAttachmentName(Cube::LabelAttachment labelType) { + if(labelType == Cube::AttachedLabel) return "Attached"; + if(labelType == Cube::DetachedLabel) return "Detached"; + if(labelType == Cube::ExternalLabel) return "External"; + if(labelType == Cube::GdalLabel) return "Gdal"; QString msg = "Invalid label attachment type [" + QString::number(labelType) + "]"; throw IException(IException::Programmer, msg, _FILEINFO_); @@ -67,18 +47,17 @@ namespace Isis { * * @return The RangeType enum corresponding to the string parameter */ - inline LabelAttachment LabelAttachmentEnumeration(const QString &labelType) { + inline Cube::LabelAttachment LabelAttachmentEnumeration(const QString &labelType) { QString temp = labelType.toUpper(); - if(temp == "ATTACHED") return AttachedLabel; - if(temp == "DETACHED") return DetachedLabel; - if(temp == "External") return ExternalLabel; + if(temp == "ATTACHED") return Cube::AttachedLabel; + if(temp == "DETACHED") return Cube::DetachedLabel; + if(temp == "EXTERNAL") return Cube::ExternalLabel; + if(temp == "GDAL") return Cube::GdalLabel; QString msg = "Invalid label attachment type string [" + labelType + "]"; throw IException(IException::Unknown, msg, _FILEINFO_); } - - /** * @brief Parent class for CubeAttributeInput and CubeAttributeOutput. * @@ -539,9 +518,9 @@ namespace Isis { void setPixelType(PixelType type); //! Set the label attachment type to the parameter value - void setLabelAttachment(LabelAttachment attachment); + void setLabelAttachment(Cube::LabelAttachment attachment); - LabelAttachment labelAttachment() const; + Cube::LabelAttachment labelAttachment() const; using CubeAttribute::toString; diff --git a/isis/src/base/objs/CubeAttribute/unitTest.cpp b/isis/src/base/objs/CubeAttribute/unitTest.cpp index 1892e65209..d4832be869 100644 --- a/isis/src/base/objs/CubeAttribute/unitTest.cpp +++ b/isis/src/base/objs/CubeAttribute/unitTest.cpp @@ -205,10 +205,10 @@ int main(int argc, char *argv[]) { att.setPixelType(Real); cout << att.toString() << endl; - att.setLabelAttachment(DetachedLabel); + att.setLabelAttachment(Cube::DetachedLabel); cout << att.toString() << endl; - att.setLabelAttachment(ExternalLabel); + att.setLabelAttachment(Cube::ExternalLabel); cout << att.toString() << endl; } catch (IException &e) { @@ -300,8 +300,8 @@ void reportOutput(const CubeAttributeOutput &att, QString orderHint) { } cout << "Label attachment = "; - if(att.labelAttachment() == AttachedLabel) cout << LabelAttachmentName(AttachedLabel) << endl; - if(att.labelAttachment() == DetachedLabel) cout << LabelAttachmentName(DetachedLabel) << endl; + if(att.labelAttachment() == Cube::AttachedLabel) cout << LabelAttachmentName(Cube::AttachedLabel) << endl; + if(att.labelAttachment() == Cube::DetachedLabel) cout << LabelAttachmentName(Cube::DetachedLabel) << endl; #if 0 fstream stream("CubeAttribute.truth", ios::in | ios::out); diff --git a/isis/src/base/objs/CubeDataThread/CubeDataThread.cpp b/isis/src/base/objs/CubeDataThread/CubeDataThread.cpp index d566ea8a4a..ed835e4283 100644 --- a/isis/src/base/objs/CubeDataThread/CubeDataThread.cpp +++ b/isis/src/base/objs/CubeDataThread/CubeDataThread.cpp @@ -255,13 +255,13 @@ namespace Isis { * @param sharedLock True if read-only, false if read-write */ void CubeDataThread::GetCubeData(int cubeId, int ss, int sl, int es, int el, - int band, void *caller, bool sharedLock) { + int band, void *caller, bool sharedLock, double scale) { Brick *requestedBrick = NULL; p_threadSafeMutex->lock(); requestedBrick = new Brick(*p_managedCubes->value(cubeId).second, es - - ss + 1, el - sl + 1, 1); + - ss + 1, (el - sl + 1) / scale, 1, false, scale); requestedBrick->SetBasePosition(ss, sl, band); p_threadSafeMutex->unlock(); @@ -425,7 +425,7 @@ namespace Isis { */ void CubeDataThread::ReadCube(int cubeId, int startSample, int startLine, int endSample, int endLine, int band, - void *caller) { + void *caller, double scale) { if(!p_managedCubes->contains(cubeId)) { IString msg = "cube ID ["; @@ -434,8 +434,12 @@ namespace Isis { throw IException(IException::Programmer, msg, _FILEINFO_); } + if (scale > 1) { + scale = 1.0; + } + GetCubeData(cubeId, startSample, startLine, endSample, endLine, band, - caller, true); + caller, true, scale); } /** @@ -469,7 +473,7 @@ namespace Isis { } GetCubeData(cubeId, startSample, startLine, endSample, endLine, band, - caller, false); + caller, false, 1); } /** diff --git a/isis/src/base/objs/CubeDataThread/CubeDataThread.h b/isis/src/base/objs/CubeDataThread/CubeDataThread.h index 4c3af01b71..94f2a1530e 100644 --- a/isis/src/base/objs/CubeDataThread/CubeDataThread.h +++ b/isis/src/base/objs/CubeDataThread/CubeDataThread.h @@ -81,7 +81,7 @@ namespace Isis { public slots: void ReadCube(int cubeId, int startSample, int startLine, - int endSample, int endLine, int band, void *caller); + int endSample, int endLine, int band, void *caller, double scale); void ReadWriteCube(int cubeId, int startSample, int startLine, int endSample, int endLine, int band, void *caller); @@ -155,7 +155,7 @@ namespace Isis { int instanceNum, bool &exact); void GetCubeData(int cubeId, int ss, int sl, int es, int el, int band, - void *caller, bool sharedLock); + void *caller, bool sharedLock, double scale=1); void AcquireLock(QReadWriteLock *lockObject, bool readLock); diff --git a/isis/src/base/objs/CubeDataThread/CubeDataThreadTester.cpp b/isis/src/base/objs/CubeDataThread/CubeDataThreadTester.cpp index 8399b3bcef..50276bf523 100644 --- a/isis/src/base/objs/CubeDataThread/CubeDataThreadTester.cpp +++ b/isis/src/base/objs/CubeDataThread/CubeDataThreadTester.cpp @@ -51,9 +51,9 @@ namespace Isis { */ void CubeDataThreadTester::Connect() { connect(this, - SIGNAL(RequestReadCube(int, int, int, int, int, int, void *)), + SIGNAL(RequestReadCube(int, int, int, int, int, int, void *, double)), p_cubeDataThread, - SLOT(ReadCube(int, int, int, int, int, int, void *))); + SLOT(ReadCube(int, int, int, int, int, int, void *, double))); connect(this, SIGNAL(RequestReadWriteCube(int, int, int, int, int, int, void *)), @@ -98,7 +98,7 @@ namespace Isis { */ void CubeDataThreadTester::ReadCubeTest(int cubeId) { cout << "=============== Testing Basic Read ===============" << endl; - emit RequestReadCube(cubeId, 1, 1, 2, 2, 1, this); + emit RequestReadCube(cubeId, 1, 1, 2, 2, 1, this, 1.0); } @@ -113,8 +113,8 @@ namespace Isis { "===============" << endl; p_notifyDone = false; - emit RequestReadCube(cubeId1, 1, 1, 3, 2, 1, this); - emit RequestReadCube(cubeId2, 1, 2, 3, 2, 1, this); + emit RequestReadCube(cubeId1, 1, 1, 3, 2, 1, this, 1.0); + emit RequestReadCube(cubeId2, 1, 2, 3, 2, 1, this, 1.0); } @@ -128,8 +128,8 @@ namespace Isis { << endl << endl; p_notifyDone = false; - emit RequestReadCube(cubeId, 1, 2, 2, 2, 1, this); - emit RequestReadCube(cubeId, 1, 2, 2, 2, 1, this); + emit RequestReadCube(cubeId, 1, 2, 2, 2, 1, this, 1.0); + emit RequestReadCube(cubeId, 1, 2, 2, 2, 1, this, 1.0); } @@ -141,7 +141,7 @@ namespace Isis { void CubeDataThreadTester::WriteCubeTest(int cubeId) { cout << "=============== Testing Basic R/W ===============" << endl << endl; emit RequestReadWriteCube(cubeId, 1, 1, 2, 2, 1, this); - emit RequestReadCube(cubeId, 1, 1, 2, 2, 1, this); + emit RequestReadCube(cubeId, 1, 1, 2, 2, 1, this, 1.0); } diff --git a/isis/src/base/objs/CubeDataThread/CubeDataThreadTester.h b/isis/src/base/objs/CubeDataThread/CubeDataThreadTester.h index 8134df48d7..04c6d85334 100644 --- a/isis/src/base/objs/CubeDataThread/CubeDataThreadTester.h +++ b/isis/src/base/objs/CubeDataThread/CubeDataThreadTester.h @@ -67,7 +67,7 @@ namespace Isis { * @param caller A this pointer */ void RequestReadCube(int cubeId, int startSample, int startLine, - int endSample, int endLine, int band, void *caller); + int endSample, int endLine, int band, void *caller, double scale); /** diff --git a/isis/src/base/objs/CubeManager/CubeManager.truth b/isis/src/base/objs/CubeManager/CubeManager.truth index 064d543ea9..0465b4a2f5 100644 --- a/isis/src/base/objs/CubeManager/CubeManager.truth +++ b/isis/src/base/objs/CubeManager/CubeManager.truth @@ -49,4 +49,6 @@ Verify isisTruth2.cub is now managed and limit of 2 is enforced: Setting number of open cubes > 60 percent of system open file limit Attempting to open a file that does not exist: - **I/O ERROR** Unable to open [dne.cub]. + **I/O ERROR** Failed to open [dne.cub]. +**PROGRAMMER ERROR** Gdal failed to open [dne.cub]. +**I/O ERROR** Unable to open [dne.cub]. diff --git a/isis/src/base/objs/EquatorialCylindricalShape/EquatorialCylindricalShape.cpp b/isis/src/base/objs/EquatorialCylindricalShape/EquatorialCylindricalShape.cpp index ba969599db..777c5ee2d9 100644 --- a/isis/src/base/objs/EquatorialCylindricalShape/EquatorialCylindricalShape.cpp +++ b/isis/src/base/objs/EquatorialCylindricalShape/EquatorialCylindricalShape.cpp @@ -32,8 +32,6 @@ find files of those names at the top level of this repository. **/ using namespace std; -#define MAX(x,y) (((x) > (y)) ? (x) : (y)) - namespace Isis { /** * Initialize the ISIS Equatorial Cylindrical shape model. @@ -222,7 +220,7 @@ namespace Isis { // Set dalpha to be half the grid spacing for nyquist sampling //double dalpha = (PI/180.0)/(2.0*p_demScale); double cmin = cos((90.0 - 1.0 / (2.0 * demScale())) * DEG2RAD); - double dalpha = MAX(cos(g1lat * DEG2RAD), cmin) / (2.0 * demScale() * RAD2DEG); + double dalpha = std::max(cos(g1lat * DEG2RAD), cmin) / (2.0 * demScale() * RAD2DEG); // Previous Sensor version used local version of this method with lat and lon doubles. Steven said // it didn't make a significant difference in speed. @@ -390,7 +388,7 @@ namespace Isis { // put in a test (above) for dd being smaller than the pixel // convergence tolerance. If so the loop exits without an // intersection - dalpha = MAX(cos(g2lat * DEG2RAD), cmin) / (2.0 * demScale() * RAD2DEG); + dalpha = std::max(cos(g2lat * DEG2RAD), cmin) / (2.0 * demScale() * RAD2DEG); } // end while SpiceDouble intersectionPoint[3]; diff --git a/isis/src/base/objs/FileName/FileName.cpp b/isis/src/base/objs/FileName/FileName.cpp index 425e0801a5..0ef719ced7 100644 --- a/isis/src/base/objs/FileName/FileName.cpp +++ b/isis/src/base/objs/FileName/FileName.cpp @@ -447,6 +447,9 @@ namespace Isis { * @return Boolean */ bool FileName::fileExists() const { + if (toString().contains((QString)"https://")) { + return true; + } return QFileInfo(expanded()).exists(); } diff --git a/isis/src/base/objs/Gui/GuiOutputAttribute.cpp b/isis/src/base/objs/Gui/GuiOutputAttribute.cpp index cff2ed9119..04579559bb 100644 --- a/isis/src/base/objs/Gui/GuiOutputAttribute.cpp +++ b/isis/src/base/objs/Gui/GuiOutputAttribute.cpp @@ -244,7 +244,7 @@ namespace Isis { p_msb->setChecked(true); } - if(att.labelAttachment() == AttachedLabel) { + if(att.labelAttachment() == Cube::AttachedLabel) { p_attached->setChecked(true); } else { diff --git a/isis/src/base/objs/IException/IException.cpp b/isis/src/base/objs/IException/IException.cpp index cd42a87e53..aa79d36efb 100644 --- a/isis/src/base/objs/IException/IException.cpp +++ b/isis/src/base/objs/IException/IException.cpp @@ -639,6 +639,10 @@ namespace Isis { return *this; } + int IException::length() { + return m_previousExceptions->length(); + } + /** * Returns the source of the error in string format for the given ErrorType. diff --git a/isis/src/base/objs/IException/IException.h b/isis/src/base/objs/IException/IException.h index 17c0c84fac..f78f00f491 100644 --- a/isis/src/base/objs/IException/IException.h +++ b/isis/src/base/objs/IException/IException.h @@ -197,6 +197,8 @@ namespace Isis { void swap(IException &other); IException &operator=(const IException &rhs); + int length(); + private: static IException createStackTrace(); static ErrorType stringToErrorType(const QString &s); diff --git a/isis/src/base/objs/ImageIoHandler/GdalIoHandler.cpp b/isis/src/base/objs/ImageIoHandler/GdalIoHandler.cpp new file mode 100644 index 0000000000..a5af442c9d --- /dev/null +++ b/isis/src/base/objs/ImageIoHandler/GdalIoHandler.cpp @@ -0,0 +1,873 @@ +#include "GdalIoHandler.h" + +#include "Brick.h" +#include "Buffer.h" +#include "CubeCachingAlgorithm.h" +#include "IException.h" +#include "Pvl.h" +#include "PvlObject.h" +#include "SpecialPixel.h" + +#include +#include +#include + +#include + +using namespace std; + +namespace Isis { + GdalIoHandler::GdalIoHandler(QString &dataFilePath, const QList *virtualBandList, GDALDataType pixelType, GDALAccess eAccess) : + ImageIoHandler(virtualBandList) { + GDALAllRegister(); + m_geodataSetPath = dataFilePath.toUtf8().constData(); + m_geodataSet = GDALDataset::FromHandle(GDALOpen(m_geodataSetPath.c_str(), eAccess)); + m_datasetOwner = true; + m_pixelType = pixelType; + init(); + } + + GdalIoHandler::GdalIoHandler(GDALDataset *geodataSet, const QList *virtualBandList, GDALDataType pixelType) : + ImageIoHandler(virtualBandList) { + m_geodataSet = geodataSet; + if (!m_geodataSet) { + QString msg = "Constructing GdalIoHandler failed. Null geodataSet pointer passed"; + throw IException(IException::Programmer, msg, _FILEINFO_); + } + m_datasetOwner = false; + m_pixelType = pixelType; + init(); + } + + void GdalIoHandler::init() { + m_samples = m_geodataSet->GetRasterXSize(); + m_lines = m_geodataSet->GetRasterYSize(); + m_bands = m_geodataSet->GetRasterCount(); + + m_driverName = std::string(m_geodataSet->GetDriverName()); + + // Check if we need to create the mask band + if ((m_geodataSet->GetAccess() == GA_Update) && (m_driverName != "ISIS3")) { + m_geodataSet->CreateMaskBand(8); + m_geodataSet->GetRasterBand(1)->GetMaskBand()->Fill(255); + } + + GDALRasterBand *band = m_geodataSet->GetRasterBand(1); + m_offset = band->GetOffset(); + m_scale = band->GetScale(); + int *pbSuccess = new int; + m_gdalNoDataValue = band->GetNoDataValue(pbSuccess); + if (!pbSuccess) { + m_gdalNoDataValue = NULL8; + } + delete pbSuccess; + } + + GdalIoHandler::~GdalIoHandler() { + clearCache(); + if (m_maskBuff) { + delete m_maskBuff; + } + if (m_geodataSet && m_datasetOwner) { + m_geodataSet->Close(); + } + } + + void GdalIoHandler::read(Buffer &bufferToFill) const { + GDALRasterBand *poBand; + + bufferToFill = NULL8; + int maxBand = bufferToFill.BandDimension() + bufferToFill.Band() - 1; + for (int band = bufferToFill.Band(); band <= maxBand; band++) { + int vband = band; + if (m_virtualBands) + vband = m_virtualBands->at(band - 1); + + if (vband > m_bands) { + return; + } + + poBand = m_geodataSet->GetRasterBand(vband); + + // Account for 1 based line and sample reading from ISIS process classes + // as gdal reads 0 based lines and samples + int lineStart = bufferToFill.Line() - 1; + int sampleStart = bufferToFill.Sample() - 1; + int lineSize = bufferToFill.LineDimension(); + int sampleSize = bufferToFill.SampleDimension(); + + // Handle reading out of bound pixels as cube IO handler would + bool outOfBounds = false; + if (lineStart < 0) { + lineStart = 0; + outOfBounds = true; + } + if (lineStart + lineSize > m_lines) { + lineSize = m_lines - lineStart; + outOfBounds = true; + } + if (sampleStart < 0) { + sampleStart = 0; + outOfBounds = true; + } + if (sampleStart + sampleSize > m_samples) { + sampleSize = m_samples - sampleStart; + outOfBounds = true; + } + + if (sampleSize > 0 && lineSize > 0) { + if (outOfBounds) { + Brick boundedBrick(sampleSize, lineSize, bufferToFill.BandDimension(), GdalPixelToIsis(m_pixelType), false, bufferToFill.scale()); + boundedBrick.SetBasePosition(sampleStart + 1, lineStart + 1, bufferToFill.Band()); + int bufferSize = boundedBrick.SampleDimensionScaled() * boundedBrick.LineDimensionScaled(); + int currentBandIdx = bufferSize * (vband - boundedBrick.Band()); + char *buffersRawBuf = &(((char *)boundedBrick.RawBuffer())[(int)(currentBandIdx * SizeOf(boundedBrick.PixelType()))]); + double *buffersDoubleBuf = &(boundedBrick.DoubleBuffer()[currentBandIdx]); + CPLErr err = poBand->RasterIO(GF_Read, sampleStart, lineStart, + sampleSize, lineSize, + buffersRawBuf, + boundedBrick.SampleDimensionScaled(), boundedBrick.LineDimensionScaled(), + m_pixelType, + 0, 0); + + if (err >= CE_Failure) { + QString msg = "Failure when trying to read image"; + throw IException(IException::Unknown, msg, _FILEINFO_); + } + + // Handle pixel type conversion + for (int bufferIdx = 0; bufferIdx < bufferSize; bufferIdx++) { + readPixelType(buffersDoubleBuf, buffersRawBuf, bufferIdx); + } + bufferToFill.CopyOverlapFrom(boundedBrick); + } + else { + int bufferSize = bufferToFill.SampleDimensionScaled() * bufferToFill.LineDimensionScaled();; + int currentBandIdx = bufferSize * (vband - bufferToFill.Band()); + // silence warnings + char *buffersRawBuf = &(((char *)bufferToFill.RawBuffer())[(int)(currentBandIdx * SizeOf(bufferToFill.PixelType()))]); + double *buffersDoubleBuf = &(bufferToFill.DoubleBuffer()[currentBandIdx]); + CPLErr err = poBand->RasterIO(GF_Read, sampleStart, lineStart, + sampleSize, lineSize, + buffersRawBuf, + bufferToFill.SampleDimensionScaled(), bufferToFill.LineDimensionScaled(), + m_pixelType, + 0, 0); + if (err >= CE_Failure) { + QString msg = "Failure when trying to read image"; + throw IException(IException::Unknown, msg, _FILEINFO_); + } + + // Handle pixel type conversion + for (int bufferIdx = 0; bufferIdx < bufferSize; bufferIdx++) { + readPixelType(buffersDoubleBuf, buffersRawBuf, bufferIdx); + } + } + } + } + } + + void GdalIoHandler::write(const Buffer &bufferToWrite) { + CPLErr gdalerr; + GDALRasterBand *poBand; + + int maxBand = bufferToWrite.BandDimension() + bufferToWrite.Band() - 1; + for (int band = bufferToWrite.Band(); band <= maxBand; band++) { + int vband = band; + if(m_virtualBands) + vband = m_virtualBands->indexOf(band) + 1; + + if (band > m_bands) { + return; + } + + poBand = m_geodataSet->GetRasterBand(vband); + + int lineStart = bufferToWrite.Line() - 1; + int sampleStart = bufferToWrite.Sample() - 1; + int lineSize = bufferToWrite.LineDimension(); + int sampleSize = bufferToWrite.SampleDimension(); + + if (lineStart < 0) { + lineStart = 0; + } + if (lineStart + lineSize > m_lines) { + lineSize = m_lines - lineStart; + } + if (sampleStart < 0) { + sampleStart = 0; + } + if (sampleStart + sampleSize > m_samples) { + sampleSize = m_samples - sampleStart; + } + + int bufferSize = sampleSize * lineSize; + int currentBandIdx = bufferSize * (vband - bufferToWrite.Band()); + m_maskBuff = (unsigned char *) CPLMalloc(sizeof(unsigned char) * bufferSize); + for (int i = 0; i < bufferSize; i++) { + m_maskBuff[i] = 255; + } + + + // Handle pixel type conversion + char *buffersRawBuf = &(((char *)bufferToWrite.RawBuffer())[(int)(currentBandIdx * SizeOf(bufferToWrite.PixelType()))]); + double *buffersDoubleBuf = &(bufferToWrite.DoubleBuffer()[currentBandIdx]); + // std::cout << buffersDoubleBuf[0] << std::endl; + for (int bufferIdx = 0; bufferIdx < bufferSize; bufferIdx++) { + if (writePixelType(buffersDoubleBuf, buffersRawBuf, bufferIdx)) { + m_maskBuff[bufferIdx] = 0; + }; + } + + // silence warning + gdalerr = poBand->RasterIO(GF_Write, sampleStart, lineStart, + sampleSize, lineSize, + (void *)buffersRawBuf, + sampleSize, lineSize, + m_pixelType, + 0, 0); + if (gdalerr >= CE_Failure) { + QString msg = "Failure when trying to write image"; + throw IException(IException::Unknown, msg, _FILEINFO_); + } + + if (m_driverName != "ISIS3") { + poBand = m_geodataSet->GetRasterBand(band)->GetMaskBand(); + gdalerr = poBand->RasterIO(GF_Write, sampleStart, lineStart, + sampleSize, lineSize, + m_maskBuff, + sampleSize, lineSize, + GDT_Byte, + 0, 0); + if (gdalerr >= CE_Failure) { + QString msg = "Failure when trying to write msk file"; + throw IException(IException::Unknown, msg, _FILEINFO_); + } + } + } + + delete m_maskBuff; + m_maskBuff = NULL; + } + + BigInt GdalIoHandler::getDataSize() const { + return 0; + } + /** + * Function to update the labels with a Pvl object + * + * @param labels Pvl object to update with + */ + void GdalIoHandler::updateLabels(Pvl &labels) { + PvlObject &core = labels.findObject("IsisCube").findObject("Core"); + core.addKeyword(PvlKeyword("Format", "GTiff"), PvlContainer::Replace); + } + + void GdalIoHandler::readPixelType(double *doubleBuff, void *rawBuff, int idx) const { + double &bufferVal = doubleBuff[idx]; + + if (m_pixelType == GDT_Float64) { + bufferVal = ((double *)rawBuff)[idx]; + if (bufferVal == m_gdalNoDataValue) { + bufferVal = NULL8; + } + } + + else if(m_pixelType == GDT_Float32) { + float raw = ((float *)rawBuff)[idx]; + // if(m_byteSwapper) + // raw = m_byteSwapper->Float(&raw); + if (raw == (float) m_gdalNoDataValue) { + raw = NULL4; + } + + if(raw >= VALID_MIN4) { + bufferVal = (double) raw; + } + else { + if(raw == NULL4) + bufferVal = NULL8; + else if(raw == LOW_INSTR_SAT4) + bufferVal = LOW_INSTR_SAT8; + else if(raw == LOW_REPR_SAT4) + bufferVal = LOW_REPR_SAT8; + else if(raw == HIGH_INSTR_SAT4) + bufferVal = HIGH_INSTR_SAT8; + else if(raw == HIGH_REPR_SAT4) + bufferVal = HIGH_REPR_SAT8; + else + bufferVal = LOW_REPR_SAT8; + } + + ((float *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_Int32) { + int raw = ((int *)rawBuff)[idx]; + // if(m_byteSwapper) + // raw = m_byteSwapper->Uint32_t(&raw); + if (raw == (int) m_gdalNoDataValue) { + raw = NULLI4; + } + + if(raw >= VALID_MINI4) { + bufferVal = (double) raw * m_scale + m_offset; + } + else { + if (raw == NULLI4) + bufferVal = NULL8; + else if (raw == LOW_INSTR_SATI4) + bufferVal = LOW_INSTR_SAT8; + else if (raw == LOW_REPR_SATI4) + bufferVal = LOW_REPR_SAT8; + else if (raw == HIGH_INSTR_SATI4) + bufferVal = HIGH_INSTR_SAT8; + else if (raw == HIGH_REPR_SATI4) + bufferVal = HIGH_REPR_SAT8; + else + bufferVal = LOW_REPR_SAT8; + } + ((int *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_UInt32) { + unsigned int raw = ((unsigned int *)rawBuff)[idx]; + // if(m_byteSwapper) + // raw = m_byteSwapper->Uint32_t(&raw); + if (raw == (unsigned int) m_gdalNoDataValue) { + raw = NULLUI4; + } + + if(raw >= VALID_MINUI4) { + bufferVal = (double) raw * m_scale + m_offset; + if (raw >= VALID_MAXUI4) { + if(raw == HIGH_INSTR_SATUI4) + bufferVal = HIGH_INSTR_SAT8; + else if(raw == HIGH_REPR_SATUI4) + bufferVal = HIGH_REPR_SAT8; + else + bufferVal = HIGH_REPR_SAT8; + } + } + else { + if(raw == NULLUI4) + bufferVal = NULL8; + else if(raw == LOW_INSTR_SATUI4) + bufferVal = LOW_INSTR_SAT8; + else if(raw == LOW_REPR_SATUI4) + bufferVal = LOW_REPR_SAT8; + else + bufferVal = LOW_REPR_SAT8; + } + ((unsigned int *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_Int16) { + short raw = ((short *)rawBuff)[idx]; + // if(m_byteSwapper) + // raw = m_byteSwapper->ShortInt(&raw); + if (raw == (short) m_gdalNoDataValue) { + raw = NULL2; + } + + if(raw >= VALID_MIN2) { + bufferVal = (double) raw * m_scale + m_offset; + } + else { + if(raw == NULL2) + bufferVal = NULL8; + else if(raw == LOW_INSTR_SAT2) + bufferVal = LOW_INSTR_SAT8; + else if(raw == LOW_REPR_SAT2) + bufferVal = LOW_REPR_SAT8; + else if(raw == HIGH_INSTR_SAT2) + bufferVal = HIGH_INSTR_SAT8; + else if(raw == HIGH_REPR_SAT2) + bufferVal = HIGH_REPR_SAT8; + else + bufferVal = LOW_REPR_SAT8; + } + + ((short *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_UInt16) { + unsigned short raw = ((unsigned short *)rawBuff)[idx]; + // if(m_byteSwapper) + // raw = m_byteSwapper->UnsignedShortInt(&raw); + if (raw == (unsigned short) m_gdalNoDataValue) { + raw = NULLU2; + } + + if(raw >= VALID_MINU2) { + bufferVal = (double) raw * m_scale + m_offset; + if (raw > VALID_MAXU2) { + if(raw == HIGH_INSTR_SATU2) + bufferVal = HIGH_INSTR_SAT8; + else if(raw == HIGH_REPR_SATU2) + bufferVal = HIGH_REPR_SAT8; + else + bufferVal = HIGH_REPR_SAT8; + } + } + else { + if(raw == NULLU2) + bufferVal = NULL8; + else if(raw == LOW_INSTR_SATU2) + bufferVal = LOW_INSTR_SAT8; + else if(raw == LOW_REPR_SATU2) + bufferVal = LOW_REPR_SAT8; + else + bufferVal = LOW_REPR_SAT8; + } + ((unsigned short *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_Int8) { + char raw = ((char *)rawBuff)[idx]; + if (raw == (char) m_gdalNoDataValue) { + raw = NULLS1; + } + + if(raw == NULLS1) { + bufferVal = NULL8; + } + else if(raw == HIGH_REPR_SATS1) { + bufferVal = HIGH_REPR_SAT8; + } + else { + bufferVal = (double) raw * m_scale + m_offset; + } + + ((char *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_Byte) { + unsigned char raw = ((unsigned char *)rawBuff)[idx]; + if (raw == (char) m_gdalNoDataValue) { + raw = NULL1; + } + + if(raw == NULL1) { + bufferVal = NULL8; + } + else if(raw == HIGH_REPR_SAT1) { + bufferVal = HIGH_REPR_SAT8; + } + else { + bufferVal = (double) raw * m_scale + m_offset; + } + + ((unsigned char *)rawBuff)[idx] = raw; + } + } + + bool GdalIoHandler::writePixelType(double *doubleBuff, void *rawBuff, int idx) const { + double bufferVal = doubleBuff[idx]; + bool isSpecial = false; + if (m_pixelType == GDT_Float64) { + ((double *)rawBuff)[idx] = bufferVal; + } + + else if(m_pixelType == GDT_Float32) { + float raw = 0; + + if(bufferVal >= VALID_MIN8) { + double filePixelValueDbl = (bufferVal - m_offset) / + m_scale; + + if(filePixelValueDbl < (double) VALID_MIN4) { + raw = LOW_REPR_SAT4; + isSpecial = true; + } + else if(filePixelValueDbl > (double) VALID_MAX4) { + raw = HIGH_REPR_SAT4; + isSpecial = true; + } + else { + raw = (float) filePixelValueDbl; + } + } + else { + if(bufferVal == NULL8) { + raw = NULL4; + isSpecial = true; + } + else if(bufferVal == LOW_INSTR_SAT8) { + raw = LOW_INSTR_SAT4; + isSpecial = true; + } + else if(bufferVal == LOW_REPR_SAT8) { + raw = LOW_REPR_SAT4; + isSpecial = true; + } + else if(bufferVal == HIGH_INSTR_SAT8) { + raw = HIGH_INSTR_SAT4; + isSpecial = true; + } + else if(bufferVal == HIGH_REPR_SAT8) { + raw = HIGH_REPR_SAT4; + isSpecial = true; + } + else { + raw = LOW_REPR_SAT4; + isSpecial = true; + } + } + ((float *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_Int32) { + int raw; + + if(bufferVal >= VALID_MINI4) { + double filePixelValueDbl = (bufferVal - m_offset) / + m_scale; + if(filePixelValueDbl < VALID_MINI4 - 0.5) { + raw = LOW_REPR_SATI4; + isSpecial = true; + } + if(filePixelValueDbl > VALID_MAXI4) { + raw = HIGH_REPR_SATI4; + isSpecial = true; + } + else { + int filePixelValue = (int)round(filePixelValueDbl); + + if(filePixelValue < VALID_MINI4) { + raw = LOW_REPR_SATI4; + isSpecial = true; + } + else if(filePixelValue > VALID_MAXI4) { + raw = HIGH_REPR_SATI4; + isSpecial = true; + } + else { + raw = filePixelValue; + } + } + } + else { + if(bufferVal == NULL8) { + raw = NULLI4; + isSpecial = true; + } + else if(bufferVal == LOW_INSTR_SAT8) { + raw = LOW_INSTR_SATI4; + isSpecial = true; + } + else if(bufferVal == LOW_REPR_SAT8) { + raw = LOW_REPR_SATI4; + isSpecial = true; + } + else if(bufferVal == HIGH_INSTR_SAT8) { + raw = HIGH_INSTR_SATI4; + isSpecial = true; + } + else if(bufferVal == HIGH_REPR_SAT8) { + raw = HIGH_REPR_SATI4; + isSpecial = true; + } + else { + raw = LOW_REPR_SATI4; + isSpecial = true; + } + } + ((int *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_UInt32) { + unsigned int raw; + + if(bufferVal >= VALID_MINUI4) { + double filePixelValueDbl = (bufferVal - m_offset) / + m_scale; + if(filePixelValueDbl < VALID_MINUI4 - 0.5) { + raw = LOW_REPR_SATUI4; + isSpecial = true; + } + if(filePixelValueDbl > VALID_MAXUI4) { + raw = HIGH_REPR_SATUI4; + isSpecial = true; + } + else { + unsigned int filePixelValue = (unsigned int)round(filePixelValueDbl); + + if(filePixelValue < VALID_MINUI4) { + raw = LOW_REPR_SATUI4; + isSpecial = true; + } + else if(filePixelValue > VALID_MAXUI4) { + raw = HIGH_REPR_SATUI4; + isSpecial = true; + } + else { + raw = filePixelValue; + } + } + } + else { + if(bufferVal == NULL8) { + raw = NULLUI4; + isSpecial = true; + } + else if(bufferVal == LOW_INSTR_SAT8) { + raw = LOW_INSTR_SATUI4; + isSpecial = true; + } + else if(bufferVal == LOW_REPR_SAT8) { + raw = LOW_REPR_SATUI4; + isSpecial = true; + } + else if(bufferVal == HIGH_INSTR_SAT8) { + raw = HIGH_INSTR_SATUI4; + isSpecial = true; + } + else if(bufferVal == HIGH_REPR_SAT8) { + raw = HIGH_REPR_SATUI4; + isSpecial = true; + } + else { + raw = LOW_REPR_SATUI4; + isSpecial = true; + } + } + ((unsigned int *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_Int16) { + short raw; + + if(bufferVal >= VALID_MIN8) { + double filePixelValueDbl = (bufferVal - m_offset) / + m_scale; + if(filePixelValueDbl < VALID_MIN2 - 0.5) { + raw = LOW_REPR_SAT2; + isSpecial = true; + } + if(filePixelValueDbl > VALID_MAX2 + 0.5) { + raw = HIGH_REPR_SAT2; + isSpecial = true; + } + else { + int filePixelValue = (int)round(filePixelValueDbl); + + if(filePixelValue < VALID_MIN2) { + raw = LOW_REPR_SAT2; + isSpecial = true; + } + else if(filePixelValue > VALID_MAX2) { + raw = HIGH_REPR_SAT2; + isSpecial = true; + } + else { + raw = filePixelValue; + } + } + } + else { + if(bufferVal == NULL8) { + raw = NULL2; + isSpecial = true; + } + else if(bufferVal == LOW_INSTR_SAT8) { + raw = LOW_INSTR_SAT2; + isSpecial = true; + } + else if(bufferVal == LOW_REPR_SAT8) { + raw = LOW_REPR_SAT2; + isSpecial = true; + } + else if(bufferVal == HIGH_INSTR_SAT8) { + raw = HIGH_INSTR_SAT2; + isSpecial = true; + } + else if(bufferVal == HIGH_REPR_SAT8) { + raw = HIGH_REPR_SAT2; + isSpecial = true; + } + else { + raw = LOW_REPR_SAT2; + isSpecial = true; + } + } + ((short *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_UInt16) { + unsigned short raw; + + if(bufferVal >= VALID_MIN8) { + double filePixelValueDbl = (bufferVal - m_offset) / + m_scale; + if(filePixelValueDbl < VALID_MINU2 - 0.5) { + raw = LOW_REPR_SATU2; + isSpecial = true; + } + if(filePixelValueDbl > VALID_MAXU2 + 0.5) { + raw = HIGH_REPR_SATU2; + isSpecial = true; + } + else { + int filePixelValue = (int)round(filePixelValueDbl); + + if(filePixelValue < VALID_MINU2) { + raw = LOW_REPR_SATU2; + isSpecial = true; + } + else if(filePixelValue > VALID_MAXU2) { + raw = HIGH_REPR_SATU2; + isSpecial = true; + } + else { + raw = filePixelValue; + } + } + } + else { + if(bufferVal == NULL8) { + raw = NULLU2; + isSpecial = true; + } + else if(bufferVal == LOW_INSTR_SAT8) { + raw = LOW_INSTR_SATU2; + isSpecial = true; + } + else if(bufferVal == LOW_REPR_SAT8) { + raw = LOW_REPR_SATU2; + isSpecial = true; + } + else if(bufferVal == HIGH_INSTR_SAT8) { + raw = HIGH_INSTR_SATU2; + isSpecial = true; + } + else if(bufferVal == HIGH_REPR_SAT8) { + raw = HIGH_REPR_SATU2; + isSpecial = true; + } + else { + raw = LOW_REPR_SATU2; + isSpecial = true; + } + } + ((unsigned short *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_Int8) { + char raw; + + if(bufferVal >= VALID_MIN8) { + double filePixelValueDbl = (bufferVal - m_offset) / + m_scale; + if(filePixelValueDbl < VALID_MINS1 - 0.5) { + raw = LOW_REPR_SATS1; + isSpecial = true; + } + else if(filePixelValueDbl > VALID_MAXS1 + 0.5) { + raw = HIGH_REPR_SATS1; + isSpecial = true; + } + else { + int filePixelValue = (int)(filePixelValueDbl + 0.5); + if(filePixelValue < VALID_MINS1) { + raw = LOW_REPR_SATS1; + isSpecial = true; + } + else if(filePixelValue > VALID_MAXS1) { + raw = HIGH_REPR_SATS1; + isSpecial = true; + } + else { + raw = (char)(filePixelValue); + } + } + } + else { + if(bufferVal == NULL8) { + raw = NULLS1; + isSpecial = true; + } + else if(bufferVal == LOW_INSTR_SAT8) { + raw = LOW_INSTR_SATS1; + isSpecial = true; + } + else if(bufferVal == LOW_REPR_SAT8) { + raw = LOW_REPR_SATS1; + isSpecial = true; + } + else if(bufferVal == HIGH_INSTR_SAT8) { + raw = HIGH_INSTR_SATS1; + isSpecial = true; + } + else if(bufferVal == HIGH_REPR_SAT8) { + raw = HIGH_REPR_SATS1; + isSpecial = true; + } + else { + raw = LOW_REPR_SATS1; + isSpecial = true; + } + } + ((char *)rawBuff)[idx] = raw; + } + + else if(m_pixelType == GDT_Byte) { + unsigned char raw; + + if(bufferVal >= VALID_MIN8) { + double filePixelValueDbl = (bufferVal - m_offset) / + m_scale; + if(filePixelValueDbl < VALID_MIN1 - 0.5) { + raw = LOW_REPR_SAT1; + isSpecial = true; + } + else if(filePixelValueDbl > VALID_MAX1 + 0.5) { + raw = HIGH_REPR_SAT1; + isSpecial = true; + } + else { + int filePixelValue = (int)(filePixelValueDbl + 0.5); + if(filePixelValue < VALID_MIN1) { + raw = LOW_REPR_SAT1; + isSpecial = true; + } + else if(filePixelValue > VALID_MAX1) { + raw = HIGH_REPR_SAT1; + isSpecial = true; + } + else { + raw = (unsigned char)(filePixelValue); + } + } + } + else { + if(bufferVal == NULL8) { + raw = NULL1; + isSpecial = true; + } + else if(bufferVal == LOW_INSTR_SAT8) { + raw = LOW_INSTR_SAT1; + isSpecial = true; + } + else if(bufferVal == LOW_REPR_SAT8) { + raw = LOW_REPR_SAT1; + isSpecial = true; + } + else if(bufferVal == HIGH_INSTR_SAT8) { + raw = HIGH_INSTR_SAT1; + isSpecial = true; + } + else if(bufferVal == HIGH_REPR_SAT8) { + raw = HIGH_REPR_SAT1; + isSpecial = true; + } + else { + raw = LOW_REPR_SAT1; + isSpecial = true; + } + } + ((unsigned char *)rawBuff)[idx] = raw; + } + return isSpecial; + } +} \ No newline at end of file diff --git a/isis/src/base/objs/ImageIoHandler/GdalIoHandler.h b/isis/src/base/objs/ImageIoHandler/GdalIoHandler.h new file mode 100644 index 0000000000..bdc5079d81 --- /dev/null +++ b/isis/src/base/objs/ImageIoHandler/GdalIoHandler.h @@ -0,0 +1,85 @@ +/** This is free and unencumbered software released into the public domain. +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ + +#ifndef GdalIoHandler_h +#define GdalIoHandler_h + +#include "Constants.h" +#include "ImageIoHandler.h" +#include "SpecialPixel.h" + +#include "gdal_priv.h" + +class QFile; +class QMutex; +class QString; +template class QList; + +namespace Isis { + class Buffer; + class CubeCachingAlgorithm; + class Pvl; + + /** + * @ingroup Low Level Image IO + * + * @brief Handles converting buffers to and from disk. + * + * This class handles converting buffers to and from disk. This class holds + * the cube chunks in memory and is capable of reading and writing them. It + * asks the caching algorithms to recommend cube chunks to not keep in + * memory. Children need to call setChunkSizes() in their constructor. + * + * This class handles all of the virtual band conversions. This class also + * guarantees that unwritten cube data ends up read and written as NULLs. + * The default caching algorithm is a RegionalCachingAlgorithm. + * + * @author 2024-04-30 Adam Paquette + * + */ + class GdalIoHandler : public ImageIoHandler { + public: + GdalIoHandler(QString &dataFilePath, const QList *virtualBandList, GDALDataType pixelType = GDT_Float64, GDALAccess eAccess=GA_ReadOnly); + GdalIoHandler(GDALDataset *geodataSet, const QList *virtualBandList, GDALDataType pixelType = GDT_Float64); + void init(); + virtual ~GdalIoHandler(); + + virtual void read(Buffer &bufferToFill) const; + virtual void write(const Buffer &bufferToWrite); + + virtual BigInt getDataSize() const; + /** + * Function to update the labels with a Pvl object + * + * @param labels Pvl object to update with + */ + virtual void updateLabels(Pvl &labels); + + virtual void clearCache(bool blockForWriteCache=false) { + m_geodataSet->FlushCache(blockForWriteCache); + } + + private: + void readPixelType(double *doubleBuff, void *rawBuff, int idx) const; + bool writePixelType(double *doubleBuff, void *rawBuff, int idx) const; + + GDALDataset *m_geodataSet = nullptr; + std::string m_geodataSetPath = ""; + GDALDataType m_pixelType; + int m_lines; + int m_samples; + int m_bands; + double m_offset; + double m_scale; + unsigned char *m_maskBuff = nullptr; + bool m_datasetOwner = false; + double m_gdalNoDataValue = NULL8; + std::string m_driverName; + }; +} + +#endif diff --git a/isis/src/base/objs/ImageIoHandler/ImageIoHandler.cpp b/isis/src/base/objs/ImageIoHandler/ImageIoHandler.cpp new file mode 100644 index 0000000000..7965c69e2b --- /dev/null +++ b/isis/src/base/objs/ImageIoHandler/ImageIoHandler.cpp @@ -0,0 +1,61 @@ +#include "ImageIoHandler.h" + +#include "Buffer.h" +#include "CubeCachingAlgorithm.h" + +#include +#include + +using namespace std; + +namespace Isis { + ImageIoHandler::ImageIoHandler(const QList *virtualBandList) { + m_writeThreadMutex = NULL; + m_writeThreadMutex = new QMutex; + m_virtualBands = NULL; + + setVirtualBands(virtualBandList); + } + + ImageIoHandler::~ImageIoHandler() { + delete m_virtualBands; + m_virtualBands = NULL; + + delete m_writeThreadMutex; + m_writeThreadMutex = NULL; + } + + void ImageIoHandler::addCachingAlgorithm(CubeCachingAlgorithm *algorithm) {} + + void ImageIoHandler::clearCache(bool blockForWriteCache) const {} + + /** + * This changes the virtual band list. + * + * @param virtualBandList A list where the indices are the vbands and the + * values are the physical bands. The values are 1-based. This can + * be specified as NULL, in which case the vbands are the physical + * bands. The virtual band list is copied + * (the pointer provided isn't remembered). + */ + void ImageIoHandler::setVirtualBands(const QList *virtualBandList) { + if(m_virtualBands) { + delete m_virtualBands; + m_virtualBands = NULL; + } + + if(virtualBandList && !virtualBandList->empty()) + m_virtualBands = new QList(*virtualBandList); + } + + /** + * Get the mutex that this IO handler is using around I/Os on the given + * data file. A lock should be acquired before doing any reads/writes on + * the data file externally. + * + * @return A mutex that can guarantee exclusive access to the data file + */ + QMutex *ImageIoHandler::dataFileMutex() { + return m_writeThreadMutex; + } +} \ No newline at end of file diff --git a/isis/src/base/objs/ImageIoHandler/ImageIoHandler.h b/isis/src/base/objs/ImageIoHandler/ImageIoHandler.h new file mode 100644 index 0000000000..baab584cd7 --- /dev/null +++ b/isis/src/base/objs/ImageIoHandler/ImageIoHandler.h @@ -0,0 +1,128 @@ +/** This is free and unencumbered software released into the public domain. +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ + +#ifndef ImageIoHandler_h +#define ImageIoHandler_h + +#include "Constants.h" + +class QFile; +class QMutex; +template class QList; + +namespace Isis { + class Buffer; + class CubeCachingAlgorithm; + class Pvl; + + /** + * @ingroup Low Level Image IO + * + * @brief Handles converting buffers to and from disk. + * + * This class handles converting buffers to and from disk. This class holds + * the cube chunks in memory and is capable of reading and writing them. It + * asks the caching algorithms to recommend cube chunks to not keep in + * memory. Children need to call setChunkSizes() in their constructor. + * + * This class handles all of the virtual band conversions. This class also + * guarantees that unwritten cube data ends up read and written as NULLs. + * The default caching algorithm is a RegionalCachingAlgorithm. + * + * @author 2011-??-?? Jai Rideout and Steven Lambright + * + * @internal + * @history 2011-06-27 Steven Lambright - Added addCachingAlgorithm() + * @history 2011-07-18 Jai Rideout and Steven Lambright - Added + * unimplemented copy constructor and assignment + * operator. + * @history 2011-07-18 Jai Rideout and Steven Lambright - getDataFile() is + * no longer const, along with readRaw() and + * writeRaw(). + * @history 2011-11-23 Jai Rideout - Added fix to findCubeChunks() and + * findIntersection() so that requested areas that + * are outside of the virtual bands of the cube will + * no longer fail, but instead will fill the buffer + * with nulls. + * @history 2012-02-17 Steven Lambright - The read() method is now const - + * the caching should be transparent to the API. It + * is still important to note that you cannot call + * read() and write() simultaneously - Cube is + * guaranteeing this. Backgrounded writes are now + * implemented and fully supported. Timing tests + * show results like this: + * Before: + * User: 358.71 Kernel: 38.29 Total Elapsed: 6:49.83 + * After: + * User: 380.01 Kernel: 44.97 Total Elapsed: 6:19.96 + * User- user-space CPU time consumed. + * Kernel- kernel-space CPU time consumed. + * The CPU consumption and kernel time consumption + * goes up, but the overall run time is lower. This + * makes the option perfectly viable for typical + * multi-core desktop solutions. Turning off the + * cube write optimization results in equivalent run + * times as before implementation. These time tests + * were run before the adaptive cache flush size was + * added, which improved performance further. + * References #727. + * @history 2012-06-06 Jeff Anderson - The read() method was modified to + * improve the speed for small buffer sizes as seen + * in ProcessByBoxcar and ProcessRubbersheet. The + * internal cache was always checked to be minimized + * which is slow for every read. Now the cache is + * only mimimized if it changed in size. + * Reference #894. + * @history 2014-04-07 Kimberly Oyama and Stuart Sides - Modified the findCubeChunks, + * writeIntoDouble, and writeIntoRaw methods to handle + * repeating virtual bands. Fixes #1927. + * @history 2014-09-30 Ian Humphrey - Modified read method to correctly access virtual bands + * when cube dimensions and buffer shape are same size. Fixes #1689. + * @history 2015-01-30 Ian Humphrey - Modified destructor to free m_writThreadMutex to + * prevent memory leaks upon destruction. Fixes #2082. + * @history 2016-04-21 Makayla Shepherd - Added UnsignedWord pixel type handling. + * @history 2016-06-21 Kris Becker - Properly forward declare QPair as struct not class + * @history 2016-08-28 Kelvin Rodriguez - updateLabels now a pure virtual, it had no + * implementation causing warnings in clang. Part of OS X 10.11 porting. + * QPair forward declaration now properly claims it as a struct. + * @history 2017-09-22 Cole Neubauer - Fixed documentation. References #4807 + * @history 2018-07-20 Tyler Wilson - Added support for unsigned integer special pixel values. + * in functions writeIntoRaw(...) and writeIntoDouble(...) + * References #971. + * @history 2018-08-13 Summer Stapleton - Fixed incoming buffer comparison values for + * unsigned int type in writeIntoRaw(...). + */ + class ImageIoHandler { + public: + ImageIoHandler(const QList *virtualBandList); + virtual ~ImageIoHandler(); + + virtual void read(Buffer &bufferToFill) const = 0; + virtual void write(const Buffer &bufferToWrite) = 0; + + virtual void addCachingAlgorithm(CubeCachingAlgorithm *algorithm); + virtual void clearCache(bool blockForWriteCache = true) const; + virtual BigInt getDataSize() const = 0; + void setVirtualBands(const QList *virtualBandList); + /** + * Function to update the labels with a Pvl object + * + * @param labels Pvl object to update with + */ + virtual void updateLabels(Pvl &labels) = 0; + QMutex *dataFileMutex(); + + protected: + //! This enables us to block while the write thread is working + QMutex *m_writeThreadMutex; + + //! Converts from virtual band to physical band. + QList *m_virtualBands; + }; +} + +#endif diff --git a/isis/src/base/objs/PixelType/PixelType.h b/isis/src/base/objs/PixelType/PixelType.h index c9f5868fe8..8f2de66d2c 100644 --- a/isis/src/base/objs/PixelType/PixelType.h +++ b/isis/src/base/objs/PixelType/PixelType.h @@ -8,6 +8,8 @@ find files of those names at the top level of this repository. **/ /* SPDX-License-Identifier: CC0-1.0 */ #include +#include "gdal_priv.h" + namespace Isis { /** * @brief Enumerations for Isis Pixel Types @@ -98,6 +100,31 @@ namespace Isis { if(temp == "DOUBLE") return Isis::Double; return Isis::None; } + + inline Isis::PixelType GdalPixelToIsis(GDALDataType type) { + if (type == GDT_Byte) return Isis::UnsignedByte; + if (type == GDT_Int8) return Isis::SignedByte; + if (type == GDT_UInt16) return Isis::UnsignedWord; + if (type == GDT_Int16) return Isis::SignedWord; + if (type == GDT_UInt32) return Isis::UnsignedInteger; + if (type == GDT_Int32) return Isis::SignedInteger; + if (type == GDT_Float32) return Isis::Real; + if (type == GDT_Float64) return Isis::Double; + return Isis::None; + } + + inline GDALDataType IsisPixelToGdal(Isis::PixelType type) { + if(type == Isis::None) return GDT_Unknown; + if(type == Isis::UnsignedByte) return GDT_Byte; + if(type == Isis::SignedByte) return GDT_Int8; + if(type == Isis::UnsignedWord) return GDT_UInt16; + if(type == Isis::SignedWord) return GDT_Int16; + if(type == Isis::UnsignedInteger) return GDT_UInt32; + if(type == Isis::SignedInteger) return GDT_Int32; + if(type == Isis::Real) return GDT_Float32; + if(type == Isis::Double) return GDT_Float64; + return GDT_Unknown; + } } #endif diff --git a/isis/src/base/objs/Process/Process.cpp b/isis/src/base/objs/Process/Process.cpp index b004663529..0d00aafe73 100644 --- a/isis/src/base/objs/Process/Process.cpp +++ b/isis/src/base/objs/Process/Process.cpp @@ -330,7 +330,7 @@ Isis::Cube *Process::SetOutputCubeStretch(const QString ¶meter, const int ns cube->setDimensions(ns, nl, nb); cube->setByteOrder(att.byteOrder()); cube->setFormat(att.fileFormat()); - cube->setLabelsAttached(att.labelAttachment() == AttachedLabel); + cube->setLabelsAttached(att.labelAttachment()); if(att.propagatePixelType()) { if(InputCubes.size() > 0) { cube->setPixelType(InputCubes[0]->pixelType()); diff --git a/isis/src/base/objs/ProcessMapMosaic/ProcessMapMosaic.cpp b/isis/src/base/objs/ProcessMapMosaic/ProcessMapMosaic.cpp index 90368f87ce..48d9292d49 100644 --- a/isis/src/base/objs/ProcessMapMosaic/ProcessMapMosaic.cpp +++ b/isis/src/base/objs/ProcessMapMosaic/ProcessMapMosaic.cpp @@ -508,7 +508,8 @@ 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) { - Pvl fileLab(inputFile); + Cube cube(inputFile); + Pvl fileLab = *cube.label(); PvlGroup &mapping = fileLab.findGroup("Mapping", Pvl::Traverse); // All mosaicking programs use only the upper left x and y to determine where to diff --git a/isis/src/base/objs/Pvl/Pvl.cpp b/isis/src/base/objs/Pvl/Pvl.cpp index 1c62405093..318913bca1 100644 --- a/isis/src/base/objs/Pvl/Pvl.cpp +++ b/isis/src/base/objs/Pvl/Pvl.cpp @@ -11,6 +11,11 @@ find files of those names at the top level of this repository. **/ #include #include #include +#include + +#include +#include +#include #include "FileName.h" #include "IException.h" @@ -18,6 +23,10 @@ find files of those names at the top level of this repository. **/ #include "PvlTokenizer.h" #include "PvlFormat.h" +namespace fs = std::filesystem; +using json = nlohmann::json; +using ordered_json = nlohmann::ordered_json; + using namespace std; namespace Isis { //! Constructs an empty Pvl object. @@ -36,7 +45,6 @@ namespace Isis { read(file); } - //! Copy constructor Pvl::Pvl(const Pvl &other) : PvlObject::PvlObject(other) { m_internalTemplate = false; @@ -51,6 +59,109 @@ namespace Isis { m_internalTemplate = false; } + // This function specifically reads from GDAL-style JSON metadata. + Isis::PvlObject &Pvl::readObject(Isis::PvlObject &pvlobj, nlohmann::ordered_json jdata) { + for(auto &[key, value] : jdata.items()) { + string name = key; + if(value.contains("_type")) { + string type = value.at("_type"); + value.erase("_type"); + if (value.contains("_container_name")) { + name = value["_container_name"]; + value.erase("_container_name"); + } + + if(type == "object") { + PvlObject nestedObj(QString::fromStdString(name)); + pvlobj.addObject(readObject(nestedObj, value)); + } + if(type == "group") { + // parse keys + PvlGroup group(QString::fromStdString(name)); + for(auto &[pvlkeyword, pvlvalue] : value.items()) { + PvlKeyword keyword; + keyword.setName(QString::fromStdString(pvlkeyword)); + if (pvlvalue.is_array()) + keyword.addJsonArrayValue(pvlvalue); + else + keyword.setJsonValue(pvlvalue); + group.addKeyword(keyword); + } + pvlobj.addGroup(group); + } // end of group + } // end of _type search + + // not a group or object, must be a keyword + else if (key != "_type" && key != "_filename" && key != "Data") { + PvlKeyword keyword; + keyword.setName(QString::fromStdString(key)); + if (value.is_array()) + keyword.setJsonArrayValue(value); + else + keyword.setJsonValue(value); + + pvlobj.addKeyword(keyword); + } + } + return pvlobj; + } + + + ordered_json Pvl::toJson() { + // needs to be a function because recursion + function pvlobject_to_json = [&](PvlObject &pvlobj) -> ordered_json { + ordered_json jsonobj; + string okey = (pvlobj.name()).toStdString(); + if (pvlobj.hasKeyword("Name")) { + okey = (pvlobj.name()+"_"+(QString)pvlobj.findKeyword("Name")).toStdString(); + } + jsonobj[okey] = {}; + if (pvlobj.hasKeyword("Name")) { + jsonobj[okey]["_container_name"] = pvlobj.name().toStdString(); + } + + if (okey != "Root") { + jsonobj[okey]["_type"] = "object"; + } + + for (int i=0; i < pvlobj.objects(); i++) { + jsonobj[okey].merge_patch(pvlobject_to_json(pvlobj.object(i))); + } + + for (int i=0; iname() == "Root" && this->objects() == 1) { + return pvlobject_to_json(this->object(0)); + } + else { + return pvlobject_to_json(*this); + } + } /** * Load PVL information from a string diff --git a/isis/src/base/objs/Pvl/Pvl.h b/isis/src/base/objs/Pvl/Pvl.h index d634ac5a6d..df89e8db6c 100644 --- a/isis/src/base/objs/Pvl/Pvl.h +++ b/isis/src/base/objs/Pvl/Pvl.h @@ -8,8 +8,11 @@ find files of those names at the top level of this repository. **/ /* SPDX-License-Identifier: CC0-1.0 */ #include + #include "PvlObject.h" +#include + namespace Isis { /** * @brief Container for cube-like labels @@ -126,12 +129,15 @@ namespace Isis { friend std::ostream &operator<<(std::ostream &os, Isis::Pvl &pvl); void fromString(const std::string &str); + static Isis::PvlObject &readObject(Isis::PvlObject &pvlobj, nlohmann::ordered_json jdata); + nlohmann::ordered_json toJson(); + ~Pvl() { if(m_internalTemplate) delete m_formatTemplate; }; void read(const QString &file); - + void write(const QString &file); void append(const QString &file); diff --git a/isis/src/base/objs/PvlKeyword/PvlKeyword.cpp b/isis/src/base/objs/PvlKeyword/PvlKeyword.cpp index 0fa0aca35b..4fa5c779d2 100644 --- a/isis/src/base/objs/PvlKeyword/PvlKeyword.cpp +++ b/isis/src/base/objs/PvlKeyword/PvlKeyword.cpp @@ -19,6 +19,7 @@ find files of those names at the top level of this repository. **/ using namespace std; using json = nlohmann::json; + namespace Isis { //! Constructs a blank PvlKeyword object. PvlKeyword::PvlKeyword() { @@ -172,10 +173,9 @@ namespace Isis { * * @see addJsonValue() */ - void PvlKeyword::setJsonValue(json jsonobj, QString unit) - { + void PvlKeyword::setJsonValue(json jsonobj) { clear(); - addJsonValue(jsonobj, unit); + addJsonValue(jsonobj); } /** @@ -301,31 +301,108 @@ namespace Isis { * * @throws Isis::iException::Unknown - jsonobj cannot be an array of values */ - void PvlKeyword::addJsonValue(json jsonobj, QString unit) { + void PvlKeyword::addJsonValue(json jsonobj) { QString value; - if (jsonobj.is_array()) { + QString unit = ""; + json jvalue = jsonobj; + if (jsonobj.contains("unit")) { + unit = QString::fromStdString(jsonobj["unit"].get()); + jvalue = jsonobj["value"]; + } + + if (jvalue.is_array()) { QString msg = "Unable to convert " + name() + " with nested json array value into PvlKeyword"; throw IException(IException::Unknown, msg, _FILEINFO_); } - else if (jsonobj.is_number()) - { - value = QString::number(jsonobj.get(), 'g', 16); + else if (jvalue.is_number()) { + value = QString::number(jvalue.get(), 'g', 16); } - else if (jsonobj.is_boolean()) - { - value = QString(jsonobj.get() ? "true" : "false"); + else if (jvalue.is_boolean()) { + value = QString(jvalue.get() ? "true" : "false"); } - else if (jsonobj.is_null()) - { + else if (jvalue.is_null()) { value = QString("Null"); } - else - { - value = QString::fromStdString(jsonobj); + else { + value = QString::fromStdString(jvalue); } addValue(value, unit); } + /** + * Adds multiple items from a json array. + * + * If no current value exists, this method sets the given json value. + * Otherwise, it retains any current values and adds the json value + * given to the array of values for this PvlKeyword object using addValue. + * Defaults to unit = "" (empty QString). + * + * @param jsonobj New jsonobj to be parsed and assigned. + * @param unit Units of measurement corresponding to the value. + * + * @see setJsonValue() + * @see addValue() + * + * @throws Isis::iException::Unknown - jsonobj must be a json array + */ + void PvlKeyword::addJsonArrayValue(json jsonobj) { + if(!jsonobj.is_array()) { + QString msg = "Unable to convert to a json array:\n" + QString::fromStdString(jsonobj.dump()); + throw IException(IException::Unknown, msg, _FILEINFO_); + } + + for(auto ar = jsonobj.begin(); ar!=jsonobj.end(); ar++) { + try { + addJsonValue(*ar); + } + catch (IException &e) { + QString msg = "While attempting to parse " + name() + " the following occured"; + throw IException(e, IException::Unknown, msg, _FILEINFO_); + } + } + } + + + json PvlKeyword::toJson() { + if(size() == 1) { + if(unit() == "") { + return this->operator[](0).toStdString(); + } else { + return {{"unit", unit().toStdString()}, {"value", this->operator[](0).toStdString()}}; + } + } else { + // it's an array + json jsonarr = json::array(); + for(int i = 0; i < size(); i++) { + jsonarr += this->operator[](i).toStdString(); + } + return jsonarr; + } + } + + + /** + * sets multiple items from a json array. + * + * If no current value exists, this method sets the given json value. + * Otherwise, it retains any current values and adds the json value + * given to the array of values for this PvlKeyword object using addValue. + * Defaults to unit = "" (empty QString). + * + * @param jsonobj New jsonobj to be parsed and assigned. + * @param unit Units of measurement corresponding to the value. + * + * @see setJsonValue() + * @see addValue() + * + * @throws Isis::iException::Unknown - jsonobj must be a json array + */ + void PvlKeyword::setJsonArrayValue(json jsonobj) { + clear(); + addJsonArrayValue(jsonobj); + } + + /** * Adds a value. * diff --git a/isis/src/base/objs/PvlKeyword/PvlKeyword.h b/isis/src/base/objs/PvlKeyword/PvlKeyword.h index ba9800c6a1..fff827b199 100644 --- a/isis/src/base/objs/PvlKeyword/PvlKeyword.h +++ b/isis/src/base/objs/PvlKeyword/PvlKeyword.h @@ -117,7 +117,9 @@ namespace Isis { }; void setValue(QString value, QString unit = ""); - void setJsonValue(nlohmann::json jsonobj, QString unit = ""); + void setJsonValue(nlohmann::json jsonobj); + void setJsonArrayValue(nlohmann::json jsonobj); + void setUnits(QString units); void setUnits(QString value, QString units); @@ -125,7 +127,8 @@ namespace Isis { PvlKeyword &operator=(QString value); void addValue(QString value, QString unit = ""); - void addJsonValue(nlohmann::json jsonobj, QString unit = ""); + void addJsonValue(nlohmann::json jsonobj); + void addJsonArrayValue(nlohmann::json jsonobj); PvlKeyword &operator+=(QString value); @@ -155,6 +158,8 @@ namespace Isis { operator QString() const; + nlohmann::json toJson(); + const QString &operator[](int index) const; QString &operator[](int index); QString unit(const int index = 0) const; diff --git a/isis/src/base/objs/ShapeModelFactory/ShapeModelFactory.truth b/isis/src/base/objs/ShapeModelFactory/ShapeModelFactory.truth index 2750ff4e65..0ea1a8227a 100644 --- a/isis/src/base/objs/ShapeModelFactory/ShapeModelFactory.truth +++ b/isis/src/base/objs/ShapeModelFactory/ShapeModelFactory.truth @@ -39,6 +39,8 @@ Unit test for Isis::ShapeModel **PROGRAMMER ERROR** Unable to create a shape model from given target and pvl. **I/O ERROR** Invalid shape model file [NotAFile] in Kernels group. **ERROR** The given shape model file is not a valid ISIS DEM. Unable to open as an ISIS cube. +**I/O ERROR** Failed to open [NotAFile]. +**PROGRAMMER ERROR** Gdal failed to open [NotAFile]. **I/O ERROR** Unable to open [NotAFile]. **ERROR** The given shape model file is not a valid NAIF DSK file. Unable to construct a NAIF DSK shape model. **USER ERROR** NAIF DSK file [NotAFile] does not exist. diff --git a/isis/src/base/objs/SpecialPixel/SpecialPixel.h b/isis/src/base/objs/SpecialPixel/SpecialPixel.h index 2d1c56ed98..277c7e877b 100644 --- a/isis/src/base/objs/SpecialPixel/SpecialPixel.h +++ b/isis/src/base/objs/SpecialPixel/SpecialPixel.h @@ -150,10 +150,10 @@ namespace Isis { const int IVALID_MAX4 = (*((const int *) &VALID_MAX4)); // 2-byte signed special pixel values - const short VALID_MIN2 = ((short)(-32752)); - const short NULL2 = ((short)(-32768)); - const short LOW_REPR_SAT2 = ((short)(-32767)); - const short LOW_INSTR_SAT2 = ((short)(-32766)); + const short VALID_MIN2 = ((short)(-32752)); + const short NULL2 = ((short)(-32768)); + const short LOW_REPR_SAT2 = ((short)(-32767)); + const short LOW_INSTR_SAT2 = ((short)(-32766)); const short HIGH_INSTR_SAT2 = ((short)(-32765)); const short HIGH_REPR_SAT2 = ((short)(-32764)); const short VALID_MAX2 = ((short) 32767); @@ -167,6 +167,15 @@ namespace Isis { const unsigned short HIGH_REPR_SATU2 = ((unsigned short) 65535); const unsigned short VALID_MAXU2 = ((unsigned short) 65522); + // 4-byte signed special pixel values + const int VALID_MINI4 = ((int)(-2147483632)); + const int NULLI4 = ((int)(-2147483648)); + const int LOW_REPR_SATI4 = ((int)(-2147483647)); + const int LOW_INSTR_SATI4 = ((int)(-2147483646)); + const int HIGH_INSTR_SATI4 = ((int)(-2147483645)); + const int HIGH_REPR_SATI4 = ((int)(-2147483644)); + const int VALID_MAXI4 = ((int) 2147483647 ); + // 4-byte unsigned special pixel values const unsigned int VALID_MINUI4 = ((unsigned int) 3); const unsigned int NULLUI4 = ((unsigned int) 0); @@ -176,8 +185,16 @@ namespace Isis { const unsigned int HIGH_REPR_SATUI4 = ((unsigned int) 4294967295); const unsigned int VALID_MAXUI4 = ((unsigned int) 4294967282); + // 1-byte signed special pixel values + const char VALID_MINS1 = ((char) -126); + const char NULLS1 = ((char) -128); + const char LOW_REPR_SATS1 = ((char) -128); + const char LOW_INSTR_SATS1 = ((char) -128); + const char HIGH_INSTR_SATS1 = ((char) -127); + const char HIGH_REPR_SATS1 = ((char) -127); + const char VALID_MAXS1 = ((char) 127); - // 1-byte special pixel values + // 1-byte unsigned special pixel values const unsigned char VALID_MIN1 = ((unsigned char) 1); const unsigned char NULL1 = ((unsigned char) 0); const unsigned char LOW_REPR_SAT1 = ((unsigned char) 0); diff --git a/isis/src/base/objs/SpectralDefinitionFactory/SpectralDefinitionFactory.truth b/isis/src/base/objs/SpectralDefinitionFactory/SpectralDefinitionFactory.truth index 4c5ff858d3..de5d45ce46 100644 --- a/isis/src/base/objs/SpectralDefinitionFactory/SpectralDefinitionFactory.truth +++ b/isis/src/base/objs/SpectralDefinitionFactory/SpectralDefinitionFactory.truth @@ -1,5 +1,9 @@ ----- Testing SpectralDefinitionFactory ----- **ERROR** Unable to open input file [assets/test.csv]. Is it a valid CSV?. **USER ERROR** Unable to open file [assets/test.csv]. +**I/O ERROR** Failed to open [assets/test.txt]. +**PROGRAMMER ERROR** Gdal failed to open [assets/test.txt]. **I/O ERROR** Unable to open [assets/test.txt]. +**I/O ERROR** Failed to open [assets/cube.cub]. +**PROGRAMMER ERROR** Gdal failed to open [assets/cube.cub]. **I/O ERROR** Unable to open [assets/cube.cub]. diff --git a/isis/src/base/objs/Spice/Spice.cpp b/isis/src/base/objs/Spice/Spice.cpp index 1e357c77d7..b940718b80 100644 --- a/isis/src/base/objs/Spice/Spice.cpp +++ b/isis/src/base/objs/Spice/Spice.cpp @@ -68,9 +68,37 @@ namespace Isis { } else { PvlGroup kernels = lab.findGroup("Kernels", Pvl::Traverse); - bool hasTables = (kernels["TargetPosition"][0] == "Table"); + // BONUS TODO: update to pull out separate init methods - init(lab, !hasTables); + // try using ALE + bool hasTables = (kernels["TargetPosition"][0] == "Table"); + m_usingNaif = !lab.hasObject("NaifKeywords") || !hasTables; + m_usingAle = false; + + if (m_usingNaif) { + try { + std::ostringstream kernel_pvl; + kernel_pvl << kernels; + + json props; + if (kernels["InstrumentPointing"][0].toUpper() == "NADIR") { + props["nadir"] = true; + } + + props["kernels"] = kernel_pvl.str(); + + json isd = ale::load(lab.fileName().toStdString(), props.dump(), "ale", false, false, true); + m_usingAle = true; + + isdInit(lab, isd); + } + catch(...) { + init(cube, lab, !hasTables); + } + } + else { + init(cube, lab, !hasTables); + } } } @@ -82,7 +110,209 @@ namespace Isis { * @param isd ALE Json ISD */ Spice::Spice(Pvl &lab, json isd) { - init(lab, true, isd); + // Set the expected member states for other parts of the Spice + // class + m_usingNaif = true; + m_usingAle = true; + + isdInit(lab, isd); + } + + void Spice::isdInit(Pvl &lab, json isd) { + NaifStatus::CheckErrors(); + // Initialize members + defaultInit(); + + m_spkCode = new SpiceInt; + m_ckCode = new SpiceInt; + m_ikCode = new SpiceInt; + m_sclkCode = new SpiceInt; + m_spkBodyCode = new SpiceInt; + m_bodyFrameCode = new SpiceInt; + + m_naifKeywords = new PvlObject("NaifKeywords"); + + // Get the kernel group and load main kernels + PvlGroup kernels = lab.findGroup("Kernels", Pvl::Traverse); + + // Get the time padding first + if (kernels.hasKeyword("StartPadding")) { + *m_startTimePadding = toDouble(kernels["StartPadding"][0]); + } + else { + *m_startTimePadding = 0.0; + } + + if (kernels.hasKeyword("EndPadding")) { + *m_endTimePadding = toDouble(kernels["EndPadding"][0]); + } + else { + *m_endTimePadding = 0.0; + } + + try { + json aleNaifKeywords = isd["naif_keywords"]; + m_naifKeywords = new PvlObject("NaifKeywords", aleNaifKeywords); + + // Still need to load clock kernels for now + load(kernels["LeapSecond"], true); + if ( kernels.hasKeyword("SpacecraftClock")) { + load(kernels["SpacecraftClock"], true); + } + } + catch(IException &e) { + QString msg = "Failed to read naif_keywords from ISD"; + throw IException(e, IException::Programmer, msg, _FILEINFO_); + } + + // Moved the construction of the Target after the NAIF kenels have been loaded or the + // NAIF keywords have been pulled from the cube labels, so we can find target body codes + // that are defined in kernels and not just body codes build into spicelib + // TODO: Move this below the else once the rings code below has been refactored + m_target = new Target(this, lab); + + // This should not be here. Consider having spiceinit add the necessary rings kernels to the + // Extra parameter if the user has set the shape model to RingPlane. + // If Target is Saturn and ShapeModel is RingPlane, load the extra rings pck file + // which changes the prime meridian values to report longitudes with respect to + // the ascending node of the ringplane. + if (m_target->name().toUpper() == "SATURN" && m_target->shape()->name().toUpper() == "PLANE") { + PvlKeyword ringPck = PvlKeyword("RingPCK","$cassini/kernels/pck/saturnRings_v001.tpc"); + load(ringPck, true); + } + + // Get NAIF ik, spk, sclk, and ck codes + // + // Use ikcode to get parameters from instrument kernel such as focal + // length, distortions, focal plane maps, etc + // + // Use spkcode to get spacecraft position from spk file + // + // Use sclkcode to transform times from et to tics + // + // Use ckcode to transform between frames + // + // Use bodycode to obtain radii and attitude (pole position/omega0) + // + // Use spkbodycode to read body position from spk + + QString trykey = "NaifIkCode"; + if (kernels.hasKeyword("NaifFrameCode")) trykey = "NaifFrameCode"; + *m_ikCode = toInt(kernels[trykey][0]); + + *m_spkCode = *m_ikCode / 1000; + *m_sclkCode = *m_spkCode; + *m_ckCode = *m_ikCode; + + if (!m_target->isSky()) { + // Get target body code and radii and store them in the Naif group + // DAC modified to look for and store body code so that the radii keyword name + // will be able to be constructed even for new bodies not in the standard PCK yet. + QString radiiKey = "BODY" + Isis::toString(m_target->naifBodyCode()) + "_RADII"; + QVariant result = m_target->naifBodyCode(); + storeValue("BODY_CODE", 0, SpiceIntType, result); + std::vector radii(3,Distance()); + radii[0] = Distance(getDouble(radiiKey, 0), Distance::Kilometers); + radii[1] = Distance(getDouble(radiiKey, 1), Distance::Kilometers); + radii[2] = Distance(getDouble(radiiKey, 2), Distance::Kilometers); + // m_target doesn't have the getDouble method so Spice gets the radii for it + m_target->setRadii(radii); + } + + *m_spkBodyCode = m_target->naifBodyCode(); + + // Override them if they exist in the labels + if (kernels.hasKeyword("NaifSpkCode")) { + *m_spkCode = (int) kernels["NaifSpkCode"]; + } + + if (kernels.hasKeyword("NaifCkCode")) { + *m_ckCode = (int) kernels["NaifCkCode"]; + } + + if (kernels.hasKeyword("NaifSclkCode")) { + *m_sclkCode = (int) kernels["NaifSclkCode"]; + } + + if (!m_target->isSky()) { + if (kernels.hasKeyword("NaifSpkBodyCode")) { + *m_spkBodyCode = (int) kernels["NaifSpkBodyCode"]; + } + } + + if (m_target->isSky()) { + // Create the identity rotation for sky targets + // Everything in bodyfixed will really be J2000 + m_bodyRotation = new SpiceRotation(1); + } + else { + SpiceInt frameCode; + try { + frameCode = getInteger("BODY_FRAME_CODE", 0); + } + catch(IException &e) { + QString msg = "Unable to read BODY_FRAME_CODE from naifkeywords group"; + throw IException(IException::Io, msg, _FILEINFO_); + } + + m_bodyRotation = new SpiceRotation(frameCode); + *m_bodyFrameCode = frameCode; + } + + m_instrumentRotation = new SpiceRotation(*m_ckCode); + + // Set up for observer/target and light time correction to between s/c + // and target body. + LightTimeCorrectionState ltState(*m_ikCode, this); + ltState.checkSpkKernelsForAberrationCorrection(); + + vector radius = m_target->radii(); + Distance targetRadius((radius[0] + radius[2])/2.0); + m_instrumentPosition = new SpacecraftPosition(*m_spkCode, *m_spkBodyCode, + ltState, targetRadius); + + m_sunPosition = new SpicePosition(10, m_target->naifBodyCode()); + + + // Check to see if we have nadir pointing that needs to be computed & + // See if we have table blobs to load + m_sunPosition->LoadCache(isd["sun_position"]); + if (m_sunPosition->cacheSize() > 3) { + m_sunPosition->Memcache2HermiteCache(0.01); + } + m_bodyRotation->LoadCache(isd["body_rotation"]); + m_bodyRotation->MinimizeCache(SpiceRotation::DownsizeStatus::Yes); + if (m_bodyRotation->cacheSize() > 5) { + m_bodyRotation->LoadTimeCache(); + } + solarLongitude(); + + // We can't assume InstrumentPointing & InstrumentPosition exist, old + // files may be around with the old keywords, SpacecraftPointing & + // SpacecraftPosition. The old keywords were in existance before the + // Table option, so we don't need to check for Table under the old + // keywords. + if (kernels["InstrumentPointing"].size() == 0) { + throw IException(IException::Unknown, + "No camera pointing available", + _FILEINFO_); + } + + m_instrumentRotation->LoadCache(isd["instrument_pointing"]); + m_instrumentRotation->MinimizeCache(SpiceRotation::DownsizeStatus::Yes); + if (m_instrumentRotation->cacheSize() > 5) { + m_instrumentRotation->LoadTimeCache(); + } + + + if (kernels["InstrumentPosition"].size() == 0) { + throw IException(IException::Unknown, + "No instrument position available", + _FILEINFO_); + } + + m_instrumentPosition->LoadCache(isd["instrument_position"]); + NaifStatus::CheckErrors(); } @@ -151,7 +381,7 @@ namespace Isis { * @internal * @history 2011-02-08 Jeannie Walldren - Initialize pointers to null. */ - void Spice::init(Pvl &lab, bool noTables, json isd) { + void Spice::init(Cube &cube, Pvl &lab, bool noTables) { NaifStatus::CheckErrors(); // Initialize members defaultInit(); @@ -184,73 +414,40 @@ namespace Isis { *m_endTimePadding = 0.0; } - // We should remove this completely in the near future - m_usingNaif = !lab.hasObject("NaifKeywords") || noTables; - m_usingAle = false; - - // Modified to load planetary ephemeris SPKs before s/c SPKs since some // missions (e.g., MESSENGER) may augment the s/c SPK with new planet // ephemerides. (2008-02-27 (KJB)) if (m_usingNaif) { - try { - if (isd == NULL) { - // try using ALE - std::ostringstream kernel_pvl; - kernel_pvl << kernels; - - json props; - if (kernels["InstrumentPointing"][0].toUpper() == "NADIR") { - props["nadir"] = true; - } - - props["kernels"] = kernel_pvl.str(); - - isd = ale::load(lab.fileName().toStdString(), props.dump(), "ale", false, false, true); - } - - json aleNaifKeywords = isd["naif_keywords"]; - m_naifKeywords = new PvlObject("NaifKeywords", aleNaifKeywords); - - // Still need to load clock kernels for now - load(kernels["LeapSecond"], noTables); - if ( kernels.hasKeyword("SpacecraftClock")) { - load(kernels["SpacecraftClock"], noTables); - } - m_usingAle = true; + // Backup to standard ISIS implementation + if (noTables) { + load(kernels["TargetPosition"], noTables); + load(kernels["InstrumentPosition"], noTables); + load(kernels["InstrumentPointing"], noTables); } - catch(...) { - // Backup to standard ISIS implementation - if (noTables) { - load(kernels["TargetPosition"], noTables); - load(kernels["InstrumentPosition"], noTables); - load(kernels["InstrumentPointing"], noTables); - } - if (kernels.hasKeyword("Frame")) { - load(kernels["Frame"], noTables); - } + if (kernels.hasKeyword("Frame")) { + load(kernels["Frame"], noTables); + } - load(kernels["TargetAttitudeShape"], noTables); - if (kernels.hasKeyword("Instrument")) { - load(kernels["Instrument"], noTables); - } - // Always load after instrument - if (kernels.hasKeyword("InstrumentAddendum")) { - load(kernels["InstrumentAddendum"], noTables); - } + load(kernels["TargetAttitudeShape"], noTables); + if (kernels.hasKeyword("Instrument")) { + load(kernels["Instrument"], noTables); + } + // Always load after instrument + if (kernels.hasKeyword("InstrumentAddendum")) { + load(kernels["InstrumentAddendum"], noTables); + } - // Still need to load clock kernels for now - load(kernels["LeapSecond"], noTables); - if ( kernels.hasKeyword("SpacecraftClock")) { - load(kernels["SpacecraftClock"], noTables); - } + // Still need to load clock kernels for now + load(kernels["LeapSecond"], noTables); + if ( kernels.hasKeyword("SpacecraftClock")) { + load(kernels["SpacecraftClock"], noTables); + } - // Modified to load extra kernels last to allow overriding default values - // (2010-04-07) (DAC) - if (kernels.hasKeyword("Extra")) { - load(kernels["Extra"], noTables); - } + // Modified to load extra kernels last to allow overriding default values + // (2010-04-07) (DAC) + if (kernels.hasKeyword("Extra")) { + load(kernels["Extra"], noTables); } // Moved the construction of the Target after the NAIF kenels have been loaded or the @@ -347,7 +544,7 @@ namespace Isis { else { // JAA - Modified to store and look for the frame body code in the cube labels SpiceInt frameCode; - if (((m_usingNaif) || (!m_naifKeywords->hasKeyword("BODY_FRAME_CODE"))) && !isUsingAle()) { + if ((m_usingNaif) || (!m_naifKeywords->hasKeyword("BODY_FRAME_CODE"))) { char frameName[32]; SpiceBoolean found; cidfrm_c(*m_spkBodyCode, sizeof(frameName), &frameCode, frameName, &found); @@ -396,23 +593,11 @@ namespace Isis { // Check to see if we have nadir pointing that needs to be computed & // See if we have table blobs to load - if (m_usingAle) { - m_sunPosition->LoadCache(isd["sun_position"]); - if (m_sunPosition->cacheSize() > 3) { - m_sunPosition->Memcache2HermiteCache(0.01); - } - m_bodyRotation->LoadCache(isd["body_rotation"]); - m_bodyRotation->MinimizeCache(SpiceRotation::DownsizeStatus::Yes); - if (m_bodyRotation->cacheSize() > 5) { - m_bodyRotation->LoadTimeCache(); - } - solarLongitude(); - } - else if (kernels["TargetPosition"][0].toUpper() == "TABLE") { - Table t("SunPosition", lab.fileName(), lab); + if (kernels["TargetPosition"][0].toUpper() == "TABLE") { + Table t = cube.readTable("SunPosition"); m_sunPosition->LoadCache(t); - Table t2("BodyRotation", lab.fileName(), lab); + Table t2 = cube.readTable("BodyRotation"); m_bodyRotation->LoadCache(t2); if (t2.Label().hasKeyword("SolarLongitude")) { *m_solarLongitude = Longitude(t2.Label()["SolarLongitude"], @@ -437,7 +622,7 @@ namespace Isis { // 2009-03-18 Tracie Sucharski - Removed test for old keywords, any files // with the old keywords should be re-run through spiceinit. - if (kernels["InstrumentPointing"][0].toUpper() == "NADIR" && !isUsingAle()) { + if (kernels["InstrumentPointing"][0].toUpper() == "NADIR") { if (m_instrumentRotation) { delete m_instrumentRotation; m_instrumentRotation = NULL; @@ -445,15 +630,8 @@ namespace Isis { m_instrumentRotation = new SpiceRotation(*m_ikCode, *m_spkBodyCode); } - else if (m_usingAle) { - m_instrumentRotation->LoadCache(isd["instrument_pointing"]); - m_instrumentRotation->MinimizeCache(SpiceRotation::DownsizeStatus::Yes); - if (m_instrumentRotation->cacheSize() > 5) { - m_instrumentRotation->LoadTimeCache(); - } - } else if (kernels["InstrumentPointing"][0].toUpper() == "TABLE") { - Table t("InstrumentPointing", lab.fileName(), lab); + Table t = cube.readTable("InstrumentPointing"); m_instrumentRotation->LoadCache(t); } @@ -464,13 +642,11 @@ namespace Isis { _FILEINFO_); } - if (m_usingAle) { - m_instrumentPosition->LoadCache(isd["instrument_position"]); - } - else if (kernels["InstrumentPosition"][0].toUpper() == "TABLE") { - Table t("InstrumentPosition", lab.fileName(), lab); + if (kernels["InstrumentPosition"][0].toUpper() == "TABLE") { + Table t = cube.readTable("InstrumentPosition"); m_instrumentPosition->LoadCache(t); } + NaifStatus::CheckErrors(); } diff --git a/isis/src/base/objs/Spice/Spice.h b/isis/src/base/objs/Spice/Spice.h index f95b73cec2..4ffa4097cb 100644 --- a/isis/src/base/objs/Spice/Spice.h +++ b/isis/src/base/objs/Spice/Spice.h @@ -284,7 +284,7 @@ namespace Isis { public: // constructors Spice(Cube &cube); - Spice(Pvl &lab, nlohmann::json); + Spice(Pvl &lab, nlohmann::json isd); // destructor virtual ~Spice(); @@ -387,7 +387,8 @@ namespace Isis { Spice(const Spice &other); Spice &operator=(const Spice &other); - void init(Pvl &pvl, bool noTables, nlohmann::json isd = NULL); + void init(Cube &cube, Pvl &pvl, bool noTables); + void isdInit(Pvl &pvl, nlohmann::json isd); void csmInit(Cube &cube, Pvl label); void defaultInit(); diff --git a/isis/src/lro/apps/lronac2isis/lronac2isis.cpp b/isis/src/lro/apps/lronac2isis/lronac2isis.cpp index d24d3ba0a8..addabeb0e5 100644 --- a/isis/src/lro/apps/lronac2isis/lronac2isis.cpp +++ b/isis/src/lro/apps/lronac2isis/lronac2isis.cpp @@ -107,7 +107,7 @@ namespace Isis { g_ocube->setByteOrder(outAtt.byteOrder()); g_ocube->setFormat(outAtt.fileFormat()); g_ocube->setMinMax((double) VALID_MIN2, (double) VALID_MAX2); - g_ocube->setLabelsAttached(outAtt.labelAttachment() == AttachedLabel); + g_ocube->setLabelsAttached(outAtt.labelAttachment()); g_ocube->setDimensions(p.Samples(), p.Lines(), p.Bands()); g_ocube->setPixelType(Isis::Real); g_ocube->create(ui.GetCubeName("TO")); diff --git a/isis/src/mex/apps/hrsc2isis/hrsc2isis.cpp b/isis/src/mex/apps/hrsc2isis/hrsc2isis.cpp index 7f0a2b9b16..24272da356 100644 --- a/isis/src/mex/apps/hrsc2isis/hrsc2isis.cpp +++ b/isis/src/mex/apps/hrsc2isis/hrsc2isis.cpp @@ -149,7 +149,7 @@ namespace Isis{ outCube->setByteOrder(outAtt.byteOrder()); outCube->setFormat(outAtt.fileFormat()); - outCube->setLabelsAttached(outAtt.labelAttachment() == AttachedLabel); + outCube->setLabelsAttached(outAtt.labelAttachment()); TableField ephTimeField("EphemerisTime", TableField::Double); TableField expTimeField("ExposureTime", TableField::Double); diff --git a/isis/src/mro/apps/ctxcal/ctxcal.cpp b/isis/src/mro/apps/ctxcal/ctxcal.cpp index 6969f691cc..04d9289a72 100644 --- a/isis/src/mro/apps/ctxcal/ctxcal.cpp +++ b/isis/src/mro/apps/ctxcal/ctxcal.cpp @@ -45,7 +45,7 @@ namespace Isis { // We will be processing by line ProcessByLine p; - Isis::Pvl lab(icube->fileName()); + Isis::Pvl lab = *(icube->label()); Isis::PvlGroup &inst = lab.findGroup("Instrument", Pvl::Traverse); diff --git a/isis/src/mro/apps/marcical/marcical.cpp b/isis/src/mro/apps/marcical/marcical.cpp index f5592320a2..f2b203f262 100644 --- a/isis/src/mro/apps/marcical/marcical.cpp +++ b/isis/src/mro/apps/marcical/marcical.cpp @@ -297,7 +297,7 @@ namespace Isis { ocube.setDimensions(icube.sampleCount(), icube.lineCount(), icube.bandCount()); ocube.setByteOrder(outAtt.byteOrder()); ocube.setFormat(outAtt.fileFormat()); - ocube.setLabelsAttached(outAtt.labelAttachment() == AttachedLabel); + ocube.setLabelsAttached(outAtt.labelAttachment()); ocube.setPixelType(outAtt.pixelType()); ocube.create(FileName(ui.GetCubeName("TO")).expanded()); diff --git a/isis/src/odyssey/apps/thmvisflat/main.cpp b/isis/src/odyssey/apps/thmvisflat/main.cpp index 64982a03bc..afaf6bedc8 100644 --- a/isis/src/odyssey/apps/thmvisflat/main.cpp +++ b/isis/src/odyssey/apps/thmvisflat/main.cpp @@ -68,7 +68,7 @@ void IsisMain() { ocube.setDimensions(icube.sampleCount(), icube.lineCount(), icube.bandCount()); ocube.setByteOrder(outAtt.byteOrder()); ocube.setFormat(outAtt.fileFormat()); - ocube.setLabelsAttached(outAtt.labelAttachment() == AttachedLabel); + ocube.setLabelsAttached(outAtt.labelAttachment()); ocube.setPixelType(outAtt.pixelType()); ocube.create(FileName(ui.GetCubeName("TO")).expanded()); diff --git a/isis/src/qisis/apps/qview/main.cpp b/isis/src/qisis/apps/qview/main.cpp index 69f06fe013..eb8a1198e2 100644 --- a/isis/src/qisis/apps/qview/main.cpp +++ b/isis/src/qisis/apps/qview/main.cpp @@ -104,7 +104,7 @@ int main(int argc, char *argv[]) { */ QChar escape(27); for (int i = 1; i < argc; i++) { - temp += QFileInfo(FileName(argv[i]).expanded()).absoluteFilePath(); + temp += FileName(argv[i]).expanded(); temp += QString(escape); } temp += "raise"; diff --git a/isis/src/qisis/objs/CubeViewport/ViewportBuffer.cpp b/isis/src/qisis/objs/CubeViewport/ViewportBuffer.cpp index 6dbecb5d68..4d2579a8de 100644 --- a/isis/src/qisis/objs/CubeViewport/ViewportBuffer.cpp +++ b/isis/src/qisis/objs/CubeViewport/ViewportBuffer.cpp @@ -61,8 +61,8 @@ namespace Isis { p_requestedFillArea = 0.0; p_bricksOrdered = true; - connect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)), - p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *))); + connect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *, double)), + p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *, double))); connect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)), this, SLOT(DataReady(void *, int, const Isis::Brick *))); @@ -76,8 +76,8 @@ namespace Isis { * */ ViewportBuffer::~ViewportBuffer() { - disconnect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)), - p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *))); + disconnect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *, double)), + p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *, double))); disconnect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)), this, SLOT(DataReady(void *, int, const Isis::Brick *))); @@ -238,24 +238,22 @@ namespace Isis { p_bricksOrdered = true; } - double samp; - // Loop through x values of rect on screen that we want to fill + int brickIndex = 0; for(int x = rect->left(); x <= rect->right(); x++) { // Index into internal buffer is minus leftmost/topmost pixel int xIndex = x - fill->getLeftmostPixelPosition(); int yIndex = y - fill->getTopmostPixelPosition(); - samp = fill->viewportToSample(x); - - // Index into buffer is current sample - start sample - // *Brick indices are in units of cube pixels, not screen pixels - int brickIndex = (int)(samp + 0.5) - brick->Sample(); + int scaledBufferIndex = brickIndex; + if (fill->scale() > 1.0) { + scaledBufferIndex /= fill->scale(); + } - if(brickIndex < 0) { + if(scaledBufferIndex < 0) { p_buffer.at(yIndex).at(xIndex) = brick->at(0); } - else if(brickIndex >= brick->size()) { + else if(scaledBufferIndex >= brick->size()) { p_buffer.at(yIndex).at(xIndex) = brick->at(brick->size() - 1); } else { @@ -277,7 +275,8 @@ namespace Isis { throw IException(IException::Programmer, msg, _FILEINFO_); } else { - p_buffer.at(yIndex).at(xIndex) = brick->at(brickIndex); + p_buffer.at(yIndex).at(xIndex) = brick->at(scaledBufferIndex); + brickIndex++; } } } @@ -547,17 +546,18 @@ namespace Isis { double esamp = fill->viewportToSample(rect.right()); - int brickWidth = (int)(ceil(esamp) - floor(ssamp)) + 1; + int brickWidth = (int)(ceil(esamp) - floor(ssamp)); if(brickWidth <= 0) return; double line = fill->viewportToLine(fill->getRequestPosition()); + line -= (1 / fill->scale()) / 2.0; int roundedSamp = (int)(ssamp + 0.5); int roundedLine = (int)(line + 0.5); - emit ReadCube(p_cubeId, roundedSamp, roundedLine, roundedSamp + brickWidth, - roundedLine, p_band, this); + emit ReadCube(p_cubeId, roundedSamp, roundedLine, roundedSamp + brickWidth - 1, + roundedLine, p_band, this, fill->scale()); fill->incRequestPosition(); } diff --git a/isis/src/qisis/objs/CubeViewport/ViewportBuffer.h b/isis/src/qisis/objs/CubeViewport/ViewportBuffer.h index 747dbbe15b..bf9b0b00c1 100644 --- a/isis/src/qisis/objs/CubeViewport/ViewportBuffer.h +++ b/isis/src/qisis/objs/CubeViewport/ViewportBuffer.h @@ -125,7 +125,7 @@ namespace Isis { * @param caller */ void ReadCube(int cubeId, int startSample, int startLine, - int endSample, int endLine, int band, void *caller); + int endSample, int endLine, int band, void *caller, double scale); //! Tell cube data thread we're done with a brick void DoneWithData(int, const Isis::Brick *); diff --git a/isis/src/qisis/objs/CubeViewport/ViewportBufferFill.h b/isis/src/qisis/objs/CubeViewport/ViewportBufferFill.h index 55428fc197..71224c09e1 100644 --- a/isis/src/qisis/objs/CubeViewport/ViewportBufferFill.h +++ b/isis/src/qisis/objs/CubeViewport/ViewportBufferFill.h @@ -111,6 +111,9 @@ namespace Isis { void stop(); + double scale() { + return p_xScale; + } private: //! Position of the cube reads diff --git a/isis/src/qisis/objs/FileDialog/FileDialog.cpp b/isis/src/qisis/objs/FileDialog/FileDialog.cpp index 0f4b95ff59..30446bfaf3 100644 --- a/isis/src/qisis/objs/FileDialog/FileDialog.cpp +++ b/isis/src/qisis/objs/FileDialog/FileDialog.cpp @@ -20,7 +20,7 @@ namespace Isis { this->setOptions(QFileDialog::DontUseNativeDialog); this->setWindowTitle(title); - this->setFileMode(QFileDialog::ExistingFiles); + this->setFileMode(QFileDialog::AnyFile); if(parent != 0) { parent->installEventFilter(this); p_appName = parent->windowTitle(); diff --git a/isis/src/qisis/objs/FileTool/FileTool.cpp b/isis/src/qisis/objs/FileTool/FileTool.cpp index 214013ed26..d56fb76284 100644 --- a/isis/src/qisis/objs/FileTool/FileTool.cpp +++ b/isis/src/qisis/objs/FileTool/FileTool.cpp @@ -559,7 +559,7 @@ namespace Isis { ocube->setDimensions(piNumSamples, piNumLines, piNumBands); ocube->setByteOrder(outAtt.byteOrder()); ocube->setFormat(outAtt.fileFormat()); - ocube->setLabelsAttached(outAtt.labelAttachment() == AttachedLabel); + ocube->setLabelsAttached(outAtt.labelAttachment()); if (outAtt.propagatePixelType()) { ocube->setPixelType(icube->pixelType()); diff --git a/isis/src/qisis/objs/Image/Image.cpp b/isis/src/qisis/objs/Image/Image.cpp index 576f8fc9a7..9a19d368ac 100644 --- a/isis/src/qisis/objs/Image/Image.cpp +++ b/isis/src/qisis/objs/Image/Image.cpp @@ -525,7 +525,7 @@ namespace Isis { // If this is an ecub (it should be) and is pointing to a relative file name, // then we want to copy the DN cube also. - if (!origImage.storesDnData() ) { + if ( origImage.labelsAttached() == Cube::ExternalLabel) { if (origImage.externalCubeFileName().path() == ".") { Cube dnFile( FileName(m_fileName).path() + "/" + origImage.externalCubeFileName().name()); diff --git a/isis/src/qisis/objs/Shape/Shape.cpp b/isis/src/qisis/objs/Shape/Shape.cpp index 0061a412fe..405fabdb8f 100644 --- a/isis/src/qisis/objs/Shape/Shape.cpp +++ b/isis/src/qisis/objs/Shape/Shape.cpp @@ -548,7 +548,7 @@ namespace Isis { // If this is an ecub (it should be) and is pointing to a relative file name, // then we want to copy the DN cube also. - if (!origShape.storesDnData()) { + if ( origShape.labelsAttached() == Cube::ExternalLabel ) { if (origShape.externalCubeFileName().path() == ".") { Cube dnFile( FileName(m_fileName).path() + "/" + origShape.externalCubeFileName().name()); diff --git a/isis/src/system/apps/blobdump/main.cpp b/isis/src/system/apps/blobdump/main.cpp index 22c57e817c..55c1d5056a 100644 --- a/isis/src/system/apps/blobdump/main.cpp +++ b/isis/src/system/apps/blobdump/main.cpp @@ -37,7 +37,14 @@ void IsisMain() { FileName file = ui.GetCubeName("FROM"); QString blobname = ui.GetString("NAME"); QString blobtype = ui.GetString("TYPE"); - Blob blob(blobname, blobtype, file.expanded()); + Blob blob(blobname, blobtype); + try { + blob.Read(file.expanded()); + } + catch (...) { + Cube cube(file); + cube.read(blob); + } FileName outfname = ui.GetFileName("TO"); blob.Write(outfname.expanded()); } @@ -49,7 +56,14 @@ void helperButtonGetBlobList() { UserInterface &ui = Application::GetUserInterface(); QString currentFile = ui.GetCubeName("FROM"); - const Pvl label(FileName(currentFile).expanded()); + Pvl label; + try { + label = Pvl(FileName(currentFile).expanded()); + } + catch (...) { + Cube cube(FileName(currentFile).expanded()); + label = *cube.label(); + } // Check to see if the "FILE" parameter has changed since last press if(currentFile != previousFile) { diff --git a/isis/tests/BlobTests.cpp b/isis/tests/BlobTests.cpp new file mode 100644 index 0000000000..9f9afeb88a --- /dev/null +++ b/isis/tests/BlobTests.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "Blob.h" + +#include "TempFixtures.h" +#include "TiffFixtures.h" +#include "TestUtilities.h" + +#include "gmock/gmock.h" + +using namespace Isis; + +class DefaultBlob : public TempTestingFiles { + + protected: + Blob *testBlob; + QString testBlobPath = tempDir.path() + "/junk_blob.pvl"; + + void SetUp() override { + TempTestingFiles::SetUp(); + + testBlob = new Blob("UnitTest", "Blob"); + char buf[] = {"ABCD"}; + testBlob->setData(buf, 4); + testBlob->Write(testBlobPath); + } + + void TearDown() override { + delete testBlob; + } + +}; + +TEST_F(TempTestingFiles, TestBlobDefault) { + Blob b("UnitTest", "Blob"); + char buf[] = {"ABCD"}; + b.setData(buf, 4); + b.Write("junk"); + + EXPECT_EQ("UnitTest", b.Name()); + EXPECT_EQ(4, b.Size()); + EXPECT_EQ("Blob", b.Type()); +} + +TEST_F(DefaultBlob, TestBlobRead) { + Blob b("UnitTest", "Blob", testBlobPath); + + EXPECT_EQ("UnitTest", b.Name()); + EXPECT_EQ(4, b.Size()); + EXPECT_EQ("Blob", b.Type()); + + std::string buffStr(b.getBuffer()); + EXPECT_EQ("ABCD", buffStr); +} + +TEST_F(DefaultBlob, TestBlobWriteExisting) { + Isis::Pvl pvl(testBlobPath); + std::fstream strm; + strm.open(testBlobPath.toStdString(), std::ios::binary | std::ios::out); + char buf[] = {"ABCD"}; + testBlob->setData(buf, 3); + testBlob->Write(pvl, strm); + strm.seekp(0, std::ios::beg); + strm << pvl; + EXPECT_EQ("UnitTest", testBlob->Name()); + EXPECT_EQ(3, testBlob->Size()); + EXPECT_EQ("Blob", testBlob->Type()); + strm.close(); + + Blob b("UnitTest", "Blob", testBlobPath); + + EXPECT_EQ("UnitTest", b.Name()); + EXPECT_EQ(3, b.Size()); + EXPECT_EQ("Blob", b.Type()); +} + +TEST_F(DefaultBlob, TestBlobWriteExistingEOF) { + Isis::Pvl pvl("junk"); + std::fstream strm; + strm.open(testBlobPath.toStdString(), std::ios::binary | std::ios::out); + char buf[] = {"ABCD"}; + testBlob->setData(buf, 4); + testBlob->Write(pvl, strm); + strm.seekp(0, std::ios::beg); + strm << pvl; + EXPECT_EQ("UnitTest", testBlob->Name()); + EXPECT_EQ(4, testBlob->Size()); + EXPECT_EQ("Blob", testBlob->Type()); + strm.close(); + + Blob b("UnitTest", "Blob", testBlobPath); + + EXPECT_EQ("UnitTest", b.Name()); + EXPECT_EQ(4, b.Size()); + EXPECT_EQ("Blob", b.Type()); +} + +TEST_F(ReadWriteTiff, TestBlobWriteReadGdal) { + createTiff(UnsignedByte, false); + GDALDataset *dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_Update)); + + Blob writeBlob("UnitTest", "Blob"); + char buf[] = {"ABCD"}; + writeBlob.setData(buf, 4); + writeBlob.WriteGdal(dataset); + + GDALClose(dataset); + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_Update)); + + Blob readBlob("UnitTest", "Blob"); + readBlob.ReadGdal(dataset); + + EXPECT_EQ(writeBlob.Name(), readBlob.Name()); + EXPECT_EQ(writeBlob.Size(), readBlob.Size()); + EXPECT_EQ(writeBlob.Type(), readBlob.Type()); + + std::string writeBuff(writeBlob.getBuffer()); + std::string readBuff(readBlob.getBuffer()); + + EXPECT_EQ(writeBuff, readBuff); + + GDALClose(dataset); +} \ No newline at end of file diff --git a/isis/tests/BufferTests.cpp b/isis/tests/BufferTests.cpp new file mode 100644 index 0000000000..49126fc251 --- /dev/null +++ b/isis/tests/BufferTests.cpp @@ -0,0 +1,157 @@ +#include +#include +#include + +#include "Buffer.h" + +#include "TempFixtures.h" +#include "TestUtilities.h" + +#include "gmock/gmock.h" + +using namespace Isis; + +TEST(BufferTest, TestBufferDefault) { + Buffer b(4, 3, 2, Isis::SignedInteger); + + for(int i = 0; i < b.size(); i++) { + b[i] = i; + } + + EXPECT_EQ(b.SampleDimension(), 4); + EXPECT_EQ(b.LineDimension(), 3); + EXPECT_EQ(b.BandDimension(), 2); + EXPECT_EQ(b.size(), 24); + + EXPECT_EQ(b.Sample(), 0); + EXPECT_EQ(b.Line(), 0); + EXPECT_EQ(b.Band(), 0); + + int samp, line, band; + b.Position(0, samp, line, band); + EXPECT_EQ(samp, 0); + EXPECT_EQ(line, 0); + EXPECT_EQ(band, 0); + + EXPECT_EQ(b.Index(samp, line, band), 0); + + EXPECT_EQ(b.at(0), 0); + EXPECT_EQ(b.at(10), 10); + EXPECT_EQ(b.at(23), 23); + EXPECT_EQ(b[0], 0); + EXPECT_EQ(b[10], 10); + EXPECT_EQ(b[23], 23); + + // b.SetBasePosition(3, 2, 1); + // EXPECT_EQ(b.Sample(), 3); + // EXPECT_EQ(b.Line(), 2); + // EXPECT_EQ(b.Band(), 1); + + // b.Position(0, samp, line, band); + // EXPECT_EQ(samp, 3); + // EXPECT_EQ(line, 2); + // EXPECT_EQ(band, 1); + + // EXPECT_EQ(b.Index(samp, line, band), 0); + + // EXPECT_EQ(b.Sample(16), 3); + // EXPECT_EQ(b.Line(16), 3); + // EXPECT_EQ(b.Band(16), 2); + + // b.Position(16, samp, line, band); + // EXPECT_EQ(samp, 3); + // EXPECT_EQ(line, 3); + // EXPECT_EQ(band, 2); + + // EXPECT_EQ(b.Index(samp, line, band), 16); +} + +TEST(BufferTest, TestBufferCopy) { + Buffer b(4, 3, 2, Isis::SignedInteger); + + for(int i = 0; i < b.size(); i++) { + b[i] = i; + } + + Buffer a = b; + + EXPECT_TRUE(a.DoubleBuffer() != b.DoubleBuffer()); + + EXPECT_EQ(a.SampleDimension(), 4); + EXPECT_EQ(a.LineDimension(), 3); + EXPECT_EQ(a.BandDimension(), 2); + EXPECT_EQ(a.size(), 24); + + EXPECT_EQ(a[0], 0); + EXPECT_EQ(a[23], 23); + + EXPECT_EQ(Isis::PixelTypeName(a.PixelType()), "SignedInteger"); +} + +TEST(BufferTest, TestBufferDefaultConstructor) { + Buffer nullbuf; + EXPECT_EQ(nullbuf.size(), 0); +} + +TEST(BufferTest, TestBufferAssignment) { + Buffer b(2, 2, 2, Isis::Double); + b = 999.0; + + EXPECT_EQ(b.size(), 8); + EXPECT_EQ(b[0], 999.0); + EXPECT_EQ(b[2], 999.0); + EXPECT_EQ(b[b.size()-1], 999.0); +} + +TEST(BufferTest, TestBufferOutOfBound) { + Buffer b(4, 3, 2, Isis::SignedInteger); + for(int i = 0; i < b.size(); i++) { + b[i] = i; + } + + try { + b.at(-1); + } + catch(Isis::IException &e) { + EXPECT_TRUE(e.toString().toLatin1().contains("Array subscript [-1] is out of array bounds")) + << e.toString().toStdString(); + } + + try { + b.at(24); + } + catch(Isis::IException &e) { + EXPECT_TRUE(e.toString().toLatin1().contains("Array subscript [24] is out of array bounds")) + << e.toString().toStdString(); + } +} + +TEST(BufferTest, TestBufferScale) { + Buffer b(4, 3, 2, Isis::SignedInteger, 0.5); + + EXPECT_EQ(b.SampleDimension(), 4); + EXPECT_EQ(b.LineDimension(), 3); + EXPECT_EQ(b.BandDimension(), 2); + + EXPECT_EQ(b.SampleDimensionScaled(), 2); + EXPECT_EQ(b.LineDimensionScaled(), 1); + + EXPECT_EQ(b.size(), 4); + + for(int i = 0; i < b.size(); i++) { + b[i] = i; + } + + Buffer d(4, 3, 2, Isis::SignedInteger, 1); + d.CopyOverlapFrom(b); + std::vector truthBuffer = {0, 0, 1, 1, + 0, 0, 1, 1, + 0, 0, 1, 1, + // Second band + 2, 2, 3, 3, + 2, 2, 3, 3, + 2, 2, 3, 3}; + for (int i = 0; i < truthBuffer.size(); i++) { + EXPECT_EQ(truthBuffer[i], d[i]); + } +} \ No newline at end of file diff --git a/isis/tests/CubeTests.cpp b/isis/tests/CubeTests.cpp index 8126140706..538fcae47b 100644 --- a/isis/tests/CubeTests.cpp +++ b/isis/tests/CubeTests.cpp @@ -30,7 +30,7 @@ void check_cube(Cube &cube, double base, double multiplier, int pixelType, - bool attached, + int attachType, int format, int isOpen, int isReadOnly, @@ -43,7 +43,7 @@ void check_cube(Cube &cube, EXPECT_EQ(cube.base(), base); EXPECT_EQ(cube.multiplier(), multiplier); EXPECT_EQ(cube.pixelType(), pixelType); - EXPECT_EQ(cube.labelsAttached(), attached); + EXPECT_EQ(cube.labelsAttached(), attachType); EXPECT_EQ(cube.format(), format); EXPECT_EQ(cube.isOpen(), isOpen); if (cube.isOpen()) { @@ -270,7 +270,7 @@ TEST(CubeTest, TestCubeAttachSpiceFromIsd) { EXPECT_PRED_FORMAT2(AssertQStringsEqual, cam->instrumentNameLong(), "Visual Imaging Subsystem Camera B"); } -TEST(CubeTest, TestCubeAttachLineScanTableFromIsd) { +TEST_F(TempTestingFiles, TestCubeAttachLineScanTableFromIsd) { std::istringstream labelStrm(R"( Object = IsisCube Object = Core @@ -786,9 +786,8 @@ TEST(CubeTest, TestCubeAttachLineScanTableFromIsd) { Pvl label; labelStrm >> label; - QTemporaryFile tempFile; Cube testCube; - testCube.fromIsd(tempFile.fileName() + ".cub", label, isd, "rw"); + testCube.fromIsd(tempDir.path() + "/test.cub", label, isd, "rw"); PvlGroup kernels = testCube.group("Kernels"); @@ -820,11 +819,11 @@ TEST_F(SmallCube, TestCubeHasBlob) { TEST_F(TempTestingFiles, TestCubeCreateWriteCopy) { Cube out; QString file = ""; - check_cube(out, file, 0, 0, 0, 0, 1, 7, 1, 1, 0, 0, 0, 65536); + check_cube(out, file, 0, 0, 0, 0, 1, 7, 0, 1, 0, 0, 0, 65536); out.setDimensions(150, 200, 2); file = QString(tempDir.path() + "/IsisCube_00.cub"); out.create(file); - check_cube(out, file, 150, 200, 2, 0, 1, 7, 1, 1, 1, 0, 1, 65536); + check_cube(out, file, 150, 200, 2, 0, 1, 7, 0, 1, 1, 0, 1, 65536); LineManager line(out); long j = 0; @@ -844,7 +843,7 @@ TEST_F(TempTestingFiles, TestCubeCreateWriteCopy) { // Test the open and read methods Cube in(file2); - check_cube(in, file2, 150, 200, 2, 0, 1, 7, 1, 1, 1, 1, 0, 6563); + check_cube(in, file2, 150, 200, 2, 0, 1, 7, 0, 1, 1, 1, 0, 6563); LineManager inLine(in); j = 0; @@ -862,15 +861,15 @@ TEST_F(TempTestingFiles, TestCubeCreateWriteCopy) { TEST_F(TempTestingFiles, TestCubeCreateWrite8bit) { Cube out; QString file = ""; - check_cube(out, file, 0, 0, 0, 0, 1, 7, 1, 1, 0, 0, 0, 65536); + check_cube(out, file, 0, 0, 0, 0, 1, 7, 0, 1, 0, 0, 0, 65536); out.setDimensions(150, 200, 1); - out.setLabelsAttached(0); + out.setLabelsAttached(Cube::DetachedLabel); out.setBaseMultiplier(200.0, -1.0); out.setByteOrder(ISIS_LITTLE_ENDIAN ? Msb : Lsb); out.setFormat(Cube::Bsq); out.setLabelSize(1000); out.setPixelType(UnsignedByte); - check_cube(out, file, 150, 200, 1, 200, -1, 1, 0, 0, 0, 0, 0, 1000); + check_cube(out, file, 150, 200, 1, 200, -1, 1, 1, 0, 0, 0, 0, 1000); file = QString(tempDir.path() + "/IsisCube_00.cub"); out.create(file); @@ -894,7 +893,7 @@ TEST_F(TempTestingFiles, TestCubeCreateWrite8bit) { catch (IException &e) { e.print(); } - check_cube(in, file, 150, 200, 1, 200, -1, 1, 0, 0, 1, 1, 0, 419); + check_cube(in, file, 150, 200, 1, 200, -1, 1, 1, 0, 1, 1, 0, 419); j = 0; LineManager inLine(in); for(inLine.begin(); !inLine.end(); inLine++) { @@ -911,12 +910,12 @@ TEST_F(TempTestingFiles, TestCubeCreateWrite8bit) { TEST_F(TempTestingFiles, TestCubeCreateWrite16bit) { Cube out; QString file = ""; - check_cube(out, file, 0, 0, 0, 0, 1, 7, 1, 1, 0, 0, 0, 65536); + check_cube(out, file, 0, 0, 0, 0, 1, 7, 0, 1, 0, 0, 0, 65536); out.setDimensions(150, 200, 2); out.setBaseMultiplier(30000.0, -1.0); out.setByteOrder(ISIS_LITTLE_ENDIAN ? Msb : Lsb); out.setPixelType(SignedWord); - check_cube(out, file, 150, 200, 2, 30000, -1, 4, 1, 1, 0, 0, 0, 65536); + check_cube(out, file, 150, 200, 2, 30000, -1, 4, 0, 1, 0, 0, 0, 65536); file = QString(tempDir.path() + "/IsisCube.cub"); out.create(file); @@ -939,7 +938,7 @@ TEST_F(TempTestingFiles, TestCubeCreateWrite16bit) { catch (IException &e) { e.print(); } - check_cube(in, file, 150, 200, 2, 30000, -1, 4, 1, 1, 1, 1, 0, 65536); + check_cube(in, file, 150, 200, 2, 30000, -1, 4, 0, 1, 1, 1, 0, 65536); j = 0; LineManager inLine(in); for(inLine.begin(); !inLine.end(); inLine++) { @@ -1053,16 +1052,16 @@ TEST_F(SmallCube, TestCubeVirutalBands) { TEST_F(SmallCube, TestCubeReopenRW) { QString path = testCube->fileName(); - check_cube(*testCube, path, 10, 10, 10, 0, 1, 7, 1, 1, 1, 0, 1, 65536); + check_cube(*testCube, path, 10, 10, 10, 0, 1, 7, 0, 1, 1, 0, 1, 65536); testCube->reopen("rw"); - check_cube(*testCube, path, 10, 10, 10, 0, 1, 7, 1, 1, 1, 0, 1, 65536); + check_cube(*testCube, path, 10, 10, 10, 0, 1, 7, 0, 1, 1, 0, 1, 65536); } TEST_F(SmallCube, TestCubeReopenR) { QString path = testCube->fileName(); - check_cube(*testCube, path, 10, 10, 10, 0, 1, 7, 1, 1, 1, 0, 1, 65536); + check_cube(*testCube, path, 10, 10, 10, 0, 1, 7, 0, 1, 1, 0, 1, 65536); testCube->reopen("r"); - check_cube(*testCube, path, 10, 10, 10, 0, 1, 7, 1, 1, 1, 1, 0, 65536); + check_cube(*testCube, path, 10, 10, 10, 0, 1, 7, 0, 1, 1, 1, 0, 65536); } TEST_F(SmallCube, TestCubeAlreadyOpenOpen) { @@ -1374,14 +1373,15 @@ TEST_F(ECube, TestCubeECubeWrite) { for (int index = 0; index < writeBrick.size(); index++) { writeBrick[index] = 1.0; } - try { - testECube->write(writeBrick); - FAIL(); - } - catch(IException &e) { - EXPECT_TRUE(e.toString().toLatin1().contains("The cube [external.ecub] does not support storing DN data because it is using an external file for DNs.")) - << e.toString().toStdString(); + testECube->write(writeBrick); + + Brick readBrick(3, 3, 2, testECube->pixelType()); + readBrick.SetBasePosition(1, 1, 1); + testECube->read(readBrick); + for (int index = 0; index < readBrick.size(); index++) { + EXPECT_EQ(readBrick[index], 1); } + } TEST_F(ECube, TestCubeECubeFromECubeRead) { diff --git a/isis/tests/FunctionalTestsCubeatt.cpp b/isis/tests/FunctionalTestsCubeatt.cpp index 174474920c..673161d529 100644 --- a/isis/tests/FunctionalTestsCubeatt.cpp +++ b/isis/tests/FunctionalTestsCubeatt.cpp @@ -46,7 +46,7 @@ TEST_F(SmallCube, FunctionalTestCubeattNoChange) { // Check attributes: pixel type, storage format, label format, storage order, pixel range, bands EXPECT_EQ(outputCube.pixelType(), PixelType::Real); EXPECT_EQ(outputCube.format(), Cube::Format::Tile); - EXPECT_TRUE(outputCube.labelsAttached()); + EXPECT_EQ(outputCube.labelsAttached(), Cube::AttachedLabel); EXPECT_EQ(outputCube.byteOrder(), ByteOrder::Lsb); // Setting the pixel range modifies the base/multiplier, so check those. EXPECT_EQ(outputCube.base(), 0); diff --git a/isis/tests/FunctionalTestsShadow.cpp b/isis/tests/FunctionalTestsShadow.cpp index 3efb78c579..c1c9acfa6e 100644 --- a/isis/tests/FunctionalTestsShadow.cpp +++ b/isis/tests/FunctionalTestsShadow.cpp @@ -37,10 +37,10 @@ TEST_F(DemCube, FunctionalTestShadowMatch) { EXPECT_DOUBLE_EQ(double(shadowStats["MinimumElevation"]), 90.0); EXPECT_DOUBLE_EQ(double(shadowStats["MaximumElevation"]), 90.0); - EXPECT_EQ(int(shadowStats["NumRays"]), 5551); - EXPECT_EQ(int(shadowStats["NumRayDemIntersections"]), 5551); + EXPECT_EQ(int(shadowStats["NumRays"]), 5550); + EXPECT_EQ(int(shadowStats["NumRayDemIntersections"]), 5550); EXPECT_DOUBLE_EQ(double(shadowStats["AverageRayDemIntersectionsPerRay"]), 1.0); - EXPECT_EQ(int(shadowStats["NumLightedPixels"]), 5551); + EXPECT_EQ(int(shadowStats["NumLightedPixels"]), 5550); EXPECT_EQ(int(shadowStats["NumShadowedPixels"]), 0); EXPECT_EQ(int(shadowStats["NumSpecialPixels"]), 2800); EXPECT_EQ(int(shadowStats["NumPixelsShadowedByRays"]), 0); @@ -49,10 +49,10 @@ TEST_F(DemCube, FunctionalTestShadowMatch) { std::unique_ptr hist (shadowCube.histogram()); - EXPECT_NEAR(hist->Average(), 0.00084506527240706553, 1e-11); - EXPECT_NEAR(hist->Sum(), 4.6909573271316205, 1e-11); - ASSERT_EQ(hist->ValidPixels(), 5551); - EXPECT_NEAR(hist->StandardDeviation(), 0.0010084740620921499, 1e-11); + EXPECT_NEAR(hist->Average(), 0.00084519596669997796, 1e-11); + EXPECT_NEAR(hist->Sum(), 4.6908376151848774, 1e-11); + EXPECT_EQ(hist->ValidPixels(), 5550); + EXPECT_NEAR(hist->StandardDeviation(), 0.0010084895548001136, 1e-11); } TEST_F(DemCube, FunctionalTestShadowTime) { @@ -77,8 +77,8 @@ TEST_F(DemCube, FunctionalTestShadowTime) { EXPECT_DOUBLE_EQ(double(shadowStats["MaximumElevation"]), 55.260883777776002); EXPECT_EQ(int(shadowStats["NumRays"]), 9604); - EXPECT_EQ(int(shadowStats["NumRayDemIntersections"]), 10177); - EXPECT_DOUBLE_EQ(double(shadowStats["AverageRayDemIntersectionsPerRay"]), 1.0596626405664); + EXPECT_EQ(int(shadowStats["NumRayDemIntersections"]), 10159); + EXPECT_DOUBLE_EQ(double(shadowStats["AverageRayDemIntersectionsPerRay"]), 1.0577884214910001); EXPECT_EQ(int(shadowStats["NumLightedPixels"]), 9500); EXPECT_EQ(int(shadowStats["NumShadowedPixels"]), 104); EXPECT_EQ(int(shadowStats["NumSpecialPixels"]), 2800); @@ -88,10 +88,10 @@ TEST_F(DemCube, FunctionalTestShadowTime) { std::unique_ptr hist (shadowCube.histogram()); - EXPECT_NEAR(hist->Average(), 0.57755590112585775, 1e-11); - EXPECT_NEAR(hist->Sum(), 5486.7810606956482, 1e-11); - ASSERT_EQ(hist->ValidPixels(), 9500); - EXPECT_NEAR(hist->StandardDeviation(), 0.0027122379225963896, 1e-11); + EXPECT_NEAR(hist->Average(), 0.5775715160056164, 1e-11); + EXPECT_NEAR(hist->Sum(), 5486.9294020533562, 1e-11); + EXPECT_EQ(hist->ValidPixels(), 9500); + EXPECT_NEAR(hist->StandardDeviation(), 0.0027113945565730518, 1e-11); } TEST_F(DemCube, FunctionalTestShadowNoShadow) { @@ -116,7 +116,7 @@ TEST_F(DemCube, FunctionalTestShadowNoShadow) { EXPECT_EQ(int(shadowStats["NumRays"]), 0); EXPECT_EQ(int(shadowStats["NumRayDemIntersections"]), 0); - EXPECT_EQ(int(shadowStats["NumLightedPixels"]), 5551); + EXPECT_EQ(int(shadowStats["NumLightedPixels"]), 5550); EXPECT_EQ(int(shadowStats["NumShadowedPixels"]), 0); EXPECT_EQ(int(shadowStats["NumSpecialPixels"]), 2800); EXPECT_EQ(int(shadowStats["NumPixelsShadowedByRays"]), 0); @@ -125,10 +125,10 @@ TEST_F(DemCube, FunctionalTestShadowNoShadow) { std::unique_ptr hist (shadowCube.histogram()); - EXPECT_NEAR(hist->Average(), 0.00084506527240706553, 1e-11); - EXPECT_NEAR(hist->Sum(), 4.6909573271316205, 1e-11); - EXPECT_EQ(hist->ValidPixels(), 5551); - EXPECT_NEAR(hist->StandardDeviation(), 0.0010084740620921499, 1e-11); + EXPECT_NEAR(hist->Average(), 0.00084519596669997796, 1e-11); + EXPECT_NEAR(hist->Sum(), 4.6908376151848774, 1e-11); + EXPECT_EQ(hist->ValidPixels(), 5550); + EXPECT_NEAR(hist->StandardDeviation(), 0.0010084895548001136, 1e-11); } TEST_F(DemCube, FunctionalTestShadowAccurate) { @@ -149,10 +149,10 @@ TEST_F(DemCube, FunctionalTestShadowAccurate) { EXPECT_DOUBLE_EQ(double(shadowStats["MinimumElevation"]), 90.0); EXPECT_DOUBLE_EQ(double(shadowStats["MaximumElevation"]), 90.0); - EXPECT_EQ(int(shadowStats["NumRays"]), 5551); - EXPECT_EQ(int(shadowStats["NumRayDemIntersections"]), 5551); + EXPECT_EQ(int(shadowStats["NumRays"]), 5550); + EXPECT_EQ(int(shadowStats["NumRayDemIntersections"]), 5550); EXPECT_DOUBLE_EQ(double(shadowStats["AverageRayDemIntersectionsPerRay"]), 1.0); - EXPECT_EQ(int(shadowStats["NumLightedPixels"]), 5551); + EXPECT_EQ(int(shadowStats["NumLightedPixels"]), 5550); EXPECT_EQ(int(shadowStats["NumShadowedPixels"]), 0); EXPECT_EQ(int(shadowStats["NumSpecialPixels"]), 2800); EXPECT_EQ(int(shadowStats["NumPixelsShadowedByRays"]), 0); @@ -161,10 +161,10 @@ TEST_F(DemCube, FunctionalTestShadowAccurate) { std::unique_ptr hist (shadowCube.histogram()); - EXPECT_NEAR(hist->Average(), 0.00084506527240706553, 1e-11); - EXPECT_NEAR(hist->Sum(), 4.6909573271316205, 1e-11); - ASSERT_EQ(hist->ValidPixels(), 5551); - EXPECT_NEAR(hist->StandardDeviation(), 0.0010084740620921499, 1e-11); + EXPECT_NEAR(hist->Average(), 0.00084519596669997796, 1e-11); + EXPECT_NEAR(hist->Sum(), 4.6908376151848774, 1e-11); + EXPECT_EQ(hist->ValidPixels(), 5550); + EXPECT_NEAR(hist->StandardDeviation(), 0.0010084895548001136, 1e-11); } TEST_F(DemCube, FunctionalTestShadowCustom) { @@ -185,10 +185,10 @@ TEST_F(DemCube, FunctionalTestShadowCustom) { EXPECT_DOUBLE_EQ(double(shadowStats["MinimumElevation"]), 90.0); EXPECT_DOUBLE_EQ(double(shadowStats["MaximumElevation"]), 90.0); - EXPECT_EQ(int(shadowStats["NumRays"]), 5551); - EXPECT_EQ(int(shadowStats["NumRayDemIntersections"]), 5551); + EXPECT_EQ(int(shadowStats["NumRays"]), 5550); + EXPECT_EQ(int(shadowStats["NumRayDemIntersections"]), 5550); EXPECT_DOUBLE_EQ(double(shadowStats["AverageRayDemIntersectionsPerRay"]), 1.0); - EXPECT_EQ(int(shadowStats["NumLightedPixels"]), 5551); + EXPECT_EQ(int(shadowStats["NumLightedPixels"]), 5550); EXPECT_EQ(int(shadowStats["NumShadowedPixels"]), 0); EXPECT_EQ(int(shadowStats["NumSpecialPixels"]), 2800); EXPECT_EQ(int(shadowStats["NumPixelsShadowedByRays"]), 0); @@ -197,10 +197,10 @@ TEST_F(DemCube, FunctionalTestShadowCustom) { std::unique_ptr hist (shadowCube.histogram()); - EXPECT_NEAR(hist->Average(), 0.00084506527240706553, 1e-11); - EXPECT_NEAR(hist->Sum(), 4.6909573271316205, 1e-11); - ASSERT_EQ(hist->ValidPixels(), 5551); - EXPECT_NEAR(hist->StandardDeviation(), 0.0010084740620921499, 1e-11); + EXPECT_NEAR(hist->Average(), 0.00084519596669997796, 1e-11); + EXPECT_NEAR(hist->Sum(), 4.6908376151848774, 1e-11); + EXPECT_EQ(hist->ValidPixels(), 5550); + EXPECT_NEAR(hist->StandardDeviation(), 0.0010084895548001136, 1e-11); } TEST_F(DemCube, FunctionalTestShadowErrors) { diff --git a/isis/tests/GTiffTests.cpp b/isis/tests/GTiffTests.cpp new file mode 100644 index 0000000000..704c8c0679 --- /dev/null +++ b/isis/tests/GTiffTests.cpp @@ -0,0 +1,225 @@ +#include +#include +#include + +#include +using json = nlohmann::json; + +#include "Blob.h" +#include "Brick.h" +#include "Camera.h" +#include "Cube.h" +#include "CubeAttribute.h" +#include "Histogram.h" +#include "LineManager.h" +#include "Statistics.h" +#include "Table.h" +#include "TableField.h" +#include "TableRecord.h" + +#include "CubeFixtures.h" +#include "TestUtilities.h" + +#include "gmock/gmock.h" + +using namespace Isis; + +void check_tiff(Cube &cube, + QString &file, + int sampleCount, + int lineCount, + int bandCount, + double base, + double multiplier, + int pixelType, + int attachType, + int format, + int isOpen, + int isReadOnly, + int isReadWrite, + int labelSize) { + EXPECT_EQ(cube.fileName().toStdString(), file.toStdString()); + EXPECT_EQ(cube.sampleCount(), sampleCount); + EXPECT_EQ(cube.lineCount(), lineCount); + EXPECT_EQ(cube.bandCount(), bandCount); + EXPECT_EQ(cube.base(), base); + EXPECT_EQ(cube.multiplier(), multiplier); + EXPECT_EQ(cube.pixelType(), pixelType); + EXPECT_EQ(cube.labelsAttached(), attachType); + EXPECT_EQ(cube.format(), format); + EXPECT_EQ(cube.isOpen(), isOpen); + if (cube.isOpen()) { + EXPECT_EQ(cube.isReadOnly(), isReadOnly); + EXPECT_EQ(cube.isReadWrite(), isReadWrite); + } + EXPECT_EQ(cube.labelSize(), labelSize); +} + + +TEST_F(TempTestingFiles, TestGTiffCreateWriteCopy) { + Cube out; + QString file = ""; + check_tiff(out, file, 0, 0, 0, 0, 1, 7, 0, 1, 0, 0, 0, 65536); + out.setDimensions(150, 200, 2); + out.setFormat(Isis::Cube::GTiff); + file = QString(tempDir.path() + "/IsisCube_00.tiff"); + out.create(file); + check_tiff(out, file, 150, 200, 2, 0, 1, 7, 0, 2, 1, 0, 1, 65536); + + LineManager line(out); + long j = 0; + for(line.begin(); !line.end(); line++) { + for(int i = 0; i < line.size(); i++) { + line[i] = (double) j; + j++; + } + j--; + out.write(line); + } + + // Copy returns the resulting Cube, we don't care about it (but we need it to flush) so delete + QString file2 = tempDir.path() + "/IsisCube_01.tiff"; + CubeAttributeOutput outAtt; + outAtt.setFileFormat(Isis::Cube::GTiff); + delete out.copy(file2, outAtt); + out.close(); + + // Test the open and read methods + Cube in(file2); + check_tiff(in, file2, 150, 200, 2, 0, 1, 7, 0, 2, 1, 1, 0, 65536); + + LineManager inLine(in); + j = 0; + for(inLine.begin(); !inLine.end(); inLine++) { + in.read(inLine); + for(int i = 0; i < inLine.size(); i++) { + EXPECT_NEAR(inLine[i], (double) j, 1e-15); + j++; + } + j--; + } + in.close(); +} + +class GdalDnTypeGenerator: public TempTestingFiles, public ::testing::WithParamInterface { + // Intentionally left empty + void SetUp() { + TempTestingFiles::SetUp(); + } +}; + +INSTANTIATE_TEST_SUITE_P (GdalDnPixelTypes, + GdalDnTypeGenerator, + ::testing::Values(Isis::UnsignedByte, + Isis::SignedByte, + Isis::UnsignedWord, + Isis::SignedWord, + Isis::UnsignedInteger, + Isis::SignedInteger, + Isis::Real, + Isis::Double)); + +TEST_P(GdalDnTypeGenerator, TestGTiffCreateWrite) { + Cube out; + QString file = ""; + check_tiff(out, file, 0, 0, 0, 0, 1, 7, 0, 1, 0, 0, 0, 65536); + int lines = 200; + int samples = 150; + int bands = 4; + out.setDimensions(samples, lines, bands); + out.setBaseMultiplier(0.0, 1.0); + out.setFormat(Cube::GTiff); + out.setPixelType(GetParam()); + check_tiff(out, file, samples, lines, bands, 0, 1, GetParam(), 0, 2, 0, 0, 0, 65536); + file = QString(tempDir.path() + "/IsisCube_00.tiff"); + out.create(file); + + // Only write DNs between 3 and 127 as those are valid + // DN values for all pixel types + // All special pixel input and output tests are handled in the + // GdalIoHandler tests + int min = 3; + int max = 127; + + long j = min; + LineManager oline(out); + for(oline.begin(); !oline.end(); oline++) { + for(int i = 0; i < oline.size(); i++) { + oline[i] = (double) j; + } + out.clearIoCache(); + out.write(oline); + j++; + if (j > max) { + j = min; + } + } + out.close(); + + Cube in; + try { + in.open(file); + } + catch (IException &e) { + e.print(); + } + check_tiff(in, file, samples, lines, bands, 0, 1, GetParam(), 0, 2, 1, 1, 0, 65536); + + Statistics *cubeStats = in.statistics(0); + EXPECT_DOUBLE_EQ(cubeStats->Average(), 62.65625); + EXPECT_DOUBLE_EQ(cubeStats->StandardDeviation(), 36.277390383170371); + EXPECT_DOUBLE_EQ(cubeStats->TotalPixels(), 120000); + EXPECT_DOUBLE_EQ(cubeStats->NullPixels(), 0); + delete cubeStats; + in.close(); +} + +// Add Test for GTiff with no ISIS metadata + +TEST_F(TempTestingFiles, TableTestsWriteReadGdal) { + TableField f1("Column1", TableField::Integer); + TableField f2("Column2", TableField::Double); + TableField f3("Column3", TableField::Text, 10); + TableField f4("Column4", TableField::Double); + TableRecord rec; + rec += f1; + rec += f2; + rec += f3; + rec += f4; + Table t("UNITTEST", rec); + + t.SetAssociation(Table::Lines); + + rec[0] = 5; + rec[1] = 3.14; + rec[2] = "PI"; + rec[3] = 3.14159; + t += rec; + + rec[0] = -1; + rec[1] = 0.5; + rec[2] = "HI"; + rec[3] = -0.55; + t += rec; + + QString cubeFile = tempDir.path() + "/testTable.tiff"; + Cube cube; + cube.setDimensions(10, 10, 1); + cube.setFormat(Isis::Cube::GTiff); + cube.create(cubeFile); + cube.write(t); + Blob tableBlob("UNITTEST", "Table"); + cube.read(tableBlob); + Table t2(tableBlob); + + EXPECT_EQ(t.RecordFields(), t2.RecordFields()); + EXPECT_EQ(t.RecordSize(), t2.RecordSize()); + EXPECT_EQ(t.IsSampleAssociated(), t2.IsSampleAssociated()); + EXPECT_EQ(t.IsLineAssociated(), t2.IsLineAssociated()); + EXPECT_EQ(t.IsBandAssociated(), t2.IsBandAssociated()); + + ASSERT_EQ(t.Records(), t2.Records()); + for (int i = 0; i < t.Records(); i++) { + EXPECT_EQ(TableRecord::toString(t[i]).toStdString(), TableRecord::toString(t2[i]).toStdString()); + } +} \ No newline at end of file diff --git a/isis/tests/GdalIoHandlerTests.cpp b/isis/tests/GdalIoHandlerTests.cpp new file mode 100644 index 0000000000..166bbf84ec --- /dev/null +++ b/isis/tests/GdalIoHandlerTests.cpp @@ -0,0 +1,672 @@ +#include +#include +#include +#include + +#include +using json = nlohmann::json; + +#include "Brick.h" +#include "PixelType.h" +#include "GdalIoHandler.h" +#include "SpecialPixel.h" +#include "TiffFixtures.h" + +#include "TestUtilities.h" + +#include "gmock/gmock.h" + +using namespace Isis; + +TEST_F(ReadWriteTiff, GdalIoTestsDefaulWrite) { + PixelType isisPixelType = Double; + createTiff(isisPixelType, false); + + { + const QList *bandList = new QList; + + GdalIoHandler handler(path, bandList, GDT_Float64, GA_Update); + Brick localBrick(6, 1, 1, isisPixelType); + + localBrick.SetBasePosition(1, 1, 1); + // init everything to 100 + for (int i = 0; i < localBrick.size(); i++) { + localBrick[i] = 100; + } + + handler.write(localBrick); + } // file is closed + + // read it back + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_ReadOnly)); + dbuf = (double *) CPLMalloc(sizeof(double)*6*1); + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Read, 0, 0, + 6, 1, + dbuf, + 6, 1, + IsisPixelToGdal(isisPixelType), + 0, 0); + + if (err > CE_Failure) { + FAIL() << "Failed to read tiff"; + } + + for (int i = 0; i<6*1; i++) { + ASSERT_EQ(((double *) dbuf)[i], 100); + } + +} + +TEST_F(ReadWriteTiff, GdalIoTestsReadFloat64) { + PixelType isisPixelType = Double; + createTiff(isisPixelType); + + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType)); + handler.read(*localBrick); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + EXPECT_EQ(brickDoubleBuff[0], HIGH_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[1], HIGH_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[2], LOW_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[3], LOW_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[4], NULL8); + EXPECT_EQ(brickDoubleBuff[5], 1000.0); +} + +TEST_F(ReadWriteTiff, GdalIoTestsReadFloat32) { + PixelType isisPixelType = Real; + createTiff(isisPixelType); + + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType)); + handler.read(*localBrick); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + EXPECT_EQ(brickDoubleBuff[0], HIGH_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[1], HIGH_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[2], LOW_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[3], LOW_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[4], NULL8); + EXPECT_EQ(brickDoubleBuff[5], 1000.0); +} + +TEST_F(ReadWriteTiff, GdalIoTestsReadInt32) { + PixelType isisPixelType = SignedInteger; + createTiff(isisPixelType); + + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType)); + handler.read(*localBrick); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + EXPECT_EQ(brickDoubleBuff[0], HIGH_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[1], HIGH_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[2], LOW_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[3], LOW_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[4], NULL8); + EXPECT_EQ(brickDoubleBuff[5], 1000.0); +} + +TEST_F(ReadWriteTiff, GdalIoTestsReadUInt32) { + PixelType isisPixelType = UnsignedInteger; + createTiff(isisPixelType); + + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType)); + handler.read(*localBrick); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + EXPECT_EQ(brickDoubleBuff[0], HIGH_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[1], HIGH_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[2], LOW_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[3], LOW_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[4], NULL8); + EXPECT_EQ(brickDoubleBuff[5], 1000.0); +} + +TEST_F(ReadWriteTiff, GdalIoTestsReadInt16) { + PixelType isisPixelType = SignedWord; + createTiff(isisPixelType); + + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType)); + handler.read(*localBrick); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + EXPECT_EQ(brickDoubleBuff[0], HIGH_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[1], HIGH_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[2], LOW_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[3], LOW_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[4], NULL8); + EXPECT_EQ(brickDoubleBuff[5], 1000.0); +} + +TEST_F(ReadWriteTiff, GdalIoTestsReadUInt16) { + PixelType isisPixelType = UnsignedWord; + createTiff(isisPixelType); + + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType)); + handler.read(*localBrick); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + EXPECT_EQ(brickDoubleBuff[0], HIGH_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[1], HIGH_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[2], LOW_INSTR_SAT8); + EXPECT_EQ(brickDoubleBuff[3], LOW_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[4], NULL8); + EXPECT_EQ(brickDoubleBuff[5], 1000.0); +} + +TEST_F(ReadWriteTiff, GdalIoTestsReadInt8) { + PixelType isisPixelType = SignedByte; + createTiff(isisPixelType); + + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType)); + handler.read(*localBrick); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + EXPECT_EQ(brickDoubleBuff[0], HIGH_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[1], HIGH_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[2], NULL8); + EXPECT_EQ(brickDoubleBuff[3], NULL8); + EXPECT_EQ(brickDoubleBuff[4], NULL8); + EXPECT_EQ(brickDoubleBuff[5], 50); +} + +TEST_F(ReadWriteTiff, GdalIoTestsReadUInt8) { + PixelType isisPixelType = UnsignedByte; + createTiff(isisPixelType); + + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType)); + handler.read(*localBrick); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + EXPECT_EQ(brickDoubleBuff[0], HIGH_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[1], HIGH_REPR_SAT8); + EXPECT_EQ(brickDoubleBuff[2], NULL8); + EXPECT_EQ(brickDoubleBuff[3], NULL8); + EXPECT_EQ(brickDoubleBuff[4], NULL8); + EXPECT_EQ(brickDoubleBuff[5], 50); +} + +TEST_F(ReadWriteTiff, GdalIoTestsWriteFloat64) { + PixelType isisPixelType = Double; + createTiff(isisPixelType, false); + // Create a context so the handler goes out of scope and closes the file + { + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType), GA_Update); + + localBrick = new Brick(6, 1, 1, isisPixelType); + localBrick->SetBasePosition(1, 1, 1); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + + brickDoubleBuff[0] = HIGH_INSTR_SAT8; + brickDoubleBuff[1] = HIGH_REPR_SAT8; + brickDoubleBuff[2] = LOW_INSTR_SAT8; + brickDoubleBuff[3] = LOW_REPR_SAT8; + brickDoubleBuff[4] = NULL8; + brickDoubleBuff[5] = 1000; + + handler.write(*localBrick); + } + + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_ReadOnly)); + dbuf = (double *)CPLMalloc(sizeof(double) * 6); + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Read, 0, 0, + 6, 1, + dbuf, + 6, 1, + IsisPixelToGdal(isisPixelType), + 0, 0); + + if (err > CE_Failure) { + FAIL() << "Failed to read tiff"; + } + + EXPECT_EQ(((double *)dbuf)[0], HIGH_INSTR_SAT8); + EXPECT_EQ(((double *)dbuf)[1], HIGH_REPR_SAT8); + EXPECT_EQ(((double *)dbuf)[2], LOW_INSTR_SAT8); + EXPECT_EQ(((double *)dbuf)[3], LOW_REPR_SAT8); + EXPECT_EQ(((double *)dbuf)[4], NULL8); + EXPECT_EQ(((double *)dbuf)[5], 1000); +} + +TEST_F(ReadWriteTiff, GdalIoTestsWriteFloat32) { + PixelType isisPixelType = Real; + createTiff(isisPixelType, false); + // Create a context so the handler goes out of scope and closes the file + { + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType), GA_Update); + + localBrick = new Brick(6, 1, 1, isisPixelType); + localBrick->SetBasePosition(1, 1, 1); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + + brickDoubleBuff[0] = HIGH_INSTR_SAT8; + brickDoubleBuff[1] = HIGH_REPR_SAT8; + brickDoubleBuff[2] = LOW_INSTR_SAT8; + brickDoubleBuff[3] = LOW_REPR_SAT8; + brickDoubleBuff[4] = NULL8; + brickDoubleBuff[5] = 1000; + + handler.write(*localBrick); + } + + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_ReadOnly)); + dbuf = (float *)CPLMalloc(sizeof(float) * 6); + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Read, 0, 0, + 6, 1, + dbuf, + 6, 1, + IsisPixelToGdal(isisPixelType), + 0, 0); + + if (err > CE_Failure) { + FAIL() << "Failed to read tiff"; + } + + EXPECT_EQ(((float *)dbuf)[0], HIGH_INSTR_SAT4); + EXPECT_EQ(((float *)dbuf)[1], HIGH_REPR_SAT4); + EXPECT_EQ(((float *)dbuf)[2], LOW_INSTR_SAT4); + EXPECT_EQ(((float *)dbuf)[3], LOW_REPR_SAT4); + EXPECT_EQ(((float *)dbuf)[4], NULL4); + EXPECT_EQ(((float *)dbuf)[5], 1000); +} + +TEST_F(ReadWriteTiff, GdalIoTestsWriteInt32) { + PixelType isisPixelType = SignedInteger; + createTiff(isisPixelType, false); + // Create a context so the handler goes out of scope and closes the file + { + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType), GA_Update); + + localBrick = new Brick(6, 1, 1, isisPixelType); + localBrick->SetBasePosition(1, 1, 1); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + + brickDoubleBuff[0] = HIGH_INSTR_SAT8; + brickDoubleBuff[1] = HIGH_REPR_SAT8; + brickDoubleBuff[2] = LOW_INSTR_SAT8; + brickDoubleBuff[3] = LOW_REPR_SAT8; + brickDoubleBuff[4] = NULL8; + brickDoubleBuff[5] = 1000; + + handler.write(*localBrick); + } + + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_ReadOnly)); + dbuf = (unsigned int *)CPLMalloc(sizeof(unsigned int) * 6); + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Read, 0, 0, + 6, 1, + dbuf, + 6, 1, + IsisPixelToGdal(isisPixelType), + 0, 0); + + if (err > CE_Failure) { + FAIL() << "Failed to read tiff"; + } + + EXPECT_EQ(((unsigned int *)dbuf)[0], HIGH_INSTR_SATI4); + EXPECT_EQ(((unsigned int *)dbuf)[1], HIGH_REPR_SATI4); + EXPECT_EQ(((unsigned int *)dbuf)[2], LOW_INSTR_SATI4); + EXPECT_EQ(((unsigned int *)dbuf)[3], LOW_REPR_SATI4); + EXPECT_EQ(((unsigned int *)dbuf)[4], NULLI4); + EXPECT_EQ(((unsigned int *)dbuf)[5], 1000); +} + +TEST_F(ReadWriteTiff, GdalIoTestsWriteUInt32) { + PixelType isisPixelType = UnsignedInteger; + createTiff(isisPixelType, false); + // Create a context so the handler goes out of scope and closes the file + { + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType), GA_Update); + + localBrick = new Brick(6, 1, 1, isisPixelType); + localBrick->SetBasePosition(1, 1, 1); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + + brickDoubleBuff[0] = HIGH_INSTR_SAT8; + brickDoubleBuff[1] = HIGH_REPR_SAT8; + brickDoubleBuff[2] = LOW_INSTR_SAT8; + brickDoubleBuff[3] = LOW_REPR_SAT8; + brickDoubleBuff[4] = NULL8; + brickDoubleBuff[5] = 1000; + + handler.write(*localBrick); + } + + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_ReadOnly)); + dbuf = (unsigned int *)CPLMalloc(sizeof(unsigned int) * 6); + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Read, 0, 0, + 6, 1, + dbuf, + 6, 1, + IsisPixelToGdal(isisPixelType), + 0, 0); + + if (err > CE_Failure) { + FAIL() << "Failed to read tiff"; + } + + EXPECT_EQ(((unsigned int *)dbuf)[0], HIGH_INSTR_SATUI4); + EXPECT_EQ(((unsigned int *)dbuf)[1], HIGH_REPR_SATUI4); + EXPECT_EQ(((unsigned int *)dbuf)[2], LOW_INSTR_SATUI4); + EXPECT_EQ(((unsigned int *)dbuf)[3], LOW_REPR_SATUI4); + EXPECT_EQ(((unsigned int *)dbuf)[4], NULLUI4); + EXPECT_EQ(((unsigned int *)dbuf)[5], 1000); +} + +TEST_F(ReadWriteTiff, GdalIoTestsWriteInt16) { + PixelType isisPixelType = SignedWord; + createTiff(isisPixelType, false); + // Create a context so the handler goes out of scope and closes the file + { + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType), GA_Update); + + localBrick = new Brick(6, 1, 1, isisPixelType); + localBrick->SetBasePosition(1, 1, 1); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + + brickDoubleBuff[0] = HIGH_INSTR_SAT8; + brickDoubleBuff[1] = HIGH_REPR_SAT8; + brickDoubleBuff[2] = LOW_INSTR_SAT8; + brickDoubleBuff[3] = LOW_REPR_SAT8; + brickDoubleBuff[4] = NULL8; + brickDoubleBuff[5] = 1000; + + handler.write(*localBrick); + } + + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_ReadOnly)); + dbuf = (short *)CPLMalloc(sizeof(short) * 6); + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Read, 0, 0, + 6, 1, + dbuf, + 6, 1, + IsisPixelToGdal(isisPixelType), + 0, 0); + + if (err > CE_Failure) { + FAIL() << "Failed to read tiff"; + } + + EXPECT_EQ(((short *)dbuf)[0], HIGH_INSTR_SAT2); + EXPECT_EQ(((short *)dbuf)[1], HIGH_REPR_SAT2); + EXPECT_EQ(((short *)dbuf)[2], LOW_INSTR_SAT2); + EXPECT_EQ(((short *)dbuf)[3], LOW_REPR_SAT2); + EXPECT_EQ(((short *)dbuf)[4], NULL2); + EXPECT_EQ(((short *)dbuf)[5], 1000); +} + +TEST_F(ReadWriteTiff, GdalIoTestsWriteUInt16) { + PixelType isisPixelType = UnsignedWord; + createTiff(isisPixelType, false); + // Create a context so the handler goes out of scope and closes the file + { + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType), GA_Update); + + localBrick = new Brick(6, 1, 1, isisPixelType); + localBrick->SetBasePosition(1, 1, 1); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + + brickDoubleBuff[0] = HIGH_INSTR_SAT8; + brickDoubleBuff[1] = HIGH_REPR_SAT8; + brickDoubleBuff[2] = LOW_INSTR_SAT8; + brickDoubleBuff[3] = LOW_REPR_SAT8; + brickDoubleBuff[4] = NULL8; + brickDoubleBuff[5] = 1000; + + handler.write(*localBrick); + } + + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_ReadOnly)); + dbuf = (unsigned short *)CPLMalloc(sizeof(unsigned short) * 6); + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Read, 0, 0, + 6, 1, + dbuf, + 6, 1, + IsisPixelToGdal(isisPixelType), + 0, 0); + + if (err > CE_Failure) { + FAIL() << "Failed to read tiff"; + } + + EXPECT_EQ(((unsigned short *)dbuf)[0], HIGH_INSTR_SATU2); + EXPECT_EQ(((unsigned short *)dbuf)[1], HIGH_REPR_SATU2); + EXPECT_EQ(((unsigned short *)dbuf)[2], LOW_INSTR_SATU2); + EXPECT_EQ(((unsigned short *)dbuf)[3], LOW_REPR_SATU2); + EXPECT_EQ(((unsigned short *)dbuf)[4], NULLU2); + EXPECT_EQ(((unsigned short *)dbuf)[5], 1000); +} + +TEST_F(ReadWriteTiff, GdalIoTestsWriteInt8) { + PixelType isisPixelType = SignedByte; + createTiff(isisPixelType, false); + // Create a context so the handler goes out of scope and closes the file + { + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType), GA_Update); + + localBrick = new Brick(6, 1, 1, isisPixelType); + localBrick->SetBasePosition(1, 1, 1); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + + brickDoubleBuff[0] = HIGH_INSTR_SAT8; + brickDoubleBuff[1] = HIGH_REPR_SAT8; + brickDoubleBuff[2] = LOW_INSTR_SAT8; + brickDoubleBuff[3] = LOW_REPR_SAT8; + brickDoubleBuff[4] = NULL8; + brickDoubleBuff[5] = 50; + + handler.write(*localBrick); + } + + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_ReadOnly)); + dbuf = (char *)CPLMalloc(sizeof(char) * 6); + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Read, 0, 0, + 6, 1, + dbuf, + 6, 1, + IsisPixelToGdal(isisPixelType), + 0, 0); + + if (err > CE_Failure) { + FAIL() << "Failed to read tiff"; + } + + EXPECT_EQ(((char *)dbuf)[0], HIGH_INSTR_SATS1); + EXPECT_EQ(((char *)dbuf)[1], HIGH_REPR_SATS1); + EXPECT_EQ(((char *)dbuf)[2], LOW_INSTR_SATS1); + EXPECT_EQ(((char *)dbuf)[3], LOW_REPR_SATS1); + EXPECT_EQ(((char *)dbuf)[4], NULLS1); + EXPECT_EQ(((char *)dbuf)[5], 50); +} + +TEST_F(ReadWriteTiff, GdalIoTestsWriteUInt8) { + PixelType isisPixelType = UnsignedByte; + createTiff(isisPixelType, false); + // Create a context so the handler goes out of scope and closes the file + { + const QList *bandList = new QList; + GdalIoHandler handler(path, bandList, IsisPixelToGdal(isisPixelType), GA_Update); + + localBrick = new Brick(6, 1, 1, isisPixelType); + localBrick->SetBasePosition(1, 1, 1); + + double *brickDoubleBuff = localBrick->DoubleBuffer(); + + brickDoubleBuff[0] = HIGH_INSTR_SAT8; + brickDoubleBuff[1] = HIGH_REPR_SAT8; + brickDoubleBuff[2] = LOW_INSTR_SAT8; + brickDoubleBuff[3] = LOW_REPR_SAT8; + brickDoubleBuff[4] = NULL8; + brickDoubleBuff[5] = 50; + + handler.write(*localBrick); + } + + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_ReadOnly)); + dbuf = (unsigned char *)CPLMalloc(sizeof(unsigned char) * 6); + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Read, 0, 0, + 6, 1, + dbuf, + 6, 1, + IsisPixelToGdal(isisPixelType), + 0, 0); + + if (err > CE_Failure) { + FAIL() << "Failed to read tiff"; + } + + EXPECT_EQ(((unsigned char *)dbuf)[0], HIGH_INSTR_SAT1); + EXPECT_EQ(((unsigned char *)dbuf)[1], HIGH_REPR_SAT1); + EXPECT_EQ(((unsigned char *)dbuf)[2], LOW_INSTR_SAT1); + EXPECT_EQ(((unsigned char *)dbuf)[3], LOW_REPR_SAT1); + EXPECT_EQ(((unsigned char *)dbuf)[4], NULL1); + EXPECT_EQ(((unsigned char *)dbuf)[5], 50); +} + +TEST_F(ReadWriteTiff, GdalIoTestsReadOutside) { + PixelType isisPixelType = SignedWord; + const QList *bandList = new QList; + + // Create a context so the handler goes out of scope and closes the file + { + int samples = 5; + int lines = 5; + createSizedTiff(samples, lines, 1, isisPixelType); + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_Update)); + dbuf = (void *)CPLMalloc(sizeof(short) * samples * lines); + + for (short i = 0; i < samples * lines; i++) { + ((short *)dbuf)[i] = i; + } + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Write, 0, 0, + samples, lines, + dbuf, + samples, lines, + IsisPixelToGdal(isisPixelType), + 0, 0); + + if (err > CE_Failure) { + FAIL() << "Failed to write tiff"; + } + + dataset->Close(); + } + + GdalIoHandler gdalHandler(path, bandList, IsisPixelToGdal(isisPixelType)); + + delete localBrick; + localBrick = NULL; + localBrick = new Brick(4, 4, 1, isisPixelType); + + // Test out of bounds read at the topleft + localBrick->SetBasePosition(-1, -1, 1); + gdalHandler.read(*localBrick); + double *brickDoubleBuff = localBrick->DoubleBuffer(); + std::vector resultVec = {NULL8, NULL8, NULL8, NULL8, + NULL8, NULL8, NULL8, NULL8, + NULL8, NULL8, 0, 1, + NULL8, NULL8, 5, 6}; + for (short i = 0; i < 4 * 4; i++) { + EXPECT_EQ(brickDoubleBuff[i], resultVec[i]); + } + + // Test out of bounds read at the bottom right + localBrick->SetBasePosition(4, 4, 1); + gdalHandler.read(*localBrick); + brickDoubleBuff = localBrick->DoubleBuffer(); + resultVec = { 18, 19, NULL8, NULL8, + 23, 24, NULL8, NULL8, + NULL8, NULL8, NULL8, NULL8, + NULL8, NULL8, NULL8, NULL8}; + for (short i = 0; i < 4 * 4; i++) { + EXPECT_EQ(brickDoubleBuff[i], resultVec[i]); + } + + delete localBrick; + localBrick = NULL; + localBrick = new Brick(7, 7, 1, isisPixelType); + localBrick->SetBasePosition(0, 0, 1); + + // Test filling a larger buffer than is in the image + gdalHandler.read(*localBrick); + brickDoubleBuff = localBrick->DoubleBuffer(); + resultVec = {NULL8, NULL8, NULL8, NULL8, NULL8, NULL8, NULL8, + NULL8, 0, 1, 2, 3, 4, NULL8, + NULL8, 5, 6, 7, 8, 9, NULL8, + NULL8, 10, 11, 12, 13, 14, NULL8, + NULL8, 15, 16, 17, 18, 19, NULL8, + NULL8, 20, 21, 22, 23, 24, NULL8, + NULL8, NULL8, NULL8, NULL8, NULL8, NULL8, NULL8}; + for (short i = 0; i < 7 * 7; i++) { + EXPECT_EQ(brickDoubleBuff[i], resultVec[i]); + } + + // Test reading completely outside of an image + delete localBrick; + localBrick = NULL; + localBrick = new Brick(2, 2, 1, isisPixelType); + + // Read non-NULL8 data + localBrick->SetBasePosition(2, 2, 1); + gdalHandler.read(*localBrick); + + // Read over to ensure brick is updated back to NULL8s + localBrick->SetBasePosition(8, 8, 1); + gdalHandler.read(*localBrick); + brickDoubleBuff = localBrick->DoubleBuffer(); + resultVec = {NULL8, NULL8, + NULL8, NULL8}; + for (short i = 0; i < 2 * 2; i++) { + EXPECT_EQ(brickDoubleBuff[i], resultVec[i]); + } + + delete localBrick; + localBrick = NULL; + localBrick = new Brick(2, 2, 1, isisPixelType); + + // Read non-NULL8 data + localBrick->SetBasePosition(2, 2, 1); + gdalHandler.read(*localBrick); + + // Read over to ensure brick is updated back to NULL8s + localBrick->SetBasePosition(-4, -4, 1); + gdalHandler.read(*localBrick); + brickDoubleBuff = localBrick->DoubleBuffer(); + resultVec = {NULL8, NULL8, + NULL8, NULL8}; + for (short i = 0; i < 2 * 2; i++) { + EXPECT_EQ(brickDoubleBuff[i], resultVec[i]); + } +} diff --git a/isis/tests/TiffFixtures.cpp b/isis/tests/TiffFixtures.cpp new file mode 100644 index 0000000000..c7af4a096a --- /dev/null +++ b/isis/tests/TiffFixtures.cpp @@ -0,0 +1,177 @@ +#include "TiffFixtures.h" + +#include "Brick.h" +#include "PixelType.h" +#include "SpecialPixel.h" + +namespace Isis { + + void ReadWriteTiff::SetUp() { + TempTestingFiles::SetUp(); + + path = tempDir.path() + "/tiny.tiff"; + } + + void ReadWriteTiff::TearDown() { + if (dataset) { + delete dataset; + } + if (localBrick) { + delete localBrick; + } + if (dbuf) { + CPLFree(dbuf); + } + } + + void ReadWriteTiff::createSizedTiff(int samples, int lines, int bands, PixelType pixelType) { + GDALAllRegister(); + GDALDriver* driver = GetGDALDriverManager()->GetDriverByName("GTiff"); + if (driver) { + char **papszOptions = NULL; + dataset = driver->Create(path.toStdString().c_str(), samples, lines, bands, IsisPixelToGdal(pixelType), papszOptions); + if (dataset) { + double noDataValue; + switch (pixelType) { + case UnsignedByte: + noDataValue = (double) NULL1; + break; + case SignedByte: + noDataValue = (double) NULLS1; + break; + case UnsignedWord: + noDataValue = (double) NULLU2; + break; + case SignedWord: + noDataValue = (double) NULL2; + break; + case UnsignedInteger: + noDataValue = (double) NULLUI4; + break; + case SignedInteger: + noDataValue = (double) NULLI4; + break; + case Real: + noDataValue = (double) NULL4; + break; + + default: + noDataValue = NULL8; + break; + } + for (int i = 1; i <= bands; i++) { + GDALRasterBand *band = dataset->GetRasterBand(i); + band->SetScale(1); + band->SetOffset(0); + band->SetNoDataValue(noDataValue); + } + dataset->CreateMaskBand(GMF_ALPHA); + dataset->GetRasterBand(1)->GetMaskBand()->Fill(255); + dataset->Close(); + } + } + } + + void ReadWriteTiff::createTiff(PixelType pixelType, bool write) { + createSizedTiff(6, 1, 1, pixelType); + if (write) { + dataset = GDALDataset::FromHandle(GDALOpen(path.toStdString().c_str(), GA_Update)); + if (pixelType == Double) { + dbuf = (void *)CPLMalloc(sizeof(double) * 6); + + ((double *)dbuf)[0] = HIGH_INSTR_SAT8; + ((double *)dbuf)[1] = HIGH_REPR_SAT8; + ((double *)dbuf)[2] = LOW_INSTR_SAT8; + ((double *)dbuf)[3] = LOW_REPR_SAT8; + ((double *)dbuf)[4] = NULL8; + ((double *)dbuf)[5] = 1000; + } + else if (pixelType == Real) { + dbuf = (void *)CPLMalloc(sizeof(float) * 6); + + ((float *)dbuf)[0] = HIGH_INSTR_SAT4; + ((float *)dbuf)[1] = HIGH_REPR_SAT4; + ((float *)dbuf)[2] = LOW_INSTR_SAT4; + ((float *)dbuf)[3] = LOW_REPR_SAT4; + ((float *)dbuf)[4] = NULL4; + ((float *)dbuf)[5] = 1000; + } + else if (pixelType == SignedInteger) { + dbuf = (void *)CPLMalloc(sizeof(int) * 6); + + ((int *)dbuf)[0] = HIGH_INSTR_SATI4; + ((int *)dbuf)[1] = HIGH_REPR_SATI4; + ((int *)dbuf)[2] = LOW_INSTR_SATI4; + ((int *)dbuf)[3] = LOW_REPR_SATI4; + ((int *)dbuf)[4] = NULLI4; + ((int *)dbuf)[5] = 1000; + } + else if (pixelType == UnsignedInteger) { + dbuf = (void *)CPLMalloc(sizeof(unsigned int) * 6); + + ((unsigned int *)dbuf)[0] = HIGH_INSTR_SATUI4; + ((unsigned int *)dbuf)[1] = HIGH_REPR_SATUI4; + ((unsigned int *)dbuf)[2] = LOW_INSTR_SATUI4; + ((unsigned int *)dbuf)[3] = LOW_REPR_SATUI4; + ((unsigned int *)dbuf)[4] = NULLUI4; + ((unsigned int *)dbuf)[5] = 1000; + } + else if (pixelType == SignedWord) { + dbuf = (void *)CPLMalloc(sizeof(short) * 6); + + ((short *)dbuf)[0] = HIGH_INSTR_SAT2; + ((short *)dbuf)[1] = HIGH_REPR_SAT2; + ((short *)dbuf)[2] = LOW_INSTR_SAT2; + ((short *)dbuf)[3] = LOW_REPR_SAT2; + ((short *)dbuf)[4] = NULL2; + ((short *)dbuf)[5] = 1000; + } + else if (pixelType == UnsignedWord) { + dbuf = (void *)CPLMalloc(sizeof(unsigned short) * 6); + + ((unsigned short *)dbuf)[0] = HIGH_INSTR_SATU2; + ((unsigned short *)dbuf)[1] = HIGH_REPR_SATU2; + ((unsigned short *)dbuf)[2] = LOW_INSTR_SATU2; + ((unsigned short *)dbuf)[3] = LOW_REPR_SATU2; + ((unsigned short *)dbuf)[4] = NULLU2; + ((unsigned short *)dbuf)[5] = 1000; + } + else if (pixelType == SignedByte) { + dbuf = (void *)CPLMalloc(sizeof(char) * 6); + + ((char *)dbuf)[0] = HIGH_INSTR_SATS1; + ((char *)dbuf)[1] = HIGH_REPR_SATS1; + ((char *)dbuf)[2] = LOW_INSTR_SATS1; + ((char *)dbuf)[3] = LOW_REPR_SATS1; + ((char *)dbuf)[4] = NULLS1; + ((char *)dbuf)[5] = 50; + } + else if (pixelType == UnsignedByte) { + dbuf = (void *)CPLMalloc(sizeof(unsigned char) * 6); + + ((unsigned char *)dbuf)[0] = HIGH_INSTR_SAT1; + ((unsigned char *)dbuf)[1] = HIGH_REPR_SAT1; + ((unsigned char *)dbuf)[2] = LOW_INSTR_SAT1; + ((unsigned char *)dbuf)[3] = LOW_REPR_SAT1; + ((unsigned char *)dbuf)[4] = NULL1; + ((unsigned char *)dbuf)[5] = 50; + } + + localBrick = new Brick(6, 1, 1, pixelType); + localBrick->SetBasePosition(1, 1, 1); + + GDALRasterBand *poBand = dataset->GetRasterBand(1); + CPLErr err = poBand->RasterIO(GF_Write, 0, 0, + 6, 1, + dbuf, + 6, 1, + IsisPixelToGdal(pixelType), + 0, 0); + if (err > CE_Failure) { + FAIL() << "Failed to write tiff"; + } + + dataset->Close(); + } + } +} \ No newline at end of file diff --git a/isis/tests/TiffFixtures.h b/isis/tests/TiffFixtures.h new file mode 100644 index 0000000000..9a9819ce4b --- /dev/null +++ b/isis/tests/TiffFixtures.h @@ -0,0 +1,28 @@ +#ifndef TiffFixtures_h +#define TiffFixtures_h + +#include "gtest/gtest.h" + +#include "Brick.h" +#include "PixelType.h" +#include "TempFixtures.h" + +#include "gdal_priv.h" + +namespace Isis { + + class ReadWriteTiff : public TempTestingFiles { + protected: + void *dbuf = NULL; + GDALDataset *dataset = NULL; + Brick *localBrick = NULL; + QString path; + + void SetUp() override; + void TearDown() override; + void createSizedTiff(int samples, int lines, int bands, PixelType pixelType); + void createTiff(PixelType pixelType, bool write=true); + }; +} + +#endif \ No newline at end of file