Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

Isis 3 Programmer Reference
Cube.cpp
1
5
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "Cube.h"
8
9#include <sstream>
10#include <unistd.h>
11
12#include <QDebug>
13#include <QDir>
14#include <QFile>
15#include <QFileInfo>
16#include <QMutex>
17
18#include "Application.h"
19#include "Blob.h"
20#include "Camera.h"
21#include "CameraFactory.h"
22#include "CubeAttribute.h"
23#include "CubeBsqHandler.h"
24#include "CubeTileHandler.h"
25#include "CubeStretch.h"
26#include "IEndian.h"
27#include "FileName.h"
28#include "GdalIoHandler.h"
29#include "History.h"
30#include "ImageHistogram.h"
31#include "ImagePolygon.h"
32#include "IException.h"
33#include "LineManager.h"
34#include "Message.h"
35#include "OriginalLabel.h"
36#include "OriginalXmlLabel.h"
37#include "Preference.h"
38#include "ProgramLauncher.h"
39#include "Projection.h"
40#include "SpecialPixel.h"
41#include "Statistics.h"
42#include "Table.h"
43#include "TProjection.h"
44#include "Longitude.h"
45
46using namespace std;
47
48namespace Isis {
51 construct();
52 }
53
62 Cube::Cube(const FileName &fileName, QString access) {
63 construct();
64 open(fileName.toString(), access);
65 }
66
76 void Cube::fromLabel(const FileName &fileName, Pvl &label, QString access) {
78 create(fileName.expanded());
79
80 PvlObject cubeLabel = label.findObject("IsisCube");
81 for (auto grpIt = cubeLabel.beginGroup(); grpIt!= cubeLabel.endGroup(); grpIt++) {
82 putGroup(*grpIt);
83 }
84
85 close();
86 open(fileName.toString(), access);
87 }
88
99 void Cube::fromIsd(const FileName &fileName, Pvl &label, nlohmann::json &isd, QString access) {
100 fromLabel(fileName, label, access);
101
102 PvlGroup &instGrp = label.findGroup("Instrument", Pvl::Traverse);
103 if (isd.contains("line_scan_rate") && (QString)instGrp["InstrumentId"] == "HRSC") {
104 attachLineScanTableFromIsd(isd);
105 }
106
107 attachSpiceFromIsd(isd);
108
109 close();
110 open(fileName.toString(), access);
111 }
112
123 void Cube::fromIsd(const FileName &fileName, FileName &labelFile, FileName &isdFile, QString access) {
124 std::ifstream isdStream(isdFile.expanded().toStdString());
125 std::ifstream labelStream(labelFile.expanded().toStdString());
126
127 if (isdStream.fail()) {
128 QString msg = QString("failed to open isd stream: %1").arg(isdFile.expanded());
129 throw IException(IException::Io, msg,
130 isdFile.baseName().toStdString().c_str(), 153);
131 }
132
133 if (labelStream.fail()) {
134 QString msg = "failed to open file stream";
135 throw IException(IException::Io, msg,
136 fileName.baseName().toStdString().c_str(), 153);
137 }
138
139 Pvl label;
140 nlohmann::json isd;
141
142 try {
143 labelStream >> label;
144 }
145 catch (std::exception &ex) {
146 QString msg = QString("Failed to open label file, %1, %2").arg(labelFile.expanded()).arg(ex.what());
147 throw IException(IException::Io, msg,
148 fileName.baseName().toStdString().c_str(), 153);
149 }
150
151
152 try {
153 isdStream >> isd;
154 }
155 catch (std::exception &ex) {
156 QString msg = QString("Failed to open ISD file, %1, %2").arg(isdFile.expanded()).arg(ex.what());
157 throw IException(IException::Io, msg,
158 fileName.baseName().toStdString().c_str(), 145);
159 }
160
161 fromIsd(fileName, label, isd, access);
162 reopen("rw");
163 }
164
167 close();
168
169 delete m_mutex;
170 m_mutex = NULL;
171
172 delete m_camera;
173 m_camera = NULL;
174
175
176 delete m_projection;
177 m_projection = NULL;
178
181 }
182
183
189 bool Cube::isOpen() const {
190 bool open = (m_ioHandler != NULL);
191
192 return open;
193 }
194
195
204 bool Cube::isProjected() const {
205 return label()->findObject("IsisCube").hasGroup("Mapping");
206 }
207
208
215 bool Cube::isReadOnly() const {
216 bool readOnly = false;
217
218 if (!isOpen()) {
219 QString msg = "No cube opened";
220 throw IException(IException::Programmer, msg, _FILEINFO_);
221 }
222
223 if (m_labelFile) {
224 if ((m_labelFile->openMode() & QIODevice::ReadWrite) != QIODevice::ReadWrite)
225 readOnly = true;
226 }
227 else if (m_geodataSet) {
228 if (m_geodataSet->GetAccess() == GA_ReadOnly)
229 readOnly = true;
230 }
231 else {
232 QString msg = "No source files open";
233 throw IException(IException::Programmer, msg, _FILEINFO_);
234 }
235
236 return readOnly;
237 }
238
239
247 bool Cube::isReadWrite() const {
248 return !isReadOnly();
249 }
250
251
263
264
272 void Cube::close(bool removeIt) {
273 if (isOpen() && isReadWrite())
274 writeLabels();
275
276 cleanUp(removeIt);
277 }
278
279
288 Cube *Cube::copy(FileName newFile, const CubeAttributeOutput &newFileAttributes) {
289 if (!isOpen()) {
290 throw IException(IException::Unknown,
291 QObject::tr("Cube::copy requires the originating cube to be open"),
292 _FILEINFO_);
293 }
294
295
296 Cube *result = new Cube;
297 result->setLabelsAttached(newFileAttributes.labelAttachment());
298
299 if (result->labelsAttached() != ExternalLabel) {
301 result->setByteOrder(newFileAttributes.byteOrder());
302 result->setFormat(newFileAttributes.fileFormat());
303
304 // if (newFileAttributes.labelAttachment() == DetachedLabel) {
305 // }
306
307 if (newFileAttributes.propagatePixelType()) {
308 result->setPixelType(pixelType());
309 }
310 else {
311 result->setPixelType(newFileAttributes.pixelType());
312 }
313
314 if (newFileAttributes.propagateMinimumMaximum()) {
315 if(result->pixelType() == Isis::Real) {
316 result->setBaseMultiplier(0.0, 1.0);
317 }
318 else if(result->pixelType() >= pixelType()) {
319 result->setBaseMultiplier(base(), multiplier());
320 }
321 else {
322 QString msg =
323 QObject::tr("Cannot reduce the output PixelType for [%1] from [%2] without output "
324 "pixel range").arg(newFile.original()).arg(fileName());
325 throw IException(IException::User, msg, _FILEINFO_);
326 }
327 }
328 else {
329 // Not propagating so either the user entered or the programmer did
330 result->setMinMax(newFileAttributes.minimum(), newFileAttributes.maximum());
331 }
332
333 result->setLabelSize(labelSize(true) + (1024 * 6));
334 }
335 else {
336 if (isReadWrite()) {
337 writeLabels();
338 m_ioHandler->clearCache(true);
339 }
340
341 result->setExternalDnData(fileName());
342 }
343
344 // Allocate the cube
345 result->create(newFile.expanded());
346
347 PvlObject &isisCube = label()->findObject("IsisCube");
348 PvlObject &outIsisCube = result->label()->findObject("IsisCube");
349 for(int i = 0; i < isisCube.groups(); i++) {
350 outIsisCube.addGroup(isisCube.group(i));
351 }
352
353 if (label()->hasObject("NaifKeywords")) {
354 result->label()->addObject(
355 label()->findObject("NaifKeywords"));
356 }
357
358 for (int i = 0; i < m_label->objects(); i++) {
359 PvlObject &obj = m_label->object(i);
360 if (obj.isNamed("Table") || obj.isNamed("Polygon") || obj.isNamed("OriginalLabel") ||
361 obj.isNamed("History")) {
362 Isis::Blob t((QString)obj["Name"], obj.name());
363 read(t);
364 result->write(t);
365 }
366 }
367
368 if (newFileAttributes.labelAttachment() != ExternalLabel) {
370 sampleCount(), 1, 1,
371 pixelType());
373 sampleCount(), 1, 1,
374 result->pixelType());
375
376 input.begin();
377 output.begin();
378
379 while (!input.end()) {
380 read(input);
381 output.Copy(input, false);
382
383 result->write(output);
384
385 input.next();
386 output.next();
387 }
388 }
389
390// Just in case the orig label doesn't work... here's original code:
391// if((p_propagateOriginalLabel) && (InputCubes.size() > 0)) {
392// Isis::Pvl &inlab = *InputCubes[0]->label();
393// for(int i = 0; i < inlab.objects(); i++) {
394// if(inlab.Object(i).isNamed("OriginalLabel")) {
395// Isis::OriginalLabel ol;
396// InputCubes[0]->read(ol);
397// cube->write(ol);
398// }
399// }
400// }
401
402 return result;
403 }
404
405
426 void Cube::create(const QString &imageFileName) {
427 // Already opened?
428 if (isOpen()) {
429 string msg = "You already have a cube opened";
430 throw IException(IException::Programmer, msg, _FILEINFO_);
431 }
432
433 if (m_samples < 1 || m_lines < 1 || m_bands < 1) {
434 QString msg = "Number of samples [" + toString(m_samples) +
435 "], lines [" + toString(m_lines) + "], or bands [" + toString(m_bands) +
436 "] cannot be less than 1";
437 throw IException(IException::Programmer, msg, _FILEINFO_);
438 }
439
440 if (m_pixelType == None) {
441 throw IException(IException::Unknown,
442 QString("Cannot create the cube [%1] with a pixel type set to None")
443 .arg(imageFileName),
444 _FILEINFO_);
445 }
446
448 // Make sure the cube is not going to exceed the maximum size preference
449 BigInt size = (BigInt)m_samples * m_lines *
450 (BigInt)m_bands * (BigInt)SizeOf(m_pixelType);
451
452 size = size / 1024; // kb
453 size = size / 1024; // mb
454 size = size / 1024; // gb
455
456 int maxSizePreference = 0;
457
458 maxSizePreference =
459 Preference::Preferences().findGroup("CubeCustomization")["MaximumSize"];
460
461 if (size > maxSizePreference) {
462 QString msg;
463 msg += "The cube you are attempting to create [" + imageFileName + "] is ["
464 + toString(size) + "GB]. This is larger than the current allowed "
465 "size of [" + toString(maxSizePreference) + "GB]. The cube "
466 "dimensions were (S,L,B) [" + toString(m_samples) + ", " +
467 toString(m_lines) + ", " + toString(m_bands) + "] with [" +
468 toString(SizeOf(m_pixelType)) + "] bytes per pixel. If you still "
469 "wish to create this cube, the maximum value can be changed in your personal "
470 "preference file located in [~/.Isis/IsisPreferences] within the group "
471 "CubeCustomization, keyword MaximumSize. If you do not have an ISISPreference file, "
472 "please refer to the documentation 'Environment and Preference Setup'. Error ";
473 throw IException(IException::User, msg, _FILEINFO_);
474 }
475 }
476
477 // Expand output name
478 FileName imageFile(imageFileName);
479 PvlObject isiscube("IsisCube");
480 PvlObject core("Core");
481
482 // Create the size of the core
483 PvlGroup dims("Dimensions");
484 dims += PvlKeyword("Samples", toString(m_samples));
485 dims += PvlKeyword("Lines", toString(m_lines));
486 dims += PvlKeyword("Bands", toString(m_bands));
487
488 // Create the pixel type
489 PvlGroup ptype("Pixels");
490 ptype += PvlKeyword("Type", PixelTypeName(m_pixelType));
491
492 // And the byte ordering
493 ptype += PvlKeyword("ByteOrder", ByteOrderName(m_byteOrder));
494 ptype += PvlKeyword("Base", toString(m_base));
495 ptype += PvlKeyword("Multiplier", toString(m_multiplier));
496
498 if (format() != Format::GTiff) {
499 imageFile = imageFile.addExtension("cub");
500 }
501 else if (format() == Format::GTiff) {
502 if (imageFile.extension() != "tif" &&
503 imageFile.extension() != "tiff") {
504 imageFile = imageFile.addExtension("tif");
505 }
506 }
507 else {
508 QString msg = "Unknown format type [" + toString(format()) + "]";
509 throw IException(IException::Programmer, msg, _FILEINFO_);
510 }
511
512 m_dataFileName = new FileName(imageFile);
513
514 // See if we have attached or detached labels
516 // StartByte is 1-based (why!!) so we need to do + 1
517 core += PvlKeyword("StartByte", toString(m_labelBytes + 1));
518 m_labelFileName = new FileName(imageFile);
519 m_labelFile = new QFile(m_labelFileName->expanded());
520 }
522 core += PvlKeyword("StartByte", toString(1));
523 core += PvlKeyword("^Core", imageFile.name());
524 m_dataFile = new QFile(realDataFileName().expanded());
525
526 FileName labelFileName(imageFile);
527 labelFileName = labelFileName.setExtension("lbl");
528 m_labelFileName = new FileName(labelFileName);
529 m_labelFile = new QFile(m_labelFileName->expanded());
530 }
531 else {
532 QString msg = "Label type [" + LabelAttachmentName(labelsAttached()) + "] not supported";
533 throw IException(IException::Programmer, msg, _FILEINFO_);
534 }
535 core.addGroup(dims);
536 core.addGroup(ptype);
537 }
539 imageFile = imageFile.addExtension("ecub");
540 if (!m_dataFileName) {
541 if (format() == Bsq || format() == Tile) {
542 imageFile = imageFile.setExtension("cub");
543
544 Pvl dnLabel;
545 PvlObject isiscube("IsisCube");
546 PvlObject dnCore(core);
547 PvlKeyword fileFormat("Format", CubeAttributeOutput::toString(format()));
548
549 dnCore.addKeyword(fileFormat);
550 dnCore.addGroup(dims);
551 dnCore.addGroup(ptype);
552
553 isiscube.addObject(dnCore);
554 dnLabel.addObject(isiscube);
555
556 Cube dnCube;
557 dnCube.fromLabel(imageFile, dnLabel, "rw");
558 dnCube.close();
559 }
560 else if (format() == Format::GTiff) {
561 imageFile = imageFile.setExtension("tif");
562 }
563 m_dataFileName = new FileName(imageFile);
564
565 imageFile = imageFile.setExtension("ecub");
566 FileName labelFileName(imageFile);
567 m_labelFileName = new FileName(labelFileName);
568 m_labelFile = new QFile(m_labelFileName->expanded());
569 }
570
571 core += PvlKeyword("^DnFile", m_dataFileName->name());
572 m_dataFile = new QFile(m_dataFileName->expanded());
573
574 m_labelFileName = new FileName(imageFile);
575 m_labelFile = new QFile(m_labelFileName->expanded());
576 }
577 else {
578 QString msg = "Label type [" + LabelAttachmentName(labelsAttached()) + "] not supported";
579 throw IException(IException::Programmer, msg, _FILEINFO_);
580 }
581
582 isiscube.addObject(core);
583
584 m_label = new Pvl;
585 m_label->addObject(isiscube);
586
587 // Setup storage reserved for the label
588 PvlObject lbl("Label");
589 lbl += PvlKeyword("Bytes", toString(m_labelBytes));
590 m_label->addObject(lbl);
591
592 const PvlGroup &pref =
593 Preference::Preferences().findGroup("CubeCustomization");
594 bool overwrite = pref["Overwrite"][0].toUpper() == "ALLOW";
595 if (!overwrite && m_labelFile->exists() && m_labelFile->size()) {
596 QString msg = "Cube file [" + m_labelFileName->original() + "] exists, " +
597 "user preference does not allow overwrite";
598 throw IException(IException::User, msg, _FILEINFO_);
599 }
600
601 if (!m_labelFile->open(QIODevice::Truncate | QIODevice::ReadWrite)) {
602 QString msg = "Failed to create [" + m_labelFile->fileName() + "]. ";
603 msg += "Verify the output path exists and you have permission to write to the path.";
604 cleanUp(false);
605 throw IException(IException::Io, msg, _FILEINFO_);
606 }
607
608 if (m_dataFile) {
609 if (labelsAttached() != ExternalLabel && !m_dataFile->open(QIODevice::Truncate | QIODevice::ReadWrite)) {
610 QString msg = "Failed to create [" + m_dataFile->fileName() + "]. ";
611 msg += "Verify the output path exists and you have permission to write to the path.";
612 cleanUp(false);
613 throw IException(IException::Io, msg, _FILEINFO_);
614 }
615 else if (labelsAttached() == ExternalLabel && !m_dataFile->open(QIODevice::ReadWrite)) {
616 QString msg = "Failed to open [" + m_dataFile->fileName() + "] for reading. ";
617 msg += "Verify the output path exists and you have permission to read from the path.";
618 cleanUp(false);
619 throw IException(IException::Io, msg, _FILEINFO_);
620 }
621 }
622
623 bool dataAlreadyOnDisk = (labelsAttached() == ExternalLabel);
624
625 if (format() != GTiff) {
626 if (format() == Bsq) {
628 dataAlreadyOnDisk);
629 }
630 else if(format() == Tile) {
632 dataAlreadyOnDisk);
633 }
634
635 if (!dataAlreadyOnDisk) {
636 m_ioHandler->updateLabels(*m_label);
637 }
638 }
639 else if(format() == Format::GTiff) {
640 char **papszOptions = NULL;
641 // Make sure the cube is not going to exceed the maximum size preference
642 BigInt size = (BigInt)m_samples * m_lines *
643 (BigInt)m_bands * (BigInt)SizeOf(m_pixelType);
644
645 size = size / 1024; // kb
646 size = size / 1024; // mb
647 size = size / 1024; // gb
648
649 papszOptions = CSLSetNameValue(papszOptions, "PREDICTOR", "2");
650 papszOptions = CSLSetNameValue(papszOptions, "NUM_THREADS", "4");
651 if (size < 4) {
652 papszOptions = CSLSetNameValue(papszOptions, "COMPRESS", "DEFLATE");
653 }
654 else {
655 papszOptions = CSLSetNameValue(papszOptions, "COMPRESS", "LZW");
656 papszOptions = CSLSetNameValue(papszOptions, "BIGTIFF", "YES");
657 }
658
659 QString datafile = m_dataFileName->expanded();
660 QString format = "GTiff";
661 createGdal(datafile, format, papszOptions);
662
663 m_geodataSet = GDALDataset::FromHandle(GDALOpen(datafile.toStdString().c_str(), GA_Update));
664 if (!m_geodataSet) {
665 QString msg = "Failed opening GDALDataset from [" + m_dataFileName->name() + "]";
666 cleanUp(false);
667 throw IException(IException::Programmer, msg, _FILEINFO_);
668 }
669
670 m_ioHandler = new GdalIoHandler(gdalDataset(), m_virtualBandList, IsisPixelToGdal(pixelType()));
671 m_ioHandler->updateLabels(*m_label);
672 }
673 else {
674 QString msg = "Format [" + toString(format()) + "] not supported";
675 throw IException(IException::Programmer, msg, _FILEINFO_);
676 }
677
678 // Write the labels
679 writeLabels();
680 }
681
682 void Cube::createGdal(QString &dataFileName, QString &driverName, char **papszOptions) {
683 GDALDriver* driver = GetGDALDriverManager()->GetDriverByName(driverName.toStdString().c_str());
684 if (driver) {
685 double noDataValue;
686 switch (pixelType()) {
687 case UnsignedByte:
688 noDataValue = (double) NULL1;
689 break;
690 case SignedByte:
691 noDataValue = (double) NULLS1;
692 break;
693 case UnsignedWord:
694 noDataValue = (double) NULLU2;
695 break;
696 case SignedWord:
697 noDataValue = (double) NULL2;
698 break;
699 case UnsignedInteger:
700 noDataValue = (double) NULLUI4;
701 break;
702 case SignedInteger:
703 noDataValue = (double) NULLI4;
704 break;
705 case Real:
706 noDataValue = (double) NULL4;
707 break;
708
709 default:
710 noDataValue = NULL8;
711 break;
712 }
713 GDALDataset *dataset = driver->Create(dataFileName.toStdString().c_str(), sampleCount(), lineCount(), bandCount(), IsisPixelToGdal(pixelType()), papszOptions);
714 if (!dataset) {
715 QString msg = "Could not create file [" + dataFileName + "] from [" + driverName + "] Driver.";
716 throw IException(IException::Io, msg, _FILEINFO_);
717 }
718 for (int i = 1; i <= bandCount(); i++) {
719 GDALRasterBand *band = dataset->GetRasterBand(i);
720 band->SetScale(multiplier());
721 band->SetOffset(base());
722 band->SetNoDataValue(noDataValue);
723 band->Fill(noDataValue);
724 band->CreateMaskBand(GMF_ALPHA);
725 band->GetMaskBand()->Fill(255);
726 }
727 GDALClose(dataset);
728 CSLDestroy( papszOptions );
729 }
730 else {
731 QString msg = "No Driver found for name [" + driverName + "]";
732 throw IException(IException::Io, msg, _FILEINFO_);
733 }
734 }
735
736
759 const QString &cubeFileName, const CubeAttributeOutput &att) {
760
761 setByteOrder(att.byteOrder());
762 setFormat(att.fileFormat());
763 setLabelsAttached(att.labelAttachment());
764 if (!att.propagatePixelType())
765 setPixelType(att.pixelType());
766 setMinMax(att.minimum(), att.maximum());
767
768 // Allocate the cube
769 create(cubeFileName);
770 }
771
772 void Cube::checkAccess(QString access) {
773 if (!(access == "r" || access == "rw")) {
774 QString msg = "Unknown value for access [" + access + "]. Expected 'r' "
775 " or 'rw'";
776 cleanUp(false);
777 throw IException(IException::Programmer, msg, _FILEINFO_);
778 }
779 }
780
781
792 void Cube::open(const QString &cubeFileName, QString access) {
793
794 // Already opened?
795 if (isOpen()) {
796 string msg = "You already have a cube opened";
797 throw IException(IException::Programmer, msg, _FILEINFO_);
798 }
799
800 checkAccess(access);
801
802 try {
803 Isis::CubeAttributeInput att(cubeFileName);
804 if(att.bands().size() != 0) {
805 vector<QString> bands = att.bands();
806 setVirtualBands(bands);
807 }
808 } catch(IException& e) {
809 // Either the cube is an output cube that has already been opened or
810 // there is an exception parsing and adding an attribute
811 }
812
813 QString msg = "Failed to open [" + cubeFileName + "]";
814 IException exceptions(IException::Io, msg, _FILEINFO_);
815
816 GDALDriverH hDriver = GDALIdentifyDriver(FileName(cubeFileName).expanded().toStdString().c_str(), nullptr);
817 bool openWithGdal = false;
818 if (hDriver != nullptr) {
819 GDALDriver* poDriver = (GDALDriver*)hDriver;
820 QString driverDescription = poDriver->GetDescription();
821 openWithGdal = (!driverDescription.contains("ISIS"));
822 }
823
824 try{
825 if (openWithGdal) {
826 openGdal(cubeFileName, access);
827 }
828 else {
829 openCube(cubeFileName, access);
830 }
831 }
832 catch(IException &e) {
833 cleanUp(false);
834 exceptions.append(e);
835 throw exceptions;
836 }
837
839 }
840
850 void Cube::openCube(const QString &cubeFileName, QString access) {
851 checkAccess(access);
852
853 initLabelFromFile(cubeFileName, (access == "rw"));
854
855 // Figure out the name of the data file
856 try {
857 PvlObject &core = m_label->findObject("IsisCube").findObject("Core");
858 // Detached labels
859 if (core.hasKeyword("^Core")) {
860 FileName temp(core["^Core"][0]);
861
862 if (!temp.originalPath().startsWith("/")) {
863 m_dataFileName = new FileName(m_labelFileName->path() + "/" + temp.original());
864 }
865 else {
866 m_dataFileName = new FileName(temp);
867 }
869
870 FileName realDataFile = realDataFileName();
871 delete m_dataFileName;
872 m_dataFileName = new FileName(realDataFile.expanded());
873
874 m_dataFile = new QFile(m_dataFileName->expanded());
875 }
876 // External cube files (ecub), ecub contains all labels and SPICE blobs, history
877 else if (core.hasKeyword("^DnFile")) {
878 FileName dataFileName(core["^DnFile"][0]);
879
880 if (dataFileName.originalPath() == ".") {
881 m_dataFileName = new FileName(m_labelFileName->path() + "/" + dataFileName.name());
882 }
883 else {
884 m_dataFileName = new FileName(dataFileName);
885 }
887
888 FileName realDataFile = realDataFileName();
889 delete m_dataFileName;
890 m_dataFileName = new FileName(realDataFile.expanded());
891
892 m_dataFile = new QFile(m_dataFileName->expanded());
893 }
894 // Typical cube containing labels, SPICE, history and dn data
895 else {
897 m_dataFileName = new FileName(*m_labelFileName);
898 }
899 }
900 catch (IException &e) {
901 cleanUp(false);
902 throw e;
903 }
904
905 QIODevice::OpenModeFlag qtAccess = QIODevice::ReadOnly;
906 GDALAccess eAccess = GA_ReadOnly;
907 if (access == "rw") {
908 qtAccess = QIODevice::ReadWrite;
909 eAccess = GA_Update;
910 }
911
912 if (!m_labelFile->open(qtAccess)) {
913 QString msg = "Failed to open [" + m_labelFile->fileName() + "] with "
914 "access [" + access + "]";
915 cleanUp(false);
916 throw IException(IException::Io, msg, _FILEINFO_);
917 }
918
919 if (m_dataFile) {
920 if (!m_dataFile->open(qtAccess)) {
921 QString msg = "Failed to open [" + m_dataFile->fileName() + "] with "
922 "access [" + access + "]";
923 cleanUp(false);
924 throw IException(IException::Io, msg, _FILEINFO_);
925 }
926 }
927
929
930 // Determine the number of bytes in the label
932 m_labelBytes = m_label->findObject("Label")["Bytes"];
933 }
934 else {
935 m_labelBytes = labelSize(true);
936 }
937
938 // Now examine the format to see which type of handler to create
939 if (m_format == Bsq) {
941 realDataFileLabel(), true);
942 }
943 else if (m_format == Tile) {
945 realDataFileLabel(), true);
946 }
947 else if (m_format == GTiff) {
948 if (m_dataFile) {
949 m_dataFile->close();
950 }
951 m_geodataSet = GDALDataset::FromHandle(GDALOpen(m_dataFileName->expanded().toStdString().c_str(), eAccess));
952 if (!m_geodataSet) {
953 QString msg = "Opening GDALDataset from [" + m_dataFileName->name() + "] failed with access [" + QString::number(eAccess) +"]";
954 cleanUp(false);
955 throw IException(IException::Io, msg, _FILEINFO_);
956 }
957 m_ioHandler = new GdalIoHandler(gdalDataset(), m_virtualBandList, IsisPixelToGdal(pixelType()));
958 }
959 else {
960 QString msg = "Unknown format [" + toString(m_format) + "]";
961 throw IException(IException::Programmer, msg, _FILEINFO_);
962 }
963 }
964
974 void Cube::openGdal(const QString &cubeFileName, QString access) {
975 checkAccess(access);
976
977 m_labelFileName = new FileName(cubeFileName);
978
980 m_dataFileName = new FileName(*m_labelFileName);
981
982 initCoreFromGdal(m_labelFileName->expanded());
983
984 m_label = new Pvl(m_dataFileName->expanded());
985
986 GDALAccess eAccess = GA_ReadOnly;
987 if (access == "rw") {
988 eAccess = GA_Update;
989 }
990 m_geodataSet = GDALDataset::FromHandle(GDALOpen(m_dataFileName->expanded().toStdString().c_str(), eAccess));
991 if (!m_geodataSet) {
992 QString msg = "Opening GDALDataset from [" + m_dataFileName->name() + "] failed with access [" + QString::number(eAccess) +"]";
993 cleanUp(false);
994 throw IException(IException::Io, msg, _FILEINFO_);
995 }
996
997 m_ioHandler = new GdalIoHandler(gdalDataset(), m_virtualBandList, IsisPixelToGdal(pixelType()));
998 m_ioHandler->updateLabels(*m_label);
999 }
1000
1001
1010 void Cube::reopen(QString access) {
1011 if (!isOpen()) {
1012 QString msg = "Cube has not been opened yet. The filename to re-open is "
1013 "unknown";
1014 throw IException(IException::Programmer, msg, _FILEINFO_);
1015 }
1016
1017 // Preserve filename and virtual bands when re-opening
1018 FileName filename = *m_labelFileName;
1019 QList<int> virtualBandList;
1020
1022 virtualBandList = *m_virtualBandList;
1023
1024 close();
1025 open(filename.expanded(), access);
1026
1027 if (virtualBandList.size()) {
1029 *m_virtualBandList = virtualBandList;
1030 else
1031 m_virtualBandList = new QList<int>(virtualBandList);
1032 }
1033 }
1034
1035
1043 void Cube::read(Blob &blob, const std::vector<PvlKeyword> keywords) const {
1044 if (!isOpen()) {
1045 string msg = "The cube is not opened so you can't read a blob from it";
1046 throw IException(IException::Programmer, msg, _FILEINFO_);
1047 }
1048
1049 QMutexLocker locker(m_mutex);
1050
1051 FileName cubeFile = *m_labelFileName;
1052 if (m_tempCube)
1053 cubeFile = *m_tempCube;
1054
1055 QString blobKey = blob.Key();
1056 if (m_blobMap.contains(blobKey)) {
1057 blob = m_blobMap[blobKey];
1058 }
1059 else {
1060 if (format() == Format::GTiff && labelsAttached() == LabelAttachment::AttachedLabel) {
1061 blob.ReadGdal(gdalDataset());
1062 }
1063 else {
1064 QMutexLocker locker2(m_ioHandler->dataFileMutex());
1065 blob.Read(cubeFile.toString(), *label(), keywords);
1066 }
1067 }
1068 }
1069
1070
1077 void Cube::read(Buffer &bufferToFill) const {
1078 if (!isOpen()) {
1079 string msg = "Try opening a file before you read it";
1080 throw IException(IException::Programmer, msg, _FILEINFO_);
1081 }
1082
1083 QMutexLocker locker(m_mutex);
1084 m_ioHandler->read(bufferToFill);
1085 }
1086
1087
1095 History Cube::readHistory(const QString &name) const {
1096 Blob historyBlob(name, "History");
1097 try {
1098 // read history from cube, if it exists.
1099 read(historyBlob);
1100 }
1101 catch (IException &) {
1102 // if the history does not exist in the cube, this function creates it.
1103 }
1104 History history(historyBlob);
1105 return history;
1106 }
1107
1108
1115 Blob footprintBlob("Footprint", "Polygon");
1116 try {
1117 // read history from cube, if it exists.
1118 read(footprintBlob);
1119 }
1120 catch (IException &e) {
1121 QString msg = "Footprintinit must be run prior to reading the footprint";
1122 msg += " with POLYGON=TRUE for cube [" + fileName() + "]";
1123 throw IException(e, IException::User, msg, _FILEINFO_);
1124 }
1125 ImagePolygon footprint(footprintBlob);
1126 return footprint;
1127 }
1128
1129
1137 OriginalLabel Cube::readOriginalLabel(const QString &name) const {
1138 Blob origLabelBlob(name, "OriginalLabel");
1139 try {
1140 read(origLabelBlob);
1141 }
1142 catch (IException &e){
1143 QString msg = "Unable to locate OriginalLabel in " + fileName();
1144 throw IException(e, IException::User, msg, _FILEINFO_);
1145 }
1146 OriginalLabel origLabel(origLabelBlob);
1147 return origLabel;
1148 }
1149
1150
1160 CubeStretch Cube::readCubeStretch(QString name, const std::vector<PvlKeyword> keywords) const {
1161 Blob stretchBlob(name, "Stretch");
1162 try {
1163 read(stretchBlob, keywords);
1164 }
1165 catch (IException &e){
1166 QString msg = "Unable to locate Stretch information in " + fileName();
1167 throw IException(e, IException::User, msg, _FILEINFO_);
1168 }
1169 CubeStretch cubeStretch(stretchBlob);
1170 return stretchBlob;
1171 }
1172
1173
1180 Blob origXmlLabelBlob("IsisCube", "OriginalXmlLabel");
1181 try {
1182 read(origXmlLabelBlob);
1183 }
1184 catch (IException &e){
1185 QString msg = "Unable to locate OriginalXmlLabel in " + fileName();
1186 throw IException(e, IException::User, msg, _FILEINFO_);
1187 }
1188 OriginalXmlLabel origXmlLabel(origXmlLabelBlob);
1189 return origXmlLabel;
1190 }
1191
1192
1200 Table Cube::readTable(const QString &name) {
1201 Blob tableBlob(name, "Table");
1202 try {
1203 read(tableBlob);
1204 }
1205 catch (IException &e) {
1206 QString msg = "Failed to read table [" + name + "] from cube [" + fileName() + "].";
1207 throw IException(e, IException::Programmer, msg, _FILEINFO_);
1208 }
1209 return Table(tableBlob);
1210 }
1211
1212
1219 void Cube::write(Blob &blob, bool overwrite) {
1220 if (!isOpen()) {
1221 string msg = "The cube is not opened so you can't write a blob to it";
1222 throw IException(IException::Programmer, msg, _FILEINFO_);
1223 }
1224
1225 if (isReadOnly()) {
1226 string msg = "The cube must be opened in read/write mode, not readOnly";
1227 throw IException(IException::Programmer, msg, _FILEINFO_);
1228 }
1229
1230 QMutexLocker locker(m_mutex);
1231
1232 Pvl &cubeLabel = *label();
1233 PvlObject &blobLabel = blob.Label();
1235 FileName blobFileName = fileName();
1236 blobFileName = blobFileName.removeExtension();
1237 blobFileName = blobFileName.addExtension(blob.Type());
1238 blobFileName = blobFileName.addExtension(blob.Name());
1239 blobLabel += PvlKeyword("^" + blob.Type(), blobFileName.name());
1240 }
1241
1242 // See if the blob is already in the file
1243 bool found = false;
1244 if (overwrite) {
1245 for (int i = 0; i < cubeLabel.objects(); i++) {
1246 if (cubeLabel.object(i).name() == blobLabel.name()) {
1247 PvlObject &obj = cubeLabel.object(i);
1248 if ((QString)obj["Name"] == (QString)blobLabel["Name"]) {
1249 found = true;
1250 blobLabel["StartByte"] = obj["StartByte"];
1251 blobLabel["Bytes"] = obj["Bytes"];
1252 }
1253 }
1254 }
1255 }
1256 if (!found) {
1257 blobLabel["StartByte"] = toString(0);
1258 cubeLabel.addObject(blobLabel);
1259 }
1260 m_blobMap[blob.Key()] = blob;
1261 m_blobQueue.push_back(blob.Key());
1262 }
1263
1264
1272 Blob labelBlob = lab.toBlob();
1273 write(labelBlob);
1274 }
1275
1276
1284 Blob labelBlob = lab.toBlob();
1285 write(labelBlob);
1286 }
1287
1288
1297 void Cube::write(const Table &table) {
1298 Blob tableBlob = table.toBlob();
1299 write(tableBlob);
1300 }
1301
1302
1311 void Cube::write(const CubeStretch &cubeStretch) {
1312 Blob cubeStretchBlob = cubeStretch.toBlob();
1313 write(cubeStretchBlob);
1314 }
1315
1316
1327 void Cube::write(History &history, const QString &name) {
1328 Blob histBlob = history.toBlob(name);
1329 write(histBlob);
1330 }
1331
1332
1339 void Cube::write(const ImagePolygon &polygon) {
1340 Blob polyBlob = polygon.toBlob();
1341 write(polyBlob);
1342 }
1343
1344
1351 void Cube::write(Buffer &bufferToWrite) {
1352 if (!isOpen()) {
1353 string msg = "Tried to write to a cube before opening/creating it";
1354 throw IException(IException::Programmer, msg, _FILEINFO_);
1355 }
1356
1357 if (isReadOnly()) {
1358 QString msg = "Cannot write to the cube [" + (QString)QFileInfo(fileName()).fileName() +
1359 "] because it is opened read-only";
1360 throw IException(IException::Programmer, msg, _FILEINFO_);
1361 }
1362
1363 // if (labelsAttached() == ExternalLabel) {
1364 // QString msg = "The cube [" + QFileInfo(fileName()).fileName() +
1365 // "] does not support storing DN data because it is using an external file for DNs";
1366 // throw IException(IException::Unknown, msg, _FILEINFO_);
1367 // }
1368
1369 QMutexLocker locker(m_mutex);
1370 m_ioHandler->write(bufferToWrite);
1371 }
1372
1373
1384 void Cube::setBaseMultiplier(double base, double mult) {
1385 openCheck();
1386 m_base = base;
1387 m_multiplier = mult;
1388 }
1389
1390
1401 void Cube::setMinMax(double min, double max) {
1402 openCheck();
1403
1404 m_base = 0.0;
1405 m_multiplier = 1.0;
1406
1407 double x1 = 0.0, x2 = 0.0;
1408 if (m_pixelType == UnsignedByte) {
1409 x1 = VALID_MIN1;
1410 x2 = VALID_MAX1;
1411 m_multiplier = (max - min) / (x2 - x1);
1412 m_base = min - m_multiplier * x1;
1413 }
1414 else if (m_pixelType == SignedWord) {
1415 x1 = VALID_MIN2;
1416 x2 = VALID_MAX2;
1417 m_multiplier = (max - min) / (x2 - x1);
1418 m_base = min - m_multiplier * x1;
1419 }
1420 else if (m_pixelType == UnsignedWord) {
1421 x1 = VALID_MINU2;
1422 x2 = VALID_MAXU2;
1423 m_multiplier = (max - min) / (x2 - x1);
1424 m_base = min - m_multiplier * x1;
1425 }
1426 }
1427
1428
1436 openCheck();
1438 }
1439
1440
1450 void Cube::setDimensions(int ns, int nl, int nb) {
1451 openCheck();
1452 if ((ns < 1) || (nl < 1) || (nb < 1)) {
1453 string msg = "SetDimensions: Invalid number of sample, lines or bands";
1454 throw IException(IException::Programmer, msg, _FILEINFO_);
1455 }
1456 m_samples = ns;
1457 m_lines = nl;
1458 m_bands = nb;
1459 }
1460
1461
1467 void Cube::setExternalDnData(FileName cubeFileWithDnData) {
1468 try {
1469 initLabelFromFile(cubeFileWithDnData, false);
1471
1472 delete m_label;
1473 m_label = NULL;
1474 }
1475 catch (IException &e) {
1476 delete m_label;
1477 m_label = NULL;
1478 throw e;
1479 }
1480
1482 m_dataFileName = new FileName(cubeFileWithDnData);
1483 FileName realDataFile = realDataFileName();
1484 delete m_dataFileName;
1485 m_dataFileName = new FileName(realDataFile.expanded());
1486
1487 delete m_labelFile;
1488 m_labelFile = NULL;
1489
1490 delete m_labelFileName;
1491 m_labelFileName = NULL;
1492 }
1493
1494
1503 openCheck();
1504 m_format = format;
1505 }
1506
1507
1515 openCheck();
1516 m_attached = attach;
1517 }
1518
1519
1527 void Cube::setLabelSize(int labelBytes) {
1528 openCheck();
1529 m_labelBytes = labelBytes;
1530 }
1531
1532
1541 openCheck();
1543 }
1544
1545
1558 openCheck();
1560 m_virtualBandList->clear();
1561 else
1563
1564 if (vbands.size() > 0) {
1565 QListIterator<QString> it(vbands);
1566 while (it.hasNext()) {
1567 m_virtualBandList->append(toInt(it.next()));
1568 }
1569 }
1570 else {
1571 delete m_virtualBandList;
1572 m_virtualBandList = NULL;
1573 }
1574
1575 if (m_ioHandler) {
1576 m_ioHandler->setVirtualBands(m_virtualBandList);
1577 }
1578 }
1579
1580
1587 void Cube::setVirtualBands(const std::vector<QString> &vbands) {
1588 QList<QString> realVBands;
1589
1590 for(unsigned int i = 0; i < vbands.size(); i++)
1591 realVBands << vbands[i];
1592
1593 setVirtualBands(realVBands);
1594 }
1595
1596
1602 void Cube::relocateDnData(FileName dnDataFile) {
1603 if (!isOpen()) {
1604 throw IException(IException::Unknown,
1605 QString("Cannot relocate the DN data to [%1] for an external cube label "
1606 "file which is not open.")
1607 .arg(dnDataFile.original()),
1608 _FILEINFO_);
1609 }
1610
1611
1612 if (labelsAttached() != ExternalLabel) {
1613 throw IException(IException::Unknown,
1614 QString("The cube [%1] stores DN data. It cannot be relocated to [%2] - "
1615 "this is only supported for external cube label files.")
1616 .arg(m_labelFileName->original()).arg(dnDataFile.original()),
1617 _FILEINFO_);
1618 }
1619
1620 m_label->findObject("IsisCube").findObject("Core").findKeyword("^DnFile")[0] =
1621 dnDataFile.original();
1622 reopen(m_labelFile->isWritable()? "rw" : "r");
1623 }
1624
1625
1626// void Cube::relocateDnData(FileName externalLabelFile, FileName dnDataFile) {
1627// try {
1628// Pvl externalLabelData(externalLabelFile.expanded());
1629// externalLabelData.FindObject("IsisCube").FindObject("Core").FindKeyword("^DnFile")[0] =
1630// dnDataFile.original();
1631// }
1632// catch (IException &e) {
1633// throw IException(e, IException::Io,
1634// QString("File [%1] does not appear to be an external cube label file")
1635// .arg(externalLabelFile.original().ToQt()),
1636// _FILEINFO_);
1637// }
1638// }
1639
1640
1646 int Cube::bandCount() const {
1647 int numBands = m_bands;
1649 numBands = m_virtualBandList->size();
1650 return numBands;
1651 }
1652
1653
1663 double Cube::base() const {
1664 return m_base;
1665 }
1666
1667
1675 ByteOrder Cube::byteOrder() const {
1676 return m_byteOrder;
1677 }
1678
1679
1688 if (m_camera == NULL && isOpen()) {
1690 }
1691 return m_camera;
1692 }
1693
1694
1695 void Cube::attachSpiceFromIsd(nlohmann::json isd) {
1696 PvlGroup currentKernels = this->group("Kernels");
1697 PvlKeyword lkKeyword = currentKernels["LeapSecond"];
1698 PvlKeyword pckKeyword = currentKernels["TargetAttitudeShape"];
1699 PvlKeyword targetSpkKeyword = currentKernels["TargetPosition"];
1700 PvlKeyword ckKeyword = currentKernels["InstrumentPointing"];
1701 PvlKeyword ikKeyword = currentKernels["Instrument"];
1702 PvlKeyword sclkKeyword = currentKernels["SpacecraftClock"];
1703 PvlKeyword spkKeyword = currentKernels["InstrumentPosition"];
1704 PvlKeyword iakKeyword = currentKernels["InstrumentAddendum"];
1705 PvlKeyword demKeyword = currentKernels["ShapeModel"];
1706
1707 Spice spice(*this, *this->label(), isd);
1708 Table ckTable = spice.instrumentRotation()->Cache("InstrumentPointing");
1709 ckTable.Label() += PvlKeyword("Kernels");
1710
1711 for (int i = 0; i < ckKeyword.size(); i++)
1712 ckTable.Label()["Kernels"].addValue(ckKeyword[i]);
1713
1714 this->write(ckTable);
1715
1716 Table spkTable = spice.instrumentPosition()->Cache("InstrumentPosition");
1717 spkTable.Label() += PvlKeyword("Kernels");
1718 for (int i = 0; i < spkKeyword.size(); i++)
1719 spkTable.Label()["Kernels"].addValue(spkKeyword[i]);
1720
1721 this->write(spkTable);
1722
1723 Table bodyTable = spice.bodyRotation()->Cache("BodyRotation");
1724 bodyTable.Label() += PvlKeyword("Kernels");
1725 for (int i = 0; i < targetSpkKeyword.size(); i++)
1726 bodyTable.Label()["Kernels"].addValue(targetSpkKeyword[i]);
1727
1728 for (int i = 0; i < pckKeyword.size(); i++)
1729 bodyTable.Label()["Kernels"].addValue(pckKeyword[i]);
1730
1731 bodyTable.Label() += PvlKeyword("SolarLongitude",
1732 toString(spice.solarLongitude().degrees()));
1733 this->write(bodyTable);
1734
1735 Table sunTable = spice.sunPosition()->Cache("SunPosition");
1736 sunTable.Label() += PvlKeyword("Kernels");
1737 for (int i = 0; i < targetSpkKeyword.size(); i++)
1738 sunTable.Label()["Kernels"].addValue(targetSpkKeyword[i]);
1739
1740 this->write(sunTable);
1741
1742 // Save original kernels in keyword before changing to Table
1743 if (ckKeyword[0] != "Table") {
1744 currentKernels["InstrumentPointing"] = "Table";
1745 for (int i = 0; i < ckKeyword.size(); i++)
1746 currentKernels["InstrumentPointing"].addValue(ckKeyword[i]);
1747 }
1748
1749 if (spkKeyword[0] != "Table") {
1750 currentKernels["InstrumentPosition"] = "Table";
1751 for (int i = 0; i < spkKeyword.size(); i++)
1752 currentKernels["InstrumentPosition"].addValue(spkKeyword[i]);
1753 }
1754
1755 if (targetSpkKeyword[0] != "Table") {
1756 currentKernels["TargetPosition"] = "Table";
1757 for (int i = 0; i < targetSpkKeyword.size(); i++)
1758 currentKernels["TargetPosition"].addValue(targetSpkKeyword[i]);
1759 }
1760
1761 putGroup(currentKernels);
1762
1763 Pvl *label = this->label();
1764 int i = 0;
1765 while (i < label->objects()) {
1766 PvlObject currObj = label->object(i);
1767 if (currObj.isNamed("NaifKeywords")) {
1768 label->deleteObject(i);
1769 }
1770 else {
1771 i ++;
1772 }
1773 }
1774
1775 *(this->label()) += spice.getStoredNaifKeywords();
1776
1777 // Access the camera here while all of the kernels are still loaded.
1778 // This needs to be done for some cameras that need loaded spice data
1779 // to actually create the camera model. (KaguyaTC for example)
1780 this->camera();
1781 }
1782
1783 void Cube::attachLineScanTableFromIsd(nlohmann::json isd) {
1784 TableField ephTimeField("EphemerisTime", TableField::Double);
1785 TableField expTimeField("ExposureTime", TableField::Double);
1786 TableField lineStartField("LineStart", TableField::Integer);
1787
1788 TableRecord timesRecord;
1789 timesRecord += ephTimeField;
1790 timesRecord += expTimeField;
1791 timesRecord += lineStartField;
1792
1793 Table timesTable("LineScanTimes", timesRecord);
1794 for (size_t i = 0; i < isd["line_scan_rate"].size(); ++i) {
1795 timesRecord[0] = isd["line_scan_rate"][i][1].get<double>() + isd["center_ephemeris_time"].get<double>();
1796 timesRecord[1] = isd["line_scan_rate"][i][2].get<double>();
1797 timesRecord[2] = (int)(isd["line_scan_rate"][i][0].get<double>() + 0.5);
1798 timesTable += timesRecord;
1799 }
1800 this->write(timesTable);
1801 }
1802
1803
1811 if (!isOpen()) {
1812 throw IException(IException::Unknown,
1813 "An external cube label file must be opened in order to use "
1814 "Cube::getExternalCubeFileName",
1815 _FILEINFO_);
1816 }
1817
1818 if (labelsAttached() == ExternalLabel) {
1819 throw IException(IException::Unknown,
1820 "Cube::getExternalCubeFileName can only be called on an external cube label "
1821 "file",
1822 _FILEINFO_);
1823 }
1824
1825
1826 PvlObject &core = m_label->findObject("IsisCube").findObject("Core");
1827 return core["^DnFile"][0];
1828 }
1829
1830
1837 QString Cube::fileName() const {
1838 if (isOpen())
1839 return m_labelFileName->expanded();
1840 else
1841 return "";
1842 }
1843
1844
1850 return m_format;
1851 }
1852
1853
1873 Histogram *Cube::histogram(const int &band, QString msg) {
1874 return histogram(band, ValidMinimum, ValidMaximum, msg);
1875 }
1876
1877
1903 Histogram *Cube::histogram(const int &band, const double &validMin,
1904 const double &validMax, QString msg) {
1905 // Make sure cube is open
1906 if ( !isOpen() ) {
1907 QString msg = "Cannot create histogram object for an unopened cube";
1908 throw IException(IException::Programmer, msg, _FILEINFO_);
1909 }
1910
1911 // Make sure band is valid
1912 if ((band < 0) || (band > bandCount())) {
1913 QString msg = "Invalid band in [CubeInfo::Histogram]";
1914 throw IException(IException::Programmer, msg, _FILEINFO_);
1915 }
1916
1917 int bandStart = band;
1918 int bandStop = band;
1919 int maxSteps = lineCount();
1920 if (band == 0) {
1921 bandStart = 1;
1922 bandStop = bandCount();
1923 maxSteps = lineCount() * bandCount();
1924 }
1925
1926 Progress progress;
1927 Histogram *hist = new ImageHistogram(*this, band, &progress);
1928 LineManager line(*this);
1929
1930 // This range is for throwing out data; the default parameters are OK always
1931 //hist->SetValidRange(validMin, validMax);
1932
1933 // We now need to know the binning range - ValidMinimum/Maximum are no longer
1934 // acceptable, default to the bin range start/end.
1935 double binMin = validMin;
1936 double binMax = validMax;
1937 if (binMin == ValidMinimum) {
1938 binMin = hist->BinRangeStart();
1939 }
1940
1941 if (binMax == ValidMaximum) {
1942 binMax = hist->BinRangeEnd();
1943 }
1944
1945 //hist->SetBinRange(binMin, binMax);
1946 hist->SetValidRange(binMin,binMax);
1947
1948 // Loop and get the histogram
1949 progress.SetText(msg);
1950 progress.SetMaximumSteps(maxSteps);
1951 progress.CheckStatus();
1952
1953 for(int useBand = bandStart ; useBand <= bandStop ; useBand++) {
1954 for(int i = 1; i <= lineCount(); i++) {
1955 line.SetLine(i, useBand);
1956 read(line);
1957 hist->AddData(line.DoubleBuffer(), line.size());
1958 progress.CheckStatus();
1959 }
1960 }
1961
1962 return hist;
1963 }
1964
1965
1975 Pvl *Cube::label() const {
1976 return m_label;
1977 }
1978
1979
1987 int Cube::labelSize(bool actual) const {
1988 int labelSize = m_labelBytes;
1989
1990 if (actual && m_label) {
1991 ostringstream s;
1992 s << *m_label << endl;
1993 labelSize = s.tellp();
1994 }
1995 else if (actual) {
1996 labelSize = 0;
1997 }
1998
1999 return labelSize;
2000 }
2001
2002
2008 int Cube::lineCount() const {
2009 return m_lines;
2010 }
2011
2012
2022 double Cube::multiplier() const {
2023 return m_multiplier;
2024 }
2025
2026
2032 PixelType Cube::pixelType() const {
2033 return m_pixelType;
2034 }
2035
2036
2047 int Cube::physicalBand(const int &virtualBand) const {
2048 int physicalBand = virtualBand;
2049
2050 if (m_virtualBandList) {
2051 if ((virtualBand < 1) ||
2052 (virtualBand > m_virtualBandList->size())) {
2053 QString msg = "Out of array bounds [" + toString(virtualBand) + "]";
2054 throw IException(IException::Programmer, msg, _FILEINFO_);
2055 }
2056 physicalBand = m_virtualBandList->at(virtualBand - 1);
2057 }
2058
2059 return physicalBand;
2060 }
2061
2062
2069 if (m_projection == NULL && isOpen()) {
2071 }
2072 return m_projection;
2073 }
2074
2075
2081 int Cube::sampleCount() const {
2082 return m_samples;
2083 }
2084
2085
2104 Statistics *Cube::statistics(const int &band, QString msg) {
2105 return statistics(band, ValidMinimum, ValidMaximum, msg);
2106 }
2107
2108
2125 Statistics *Cube::statistics(const int &band, const double &validMin,
2126 const double &validMax, QString msg) {
2127 // Make sure cube is open
2128 if ( !isOpen() ) {
2129 QString msg = "Cannot create statistics object for an unopened cube";
2130 throw IException(IException::Programmer, msg, _FILEINFO_);
2131 }
2132
2133 // Make sure band is valid
2134 if ((band < 0) || (band > bandCount())) {
2135 string msg = "Invalid band in [CubeInfo::Statistics]";
2136 throw IException(IException::Programmer, msg, _FILEINFO_);
2137 }
2138
2139 // Construct a line buffer manager and a statistics object
2140 LineManager line(*this);
2141 Statistics *stats = new Statistics();
2142
2143 stats->SetValidRange(validMin, validMax);
2144
2145 int bandStart = band;
2146 int bandStop = band;
2147 int maxSteps = lineCount();
2148 if (band == 0) {
2149 bandStart = 1;
2150 bandStop = bandCount();
2151 maxSteps = lineCount() * bandCount();
2152 }
2153
2154 Progress progress;
2155 progress.SetText(msg);
2156 progress.SetMaximumSteps(maxSteps);
2157 progress.CheckStatus();
2158
2159 // Loop and get the statistics for a good minimum/maximum
2160 for(int useBand = bandStart ; useBand <= bandStop ; useBand++) {
2161 for(int i = 1; i <= lineCount(); i++) {
2162 line.SetLine(i, useBand);
2163 read(line);
2164 stats->AddData(line.DoubleBuffer(), line.size());
2165 progress.CheckStatus();
2166 }
2167 }
2168
2169 return stats;
2170 }
2171
2172
2187
2188 if (isOpen() && m_ioHandler) {
2189 m_ioHandler->addCachingAlgorithm(algorithm);
2190 }
2191 else if (!isOpen()) {
2192 QString msg = "Cannot add a caching algorithm until the cube is open";
2193 throw IException(IException::Programmer, msg, _FILEINFO_);
2194 }
2195 }
2196
2203 if (m_ioHandler) {
2204 QMutexLocker locker(m_mutex);
2205 m_ioHandler->clearCache();
2206 }
2207 }
2208
2209
2219 bool Cube::deleteBlob(QString BlobName, QString BlobType) {
2220 for(int i = 0; i < m_label->objects(); i++) {
2221 PvlObject obj = m_label->object(i);
2222 if (obj.name().compare(BlobType) == 0) {
2223 if (obj.findKeyword("Name")[0] == BlobName) {
2224 m_label->deleteObject(i);
2225 QString key = BlobType + "_" + BlobName;
2226
2227 if (gdalDataset()) {
2228 CPLStringList metadata = CPLStringList(gdalDataset()->GetMetadata("json:ISIS3"), false);
2229 const char *metadataJsonString = metadata[0];
2230 nlohmann::ordered_json jsonblob = nlohmann::ordered_json::parse(metadataJsonString);
2231
2232 bool keyErased = jsonblob.erase(key.toStdString());
2233 string jsonblobstr = jsonblob.dump();
2234
2235 char **outputMetadata = new char*[1];
2236 outputMetadata[0] = jsonblobstr.data();
2237 gdalDataset()->SetMetadata(outputMetadata, "json:ISIS3");
2238 delete []outputMetadata;
2239
2240 return keyErased;
2241 }
2242
2243 if (m_blobMap.contains(key)) {
2244 m_blobMap.remove(key);
2245 m_blobQueue.removeOne(key);
2246 }
2247 return true;
2248 }
2249 }
2250 }
2251 return false;
2252 }
2253
2254
2263 void Cube::deleteGroup(const QString &group) {
2264 PvlObject &isiscube = label()->findObject("IsisCube");
2265 if (!isiscube.hasGroup(group)) return;
2266 isiscube.deleteGroup(group);
2267 }
2268
2269
2277 PvlGroup &Cube::group(const QString &group) const {
2278 PvlObject &isiscube = label()->findObject("IsisCube");
2279 return isiscube.findGroup(group);
2280 }
2281
2282
2290 bool Cube::hasGroup(const QString &group) const {
2291 const PvlObject &isiscube = label()->findObject("IsisCube");
2292 if (isiscube.hasGroup(group)) return true;
2293 return false;
2294 }
2295
2296
2305 bool Cube::hasBlob(const QString &name, const QString &type) {
2306 QMutexLocker locker(m_mutex);
2307
2308 for(int o = 0; o < label()->objects(); o++) {
2309 PvlObject &obj = label()->object(o);
2310 if (obj.isNamed(type)) {
2311 if (obj.hasKeyword("Name")) {
2312 QString temp = (QString) obj["Name"];
2313 temp = temp.toUpper();
2314 QString temp2 = name;
2315 temp2 = temp2.toUpper();
2316 if (temp == temp2) return true;
2317 }
2318 }
2319 }
2320 return false;
2321 }
2322
2323
2331 bool Cube::hasTable(const QString &name) {
2332 return hasBlob(name, "Table");
2333 }
2334
2335
2344 void Cube::putGroup(const PvlGroup &group) {
2345 if (isReadOnly()) {
2346 QString msg = "Cannot add a group to the label of cube [" + (QString)QFileInfo(fileName()).fileName() +
2347 "] because it is opened read-only";
2348 throw IException(IException::Programmer, msg, _FILEINFO_);
2349 return;
2350 }
2351
2352 PvlObject &isiscube = label()->findObject("IsisCube");
2353 if (isiscube.hasGroup(group.name())) {
2354 isiscube.findGroup(group.name()) = group;
2355 }
2356 else {
2357 isiscube.addGroup(group);
2358 }
2359 }
2360
2361
2367 PvlObject &core = m_label->findObject("IsisCube").findObject("Core");
2368
2369 // Prune the band bin group if it exists
2370 if (m_label->findObject("IsisCube").hasGroup("BandBin")) {
2371 PvlGroup &bandBin = m_label->findObject("IsisCube").findGroup("BandBin");
2372 for (int k = 0;k < bandBin.keywords();k++) {
2373 if (bandBin[k].size() == m_bands && m_virtualBandList) {
2374 PvlKeyword temp = bandBin[k];
2375 bandBin[k].clear();
2376 for (int i = 0;i < m_virtualBandList->size();i++) {
2377 int physicalBand = m_virtualBandList->at(i) - 1;
2378 bandBin[k].addValue(temp[physicalBand], temp.unit(physicalBand));
2379 }
2380 }
2381 }
2382 }
2383
2384 // Change the number of bands in the labels of the cube
2385 if (m_virtualBandList && core.hasGroup("Dimensions")) core.findGroup("Dimensions")["Bands"] = toString((int)m_virtualBandList->size());
2386 }
2387
2388
2394 void Cube::cleanUp(bool removeIt) {
2395 if (m_ioHandler) {
2396 delete m_ioHandler;
2397 m_ioHandler = NULL;
2398 }
2399
2400 // Always remove a temporary file
2401 if (m_tempCube) {
2402 QFile::remove(m_tempCube->expanded());
2403 removeIt = false; // dont remove originals
2404
2405 delete m_tempCube;
2406 m_tempCube = NULL;
2407 }
2408
2409 if (removeIt) {
2410 QFile::remove(m_labelFileName->expanded());
2411
2413 QFile::remove(m_dataFileName->expanded());
2414 }
2415
2416 delete m_labelFile;
2417 m_labelFile = NULL;
2418
2419 delete m_dataFile;
2420 m_dataFile = NULL;
2421
2422 if (m_geodataSet) {
2423 GDALClose(m_geodataSet);
2424 }
2425 m_geodataSet = NULL;
2426
2427 delete m_labelFileName;
2428 m_labelFileName = NULL;
2429
2430 delete m_dataFileName;
2431 m_dataFileName = NULL;
2432
2433 delete m_label;
2434 m_label = NULL;
2435
2436 delete m_virtualBandList;
2437 m_virtualBandList = NULL;
2438
2439 initialize();
2440 }
2441
2442
2448 m_labelFile = NULL;
2449 m_dataFile = NULL;
2450 m_ioHandler = NULL;
2451 m_mutex = NULL;
2452
2453 m_camera = NULL;
2454 m_projection = NULL;
2455
2456 m_labelFileName = NULL;
2457 m_dataFileName = NULL;
2458 m_tempCube = NULL;
2459 m_formatTemplateFile = NULL;
2460 m_label = NULL;
2461
2462 m_virtualBandList = NULL;
2463
2464 m_mutex = new QMutex();
2466 new FileName("$ISISROOT/appdata/templates/labels/CubeFormatTemplate.pft");
2467
2468 initialize();
2469 }
2470
2471
2478 QFile *Cube::dataFile() const {
2479 if (m_dataFile)
2480 return m_dataFile;
2481 else
2482 return m_labelFile;
2483 }
2484
2485
2486 GDALDataset *Cube::gdalDataset() const {
2487 return m_geodataSet;
2488 }
2489
2497 FileName Cube::realDataFileName() const {
2498 FileName result;
2499
2500 // Attached, stores DN data - normal cube
2502 result = *m_labelFileName;
2503 }
2504 // Detached, stores DN data - standard detached cube
2506 result = *m_dataFileName;
2507 }
2508 // External cube - go look at our external file
2510 FileName guess = *m_dataFileName;
2511 QDir dir(guess.toString());
2512
2513 // If path is relative and there is a labelFileName, start in directory of the ecub, then
2514 // cd to the directory containing the DnFile, since it is relative to the location of the ecub.
2515 // We need to turn the relative path into an absolute path.
2516 if (dir.isRelative() && m_labelFileName) {
2517 QDir dir2(m_labelFileName->originalPath());
2518 dir2.cd(guess.path());
2519 guess = dir2.absolutePath() + "/" + guess.name();
2520 }
2521 do {
2522 Cube dataCube(guess.expanded());
2523 Pvl guessLabel = *(dataCube.label());
2524
2525 if (guessLabel.hasObject("IsisCube")) {
2526 PvlObject &core = guessLabel.findObject("IsisCube").findObject("Core");
2527
2528 if (core.hasKeyword("^DnFile")) {
2529 FileName currentGuess = guess;
2530 guess = core["^DnFile"][0];
2531
2532 if (!guess.path().startsWith("/")) {
2533 guess = currentGuess.path() + "/" + guess.original();
2534 }
2535 }
2536 else if (core.hasKeyword("^Core")) {
2537 result = core["^Core"][0];
2538 }
2539 else {
2540 result = guess;
2541 }
2542 }
2543 else {
2544 result = guess;
2545 }
2546 }
2547 while (result.name() == "");
2548 }
2549
2550 return result;
2551 }
2552
2553
2566 m_byteOrder = Lsb;
2567 if (IsBigEndian())
2568 m_byteOrder = Msb;
2569 m_format = Tile;
2570 m_pixelType = Real;
2571
2573 m_labelBytes = 65536;
2574
2575 m_samples = 0;
2576 m_lines = 0;
2577 m_bands = 0;
2578
2579 m_base = 0.0;
2580 m_multiplier = 1.0;
2581 }
2582
2583
2590 initialize();
2591 initLabelState(label);
2592 const PvlObject &core = label.findObject("IsisCube").findObject("Core");
2593
2594 if (labelsAttached() != ExternalLabel) {
2595 // Dimensions
2596 const PvlGroup &dims = core.findGroup("Dimensions");
2597 m_samples = dims["Samples"];
2598 m_lines = dims["Lines"];
2599 m_bands = dims["Bands"];
2600
2601 // Stored pixel information
2602 const PvlGroup &pixelsGroup = core.findGroup("Pixels");
2603 m_byteOrder = ByteOrderEnumeration(pixelsGroup["ByteOrder"]);
2604 m_base = pixelsGroup["Base"];
2605 m_multiplier = pixelsGroup["Multiplier"];
2606 m_pixelType = PixelTypeEnumeration(pixelsGroup["Type"]);
2607
2608 // Now examine the format to see which type of handler to create
2609 if ((QString) core["Format"] == "BandSequential") {
2611 }
2612 else if ((QString) core["Format"] == "Tile") {
2614 }
2615 else if ((QString) core["Format"] == "GTiff") {
2616 m_format = Format::GTiff;
2617 }
2618 else {
2619 QString msg = "Unknown format [" + (QString)core["Format"] + "]";
2620 throw IException(IException::Io, msg, _FILEINFO_);
2621 }
2622 }
2623 else {
2624 FileName temp(core["^DnFile"][0]);
2625 if (!temp.expanded().startsWith("/")) {
2626 temp = FileName(m_labelFileName->path() + "/" + temp.original());
2627 }
2628 try {
2629 initCoreFromLabel(Pvl(temp.toString()));
2630 }
2631 catch (IException &e) {
2632 initCoreFromGdal(temp.toString());
2633 }
2634 // Reset the label state as we recurse back out from initCoreFromLabel calls
2635 initLabelState(label);
2636 }
2637 }
2638
2639
2640 void Cube::initCoreFromGdal(const QString &labelFile) {
2641 GDALDataset *geodataSet = GDALDataset::FromHandle(GDALOpen(labelFile.toStdString().c_str(), GA_ReadOnly));
2642 if (!geodataSet) {
2643 QString msg = "Gdal failed to open [" + labelFile + "]";
2644 throw IException(IException::Programmer, msg, _FILEINFO_);
2645 }
2646
2647 setFormat(GTiff);
2648 setDimensions(geodataSet->GetRasterXSize(), geodataSet->GetRasterYSize(), geodataSet->GetRasterCount());
2649
2650 GDALRasterBand *band = geodataSet->GetRasterBand(1);
2651 setPixelType(GdalPixelToIsis(band->GetRasterDataType()));
2652 setBaseMultiplier(band->GetOffset(), band->GetScale());
2653 GDALClose(geodataSet);
2654 }
2655
2664 void Cube::initLabelFromFile(FileName labelFileName, bool readWrite) {
2665
2666 try {
2667 if (labelFileName.fileExists()) {
2668 m_label = new Pvl(labelFileName.expanded());
2669 if (!m_label->objects()) {
2670 throw IException();
2671 }
2672 }
2673 }
2674 catch(IException &) {
2675 if (m_label) {
2676 delete m_label;
2677 m_label = NULL;
2678 }
2679 }
2680
2681 try {
2682 if (!m_label) {
2683 FileName tmp(labelFileName);
2684 tmp = tmp.addExtension("cub");
2685 if (tmp.fileExists()) {
2686 m_label = new Pvl(tmp.expanded());
2687 if (!m_label->objects()) {
2688 throw IException();
2689 }
2690 labelFileName = tmp;
2691 }
2692 }
2693 }
2694 catch(IException &e) {
2695 if (m_label) {
2696 delete m_label;
2697 m_label = NULL;
2698 }
2699 }
2700
2701 try {
2702 if (!m_label) {
2703 FileName tmp(labelFileName);
2704 tmp = tmp.setExtension("lbl");
2705 if (tmp.fileExists()) {
2706 m_label = new Pvl(tmp.expanded());
2707 if (!m_label->objects()) {
2708 throw IException();
2709 }
2710 labelFileName = tmp;
2711 }
2712 }
2713 }
2714 catch(IException &e) {
2715 if (m_label) {
2716 delete m_label;
2717 m_label = NULL;
2718 }
2719 }
2720
2721 try {
2722 if (!m_label) {
2723 FileName tmp(labelFileName);
2724 tmp = tmp.addExtension("ecub");
2725 if (tmp.fileExists()) {
2726 m_label = new Pvl(tmp.expanded());
2727 if (!m_label->objects()) {
2728 throw IException();
2729 }
2730 labelFileName = tmp;
2731 }
2732 }
2733 }
2734 catch(IException &e) {
2735 if (m_label) {
2736 delete m_label;
2737 m_label = NULL;
2738 }
2739 }
2740
2741 if (!m_label) {
2742 QString msg = Message::FileOpen(labelFileName.original());
2743 throw IException(IException::Io, msg, _FILEINFO_);
2744 }
2745
2746 m_labelFileName = new FileName(labelFileName);
2747
2748 // See if this is an old Isis cube format. If so then we will
2749 // need to internalize a new label
2750 if (m_label->hasKeyword("CCSD3ZF0000100000001NJPL3IF0PDS200000001")) {
2751 if (!readWrite) {
2753 }
2754 else {
2755 QString msg = "Can not open [" + m_labelFileName->original() + "]"
2756 " because it is an ISIS2 cube.";
2757 cleanUp(false);
2758 throw IException(IException::Io, msg, _FILEINFO_);
2759 }
2760 }
2761 else {
2762 m_labelFile = new QFile(m_labelFileName->expanded());
2763 }
2764 }
2765
2766
2771 if (isOpen()) {
2772 string msg = "Sorry you can't do a SetMethod after the cube is opened";
2773 throw IException(IException::Programmer, msg, _FILEINFO_);
2774 }
2775 }
2776
2777
2784 Pvl label = *m_label;
2785 PvlObject *core = NULL;
2786
2787 do {
2788 core = &label.findObject("IsisCube").findObject("Core");
2789
2790 if (core->hasKeyword("^DnFile")) {
2791
2792 FileName temp((*core)["^DnFile"][0]);
2793 if (!temp.expanded().startsWith("/")) {
2794 temp = realDataFileName();
2795 }
2796
2797 label = Pvl(temp.toString());
2798 core = NULL;
2799 }
2800 }
2801 while (!core);
2802
2803 return label;
2804 }
2805
2806
2813 void Cube::reformatOldIsisLabel(const QString &oldCube) {
2814 QString parameters = "from=" + oldCube;
2815 FileName oldName(oldCube);
2816 FileName tempCube = FileName::createTempFile("Temporary_" + oldName.name() + ".cub");
2817 parameters += " to=" + tempCube.expanded();
2818
2819 if (iApp == NULL) {
2820 QString command = "$ISISROOT/bin/pds2isis " + parameters;
2822 }
2823 else {
2824 QString prog = "pds2isis";
2825 ProgramLauncher::RunIsisProgram(prog, parameters);
2826 }
2827
2828 m_tempCube = new FileName(tempCube);
2829 *m_label = Pvl(m_tempCube->toString());
2830 m_labelFile = new QFile(m_tempCube->expanded());
2831 }
2832
2833
2843 void Cube::latLonRange(double &minLatitude, double &maxLatitude, double &minLongitude, double &
2844 maxLongitude) {
2845 Camera *cam;
2846 TProjection *proj;
2847
2848 bool isGood = false;
2849 bool useProj = true;
2850
2851 if (hasGroup("Instrument")) {
2852 useProj = false;
2853 }
2854
2855 // setup camera or projection
2856 if (useProj) {
2857 try {
2858 proj = (TProjection *) projection();
2859 }
2860 catch(IException &e) {
2861 QString msg = "Cannot calculate lat/lon range without a camera or projection";
2862 throw IException(e, IException::User, msg, _FILEINFO_);
2863 }
2864 }
2865 else {
2866 try {
2867 cam = camera();
2868 }
2869 catch(IException &e) {
2870 QString msg = "Unable to create camera when calculating a lat/lon range.";
2871 throw IException(e, IException::User, msg, _FILEINFO_);
2872 }
2873 }
2874
2875 // Iterate over all samp/line combos in cube
2876 minLatitude = 99999;
2877 minLongitude = 99999;
2878 maxLatitude = -99999;
2879 maxLongitude = -99999;
2880
2881 for (double sample = 0.5; sample < sampleCount() + 0.5; sample++) {
2882 // Checks to see if the point is in outer space
2883 for (double line = 0.5; line < lineCount() + 0.5; line++) {
2884 if (useProj) {
2885 isGood = proj->SetWorld(sample, line);
2886 }
2887 else {
2888 isGood = cam->SetImage(sample, line);
2889 }
2890
2891 double lat, lon;
2892 if (isGood) {
2893 if (useProj) {
2894 lat = proj->UniversalLatitude();
2895 lon = proj->UniversalLongitude();
2896 }
2897 else {
2898 lat = cam->UniversalLatitude();
2899 lon = cam->UniversalLongitude();
2900 }
2901
2902 // update mix/max lat/lons
2903 if (lat < minLatitude) {
2904 minLatitude = lat;
2905 }
2906 else if (lat > maxLatitude) {
2907 maxLatitude = lat;
2908 }
2909
2910 if (lon < minLongitude) {
2911 minLongitude = lon;
2912 }
2913 else if (lon > maxLongitude) {
2914 maxLongitude = lon;
2915 }
2916 }
2917 }
2918 }
2919 if ( (minLatitude == 99999) || (minLongitude == 99999) || (maxLatitude == -99999) ||
2920 (maxLongitude == -99999) ) {
2921 QString msg = "Unable to calculate a minimum or maximum latitutde or longitude.";
2922 throw IException(IException::Unknown, msg, _FILEINFO_);
2923 }
2924 }
2925
2931 if (!isOpen()) {
2932 string msg = "Cube must be opened first before writing labels";
2933 throw IException(IException::Programmer, msg, _FILEINFO_);
2934 }
2935
2936 if (isReadOnly()) {
2937 string msg = "The cube must be opened in read/write mode, not readOnly";
2938 throw IException(IException::Programmer, msg, _FILEINFO_);
2939 }
2940
2941 if (m_format == Format::GTiff) {
2942
2943 nlohmann::ordered_json jsonOut;
2944
2945 // Check for existing data, if there is data update it
2946 CPLStringList metadata = CPLStringList(gdalDataset()->GetMetadata("json:ISIS3"), false);
2947
2948 if (metadata[0] != nullptr) {
2949 const char *metadataJsonString = metadata[0];
2950 jsonOut = nlohmann::ordered_json::parse(metadataJsonString);
2951 }
2952
2953 // update metadata
2954 nlohmann::ordered_json jsonblob = this->label()->toJson()["Root"];
2955 for (auto& [key, val] : jsonblob.items()) {
2956 if (!val.contains("Bytes") || key == "Label") {
2957 jsonOut[key] = val;
2958 }
2959 }
2960
2961 for (QString blobKey : m_blobQueue) {
2962 Blob &blob = m_blobMap[blobKey];
2963
2964 std::string blobJsonStr = "{}";
2965 blob.WriteGdal(blobJsonStr);
2966 nlohmann::ordered_json blobJson = nlohmann::ordered_json::parse(blobJsonStr);
2967 jsonOut.update(blobJson);
2968 }
2969 m_blobMap.clear();
2970 m_blobQueue.clear();
2971 std::string jsonOutStr = jsonOut.dump();
2972
2973 char ** outputMetadata = new char*[1];
2974 outputMetadata[0] = jsonOutStr.data();
2975 gdalDataset()->SetMetadata(outputMetadata, "json:ISIS3");
2976 delete []outputMetadata;
2977
2978 if (this->label()->findObject("IsisCube").hasGroup("Mapping")) {
2979 PvlGroup &mappingGroup = this->label()->findObject("IsisCube").findGroup("Mapping");
2980
2981 if (mappingGroup.hasKeyword("ProjStr")) {
2982 OGRSpatialReference *oSRS = new OGRSpatialReference();
2983 oSRS->SetFromUserInput(mappingGroup.findKeyword("ProjStr")[0].toStdString().c_str());
2984
2985 gdalDataset()->SetSpatialRef(oSRS);
2986
2987 double dfRes = (double)mappingGroup.findKeyword("PixelResolution") / oSRS->GetLinearUnits();
2988 double upperLeftX = (double) mappingGroup.findKeyword("UpperLeftCornerX");
2989 double upperLeftY = (double) mappingGroup.findKeyword("UpperLeftCornerY");
2990 double *padfTransform = new double[6];
2991 padfTransform[1] = dfRes;
2992 padfTransform[5] = -dfRes;
2993 padfTransform[0] = upperLeftX;
2994 padfTransform[3] = upperLeftY;
2995 gdalDataset()->SetGeoTransform(padfTransform);
2996 delete oSRS;
2997 delete[] padfTransform;
2998 }
2999 }
3000
3001
3002 return;
3003 }
3004
3005 // Set the pvl's format template
3006 m_label->setFormatTemplate(m_formatTemplateFile->original());
3007
3008 // Write them with attached label and blob data
3010 QMutexLocker locker(m_mutex);
3011 QMutexLocker locker2(m_ioHandler->dataFileMutex());
3012
3013 // Compute the number of bytes in the cube + label bytes and if the
3014 // endpos of the file // is not greater than this then seek to that position.
3015 fstream stream(m_labelFileName->expanded().toLatin1().data(),
3016 ios::in | ios::out | ios::binary);
3017
3018 // maxbyte = position after the cube DN data and labels
3019 streampos maxbyte = (streampos) m_labelBytes;
3020
3022 maxbyte += (streampos) m_ioHandler->getDataSize();
3023 }
3024 for (QString blobKey : m_blobQueue) {
3025 Blob &blob = m_blobMap[blobKey];
3026
3027 stream.seekp(0, ios::end);
3028
3029 // End byte = end byte of the file (aka eof position, file size)
3030 streampos endByte = stream.tellp();
3031
3032 // If EOF is too early, allocate space up to where we want the blob
3033 if (endByte < maxbyte) {
3034 stream.seekp(maxbyte, ios::beg);
3035 }
3036
3037 streampos eofbyte = stream.tellp();
3038 eofbyte += 1;
3039
3040 PvlObject &blobLabel = blob.Label();
3041
3042 BigInt oldSbyte = blobLabel["StartByte"];
3043 int oldNbytes = (int) blobLabel["Bytes"];
3044
3045 if (oldSbyte == 0) {
3046 blobLabel["StartByte"] = toString((BigInt)eofbyte);
3047 }
3048 else {
3049 // Does it fit in the old space
3050 if (blob.Size() <= oldNbytes) {
3051 stream.seekp(oldSbyte - 1, ios::beg);
3052 }
3053 // Was the old space at the end of the file
3054 else if ((oldSbyte + oldNbytes) == eofbyte) {
3055 stream.seekp(oldSbyte - 1, ios::beg);
3056 }
3057 }
3058
3059 // Use default argument of "" for detached stream
3060 try {
3061 blob.Write(*label(), stream);
3062 }
3063 catch (IException &e) {
3064 QString msg = "Failed to write blob [" + blob.Type() + ", " + blob.Name() + "]";
3065 throw IException(e, IException::Io, msg, _FILEINFO_);
3066 stream.close();
3067 }
3068 stream.flush();
3069 }
3070 m_blobMap.clear();
3071 m_blobQueue.clear();
3072 stream.close();
3073
3074 ostringstream temp;
3075 temp << *m_label << endl;
3076 string tempstr = temp.str();
3077
3078 if ((int) tempstr.length() <= m_labelBytes) {
3079 QByteArray labelArea(m_labelBytes, '\0');
3080 QByteArray labelUnpaddedContents(tempstr.c_str(), tempstr.length());
3081 labelArea.replace(0, labelUnpaddedContents.size(), labelUnpaddedContents);
3082 // Rewrite the label area
3083 m_labelFile->seek(0);
3084 m_labelFile->write(labelArea);
3085 }
3086 else {
3087 locker2.unlock();
3088 QString msg = "Label space is full in [" +
3089 (QString)FileName(*m_labelFileName).name() +
3090 "] unable to write labels";
3091 cleanUp(false);
3092 throw IException(IException::Io, msg, _FILEINFO_);
3093 }
3094 }
3095 // or detached label
3096 else {
3097 for (QString blobKey : m_blobQueue) {
3098 Blob &blob = m_blobMap[blobKey];
3099
3100 FileName blobFileName(blob.Label().findKeyword("^" + blob.Type())[0]);
3101 QString blobFile(blobFileName.expanded());
3102 ios::openmode flags = ios::in | ios::binary | ios::out | ios::trunc;
3103 fstream detachedStream;
3104 detachedStream.open(blobFile.toLatin1().data(), flags);
3105 if (!detachedStream) {
3106 QString message = "Unable to open data file [" +
3107 blobFileName.expanded() + "]";
3108 throw IException(IException::Io, message, _FILEINFO_);
3109 }
3110
3111 try {
3112 blob.Write(*label(), detachedStream);
3113 }
3114 catch (IException &e) {
3115 QString msg = "Failed to write blob [" + blob.Type() + ", " + blob.Name() + "]";
3116 throw IException(e, IException::Io, msg, _FILEINFO_);
3117 detachedStream.close();
3118 }
3119 detachedStream.flush();
3120 detachedStream.close();
3121 }
3122 m_blobMap.clear();
3123 m_blobQueue.clear();
3124
3125 m_label->write(m_labelFileName->expanded());
3126 }
3127 }
3128
3129 void Cube::initLabelState(const Pvl &label) {
3130 const PvlObject &core = label.findObject("IsisCube").findObject("Core");
3131 LabelAttachment attachmentStyle = AttachedLabel;
3132 if (core.hasKeyword("^DnFile")) {
3133 attachmentStyle = ExternalLabel;
3134 }
3135 else if (core.hasKeyword("^Core")) {
3136 attachmentStyle = DetachedLabel;
3137 }
3138
3139 setLabelsAttached(attachmentStyle);
3140 }
3141}
Buffer for reading and writing cube data.
Definition Buffer.h:53
int size() const
Returns the total number of pixels in the shape buffer.
Definition Buffer.h:97
double * DoubleBuffer() const
Returns the value of the shape buffer.
Definition Buffer.h:138
void Copy(const Buffer &in, bool includeRawBuf=true)
Allows copying of the buffer contents to another Buffer.
Definition Buffer.cpp:256
Manages a Buffer over a cube.
bool begin()
Moves the shape buffer to the first position.
bool end() const
Returns true if the shape buffer has accessed the end of the cube.
bool next()
Moves the shape buffer to the next position.
static Camera * Create(Cube &cube)
Creates a Camera object using Pvl Specifications.
virtual bool SetImage(const double sample, const double line)
Sets the sample/line values of the image to get the lat/lon values.
Definition Camera.cpp:156
Manipulate and parse attributes of input cube filenames.
std::vector< QString > bands() const
Return a vector of the input bands specified.
Manipulate and parse attributes of output cube filenames.
double minimum() const
Return the output cube attribute minimum.
ByteOrder byteOrder() const
Return the byte order as an Isis::ByteOrder.
double maximum() const
Return the output cube attribute maximum.
bool propagateMinimumMaximum() const
Return true if the min/max are to be propagated from an input cube.
bool propagatePixelType() const
Return true if the pixel type is to be propagated from an input cube.
Cube::Format fileFormat() const
Return the file format an Cube::Format.
PixelType pixelType() const
Return the pixel type as an Isis::PixelType.
IO Handler for Isis Cubes using the BSQ format.
This is the parent of the caching algorithms.
void addCachingAlgorithm(CubeCachingAlgorithm *)
This will add the given caching algorithm to the list of attempted caching algorithms.
Definition Cube.cpp:2186
void clearIoCache()
This will clear excess RAM used for quicker IO in the cube.
Definition Cube.cpp:2202
bool hasTable(const QString &name)
Check to see if the cube contains a pvl table by the provided name.
Definition Cube.cpp:2331
QFile * m_dataFile
This is only sometimes allocated.
Definition Cube.h:391
Pvl realDataFileLabel() const
Function to read data from a cube label and return it as a PVL object.
Definition Cube.cpp:2783
ImagePolygon readFootprint() const
Read the footprint polygon for the Cube.
Definition Cube.cpp:1114
Cube()
Constructs a Cube object.
Definition Cube.cpp:50
void setPixelType(PixelType pixelType)
Used prior to the Create method, this will specify the output pixel type.
Definition Cube.cpp:1540
void latLonRange(double &minLatitude, double &maxLatitude, double &minLongitude, double &maxLongitude)
Returns the latitude and longitude range for the Cube.
Definition Cube.cpp:2843
void deleteGroup(const QString &group)
Deletes a group from the cube labels.
Definition Cube.cpp:2263
void reformatOldIsisLabel(const QString &oldCube)
This is a helper, used by open(...), that handles opening Isis 2 cubes as if they were Isis cubes.
Definition Cube.cpp:2813
void setFormat(Format format)
Used prior to the Create method, this will specify the format of the cube, either band,...
Definition Cube.cpp:1502
bool deleteBlob(QString BlobName, QString BlobType)
This method will delete a blob label object from the cube as specified by the Blob type and name.
Definition Cube.cpp:2219
int m_bands
The band count of the open cube or the cube that will be created.
Definition Cube.h:470
void initialize()
This sets Cube to its default state: Native byte order Format = Tile PixelType = Real (4 bytes per pi...
Definition Cube.cpp:2565
Format format() const
Definition Cube.cpp:1849
PixelType m_pixelType
This is the pixel type on disk.
Definition Cube.h:423
void relocateDnData(FileName dnDataFile)
Relocates the DN data for a cube to an external cube label file.
Definition Cube.cpp:1602
void construct()
Initialize members from their initial undefined states.
Definition Cube.cpp:2447
int m_labelBytes
The maximum allowed size of the label; the allocated space.
Definition Cube.h:461
virtual Histogram * histogram(const int &band=1, QString msg="Gathering histogram")
This method returns a pointer to a Histogram object which allows the program to obtain and use variou...
Definition Cube.cpp:1873
int lineCount() const
Definition Cube.cpp:2008
void initLabelFromFile(FileName labelFileName, bool readWrite)
This function initializes the Cube label from a file passed as a parameter.
Definition Cube.cpp:2664
double multiplier() const
Returns the multiplier value for converting 8-bit/16-bit pixels to 32-bit.
Definition Cube.cpp:2022
FileName realDataFileName() const
This gets the file name of the file which actually contains the DN data.
Definition Cube.cpp:2497
CubeStretch readCubeStretch(QString name="CubeStretch", const std::vector< PvlKeyword > keywords=std::vector< PvlKeyword >()) const
Read a Stretch from a cube.
Definition Cube.cpp:1160
Statistics * statistics(const int &band=1, QString msg="Gathering statistics")
This method returns a pointer to a Statistics object which allows the program to obtain and use vario...
Definition Cube.cpp:2104
GDALDataset * m_geodataSet
TODO: Write description.
Definition Cube.h:396
void setDimensions(int ns, int nl, int nb)
Used prior to the Create method to specify the size of the cube.
Definition Cube.cpp:1450
Camera * camera()
Return a camera associated with the cube.
Definition Cube.cpp:1687
FileName externalCubeFileName() const
If this is an external cube label file, this will give you the cube dn file that this label reference...
Definition Cube.cpp:1810
LabelAttachment
Input cube label type tracker.
Definition Cube.h:245
@ ExternalLabel
The label is pointing to an external DN file - the label is also external to the data.
Definition Cube.h:254
@ AttachedLabel
The input label is embedded in the image file.
Definition Cube.h:246
@ DetachedLabel
The input label is in a separate data file from the image.
Definition Cube.h:247
PvlGroup & group(const QString &group) const
Read a group from the cube into a Label.
Definition Cube.cpp:2277
double m_base
The base of the open cube or the cube that will be created; does not apply if m_pixelType is Real.
Definition Cube.h:476
int sampleCount() const
Definition Cube.cpp:2081
void putGroup(const PvlGroup &group)
Adds a group in a Label to the cube.
Definition Cube.cpp:2344
bool isOpen() const
Test if a cube file has been opened/created.
Definition Cube.cpp:189
void setBaseMultiplier(double base, double mult)
Used prior to the Create method, this will specify the base and multiplier for converting 8-bit/16-bi...
Definition Cube.cpp:1384
bool isReadOnly() const
Test if the opened cube is read-only, that is write operations will fail if this is true.
Definition Cube.cpp:215
int m_lines
The line count of the open cube or the cube that will be created.
Definition Cube.h:467
double base() const
Returns the base value for converting 8-bit/16-bit pixels to 32-bit.
Definition Cube.cpp:1663
void fromIsd(const FileName &fileName, Pvl &label, nlohmann::json &isd, QString access)
Initialize Cube data from a PVL label and JSON ISD.
Definition Cube.cpp:99
LabelAttachment m_attached
True if labels are attached.
Definition Cube.h:451
void openGdal(const QString &cubeFileName, QString access)
This method will open an existing geotiff for reading or reading/writing.
Definition Cube.cpp:974
void setMinMax(double min, double max)
Used prior to the Create method, this will compute a good base and multiplier value given the minimum...
Definition Cube.cpp:1401
QFile * dataFile() const
This returns the QFile with cube DN data in it.
Definition Cube.cpp:2478
Cube * copy(FileName newFile, const CubeAttributeOutput &newFileAttributes)
Copies the cube to the new fileName.
Definition Cube.cpp:288
void create(const QString &cfile)
This method will create an isis cube for writing.
Definition Cube.cpp:426
ImageIoHandler * m_ioHandler
This does the heavy lifting for cube DN IO and is always allocated when isOpen() is true.
Definition Cube.h:402
FileName * m_tempCube
If open was called with an Isis 2 cube, then this will be the name of the imported ISIS cube.
Definition Cube.h:445
bool hasBlob(const QString &name, const QString &type)
Check to see if the cube contains a BLOB.
Definition Cube.cpp:2305
void openCube(const QString &cubeFileName, QString access)
This method will open an existing isis cube for reading or reading/writing.
Definition Cube.cpp:850
void cleanUp(bool remove)
This clears all of the allocated memory associated with an open cube.
Definition Cube.cpp:2394
ByteOrder byteOrder() const
Returns the byte order/endian-ness of the cube file.
Definition Cube.cpp:1675
ByteOrder m_byteOrder
The byte order of the opened cube; if there is no open cube then this is the byte order that will be ...
Definition Cube.h:409
QFile * m_labelFile
This is the file that contains the labels always; if labels are attached then this contains the file ...
Definition Cube.h:384
void open(const QString &cfile, QString access="r")
This method will try to open a file as either a cube or geotiff in either read or read/write.
Definition Cube.cpp:792
PixelType pixelType() const
Definition Cube.cpp:2032
void setVirtualBands(const QList< QString > &vbands)
This allows the programmer to specify a subset of bands to work with.
Definition Cube.cpp:1557
FileName * m_formatTemplateFile
Label pvl format template file (describes how to format labels)
Definition Cube.h:448
Pvl * m_label
The label if IsOpen(), otherwise NULL.
Definition Cube.h:454
LabelAttachment labelsAttached() const
Test if labels are attached.
Definition Cube.cpp:260
void read(Blob &blob, const std::vector< PvlKeyword > keywords=std::vector< PvlKeyword >()) const
This method will read data from the specified Blob object.
Definition Cube.cpp:1043
bool isProjected() const
Returns true if the labels of the cube appear to have a valid mapping group.
Definition Cube.cpp:204
OriginalLabel readOriginalLabel(const QString &name="IsisCube") const
Read the original PDS3 label from a cube.
Definition Cube.cpp:1137
Format
These are the possible storage formats of ISIS cubes.
Definition Cube.h:179
@ Tile
Cubes are stored in tile format, that is the order of the pixels in the file (on disk) is BSQ within ...
Definition Cube.h:233
@ Bsq
Cubes are stored in band-sequential format, that is the order of the pixels in the file (on disk) is:
Definition Cube.h:200
virtual int physicalBand(const int &virtualBand) const
This method will return the physical band number given a virtual band number.
Definition Cube.cpp:2047
Table readTable(const QString &name)
Read a Table from the cube.
Definition Cube.cpp:1200
Camera * m_camera
Camera allocated from the camera() method.
Definition Cube.h:429
bool hasGroup(const QString &group) const
Return if the cube has a specified group in the labels.
Definition Cube.cpp:2290
void setLabelSize(int labelBytes)
Used prior to the Create method, this will allocate a specific number of bytes in the label area for ...
Definition Cube.cpp:1527
OriginalXmlLabel readOriginalXmlLabel() const
Read the original PDS4 label from a cube.
Definition Cube.cpp:1179
virtual QString fileName() const
Returns the opened cube's filename.
Definition Cube.cpp:1837
void fromLabel(const FileName &fileName, Pvl &label, QString access)
Initialize Cube data from a PVL label.
Definition Cube.cpp:76
virtual ~Cube()
Destroys the Cube object.
Definition Cube.cpp:166
QList< int > * m_virtualBandList
If allocated, converts from physical on-disk band # to virtual band #.
Definition Cube.h:485
void write(Blob &blob, bool overwrite=true)
This method will write a blob of data (e.g.
Definition Cube.cpp:1219
bool isReadWrite() const
Test if the opened cube is read-write, that is read and write operations should succeed if this is tr...
Definition Cube.cpp:247
FileName * m_labelFileName
The full filename of the label file (.lbl or .cub)
Definition Cube.h:435
void setExternalDnData(FileName cubeFileWithDnData)
Used to set external dn data to cube.
Definition Cube.cpp:1467
void close(bool remove=false)
Closes the cube and updates the labels.
Definition Cube.cpp:272
History readHistory(const QString &name="IsisCube") const
Read the History from the Cube.
Definition Cube.cpp:1095
void writeLabels()
Write the Pvl labels to the cube's label file.
Definition Cube.cpp:2930
QMutex * m_mutex
Basic thread-safety mutex; this class is not optimized for threads.
Definition Cube.h:426
void initCoreFromLabel(const Pvl &label)
This function initializes the Cube core from a Pvl Label passed as a parameter.
Definition Cube.cpp:2589
int labelSize(bool actual=false) const
Returns the number of bytes used by the label.
Definition Cube.cpp:1987
int m_samples
The sample count of the open cube or the cube that will be created.
Definition Cube.h:464
void openCheck()
Throw an exception if the cube is not open.
Definition Cube.cpp:2770
Projection * projection()
Definition Cube.cpp:2068
void setLabelsAttached(LabelAttachment attached)
Use prior to calling create, this sets whether or not to use separate label and data files.
Definition Cube.cpp:1514
double m_multiplier
The multiplier of the open cube or the cube that will be created; does not apply if m_pixelType is Re...
Definition Cube.h:482
virtual int bandCount() const
Returns the number of virtual bands for the cube.
Definition Cube.cpp:1646
Pvl * label() const
Returns a pointer to the IsisLabel object associated with the cube.
Definition Cube.cpp:1975
void reopen(QString access="r")
This method will reopen an isis sube for reading or reading/writing.
Definition Cube.cpp:1010
Projection * m_projection
Projection allocated from the projection() method.
Definition Cube.h:432
void setByteOrder(ByteOrder byteOrder)
Used prior to the Create method, this will specify the byte order of pixels, either least or most sig...
Definition Cube.cpp:1435
FileName * m_dataFileName
The full filename of the data file (.cub)
Definition Cube.h:438
void applyVirtualBandsToLabel()
Applies virtual bands to label.
Definition Cube.cpp:2366
Format m_format
If isOpen() then this is the IO format that the cube uses.
Definition Cube.h:416
Stores stretch information for a cube.
Definition CubeStretch.h:27
Isis::Blob toBlob() const
Serialize the CubeStretch to a Blob.
IO Handler for Isis Cubes using the tile format.
Handles converting buffers to and from disk.
Container of a cube histogram.
Definition Histogram.h:74
void SetValidRange(const double minimum=Isis::ValidMinimum, const double maximum=Isis::ValidMaximum)
Changes the range of the bins.
virtual void AddData(const double *data, const unsigned int count)
Add an array of doubles to the histogram counters.
Blob toBlob(const QString &name="IsisCube")
Converts a history object into a new blob object.
Definition History.cpp:79
Container of a cube histogram.
Create cube polygons, read/write polygons to blobs.
Blob toBlob() const
Serialize the ImagePolygon to a Blob.
Buffer manager, for moving through a cube in lines.
Definition LineManager.h:39
bool SetLine(const int line, const int band=1)
Positions the buffer at the requested line and returns a status indicator if the set was succesful or...
Read and store original labels.
Isis::Blob toBlob()
Serialize the OriginalLabel data to a Blob.
Read and store original Xml labels.
Blob toBlob() const
Serialize the OriginalXmlLabel to a Blob.
static void RunIsisProgram(QString isisProgramName, QString arguments)
Executes the Isis program with the given arguments.
static void RunSystemCommand(QString commandLine)
This runs arbitrary system commands.
Program progress reporter.
Definition Progress.h:42
void SetMaximumSteps(const int steps)
This sets the maximum number of steps in the process.
Definition Progress.cpp:85
void SetText(const QString &text)
Changes the value of the text string reported just before 0% processed.
Definition Progress.cpp:61
void CheckStatus()
Checks and updates the status.
Definition Progress.cpp:105
static Isis::Projection * CreateFromCube(Isis::Cube &cube)
This method is a helper method.
Base class for Map Projections.
Definition Projection.h:154
virtual bool SetWorld(const double x, const double y)
This method is used to set a world coordinate.
virtual double UniversalLatitude() const
Returns the planetocentric latitude, in degrees, at the surface intersection point in the body fixed ...
Definition Sensor.cpp:210
virtual double UniversalLongitude() const
Returns the positive east, 0-360 domain longitude, in degrees, at the surface intersection point in t...
Definition Sensor.cpp:233
Obtain SPICE information for a spacecraft.
Definition Spice.h:283
This class is used to accumulate statistics on double arrays.
Definition Statistics.h:93
void AddData(const double *data, const unsigned int count)
Add an array of doubles to the accumulators and counters.
Base class for Map TProjections.
virtual double UniversalLongitude()
This returns a universal longitude (positive east in 0 to 360 domain).
virtual double UniversalLatitude()
This returns a universal latitude (planetocentric).
This is free and unencumbered software released into the public domain.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(const LinearAlgebra::Vector &vector, int precision)
A global function to format LinearAlgebra::Vector as a QString with the given precision.
QString LabelAttachmentName(Cube::LabelAttachment labelType)
Return the string representation of the contents of a variable of type LabelAttachment.
Namespace for the standard library.