Isis 3 Programmer Reference
ImportImagesWorkOrder.cpp
Go to the documentation of this file.
1
24
25#include <QDebug>
26#include <QFileDialog>
27#include <QMessageBox>
28#include <QtConcurrentMap>
29
30#include "Camera.h"
31#include "Cube.h"
32#include "CubeAttribute.h"
33#include "FileName.h"
34#include "Project.h"
35#include "ProjectItem.h"
36#include "ProjectItemModel.h"
38#include "Target.h"
39#include "TextFile.h"
40
41namespace Isis {
42
49 WorkOrder(project) {
50 // This is an asynchronous work order
51 m_isSynchronous = false;
52 m_newImages = NULL;
53 m_list = NULL;
54
55 QAction::setText(tr("Import &Images..."));
56 QUndoCommand::setText(tr("Import Images"));
58 }
59
60
67 WorkOrder(other) {
68 m_newImages = NULL;
69 m_list = other.m_list;
70 }
71
72
84
85
96
97
108
109 if (item) {
110 return (item->text() == "Images");
111 }
112
113 return false;
114 }
115
116
133 try {
135
136 QStringList fileNames = QFileDialog::getOpenFileNames(
137 qobject_cast<QWidget *>(parent()),
138 tr("Import Images"), "",
139 tr("Isis cubes and list files (*.cub *.lis);;All Files (*)"));
140
141 QStringList* stateToSave = new QStringList();
142
143 if (!fileNames.isEmpty()) {
144 foreach (FileName fileName, fileNames) {
145 if (fileName.extension() == "lis") {
146 TextFile listFile(fileName.expanded());
147 QString path = fileName.path();
148 QString lineOfListFile;
149
150 while (listFile.GetLine(lineOfListFile)) {
151 FileName relFileName(path + "/" + lineOfListFile);
152 if (relFileName.fileExists() ) {
153 stateToSave->append(path + "/" + lineOfListFile);
154 }
155 else {
156 FileName absFileName(lineOfListFile);
157 if ( absFileName.fileExists() && lineOfListFile.startsWith("/") ) {
158 stateToSave->append(lineOfListFile);
159 }
160
161 else {
162 project()->warn("File " + lineOfListFile + " not found");
163 }
164 }
165 }
166 }
167 else {
168 stateToSave->append(fileName.original());
169 }
170 }
171
172 QMessageBox::StandardButton saveProjectAnswer = QMessageBox::No;
173 if (stateToSave->count() >= 100 && project()->isTemporaryProject()) {
174 saveProjectAnswer = QMessageBox::question(qobject_cast<QWidget *>(parent()),
175 tr("Save Project Before Importing Images"),
176 tr("Would you like to save your project <b>before</b> importing images? It can be "
177 "slow to save your project after these images have been loaded if you do not "
178 "save now. <br><br>IMPORTANT: WHEN IMPORTING LARGE DATA SETS, SAVING YOUR "
179 "PROJECT BEFORE IMPORTING IS HIGHLY RECOMMENDED."),
180 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
181 QMessageBox::Yes);
182 }
183
184 if (saveProjectAnswer == QMessageBox::Yes) {
185 SaveProjectWorkOrder saveWorkOrder(project());
186 saveWorkOrder.trigger();
187 }
188
189 QMessageBox::StandardButton copyImagesAnswer = QMessageBox::No;
190 if (!stateToSave->isEmpty() && saveProjectAnswer != QMessageBox::Cancel) {
191 copyImagesAnswer = QMessageBox::question(qobject_cast<QWidget *>(parent()),
192 tr("Copy Images into Project"),
193 tr("Should images (DN data) be copied into project?"),
194 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
195 QMessageBox::No);
196 }
197
198 bool copyDnData = (copyImagesAnswer == QMessageBox::Yes);
199
200 stateToSave->prepend(copyDnData? "copy" : "nocopy");
201
202 if (fileNames.count() > 1) {
203 QUndoCommand::setText(tr("Import %1 Images").arg(stateToSave->count() - 1));
204 }
205 else if (fileNames.count() == 1 && stateToSave->count() > 2) {
206 QUndoCommand::setText(tr("Import %1 Images from %2").arg(
207 QString::number(stateToSave->count() - 1), fileNames.first()));
208 }
209 else {
210 QUndoCommand::setText(tr("Import %1").arg(fileNames.first()));
211 }
212
213 // The internal data will look like: [ copy|nocopy, img1, img2, ... ]
214 setInternalData(*stateToSave);
215
216 bool doImport = stateToSave->count() > 1 && saveProjectAnswer != QMessageBox::Cancel &&
217 copyImagesAnswer != QMessageBox::Cancel;
218
219 return doImport;
220 }
221
222 }
223 catch (IException &e) {
224 QMessageBox::critical(NULL, tr("Error"), tr(e.what()));
225 }
226 return false;
227
228 }
229
230
241 if (m_list && project()->images().size() > 0 ) {
243 // Remove the images from disk.
245 // Remove the images from the model, which updates the tree view.
246 ProjectItem *currentItem =
247 project()->directory()->model()->findItemData( QVariant::fromValue(m_list) );
248 project()->directory()->model()->removeItem(currentItem);
249 }
250 }
251
252
263 if (m_list && project()->images().size() > 0 ) {
264 foreach (Image *image, *m_list) {
265 delete image;
266 }
267 delete m_list;
268 }
269 }
270
271
283 try {
284 QObject tmpObj;
285 if (internalData().count() > 0) {
286 // Recall in setupExecution() that first element in internal data is copy|nocopy,
287 // and rest of elements are the expanded names of images to import.
288 importConfirmedImages(internalData().mid(1), (internalData()[0] == "copy"));
289 project()->setClean(false);
290 }
291 }
292 catch (IException &e) {
293 QMessageBox::critical(NULL, tr("Error"), tr(e.what()));
294 }
295 }
296
297
309 try {
310 if (!m_newImages->isEmpty()) {
312 m_list = project()->images().last();
313
314 delete m_newImages;
315 m_newImages = NULL;
316 }
317 }
318 catch (IException &e) {
319 m_status = WorkOrderFinished;
320 m_warning.append(e.what());
321 }
322 if (m_warning != "") {
323 project()->warn(m_warning);
324 }
325 }
326
327
339 QThread *guiThread, QDir destinationFolder, bool copyDnData) : m_errors(new IException),
340 m_numErrors(new int(0)) {
341 m_destinationFolder = destinationFolder;
342 m_copyDnData = copyDnData;
343 m_guiThread = guiThread;
344 }
345
346
353 const OriginalFileToProjectCubeFunctor &other) : m_errors(other.m_errors),
354 m_numErrors(other.m_numErrors) {
355 m_destinationFolder = other.m_destinationFolder;
356 m_copyDnData = other.m_copyDnData;
357 m_guiThread = other.m_guiThread;
358 }
359
360
367 m_destinationFolder = QDir();
368 m_copyDnData = false;
369 m_guiThread = NULL;
370 }
371
372
388 const FileName &original) {
389 Cube *result = NULL;
390
391 // As long as we haven't encountered 20 errors related to importing images, we can continue
392 // to import images.
393 if (*m_numErrors < 20) {
394 try {
395 QString destination = QFileInfo(m_destinationFolder, original.name())
396 .absoluteFilePath();
397 Cube *input = new Cube(original, "r");
398
399 if (m_copyDnData) {
400 Cube *copiedCube = input->copy(destination, CubeAttributeOutput());
401 delete input;
402 input = copiedCube;
403 }
404
405 FileName externalLabelFile(destination);
406 externalLabelFile = externalLabelFile.setExtension("ecub");
407
408 Cube *projectImage = input->copy(externalLabelFile, CubeAttributeOutput("+External"));
409
410 if (m_copyDnData) {
411 // Make sure the external label has a fully relative path to the DN data
412 projectImage->relocateDnData(FileName(destination).name());
413 }
414
415 // Set new ecub to readOnly. When closing cube, the labels were being re-written because
416 // the cube was read/write. This caused a segfault when imported large number of images
417 // because of a label template file being opened too many times.
418 projectImage->reopen();
419
420 delete input;
421
422 result = projectImage;
423 }
424 // When we encounter an exception, update the m_errors and m_numErrors to with the exception
425 // that occurred.
426 catch (IException &e) {
427 m_errorsLock.lock();
428
429 m_errors->append(e);
430 (*m_numErrors)++;
431
432 m_errorsLock.unlock();
433 }
434 }
435
436 return result;
437 }
438
439
450 IException result;
451
452 result.append(*m_errors);
453
454 if (*m_numErrors >= 20) {
455 result.append(
457 tr("Aborted import images due to a high number of errors"),
458 _FILEINFO_));
459 }
460 return result;
461 }
462
463
482 void ImportImagesWorkOrder::importConfirmedImages(QStringList confirmedImages, bool copyDnData) {
483 try {
484 if (!confirmedImages.isEmpty()) {
485 QDir folder = project()->addImageFolder("import");
486
487 setProgressRange(0, confirmedImages.count());
488
489 // We are creating a new QObject within an asynchronous execute(), which means that this
490 // variable, m_newImages, has thread affinity with a thread in the gloabal thread pool
491 // (i.e. m_newImages lives in a thread in the global thread pool).
492 // see WorkOrder::redo().
494 m_newImages->reserve(confirmedImages.count());
495
496 QStringList confirmedImagesFileNames;
497 QStringList confirmedImagesIds;
498
499 foreach (QString confirmedImage, confirmedImages) {
500 QStringList fileNameAndId = confirmedImage.split(",");
501 confirmedImagesFileNames.append(fileNameAndId.first());
502
503 // Determine if there was already a unique id provided for the file.
504 if (fileNameAndId.count() == 2) {
505 confirmedImagesIds.append(fileNameAndId.last());
506 }
507 else {
508 confirmedImagesIds.append(QString());
509 }
510 }
511
512 OriginalFileToProjectCubeFunctor functor(thread(), folder, copyDnData);
513 // Start concurrently copying the images to import.
514 QFuture<Cube *> future = QtConcurrent::mapped(confirmedImagesFileNames, functor);
515
516 // The new internal data will store the copied files as well as their associated unique id's.
517 QStringList newInternalData;
518 newInternalData.append(internalData().first());
519
520 // By releasing a thread from the global thread pool, we are effectively temporarily
521 // increasing the max number of available threads. This is useful when a thread goes to sleep
522 // waiting for more work, so we can allow other threads to continue.
523 // See Qt's QThreadPool::releaseThread() documentation.
524 QThreadPool::globalInstance()->releaseThread();
525 for (int i = 0; i < confirmedImages.count(); i++) {
527
528 // This will wait for the result at i to finish (the functor invocation finishes) and
529 // get the cube.
530 Cube *cube = future.resultAt(i);
531
532 if (cube) {
533
534 // Confirm that the target body and the gui camera do not exist before creating and
535 // and adding them for each image. Since a target may be covered by many cameras and a
536 // camera may cover many targets, have to get tricky with the checking.
537 QString instrumentId = cube->label()->findGroup("Instrument",
538 PvlObject::FindOptions::Traverse).findKeyword("InstrumentId")[0];
539 QString targetName = cube->label()->findGroup("Instrument",
540 PvlObject::FindOptions::Traverse).findKeyword("TargetName")[0];
541 if (!project()->hasTarget(targetName)) {
542 Camera *camera = cube->camera();
543 Target *target = camera->target();
544 project()->addTarget(target);
545
546 if (!project()->hasCamera(instrumentId)) {
547 project()->addCamera(camera);
548 }
549 }
550 else if (!project()->hasCamera(instrumentId)) {
551 Camera *camera = cube->camera();
552 project()->addCamera(camera);
553 }
554
555 // Create a new image from the result in the thread spawned in WorkOrder::redo().
556 Image *newImage = new Image(cube);
557 newImage->closeCube();
558 // Memory for cube is deleted in Image::closeCube()
559 cube = NULL;
560
561 // Either use a unique id that was already provided or create one for the new image.
562 if (confirmedImagesIds[i].isEmpty()) {
563 confirmedImagesIds[i] = newImage->id();
564 }
565 else {
566 newImage->setId(confirmedImagesIds[i]);
567 }
568
569 QStringList imageInternalData;
570 imageInternalData.append(confirmedImagesFileNames[i]);
571 imageInternalData.append(confirmedImagesIds[i]);
572
573 newInternalData.append(imageInternalData.join(","));
574
575 m_newImages->append(newImage);
576
577 // Move the new image back and its display properities to the GUI thread.
578 // Note: thread() returns the GUI thread because this ImportImagesWorkOrder lives
579 // (was created) in the GUI thread.
580 newImage->moveToThread(thread());
581 newImage->displayProperties()->moveToThread(thread());
582 }
583 }
584 // Since we temporarily increased the max thread count (by releasing a thread), make sure
585 // to re-reserve the thread for the global thread pool's accounting.
586 // See Qt's QThreadPool::reserveThread().
587 QThreadPool::globalInstance()->reserveThread();
588
589 m_warning = functor.errors().toString();
590
591 // Recall that m_newImages has thread affinity with a thread in the global thread pool.
592 // Move it to the GUI-thread because these threads in the pool do not run in an event loop,
593 // so they cannot process events.
594 // See https://doc.qt.io/qt-5/threads-qobject.html#per-thread-event-loop
595 // See http://doc.qt.io/qt-5/threads-technologies.html#comparison-of-solutions
596 m_newImages->moveToThread(thread());
597
598 if (m_newImages->isEmpty()) {
599 folder.removeRecursively();
600 }
601
602 setInternalData(newInternalData);
603 }
604 }
605 catch (IException &e) {
606 QMessageBox::critical(NULL, tr("Error"), tr(e.what()));
607 }
608 }
609}
Manipulate and parse attributes of output cube filenames.
IO Handler for Isis Cubes.
Definition Cube.h:168
void relocateDnData(FileName dnDataFile)
Relocates the DN data for a cube to an external cube label file.
Definition Cube.cpp:1372
Camera * camera()
Return a camera associated with the cube.
Definition Cube.cpp:1457
Cube * copy(FileName newFile, const CubeAttributeOutput &newFileAttributes)
Copies the cube to the new fileName.
Definition Cube.cpp:271
Pvl * label() const
Returns a pointer to the IsisLabel object associated with the cube.
Definition Cube.cpp:1707
ProjectItemModel * model()
Gets the ProjectItemModel for this directory.
File name manipulation and expansion.
Definition FileName.h:100
QString path() const
Returns the path of the file name.
Definition FileName.cpp:103
QString name() const
Returns the name of the file excluding the path and the attributes in the file name.
Definition FileName.cpp:162
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition FileName.cpp:196
QString original() const
Returns the full file name including the file path.
Definition FileName.cpp:212
QString extension() const
Returns the last extension of the file name.
Definition FileName.cpp:178
Isis exception class.
Definition IException.h:91
@ Unknown
A type of error that cannot be classified as any of the other error types.
Definition IException.h:118
void append(const IException &exceptionSource)
Appends the given exception (and its list of previous exceptions) to this exception's causational exc...
This represents a cube in a project-based GUI interface.
Definition Image.h:107
void setId(QString id)
Override the automatically generated ID with the given ID.
Definition Image.cpp:383
void closeCube()
Cleans up the Cube pointer.
Definition Image.cpp:307
ImageDisplayProperties * displayProperties()
Get the display (GUI) properties (information) associated with this image.
Definition Image.cpp:320
QString id() const
Get a unique, identifying string associated with this image.
Definition Image.cpp:445
Internalizes a list of images and allows for operations on the entire list.
Definition ImageList.h:55
void deleteFromDisk(Project *project)
Delete all of the contained Images from disk.
void append(Image *const &value)
Appends an image to the image list.
IException errors() const
Indicates if any errors occurred during the import.
Cube * operator()(const FileName &original)
Overloads the callable operator to invoke this functor.
OriginalFileToProjectCubeFunctor(QThread *guiThread, QDir destinationFolder, bool copyDnData)
Creates the internal functor.
bool m_copyDnData
Indicates whether the cube data will be copied to the project.
QDir m_destinationFolder
Directory where to import the images to.
QThread * m_guiThread
Pointer to the GUI thread. Not used?
QString m_warning
String of any errors/warnings that occurred during import.
virtual void undoExecution()
Undoes the work order's execute.
ImageList * m_list
List of images that was succesfully imported into project.
ImportImagesWorkOrder(Project *project)
Creates an asynchronous WorkOrder for importing images to the project.
virtual bool setupExecution()
Sets up this work order before being executed.
void importConfirmedImages(QStringList confirmedImages, bool copyDnData)
Imports the images.
virtual void execute()
Executes the work order.
virtual void postExecution()
Associates the imported images to the project.
ImageList * m_newImages
List of images that are being imported in this work order.
virtual bool isExecutable(ProjectItem *item)
This method returns true if the user clicked on a project tree node with the text "Images".
virtual ImportImagesWorkOrder * clone() const
Creates a clone of this work order.
virtual void postUndoExecution()
Cleans up memory (images) after the undo execution occurs.
The main project for ipce.
Definition Project.h:289
void waitForImageReaderFinished()
Locks program if another spot in code is still running and called this function.
Definition Project.cpp:1731
void addTarget(Target *target)
Adds a new target to the project.
Definition Project.cpp:2729
static QStringList images(QStringList)
Verify that the input fileNames are image files.
Definition Project.cpp:893
Directory * directory() const
Returns the directory associated with this Project.
Definition Project.cpp:1228
void setClean(bool value)
Function to change the clean state of the project.
Definition Project.cpp:1594
void addCamera(Camera *camera)
Adds a new camera to the project.
Definition Project.cpp:2760
QDir addImageFolder(QString prefix)
Create and return the name of a folder for placing images.
Definition Project.cpp:1002
void addImages(QStringList imageFiles)
Read the given cube file names as Images and add them to the project.
Definition Project.cpp:1031
Represents an item of a ProjectItemModel in Qt's model-view framework.
ProjectItem * findItemData(const QVariant &data, int role=Qt::UserRole+1)
Returns the first item found that contains the given data in the given role or a null pointer if no i...
virtual void removeItem(ProjectItem *item)
Removes an item and its children from the model.
@ Traverse
Search child objects.
Definition PvlObject.h:158
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition PvlObject.h:129
Saves a project to disk (File->Save Project...)
virtual Target * target() const
Returns a pointer to the target object.
Definition Spice.cpp:1380
This class is used to create and store valid Isis targets.
Definition Target.h:63
Provides access to sequential ASCII stream I/O.
Definition TextFile.h:38
Provide Undo/redo abilities, serialization, and history for an operation.
Definition WorkOrder.h:311
void setProgressRange(int, int)
Sets the progress range of the WorkOrder.
bool m_isSynchronous
This is defaulted to true.
Definition WorkOrder.h:530
@ WorkOrderFinished
This is used for work orders that will not undo or redo (See createsCleanState())
Definition WorkOrder.h:331
void setProgressValue(int)
Sets the current progress value for the WorkOrder.
virtual bool setupExecution()
This sets up the state for the work order.
QStringList internalData() const
Gets the internal data for this WorkOrder.
void setModifiesDiskState(bool changesProjectOnDisk)
void setInternalData(QStringList data)
Sets the internal data for this WorkOrder.
Project * project() const
Returns the Project this WorkOrder is attached to.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16