USGS

Isis 3.0 Object Programmers' Reference

Home

Image.cpp
1 #include "Image.h"
2 
3 #include <QBuffer>
4 #include <QDataStream>
5 #include <QDir>
6 #include <QFileInfo>
7 #include <QMutexLocker>
8 #include <QScopedPointer>
9 #include <QString>
10 #include <QUuid>
11 #include <QXmlStreamWriter>
12 
13 #include <geos/geom/MultiPolygon.h>
14 #include <geos/io/WKTReader.h>
15 #include <geos/io/WKTWriter.h>
16 
17 #include "Angle.h"
18 #include "Cube.h"
19 #include "CubeAttribute.h"
20 #include "DisplayProperties.h"
21 #include "Distance.h"
22 #include "ImageDisplayProperties.h"
23 #include "IString.h"
24 #include "FileName.h"
25 #include "ImagePolygon.h"
26 #include "PolygonTools.h"
27 #include "Project.h"
28 #include "XmlStackedHandlerReader.h"
29 
30 namespace Isis {
37  Image::Image(QString imageFileName, QObject *parent) : QObject(parent) {
38  m_cube = NULL;
39  m_displayProperties = NULL;
40  m_footprint = NULL;
41  m_id = NULL;
42 
43  m_aspectRatio = Null;
44  m_resolution = Null;
45  m_lineResolution = Null;
46  m_sampleResolution = Null;
47 
48  m_fileName = imageFileName;
49 
50  cube();
51 
52  initCamStats();
53 
54  try {
55  initQuickFootprint();
56  }
57  catch (IException &) {
58  }
59 
61 
62  m_id = new QUuid(QUuid::createUuid());
63  }
64 
65 
72  Image::Image(Cube *imageCube, QObject *parent) : QObject(parent) {
73  m_fileName = imageCube->fileName();
74 
75  m_cube = imageCube;
76  m_displayProperties = NULL;
77  m_footprint = NULL;
78  m_id = NULL;
79 
80  m_aspectRatio = Null;
81  m_resolution = Null;
82  m_lineResolution = Null;
83  m_sampleResolution = Null;
84 
85  initCamStats();
86 
87  try {
88  initQuickFootprint();
89  }
90  catch (IException &e) {
91  }
92 
94 
95  m_id = new QUuid(QUuid::createUuid());
96  }
97 
98 
106  Image::Image(FileName imageFolder, XmlStackedHandlerReader *xmlReader, QObject *parent) :
107  QObject(parent) {
108  m_cube = NULL;
109  m_displayProperties = NULL;
110  m_footprint = NULL;
111  m_id = NULL;
112 
113  m_aspectRatio = Null;
114  m_resolution = Null;
115  m_lineResolution = Null;
116  m_sampleResolution = Null;
117 
118  xmlReader->pushContentHandler(new XmlHandler(this, imageFolder));
119  }
120 
121 
126  delete m_cube;
127  m_cube = NULL;
128 
129  delete m_footprint;
130  m_footprint = NULL;
131 
132  delete m_id;
133  m_id = NULL;
134 
135  // Image is a "Qt" parent of display properties, so the Image QObject
136  // destructor will take care of deleting the display props. See call to
137  // DisplayProperties' constructor.
138  m_displayProperties = NULL;
139  }
140 
141 
154  void Image::fromPvl(const PvlObject &pvl) {
155  QString pvlFileName = ((IString)pvl["FileName"][0]).ToQt();
156  if (m_fileName != pvlFileName) {
158  tr("Tried to load Image [%1] with properties/information from [%2].")
159  .arg(m_fileName).arg(pvlFileName),
160  _FILEINFO_);
161  }
162 
163  displayProperties()->fromPvl(pvl.findObject("DisplayProperties"));
164 
165  if (pvl.hasKeyword("ID")) {
166  QByteArray hexValues(pvl["ID"][0].toAscii());
167  QDataStream valuesStream(QByteArray::fromHex(hexValues));
168  valuesStream >> *m_id;
169  }
170  }
171 
172 
187  PvlObject output("Image");
188 
189  output += PvlKeyword("FileName", m_fileName);
190 
191  // Do m_id
192  QBuffer dataBuffer;
193  dataBuffer.open(QIODevice::ReadWrite);
194 
195  QDataStream idStream(&dataBuffer);
196  idStream << *m_id;
197 
198  dataBuffer.seek(0);
199 
200  output += PvlKeyword("ID", QString(dataBuffer.data().toHex()));
201 
202  output += displayProperties()->toPvl();
203 
204  return output;
205  }
206 
207 
212  bool Image::isFootprintable() const {
213  bool result = false;
214 
215  if (m_footprint)
216  result = true;
217 
218  if (!result && m_cube) {
219  // TODO: Move this to Blob!
220  ImagePolygon example;
221 
222  QString blobType = example.Type();
223  QString blobName = example.Name();
224 
225  Pvl &labels = *m_cube->label();
226 
227  for (int i = 0; i < labels.objects(); i++) {
228  PvlObject &obj = labels.object(i);
229 
230  if (obj.isNamed(blobType) && obj.hasKeyword("Name") && obj["Name"][0] == blobName)
231  result = true;
232  }
233  }
234 
235  return result;
236  }
237 
238 
244  if (!m_cube) {
245  m_cube = new Cube(m_fileName);
246  }
247 
248  return m_cube;
249  }
250 
251 
257  if (m_cube) {
258  delete m_cube;
259  m_cube = NULL;
260  }
261  }
262 
263 
270  return m_displayProperties;
271  }
272 
273 
281  return m_displayProperties;
282  }
283 
284 
290  QString Image::fileName() const {
291  return m_fileName;
292  }
293 
294 
300  geos::geom::MultiPolygon *Image::footprint() {
301  return m_footprint;
302  }
303 
304 
308  void Image::setId(QString id) {
309  *m_id = QUuid(QString("{%1}").arg(id));
310  }
311 
312 
318  const geos::geom::MultiPolygon *Image::footprint() const {
319  return m_footprint;
320  }
321 
322 
327  bool Image::initFootprint(QMutex *cameraMutex) {
328  if (!m_footprint) {
329  try {
330  initQuickFootprint();
331  }
332  catch (IException &e) {
333  try {
334  m_footprint = createFootprint(cameraMutex);
335  }
336  catch(IException &e) {
337  IString msg = "Could not read the footprint from cube [" +
338  displayProperties()->displayName() + "]. Please make "
339  "sure footprintinit has been run";
340  throw IException(e, IException::Io, msg, _FILEINFO_);
341  }
342  }
343  }
344 
345  // I'm not sure how this could ever be NULL. -SL
346  return (m_footprint != NULL);
347  }
348 
349 
355  double Image::aspectRatio() const {
356  return m_aspectRatio;
357  }
358 
359 
365  QString Image::id() const {
366  return m_id->toString().remove(QRegExp("[{}]"));
367  }
368 
369 
376  double Image::resolution() const {
377  return m_resolution;
378  }
379 
380 
388  return m_emissionAngle;
389  }
390 
391 
399  return m_incidenceAngle;
400  }
401 
402 
409  double Image::lineResolution() const {
410  return m_lineResolution;
411  }
412 
413 
421  return m_localRadius;
422  }
423 
424 
432  return m_northAzimuth;
433  }
434 
435 
443  return m_phaseAngle;
444  }
445 
446 
453  double Image::sampleResolution() const {
454  return m_sampleResolution;
455  }
456 
457 
461  void Image::copyToNewProjectRoot(const Project *project, FileName newProjectRoot) {
462  if (FileName(newProjectRoot) != FileName(project->projectRoot())) {
463  Cube origImage(m_fileName);
464 
465  FileName newExternalLabelFileName(Project::imageDataRoot(newProjectRoot.toString()) + "/" +
466  FileName(m_fileName).dir().dirName() + "/" + FileName(m_fileName).name());
467 
468  QScopedPointer<Cube> newExternalLabel(
469  origImage.copy(newExternalLabelFileName, CubeAttributeOutput("+External")));
470 
471  // If this is an ecub (it should be) and is pointing to a relative file name,
472  // then we want to copy the DN cube also.
473  if (!origImage.storesDnData()) {
474  if (origImage.externalCubeFileName().path() == ".") {
475  Cube dnFile(
476  FileName(m_fileName).path() + "/" + origImage.externalCubeFileName().name());
477 
478  FileName newDnFileName = newExternalLabelFileName.setExtension("cub");
479 
480  QScopedPointer<Cube> newDnFile(dnFile.copy(newDnFileName, CubeAttributeOutput()));
481  newDnFile->close();
482 
483  newExternalLabel->relocateDnData(newDnFileName.name());
484  }
485  else {
486  newExternalLabel->relocateDnData(origImage.externalCubeFileName());
487  }
488  }
489  }
490  }
491 
492 
498  bool deleteCubAlso = (cube()->externalCubeFileName().path() == ".");
499  closeCube();
500 
501  if (!QFile::remove(m_fileName)) {
503  tr("Could not remove file [%1]").arg(m_fileName),
504  _FILEINFO_);
505  }
506 
507  if (deleteCubAlso) {
508  FileName cubFile = FileName(m_fileName).setExtension("cub");
509  if (!QFile::remove(cubFile.expanded())) {
511  tr("Could not remove file [%1]").arg(m_fileName),
512  _FILEINFO_);
513  }
514  }
515 
516  // If we're the last thing in the folder, remove the folder too.
517  QDir dir;
518  dir.rmdir(FileName(m_fileName).path());
519  }
520 
521 
532  void Image::save(QXmlStreamWriter &stream, const Project *project, FileName newProjectRoot)
533  const {
534  stream.writeStartElement("image");
535 
536  stream.writeAttribute("id", m_id->toString());
537  stream.writeAttribute("fileName", FileName(m_fileName).name());
538 
539  if (!IsSpecial(m_aspectRatio)) {
540  stream.writeAttribute("aspectRatio", IString(m_aspectRatio).ToQt());
541  }
542 
543  if (!IsSpecial(m_resolution)) {
544  stream.writeAttribute("resolution", IString(m_resolution).ToQt());
545  }
546 
547  if (m_emissionAngle.isValid()) {
548  stream.writeAttribute("emissionAngle", IString(m_emissionAngle.radians()).ToQt());
549  }
550 
551  if (m_incidenceAngle.isValid()) {
552  stream.writeAttribute("incidenceAngle", IString(m_incidenceAngle.radians()).ToQt());
553  }
554 
555  if (!IsSpecial(m_lineResolution)) {
556  stream.writeAttribute("lineResolution", IString(m_lineResolution).ToQt());
557  }
558 
559  if (m_localRadius.isValid()) {
560  stream.writeAttribute("localRadius", IString(m_localRadius.meters()).ToQt());
561  }
562 
563  if (m_northAzimuth.isValid()) {
564  stream.writeAttribute("northAzimuth", IString(m_northAzimuth.radians()).ToQt());
565  }
566 
567  if (m_phaseAngle.isValid()) {
568  stream.writeAttribute("phaseAngle", IString(m_phaseAngle.radians()).ToQt());
569  }
570 
571  if (!IsSpecial(m_sampleResolution)) {
572  stream.writeAttribute("sampleResolution", IString(m_sampleResolution).ToQt());
573  }
574 
575  if (m_footprint) {
576  stream.writeStartElement("footprint");
577 
578  geos::io::WKTWriter wktWriter;
579  stream.writeCharacters(QString::fromStdString(wktWriter.write(m_footprint)));
580 
581  stream.writeEndElement();
582  }
583 
584  m_displayProperties->save(stream, project, newProjectRoot);
585 
586  stream.writeEndElement();
587  }
588 
589 
597  closeCube();
598 
599  FileName original(m_fileName);
600  FileName newName(project->imageDataRoot() + "/" +
601  original.dir().dirName() + "/" + original.name());
602  m_fileName = newName.expanded();
603  }
604 
605 
612  geos::geom::MultiPolygon *Image::createFootprint(QMutex *cameraMutex) {
613  QMutexLocker lock(cameraMutex);
614 
615  // We need to walk the image to create the polygon...
616  ImagePolygon imgPoly;
617 
618  int sampleStepSize = cube()->sampleCount() / 10;
619  if (sampleStepSize <= 0) sampleStepSize = 1;
620 
621  int lineStepSize = cube()->lineCount() / 10;
622  if (lineStepSize <= 0) lineStepSize = 1;
623 
624  imgPoly.Create(*cube(), sampleStepSize, lineStepSize);
625 
627  tr("Warning: Polygon re-calculated for [%1] which can be very slow")
628  .arg(displayProperties()->displayName()),
629  _FILEINFO_);
630  e.print();
631 
632  return PolygonTools::MakeMultiPolygon(imgPoly.Polys()->clone());
633  }
634 
635 
636  void Image::initCamStats() {
637  bool hasCamStats = false;
638 
639  Pvl &label = *cube()->label();
640  for (int i = 0; !hasCamStats && i < label.objects(); i++) {
641  PvlObject &obj = label.object(i);
642 
643  try {
644  if (obj.name() == "Table") {
645  if (obj["Name"][0] == "CameraStatistics") {
646  hasCamStats = true;
647  }
648  }
649  }
650  catch (IException &) {
651  }
652  }
653 
654  if (hasCamStats) {
655  Table camStatsTable("CameraStatistics", m_fileName, label);
656 
657  int numRecords = camStatsTable.Records();
658  for (int recordIndex = 0; recordIndex < numRecords; recordIndex++) {
659  TableRecord &record = camStatsTable[recordIndex];
660 
661  // The TableField class gives us a std::string with NULL (\0) characters... be careful not
662  // to keep them when going to QString.
663  QString recordName((QString)record["Name"]);
664  double avgValue = (double)record["Average"];
665 
666  if (recordName == "AspectRatio") {
667  m_aspectRatio = avgValue;
668  }
669  else if (recordName == "Resolution") {
670  m_resolution = avgValue;
671  }
672  else if (recordName == "EmissionAngle") {
673  m_emissionAngle = Angle(avgValue, Angle::Degrees);
674  }
675  else if (recordName == "IncidenceAngle") {
676  m_incidenceAngle = Angle(avgValue, Angle::Degrees);
677  }
678  else if (recordName == "LineResolution") {
679  m_lineResolution = avgValue;
680  }
681  else if (recordName == "LocalRadius") {
682  m_localRadius = Distance(avgValue, Distance::Meters);
683  }
684  else if (recordName == "NorthAzimuth") {
685  m_northAzimuth = Angle(avgValue, Angle::Degrees);
686  }
687  else if (recordName == "PhaseAngle") {
688  m_phaseAngle = Angle(avgValue, Angle::Degrees);
689  }
690  else if (recordName == "SampleResolution") {
691  m_sampleResolution = avgValue;
692  }
693  }
694  }
695  }
696 
697 
698  void Image::initQuickFootprint() {
699  ImagePolygon poly;
700  cube()->read(poly);
701  m_footprint = PolygonTools::MakeMultiPolygon(poly.Polys()->clone());
702  }
703 
704 
713  m_image = image;
714  m_imageFolder = imageFolder;
715  }
716 
717 
723  bool Image::XmlHandler::startElement(const QString &namespaceURI, const QString &localName,
724  const QString &qName, const QXmlAttributes &atts) {
725  m_characters = "";
726 
727  if (XmlStackedHandler::startElement(namespaceURI, localName, qName, atts)) {
728  if (localName == "image") {
729  QString id = atts.value("id");
730  QString fileName = atts.value("fileName");
731 
732  QString aspectRatioStr = atts.value("aspectRatio");
733  QString resolutionStr = atts.value("resolution");
734  QString emissionAngleStr = atts.value("emissionAngle");
735  QString incidenceAngleStr = atts.value("incidenceAngle");
736  QString lineResolutionStr = atts.value("lineResolution");
737  QString localRadiusStr = atts.value("localRadius");
738  QString northAzimuthStr = atts.value("northAzimuth");
739  QString phaseAngleStr = atts.value("phaseAngle");
740  QString sampleResolutionStr = atts.value("sampleResolution");
741 
742  if (!id.isEmpty()) {
743  delete m_image->m_id;
744  m_image->m_id = NULL;
745  m_image->m_id = new QUuid(id.toAscii());
746  }
747 
748  if (!fileName.isEmpty()) {
749  m_image->m_fileName = m_imageFolder.expanded() + "/" + fileName;
750  }
751 
752  if (!aspectRatioStr.isEmpty()) {
753  m_image->m_aspectRatio = aspectRatioStr.toDouble();
754  }
755 
756  if (!resolutionStr.isEmpty()) {
757  m_image->m_resolution = resolutionStr.toDouble();
758  }
759 
760  if (!emissionAngleStr.isEmpty()) {
761  m_image->m_emissionAngle = Angle(emissionAngleStr.toDouble(), Angle::Radians);
762  }
763 
764  if (!incidenceAngleStr.isEmpty()) {
765  m_image->m_incidenceAngle = Angle(incidenceAngleStr.toDouble(), Angle::Radians);
766  }
767 
768  if (!lineResolutionStr.isEmpty()) {
769  m_image->m_lineResolution = lineResolutionStr.toDouble();
770  }
771 
772  if (!localRadiusStr.isEmpty()) {
773  m_image->m_localRadius = Distance(localRadiusStr.toDouble(), Distance::Meters);
774  }
775 
776  if (!northAzimuthStr.isEmpty()) {
777  m_image->m_northAzimuth = Angle(northAzimuthStr.toDouble(), Angle::Radians);
778  }
779 
780  if (!phaseAngleStr.isEmpty()) {
781  m_image->m_phaseAngle = Angle(phaseAngleStr.toDouble(), Angle::Radians);
782  }
783 
784  if (!sampleResolutionStr.isEmpty()) {
785  m_image->m_sampleResolution = sampleResolutionStr.toDouble();
786  }
787  }
788  else if (localName == "displayProperties") {
789  m_image->m_displayProperties = new ImageDisplayProperties(reader());
790  }
791  }
792 
793  return true;
794  }
795 
796 
797 
798  bool Image::XmlHandler::characters(const QString &ch) {
799  m_characters += ch;
800 
801  return XmlStackedHandler::characters(ch);
802  }
803 
804 
805 
806  bool Image::XmlHandler::endElement(const QString &namespaceURI, const QString &localName,
807  const QString &qName) {
808  if (localName == "footprint" && !m_characters.isEmpty()) {
809  geos::io::WKTReader wktReader(&globalFactory);
810  m_image->m_footprint = PolygonTools::MakeMultiPolygon(
811  wktReader.read(m_characters.toStdString()));
812  }
813  else if (localName == "image" && !m_image->m_footprint) {
814  QMutex mutex;
815  m_image->initFootprint(&mutex);
816  m_image->closeCube();
817  }
818 
819  m_characters = "";
820  return XmlStackedHandler::endElement(namespaceURI, localName, qName);
821  }
822 }
823 
824