Isis 3 Programmer Reference
ProcessExportPds4.cpp
1 
19 #include "ProcessExportPds4.h"
20 
21 #include <cmath>
22 #include <iostream>
23 #include <sstream>
24 
25 #include <QDomDocument>
26 #include <QMap>
27 #include <QRegularExpression>
28 #include <QString>
29 
30 #include "Application.h"
31 #include "FileName.h"
32 #include "IException.h"
33 #include "Projection.h"
34 #include "ProjectionFactory.h"
35 #include "Pvl.h"
37 
38 using namespace std;
39 
40 namespace Isis {
41 
42 
47  ProcessExportPds4::ProcessExportPds4() {
48 
49  m_lid = "";
50  m_versionId = "";
51  m_title = "";
52 
53  m_imageType = StandardImage;
54 
55  qSetGlobalQHashSeed(1031); // hash seed to force consistent output
56 
57  m_domDoc = new QDomDocument("");
58 
59  // base xml file
60  // <?xml version="1.0" encoding="UTF-8"?>
61  QString xmlVersion = "version=\"1.0\" encoding=\"utf-8\"";
62  QDomProcessingInstruction xmlHeader =
63  m_domDoc->createProcessingInstruction("xml", xmlVersion);
64  m_domDoc->appendChild(xmlHeader);
65 
66  // base pds4 schema location
67  m_schemaLocation = "http://pds.nasa.gov/pds4/pds/v1 http://pds.nasa.gov/pds4/pds/v1/PDS4_PDS_1B00.xsd";
68 
69  QString xmlModel;
70  xmlModel += "href=\"http://pds.nasa.gov/pds4/pds/v1/PDS4_PDS_1B00.sch\" ";
71  xmlModel += "schematypens=\"http://purl.oclc.org/dsdl/schematron\"";
72  QDomProcessingInstruction header =
73  m_domDoc->createProcessingInstruction("xml-model", xmlModel);
74  m_domDoc->appendChild(header);
75 
76  }
77 
78 
83  ProcessExportPds4::~ProcessExportPds4() {
84  delete m_domDoc;
85  m_domDoc = NULL;
86  }
87 
88 
94  QDomDocument &ProcessExportPds4::StandardPds4Label() {
95  CreateImageLabel();
96  translateUnits(*m_domDoc);
97  return *m_domDoc;
98  }
99 
100 
106  void ProcessExportPds4::setImageType(ImageType imageType) {
107  m_imageType = imageType;
108  }
109 
110 
120  void ProcessExportPds4::CreateImageLabel() {
121  if (InputCubes.size() == 0) {
122  QString msg("Must set an input cube before creating a PDS4 label.");
123  throw IException(IException::Programmer, msg, _FILEINFO_);
124  }
125  if (m_domDoc->documentElement().isNull()) {
126  QDomElement root = m_domDoc->createElement("Product_Observational");
127  root.setAttribute("xmlns", "http://pds.nasa.gov/pds4/pds/v1");
128  root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
129  root.setAttribute("xsi:schemaLocation",
130  "http://pds.nasa.gov/pds4/pds/v1 http://pds.nasa.gov/pds4/pds/v1");
131  m_domDoc->appendChild(root);
132  }
133 
134  try {
135  // <Product_Observational>
136  // <Identification_Area>
137  identificationArea();
138  }
139  catch (IException &e) {
140  QString msg = "Unable to translate and export identification information.";
141  throw IException(e, IException::Programmer, msg, _FILEINFO_);
142  }
143  try {
144  // <Product_Observational>
145  // <Observation_Area>
146  standardInstrument();
147  }
148  catch (IException &e) {
149  QString msg = "Unable to translate and export instrument information.";
150  throw IException(e, IException::Programmer, msg, _FILEINFO_);
151  }
152  try {
153  // <Product_Observational>
154  // <Observation_Area>
155  // <Discipline_Area>
156  // <disp:Display_Settings>
157  displaySettings();
158  }
159  catch (IException &e) {
160  QString msg = "Unable to translate and export display settings.";
161  throw IException(e, IException::Programmer, msg, _FILEINFO_);
162  }
163 
164  try {
165  // <Product_Observational>
166  // <Observation_Area>
167  // <Discipline_Area>
168  // <sp:Spectral_Characteristics> OR <img:Imaging>
169  standardBandBin();
170  }
171  catch (IException &e) {
172  QString msg = "Unable to translate and export spectral information.";
173  throw IException(e, IException::Programmer, msg, _FILEINFO_);
174  }
175 
176  try {
177  // <Product_Observational>
178  // <Observation_Area>
179  // <Discipline_Area>
180  // <card:Cartography>
181  StandardAllMapping();
182  }
183  catch (IException &e) {
184  QString msg = "Unable to translate and export mapping group.";
185  throw IException(e, IException::Programmer, msg, _FILEINFO_);
186  }
187  try {
188  // <Product_Observational>
189  // <File_Area_Observational>
190  fileAreaObservational();
191  }
192  catch (IException &e) {
193  QString msg = "Unable to translate and export standard image information.";
194  throw IException(e, IException::Programmer, msg, _FILEINFO_);
195  }
196  }
197 
198 
203  void ProcessExportPds4::standardInstrument() {
204  Pvl *inputLabel = InputCubes[0]->label();
205  FileName translationFileName;
206 
207  if (inputLabel->findObject("IsisCube").hasGroup("Instrument")) {
208 
209  // Translate the Instrument group
210  translationFileName = "$base/translations/pds4ExportInstrument.trn";
211  PvlToXmlTranslationManager instXlator(*inputLabel, translationFileName.expanded());
212  instXlator.Auto(*m_domDoc);
213 
214  // If instrument and spacecraft values were translated, create the combined name
215  QDomElement obsAreaNode = m_domDoc->documentElement().firstChildElement("Observation_Area");
216 
217  if ( !obsAreaNode.isNull() ) {
218 
219  // fix start/stop times, if needed
220  QDomElement timeNode = obsAreaNode.firstChildElement("Time_Coordinates");
221  if (!timeNode.isNull()) {
222  QDomElement startTime = timeNode.firstChildElement("start_date_time");
223  if (startTime.text() == "") {
224  startTime.setAttribute("xsi:nil", "true");
225  }
226  else {
227  QString timeValue = startTime.text();
228  PvlToXmlTranslationManager::resetElementValue(startTime, timeValue + "Z");
229  }
230  QDomElement stopTime = timeNode.firstChildElement("stop_date_time");
231  if (stopTime.text() == "") {
232  stopTime.setAttribute("xsi:nil", "true");
233  }
234  else {
235  QString timeValue = stopTime.text();
236  PvlToXmlTranslationManager::resetElementValue(stopTime, timeValue + "Z");
237  }
238  }
239 
240  QDomElement obsSysNode = obsAreaNode.firstChildElement("Observing_System");
241  if ( !obsSysNode.isNull() ) {
242  QString instrumentName;
243  QString spacecraftName;
244  QDomElement obsSysCompNode = obsSysNode.firstChildElement("Observing_System_Component");
245  while ( !obsSysCompNode.isNull()) {
246  QDomElement compTypeNode = obsSysCompNode.firstChildElement("type");
247  if ( compTypeNode.text().compare("Spacecraft") == 0 ) {
248  QString componentName = obsSysCompNode.firstChildElement("name").text();
249  if (QString::compare(componentName, "TBD", Qt::CaseInsensitive) != 0) {
250  spacecraftName = componentName;
251  }
252  }
253  else if ( compTypeNode.text().compare("Instrument") == 0 ) {
254  QString componentName = obsSysCompNode.firstChildElement("name").text();
255  if (QString::compare(componentName, "TBD", Qt::CaseInsensitive) != 0) {
256  instrumentName = componentName;
257  }
258  }
259  obsSysCompNode = obsSysCompNode.nextSiblingElement("Observing_System_Component");
260  }
261  QDomElement combinedNode = m_domDoc->createElement("name");
262  QString combinedValue = "TBD";
263  if ( !instrumentName.isEmpty() && !spacecraftName.isEmpty() ) {
264  combinedValue = spacecraftName + " " + instrumentName;
265  }
266  combinedNode.appendChild( m_domDoc->createTextNode(combinedValue) );
267  obsSysNode.insertBefore( combinedNode, obsSysNode.firstChild() );
268  }
269  }
270 
271  // Translate the Target name
272  translationFileName = "$base/translations/pds4ExportTargetFromInstrument.trn";
273  PvlToXmlTranslationManager targXlator(*inputLabel, translationFileName.expanded());
274  targXlator.Auto(*m_domDoc);
275 
276  // Move target to just below Observing_System.
277  QDomElement targetIdNode = obsAreaNode.firstChildElement("Target_Identification");
278  obsAreaNode.insertAfter(targetIdNode, obsAreaNode.firstChildElement("Observing_System"));
279  }
280  else if (inputLabel->findObject("IsisCube").hasGroup("Mapping")) {
281 
282  translationFileName = "$base/translations/pds4ExportTargetFromMapping.trn";
283  PvlToXmlTranslationManager targXlator(*inputLabel, translationFileName.expanded());
284  targXlator.Auto(*m_domDoc);
285  }
286  else {
287  throw IException(IException::Unknown, "Unable to find a target in input cube.", _FILEINFO_);
288  }
289  }
290 
291 
296  void ProcessExportPds4::reorder() {
297  QDomElement obsAreaNode = m_domDoc->documentElement().firstChildElement("Observation_Area");
298  if ( !obsAreaNode.isNull() ) {
299 
300  // fix times
301  QDomElement timeNode = obsAreaNode.firstChildElement("Time_Coordinates");
302  if (!timeNode.isNull()) {
303  QDomElement startTime = timeNode.firstChildElement("start_date_time");
304  if (startTime.text() == "") {
305  startTime.setAttribute("xsi:nil", "true");
306  }
307  else {
308  QString timeValue = startTime.text();
309  if (!timeValue.contains("Z")) {
310  PvlToXmlTranslationManager::resetElementValue(startTime, timeValue + "Z");
311  }
312  }
313 
314  QDomElement stopTime = timeNode.firstChildElement("stop_date_time");
315  if (stopTime.text() == "") {
316  stopTime.setAttribute("xsi:nil", "true");
317  }
318  else {
319  QString timeValue = stopTime.text();
320  if (!timeValue.contains("Z")) {
321  PvlToXmlTranslationManager::resetElementValue(stopTime, timeValue + "Z");
322  }
323  }
324  QStringList xmlPath;
325  xmlPath << "Product_Observational"
326  << "Observation_Area"
327  << "Discipline_Area"
328  << "geom:Geometry"
329  << "geom:Geometry_Orbiter"
330  << "geom:geometry_reference_time_utc";
331 
332  QDomElement baseElement = m_domDoc->documentElement();
333  QDomElement geomRefTime = getElement(xmlPath, baseElement);
334  if (geomRefTime.text() == "") {
335  geomRefTime.setAttribute("xsi:nil", "true");
336  }
337  else {
338  QString timeValue = geomRefTime.text();
339  PvlToXmlTranslationManager::resetElementValue(geomRefTime, timeValue + "Z");
340  }
341  xmlPath.clear();
342  xmlPath << "Product_Observational"
343  << "Observation_Area"
344  << "Discipline_Area"
345  << "geom:Geometry"
346  << "geom:Image_Display_Geometry"
347  << "geom:Object_Orientation_North_East"
348  << "geom:east_azimuth";
349  QDomElement eastAzimuth = getElement(xmlPath, baseElement);
350  if (eastAzimuth.text() != "") {
351  PvlToXmlTranslationManager::resetElementValue(eastAzimuth, eastAzimuth.text(), "deg");
352  }
353  }
354 
355  QDomElement investigationAreaNode = obsAreaNode.firstChildElement("Investigation_Area");
356  obsAreaNode.insertAfter(investigationAreaNode, obsAreaNode.firstChildElement("Time_Coordinates"));
357 
358  QDomElement obsSystemNode = obsAreaNode.firstChildElement("Observing_System");
359  obsAreaNode.insertAfter(obsSystemNode, obsAreaNode.firstChildElement("Investigation_Area"));
360 
361  QDomElement targetIdNode = obsAreaNode.firstChildElement("Target_Identification");
362  obsAreaNode.insertAfter(targetIdNode, obsAreaNode.firstChildElement("Observing_System"));
363 
364  QDomElement missionAreaNode = obsAreaNode.firstChildElement("Mission_Area");
365  obsAreaNode.insertAfter(missionAreaNode, obsAreaNode.firstChildElement("Target_Identification"));
366 
367  QDomElement disciplineAreaNode = obsAreaNode.firstChildElement("Discipline_Area");
368  obsAreaNode.insertAfter(disciplineAreaNode, obsAreaNode.firstChildElement("Mission_Area"));
369  }
370 
371  QDomElement identificationAreaNode = m_domDoc->documentElement().firstChildElement("Identification_Area");
372  if ( !identificationAreaNode.isNull() ) {
373  QDomElement aliasListNode = identificationAreaNode.firstChildElement("Alias_List");
374  identificationAreaNode.insertAfter(aliasListNode, identificationAreaNode.firstChildElement("product_class"));
375  }
376 
377  QDomElement fileAreaObservationalNode = m_domDoc->documentElement().firstChildElement("File_Area_Observational");
378  QDomElement array2DImageNode = fileAreaObservationalNode.firstChildElement("Array_2D_Image");
379  if ( !array2DImageNode.isNull() ) {
380  QDomElement descriptionNode = array2DImageNode.firstChildElement("description");
381  array2DImageNode.insertAfter(descriptionNode, array2DImageNode.firstChildElement("axis_index_order"));
382  }
383  }
384 
413  void ProcessExportPds4::setLogicalId(QString lid) {
414  m_lid = lid.toLower();
415  }
416 
417 
431  void ProcessExportPds4::setVersionId(QString versionId) {
432  m_versionId = versionId;
433  }
434 
435 
446  void ProcessExportPds4::setTitle(QString title) {
447  m_title = title;
448  }
449 
450 
459  void ProcessExportPds4::setSchemaLocation(QString schema) {
460  m_schemaLocation = schema;
461  }
462 
463 
468  void ProcessExportPds4::identificationArea() {
469  Pvl *inputLabel = InputCubes[0]->label();
470  FileName translationFileName;
471  translationFileName = "$base/translations/pds4ExportIdentificationArea.trn";
472  PvlToXmlTranslationManager xlator(*inputLabel, translationFileName.expanded());
473  xlator.Auto(*m_domDoc);
474 
475  if (m_lid.isEmpty()) {
476  m_lid = "urn:nasa:pds:TBD:TBD:TBD";
477  }
478 
479  QDomElement identificationElement;
480  QStringList identificationPath;
481  identificationPath.append("Product_Observational");
482  identificationPath.append("Identification_Area");
483  try {
484  identificationElement = getElement(identificationPath);
485  if( identificationElement.isNull() ) {
486  throw IException(IException::Unknown, "", _FILEINFO_);
487  }
488  }
489  catch(IException &e) {
490  QString msg = "Could not find Identification_Area element "
491  "to add modification history under.";
492  throw IException(IException::Programmer, msg, _FILEINFO_);
493  }
494 
495  QDomElement lidElement = identificationElement.firstChildElement("logical_identifier");
496  PvlToXmlTranslationManager::resetElementValue(lidElement, m_lid);
497 
498  if (m_versionId != "") {
499  QDomElement versionElement = identificationElement.firstChildElement("version_id");
500  PvlToXmlTranslationManager::resetElementValue(versionElement, m_versionId);
501  }
502 
503  if (m_title != "") {
504  QDomElement titleElement = identificationElement.firstChildElement("title");
505  PvlToXmlTranslationManager::resetElementValue(titleElement, m_title);
506  }
507 
508  // Get export history and add <Modification_History> element.
509  // These regular expressions match the pipe followed by the date from
510  // the Application::Version() return value.
511  QRegularExpression versionRegex(" \\| \\d{4}\\-\\d{2}\\-\\d{2}");
512  QString historyDescription = "Created PDS4 output product from ISIS cube with the "
513  + FileName(Application::Name()).baseName()
514  + " application from ISIS version "
515  + Application::Version().remove(versionRegex) + ".";
516  // This regular expression matches the time from the Application::DateTime return value.
517  QRegularExpression dateRegex("T\\d{2}:\\d{2}:\\d{2}");
518  QString historyDate = Application::DateTime().remove(dateRegex);
519  addHistory(historyDescription, historyDate);
520  }
521 
522 
527  void ProcessExportPds4::displaySettings() {
528  // Add header info
529  addSchema("PDS4_DISP_1B00.sch",
530  "PDS4_DISP_1B00.xsd",
531  "xmlns:disp",
532  "http://pds.nasa.gov/pds4/disp/v1");
533 
534  Pvl *inputLabel = InputCubes[0]->label();
535  FileName translationFileName;
536  translationFileName = "$base/translations/pds4ExportDisplaySettings.trn";
537  PvlToXmlTranslationManager xlator(*inputLabel, translationFileName.expanded());
538  xlator.Auto(*m_domDoc);
539  }
540 
541 
546  void ProcessExportPds4::standardBandBin() {
547  Pvl *inputLabel = InputCubes[0]->label();
548  if ( !inputLabel->findObject("IsisCube").hasGroup("BandBin") ) return;
549  // Add header info
550  addSchema("PDS4_IMG_1A10_1510.sch",
551  "PDS4_IMG_1A10_1510.xsd",
552  "xmlns:img",
553  "http://pds.nasa.gov/pds4/img/v1");
554 
555  // Get the input Isis cube label and find the BandBin group if it has one
556  if (m_imageType == StandardImage) {
557  translateBandBinImage(*inputLabel);
558  }
559  else {
560  // Add header info
561  addSchema("PDS4_SP_1100.sch",
562  "PDS4_SP_1100.xsd",
563  "xmlns:sp",
564  "http://pds.nasa.gov/pds4/sp/v1");
565  if (m_imageType == UniformlySampledSpectrum) {
566  translateBandBinSpectrumUniform(*inputLabel);
567  }
568  else if (m_imageType == BinSetSpectrum) {
569  translateBandBinSpectrumBinSet(*inputLabel);
570  }
571  }
572  }
573 
574 
578  void ProcessExportPds4::translateBandBinImage(Pvl &inputLabel) {
579  QString translationFile = "$base/translations/";
580  translationFile += "pds4ExportBandBinImage.trn";
581  FileName translationFileName(translationFile);
582  PvlToXmlTranslationManager xlator(inputLabel, translationFileName.expanded());
583  xlator.Auto(*m_domDoc);
584  }
585 
586 
590  void ProcessExportPds4::translateBandBinSpectrumUniform(Pvl &inputLabel) {
591  QString translationFile = "$base/translations/";
592  translationFile += "pds4ExportBandBinSpectrumUniform.trn";
593  FileName translationFileName(translationFile);
594  PvlToXmlTranslationManager xlator(inputLabel, translationFileName.expanded());
595  xlator.Auto(*m_domDoc);
596 
597  PvlGroup bandBinGroup = inputLabel.findObject("IsisCube").findGroup("BandBin");
598  // fix multi-valued bandbin info
599  QStringList xmlPath;
600  xmlPath << "Product_Observational"
601  << "Observation_Area"
602  << "Discipline_Area"
603  << "sp:Spectral_Characteristics";
604  QDomElement baseElement = m_domDoc->documentElement();
605  QDomElement spectralCharElement = getElement(xmlPath, baseElement);
606 
607  // Axis_Bin_Set for variable bin widths
608  // required - bin_sequence_number, center_value, bin_width
609  // optional - detector_number, grating_position, original_bin_number, scaling_factor, value_offset, Filter
610  // ... see schema for more...
611  PvlKeyword center;
612  if (bandBinGroup.hasKeyword("Center")) {
613  center = bandBinGroup["Center"];
614  }
615  else if (bandBinGroup.hasKeyword("FilterCenter")) {
616  center = bandBinGroup["FilterCenter"];
617  }
618  else {
619  QString msg = "Unable to translate BandBin info for BinSetSpectrum. "
620  "Translation for PDS4 required value [center_value] not found.";
621  throw IException(IException::Programmer, msg, _FILEINFO_);
622  }
623  PvlKeyword width;
624  if (bandBinGroup.hasKeyword("Width")) {
625  width = bandBinGroup["Width"];
626  }
627  else if (bandBinGroup.hasKeyword("FilterWidth")) {
628  width = bandBinGroup["FilterWidth"];
629  }
630  else {
631  QString msg = "Unable to translate BandBin info for BinSetSpectrum. "
632  "Translation for PDS4 required value [bin_width] not found.";
633  throw IException(IException::Programmer, msg, _FILEINFO_);
634  }
635 
636  QString units = center.unit();
637 
638  if (!width.unit().isEmpty() ) {
639  if (units.isEmpty()) {
640  units = width.unit();
641  }
642  if (units.compare(width.unit(), Qt::CaseInsensitive) != 0) {
643  QString msg = "Unable to translate BandBin info for BinSetSpectrum. "
644  "Unknown or unmatching units for [center_value] and [bin_width].";
645  throw IException(IException::Programmer, msg, _FILEINFO_);
646  }
647  }
648 
649  PvlKeyword originalBand;
650  if (bandBinGroup.hasKeyword("OriginalBand")) {
651  originalBand = bandBinGroup["OriginalBand"];
652  }
653  PvlKeyword name;
654  if (bandBinGroup.hasKeyword("Name")) {
655  name = bandBinGroup["Name"];
656  }
657  else if (bandBinGroup.hasKeyword("FilterName")) {
658  name = bandBinGroup["FilterName"];
659  }
660  else if (bandBinGroup.hasKeyword("FilterId")) {
661  name = bandBinGroup["FilterId"];
662  }
663  PvlKeyword number;
664  if (bandBinGroup.hasKeyword("Number")) {
665  number = bandBinGroup["Number"];
666  }
667  else if (bandBinGroup.hasKeyword("FilterNumber")) {
668  number = bandBinGroup["FilterNumber"];
669  }
670 
671  QDomElement axisBinSetElement = spectralCharElement.firstChildElement("sp:Axis_Bin_Set");
672  if (axisBinSetElement.isNull()) {
673  axisBinSetElement = m_domDoc->createElement("sp:Axis_Bin_Set");
674  spectralCharElement.appendChild(axisBinSetElement);
675  }
676  int bands = (int)inputLabel.findObject("IsisCube")
677  .findObject("Core")
678  .findGroup("Dimensions")
679  .findKeyword("Bands");
680 
681  for (int i = 0; i < bands; i++) {
682 
683  QDomElement bin = m_domDoc->createElement("sp:Bin");
684  axisBinSetElement.appendChild(bin);
685 
686  QDomElement binSequenceNumber = m_domDoc->createElement("sp:bin_sequence_number");
687  PvlToXmlTranslationManager::setElementValue(binSequenceNumber, toString(i+1));
688  bin.appendChild(binSequenceNumber);
689 
690 
691  QDomElement centerValue = m_domDoc->createElement("sp:center_value");
692  PvlToXmlTranslationManager::setElementValue(centerValue, center[i], units);
693  bin.appendChild(centerValue);
694 
695  QDomElement binWidth = m_domDoc->createElement("sp:bin_width");
696  if (width.size() == bands) {
697  PvlToXmlTranslationManager::setElementValue(binWidth, width[i] , units);
698  }
699  else {
700  PvlToXmlTranslationManager::setElementValue(binWidth, width[0] , units);
701  }
702  bin.appendChild(binWidth);
703 
704  QDomElement originalBinNumber = m_domDoc->createElement("sp:original_bin_number");
705  if (originalBand.size() > 0) {
706  PvlToXmlTranslationManager::setElementValue(originalBinNumber, originalBand[i]);
707  bin.appendChild(originalBinNumber);
708  }
709 
710  if (name.size() > 0 || number.size() > 0) {
711  QDomElement filter = m_domDoc->createElement("sp:Filter");
712  bin.appendChild(filter);
713  if (name.size() > 0) {
714  QDomElement filterName = m_domDoc->createElement("sp:filter_name");
715  PvlToXmlTranslationManager::setElementValue(filterName, name[i]);
716  filter.appendChild(filterName);
717  }
718  if (number.size() > 0) {
719  QDomElement filterNumber= m_domDoc->createElement("sp:filter_number");
720  PvlToXmlTranslationManager::setElementValue(filterNumber, number[i]);
721  filter.appendChild(filterNumber);
722  }
723  }
724  }
725 
726  }
727 
728 
732  void ProcessExportPds4::translateBandBinSpectrumBinSet(Pvl &inputLabel) {
733  QString translationFile = "$base/translations/";
734  translationFile += "pds4ExportBandBinSpectrumBinSet.trn";
735  FileName translationFileName(translationFile);
736  PvlToXmlTranslationManager xlator(inputLabel, translationFileName.expanded());
737  xlator.Auto(*m_domDoc);
738 
739  PvlGroup bandBinGroup = inputLabel.findObject("IsisCube").findGroup("BandBin");
740  // fix multi-valued bandbin info
741  QStringList xmlPath;
742  xmlPath << "Product_Observational"
743  << "Observation_Area"
744  << "Discipline_Area"
745  << "sp:Spectral_Characteristics";
746  QDomElement baseElement = m_domDoc->documentElement();
747  QDomElement spectralCharElement = getElement(xmlPath, baseElement);
748 
749  // Axis_Uniformly_Sampled
750  // required - sampling_parameter_type (frequency, wavelength, wavenumber)
751  // sampling_interval (units Hz, Angstrom, cm**-1, respectively)
752  // bin_width (units Hz, Angstrom, cm**-1, respectively)
753  // first_center_value (units Hz, Angstrom, cm**-1, respectively)
754  // last_center_value (units Hz, Angstrom, cm**-1, respectively)
755  // Local_Internal_Reference
756  // Local_Internal_Reference:local_reference_type = spectral_characteristics_to_array_axis
757  // Local_Internal_Reference:local_identifier_reference,
758  // 1. At least one Axis_Array:axis_name must match the
759  // value of the local_identifier_reference in the
760  // Axis_Uniformly_Sampled.
761  // Set Axis_Uniformly_Sampled:local_identifier_reference = Axis_Array:axis_name = Band
762  // 2. At least one Array_3D_Spectrum:local_identifier must match
763  // the value of the local_identifier_reference in the
764  // Spectral_Characteristics.
765  // Set Spectral_Characteristics:local_identifier_reference = Array_3D_Spectrum:local_identifier = Spectral_Array_Object
766  // Local_Internal_Reference:local_reference_type = spectral_characteristics_to_array_axis
767  PvlKeyword center("Center");
768  if (bandBinGroup.hasKeyword("FilterCenter")) {
769  center = bandBinGroup["FilterCenter"];
770  }
771  else if (bandBinGroup.hasKeyword("Center")) {
772  center = bandBinGroup["Center"];
773  }
774  else {
775  QString msg = "Unable to translate BandBin info for UniformlySpacedSpectrum. "
776  "Translation for PDS4 required value [last_center_value] not found.";
777  throw IException(IException::Programmer, msg, _FILEINFO_);
778  }
779  QString lastCenter = center[center.size() - 1];
780 
781  QDomElement axisBinSetElement = spectralCharElement.firstChildElement("sp:Axis_Uniformly_Sampled");
782  if (axisBinSetElement.isNull()) {
783  axisBinSetElement = m_domDoc->createElement("sp:Axis_Uniformly_Sampled");
784  spectralCharElement.appendChild(axisBinSetElement);
785  }
786 
787  QDomElement lastCenterElement = m_domDoc->createElement("sp:last_center_value");
788  PvlToXmlTranslationManager::setElementValue(lastCenterElement, lastCenter);
789  spectralCharElement.appendChild(lastCenterElement);
790 
791  }
792 
799  void ProcessExportPds4::setPixelDescription(QString description) {
800  m_pixelDescription = description;
801  }
802 
808  void ProcessExportPds4::fileAreaObservational() {
809  Pvl *inputLabel = InputCubes[0]->label();
810  QString imageObject = "";
811 
812  QString translationFile = "$base/translations/pds4Export";
813  if (m_imageType == StandardImage) {
814  int bands = (int)inputLabel->findObject("IsisCube")
815  .findObject("Core")
816  .findGroup("Dimensions")
817  .findKeyword("Bands");
818  if (bands > 1) {
819  imageObject = "Array_3D_Image";
820  }
821  else {
822  imageObject = "Array_2D_Image";
823  }
824  translationFile += QString(imageObject).remove('_');
825  }
826  else {
827  imageObject = "Array_3D_Spectrum";
828  translationFile += QString(imageObject).remove('_');
829  if (m_imageType == UniformlySampledSpectrum) {
830  translationFile += "Uniform";
831  }
832  else if (m_imageType == BinSetSpectrum) {
833  translationFile += "BinSet";
834  }
835  }
836  translationFile += ".trn";
837  FileName translationFileName(translationFile);
838 
839  PvlToXmlTranslationManager xlator(*inputLabel, translationFileName.expanded());
840  xlator.Auto(*m_domDoc);
841 
842  QDomElement rootElement = m_domDoc->documentElement();
843  QDomElement fileAreaObservationalElement =
844  rootElement.firstChildElement("File_Area_Observational");
845 
846  // Calculate the core base/mult for the output cube
847  double base = 0.0;
848  double multiplier = 1.0;
849  double outputMin, outputMax;
850 
851  double inputMin = (p_inputMinimum.size()) ? p_inputMinimum[0] : 0.0;
852  double inputMax = (p_inputMaximum.size()) ? p_inputMaximum[0] : 0.0;
853 
854  for(unsigned int i = 0; i < p_inputMinimum.size(); i ++) {
855  inputMin = std::min(inputMin, p_inputMinimum[i]);
856  inputMax = std::max(inputMax, p_inputMaximum[i]);
857  }
858 
859  outputMin = p_outputMinimum;
860  outputMax = p_outputMaximum;
861 
862  if(p_inputMinimum.size() && ( p_pixelType == Isis::UnsignedByte ||
863  p_pixelType == Isis::SignedWord ||
864  p_pixelType == Isis::UnsignedWord ) ) {
865  multiplier = (inputMax - inputMin) / (outputMax - outputMin);
866  base = inputMin - multiplier * outputMin;
867  }
868 
869  if (!fileAreaObservationalElement.isNull()) {
870  QDomElement arrayImageElement =
871  fileAreaObservationalElement.firstChildElement(imageObject);
872  if (!arrayImageElement.isNull()) {
873 
874  // reorder axis elements.
875  // Translation order: elements, axis_name, sequence_number
876  // Correct order: axis_name, elements, sequence_number
877  QDomElement axisArrayElement = arrayImageElement.firstChildElement("Axis_Array");
878  while( !axisArrayElement.isNull() ) {
879  QDomElement axisNameElement = axisArrayElement.firstChildElement("axis_name");
880  axisArrayElement.insertBefore(axisNameElement,
881  axisArrayElement.firstChildElement("elements"));
882  axisArrayElement = axisArrayElement.nextSiblingElement("Axis_Array");
883  }
884 
885  QDomElement elementArrayElement = m_domDoc->createElement("Element_Array");
886  arrayImageElement.insertBefore(elementArrayElement,
887  arrayImageElement.firstChildElement("Axis_Array"));
888 
889  QDomElement dataTypeElement = m_domDoc->createElement("data_type");
890  PvlToXmlTranslationManager::setElementValue(dataTypeElement,
891  PDS4PixelType(p_pixelType, p_endianType));
892  elementArrayElement.appendChild(dataTypeElement);
893 
894  QDomElement scalingFactorElement = m_domDoc->createElement("scaling_factor");
895  PvlToXmlTranslationManager::setElementValue(scalingFactorElement,
896  toString(multiplier));
897  elementArrayElement.appendChild(scalingFactorElement);
898 
899  QDomElement offsetElement = m_domDoc->createElement("value_offset");
900  PvlToXmlTranslationManager::setElementValue(offsetElement,
901  toString(base));
902  elementArrayElement.appendChild(offsetElement);
903  }
904 
905  // Add the Special_Constants class to define ISIS special pixel values if pixel type is
906  QDomElement specialConstantElement = m_domDoc->createElement("Special_Constants");
907  arrayImageElement.insertAfter(specialConstantElement,
908  arrayImageElement.lastChildElement("Axis_Array"));
909 
910  switch (p_pixelType) {
911  case Real:
912  { QDomElement nullElement = m_domDoc->createElement("missing_constant");
913  PvlToXmlTranslationManager::setElementValue(nullElement, QString::number(NULL4, 'g', 18));
914  specialConstantElement.appendChild(nullElement);
915 
916  QDomElement highInstrumentSatElement = m_domDoc->createElement("high_instrument_saturation");
917  PvlToXmlTranslationManager::setElementValue(highInstrumentSatElement, QString::number(HIGH_INSTR_SAT4, 'g', 18));
918  specialConstantElement.appendChild(highInstrumentSatElement);
919 
920  QDomElement highRepresentationSatElement = m_domDoc->createElement("high_representation_saturation");
921  PvlToXmlTranslationManager::setElementValue(highRepresentationSatElement, QString::number(HIGH_REPR_SAT4, 'g', 18));
922  specialConstantElement.appendChild(highRepresentationSatElement);
923 
924  QDomElement lowInstrumentSatElement = m_domDoc->createElement("low_instrument_saturation");
925  PvlToXmlTranslationManager::setElementValue(lowInstrumentSatElement, QString::number(LOW_INSTR_SAT4, 'g', 18));
926  specialConstantElement.appendChild(lowInstrumentSatElement);
927 
928  QDomElement lowRepresentationSatElement = m_domDoc->createElement("low_representation_saturation");
929  PvlToXmlTranslationManager::setElementValue(lowRepresentationSatElement, QString::number(LOW_REPR_SAT4, 'g', 18));
930  specialConstantElement.appendChild(lowRepresentationSatElement);
931  break;}
932 
933  case UnsignedByte:
934  { QDomElement nullElement = m_domDoc->createElement("missing_constant");
935  PvlToXmlTranslationManager::setElementValue(nullElement, QString::number(NULL1, 'g', 18));
936  specialConstantElement.appendChild(nullElement);
937 
938  QDomElement highInstrumentSatElement = m_domDoc->createElement("high_instrument_saturation");
939  PvlToXmlTranslationManager::setElementValue(highInstrumentSatElement, QString::number(HIGH_INSTR_SAT1, 'g', 18));
940  specialConstantElement.appendChild(highInstrumentSatElement);
941 
942  QDomElement highRepresentationSatElement = m_domDoc->createElement("high_representation_saturation");
943  PvlToXmlTranslationManager::setElementValue(highRepresentationSatElement, QString::number(HIGH_REPR_SAT1, 'g', 18));
944  specialConstantElement.appendChild(highRepresentationSatElement);
945 
946  QDomElement lowInstrumentSatElement = m_domDoc->createElement("low_instrument_saturation");
947  PvlToXmlTranslationManager::setElementValue(lowInstrumentSatElement, QString::number(LOW_INSTR_SAT1, 'g', 18));
948  specialConstantElement.appendChild(lowInstrumentSatElement);
949 
950  QDomElement lowRepresentationSatElement = m_domDoc->createElement("low_representation_saturation");
951  PvlToXmlTranslationManager::setElementValue(lowRepresentationSatElement, QString::number(LOW_REPR_SAT1, 'g', 18));
952  specialConstantElement.appendChild(lowRepresentationSatElement);
953  break; }
954 
955  case SignedWord:
956  { QDomElement nullElement = m_domDoc->createElement("missing_constant");
957  PvlToXmlTranslationManager::setElementValue(nullElement, QString::number(NULL2, 'g', 18));
958  specialConstantElement.appendChild(nullElement);
959 
960  QDomElement highInstrumentSatElement = m_domDoc->createElement("high_instrument_saturation");
961  PvlToXmlTranslationManager::setElementValue(highInstrumentSatElement, QString::number(HIGH_INSTR_SAT2, 'g', 18));
962  specialConstantElement.appendChild(highInstrumentSatElement);
963 
964  QDomElement highRepresentationSatElement = m_domDoc->createElement("high_representation_saturation");
965  PvlToXmlTranslationManager::setElementValue(highRepresentationSatElement, QString::number(HIGH_REPR_SAT2, 'g', 18));
966  specialConstantElement.appendChild(highRepresentationSatElement);
967 
968  QDomElement lowInstrumentSatElement = m_domDoc->createElement("low_instrument_saturation");
969  PvlToXmlTranslationManager::setElementValue(lowInstrumentSatElement, QString::number(LOW_INSTR_SAT2, 'g', 18));
970  specialConstantElement.appendChild(lowInstrumentSatElement);
971 
972  QDomElement lowRepresentationSatElement = m_domDoc->createElement("low_representation_saturation");
973  PvlToXmlTranslationManager::setElementValue(lowRepresentationSatElement, QString::number(LOW_REPR_SAT2, 'g', 18));
974  specialConstantElement.appendChild(lowRepresentationSatElement);
975  break; }
976 
977  case UnsignedWord:
978  { QDomElement nullElement = m_domDoc->createElement("missing_constant");
979  PvlToXmlTranslationManager::setElementValue(nullElement, QString::number(NULLU2, 'g', 18));
980  specialConstantElement.appendChild(nullElement);
981 
982  QDomElement highInstrumentSatElement = m_domDoc->createElement("high_instrument_saturation");
983  PvlToXmlTranslationManager::setElementValue(highInstrumentSatElement, QString::number(HIGH_INSTR_SATU2, 'g', 18));
984  specialConstantElement.appendChild(highInstrumentSatElement);
985 
986  QDomElement highRepresentationSatElement = m_domDoc->createElement("high_representation_saturation");
987  PvlToXmlTranslationManager::setElementValue(highRepresentationSatElement, QString::number(HIGH_REPR_SATU2, 'g', 18));
988  specialConstantElement.appendChild(highRepresentationSatElement);
989 
990  QDomElement lowInstrumentSatElement = m_domDoc->createElement("low_instrument_saturation");
991  PvlToXmlTranslationManager::setElementValue(lowInstrumentSatElement, QString::number(LOW_INSTR_SATU2, 'g', 18));
992  specialConstantElement.appendChild(lowInstrumentSatElement);
993 
994  QDomElement lowRepresentationSatElement = m_domDoc->createElement("low_representation_saturation");
995  PvlToXmlTranslationManager::setElementValue(lowRepresentationSatElement, QString::number(LOW_REPR_SATU2, 'g', 18));
996  specialConstantElement.appendChild(lowRepresentationSatElement);
997  break; }
998 
999  case None:
1000  break;
1001 
1002  default:
1003  break;
1004  }
1005 
1006 
1007 
1008  if (!m_pixelDescription.isEmpty()) {
1009  QDomElement descriptionElement = m_domDoc->createElement("description");
1010  PvlToXmlTranslationManager::setElementValue(descriptionElement,
1011  m_pixelDescription);
1012  arrayImageElement.insertAfter(descriptionElement, arrayImageElement.lastChildElement());
1013  }
1014  }
1015  }
1016 
1017 
1026  void ProcessExportPds4::addSchema(QString xsd, QString xmlns, QString xmlnsURI) {
1027  // Add xmlns
1028  QDomElement root = m_domDoc->documentElement();
1029  root.setAttribute(xmlns, xmlnsURI);
1030 
1031  // Add to xsi:schemaLocation
1032  m_schemaLocation += " ";
1033  m_schemaLocation += xmlnsURI;
1034  m_schemaLocation += " ";
1035  m_schemaLocation += xmlnsURI;
1036  m_schemaLocation += "/";
1037  m_schemaLocation += xsd;
1038  root.setAttribute("xsi:schemaLocation", m_schemaLocation);
1039  }
1040 
1041 
1050  void ProcessExportPds4::addSchema(QString sch, QString xsd, QString xmlns, QString xmlnsURI) {
1051  // Add xml-model
1052  QString xmlModel;
1053  xmlModel += "href=\"";
1054  xmlModel += xmlnsURI;
1055  xmlModel += "/";
1056  xmlModel += sch;
1057  xmlModel += "\" schematypens=\"http://purl.oclc.org/dsdl/schematron\"";
1058  QDomProcessingInstruction header =
1059  m_domDoc->createProcessingInstruction("xml-model", xmlModel);
1060  m_domDoc->insertAfter(header, m_domDoc->firstChild());
1061 
1062  // Add xmlns
1063  addSchema(xsd, xmlns, xmlnsURI);
1064  }
1065 
1066 
1072  void ProcessExportPds4::OutputLabel(std::ofstream &os) {
1073  os << m_domDoc->toString() << endl;
1074  }
1075 
1076 
1084  void ProcessExportPds4::StartProcess(std::ofstream &fout) {
1085  ProcessExport::StartProcess(fout);
1086  }
1087 
1088 
1095  QDomDocument &ProcessExportPds4::GetLabel() {
1096  if (m_domDoc->documentElement().isNull()) {
1097  QDomElement root = m_domDoc->createElement("Product_Observational");
1098  root.setAttribute("xmlns", "http://pds.nasa.gov/pds4/pds/v1");
1099  root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1100  root.setAttribute("xsi:schemaLocation",
1101  "http://pds.nasa.gov/pds4/pds/v1 http://pds.nasa.gov/pds4/pds/v1");
1102  m_domDoc->appendChild(root);
1103  }
1104  return *m_domDoc;
1105  }
1106 
1107 
1116  void ProcessExportPds4::WritePds4(QString outFile) {
1117 
1118  FileName outputFile(outFile);
1119 
1120  // Name for output label
1121  QString path(outputFile.originalPath());
1122  QString name(outputFile.baseName());
1123  QString labelName = path + "/" + name + ".xml";
1124 
1125  // Name for output image
1126  QString imageName = outputFile.expanded();
1127 
1128  // If input file ends in .xml, the user entered a label name for the output file, not an
1129  // image name with a unique file extension.
1130  if (QString::compare(outputFile.extension(), "xml", Qt::CaseInsensitive) == 0) {
1131  imageName = path + "/" + name + ".img";
1132  }
1133 
1134  QDomElement rootElement = m_domDoc->documentElement();
1135  QDomElement fileAreaObservationalElement =
1136  rootElement.firstChildElement("File_Area_Observational");
1137 
1138  QDomElement fileElement = m_domDoc->createElement("File");
1139  fileAreaObservationalElement.insertBefore(fileElement,
1140  fileAreaObservationalElement.firstChildElement());
1141 
1142  QDomElement fileNameElement = m_domDoc->createElement("file_name");
1143  PvlToXmlTranslationManager::setElementValue(fileNameElement, outputFile.name());
1144  fileElement.appendChild(fileNameElement);
1145 
1146 // QDomElement creationElement = m_domDoc->createElement("creation_date_time");
1147 // PvlToXmlTranslationManager::setElementValue(creationElement, );
1148 // fileElement.appendChild(creationElement);
1149 
1150  ofstream outputLabel(labelName.toLatin1().data());
1151  OutputLabel(outputLabel);
1152  outputLabel.close();
1153 
1154  ofstream outputImageFile(imageName.toLatin1().data());
1155  StartProcess(outputImageFile);
1156  outputImageFile.close();
1157 
1158  EndProcess();
1159  }
1160 
1161 
1170  void ProcessExportPds4::StandardAllMapping() {
1171  // Cartography
1172  // Get the input Isis cube label and find the Mapping group if it has one
1173  Pvl *inputLabel = InputCubes[0]->label();
1174  if(inputLabel->hasObject("IsisCube") &&
1175  !(inputLabel->findObject("IsisCube").hasGroup("Mapping"))) return;
1176  PvlGroup &inputMapping = inputLabel->findGroup("Mapping", Pvl::Traverse);
1177 
1178  addSchema("PDS4_CART_1900.sch",
1179  "PDS4_CART_1900.xsd",
1180  "xmlns:cart",
1181  "http://pds.nasa.gov/pds4/cart/v1");
1182 
1183  // Translate the projection specific keywords for a PDS IMAGE_MAP_PROJECTION
1184  Projection *proj = ProjectionFactory::Create(*inputLabel);
1185  QString projName = proj->Name();
1186  try {
1187  PvlToXmlTranslationManager xlatorSpecProj(*inputLabel,
1188  "$base/translations/pds4Export" + projName + ".trn");
1189  xlatorSpecProj.Auto(*m_domDoc);
1190  }
1191  catch (IException &e) {
1192  QString msg = "Unable to export projection [" + projName + "] to PDS4 product. " +
1193  "This projection is not supported in ISIS3.";
1194  throw IException(e, IException::User, msg, _FILEINFO_);
1195  }
1196 
1197  // convert units.
1198  QStringList xmlPath;
1199  xmlPath << "Product_Observational"
1200  << "Observation_Area"
1201  << "Discipline_Area"
1202  << "cart:Cartography"
1203  << "cart:Map_Projection"
1204  << "cart:Spatial_Reference_Information"
1205  << "cart:Horizontal_Coordinate_System_Definition"
1206  << "cart:Geodetic_Model";
1207 
1208  QDomElement baseElement = m_domDoc->documentElement();
1209  QDomElement geodeticModelElement = getElement(xmlPath, baseElement);
1210  QDomElement semiMajorRadElement = geodeticModelElement.firstChildElement("cart:semi_major_radius");
1211  if (!semiMajorRadElement.isNull()) {
1212 
1213  QString units = semiMajorRadElement.attribute("unit");
1214  if( units.compare("km", Qt::CaseInsensitive) != 0 && units.compare("kilometers", Qt::CaseInsensitive) != 0) {
1215 
1216  //if no units, assume in meters
1217  double dValue = toDouble(semiMajorRadElement.text());
1218  dValue /= 1000.0;
1219  PvlToXmlTranslationManager::resetElementValue(semiMajorRadElement, toString(dValue), "km");
1220  }
1221  }
1222 
1223  QDomElement semiMinorRadElement = geodeticModelElement.firstChildElement("cart:semi_minor_radius");
1224  if (!semiMinorRadElement.isNull()) {
1225 
1226  QString units = semiMinorRadElement.attribute("unit");
1227  if( units.compare("km", Qt::CaseInsensitive) != 0 && units.compare("kilometers", Qt::CaseInsensitive) != 0) {
1228  // If no units, assume in meters
1229  double dValue = toDouble(semiMinorRadElement.text());
1230  dValue /= 1000.0;
1231  PvlToXmlTranslationManager::resetElementValue(semiMinorRadElement, toString(dValue), "km");
1232  }
1233  }
1234 
1235  QDomElement polarRadElement = geodeticModelElement.firstChildElement("cart:polar_radius");
1236  if (!polarRadElement.isNull()) {
1237  QString units = polarRadElement.attribute("unit");
1238  if( units.compare("km", Qt::CaseInsensitive) != 0 && units.compare("kilometers", Qt::CaseInsensitive) != 0) {
1239  // If no units, assume in meters
1240  double dValue = toDouble(polarRadElement.text());
1241  dValue /= 1000.0;
1242  PvlToXmlTranslationManager::resetElementValue(polarRadElement, toString(dValue), "km");
1243  }
1244  }
1245 
1246  PvlKeyword &isisLonDir = inputMapping.findKeyword("LongitudeDirection");
1247  QString lonDir = isisLonDir[0];
1248  lonDir = lonDir.toUpper();
1249 
1250  // Add Lat/Lon range
1251  double maxLon, minLon, maxLat, minLat;
1252  InputCubes[0]->latLonRange(minLat, maxLat, minLon, maxLon);
1253 
1254  xmlPath.clear();
1255  xmlPath << "Product_Observational"
1256  << "Observation_Area"
1257  << "Discipline_Area"
1258  << "cart:Cartography"
1259  << "cart:Spatial_Domain"
1260  << "cart:Bounding_Coordinates";
1261  QDomElement boundingCoordElement = getElement(xmlPath, baseElement);
1262  QDomElement eastElement = boundingCoordElement.firstChildElement("cart:east_bounding_coordinate");
1263  QDomElement westElement = boundingCoordElement.firstChildElement("cart:west_bounding_coordinate");
1264  QDomElement northElement = boundingCoordElement.firstChildElement("cart:north_bounding_coordinate");
1265  QDomElement southElement = boundingCoordElement.firstChildElement("cart:south_bounding_coordinate");
1266 
1267  // Translation files currently handles Positive West case where east = min, west = max
1268  // so if positive east, swap min/max
1269  if(QString::compare(lonDir, "PositiveEast", Qt::CaseInsensitive) == 0) {
1270  // west min, east max
1271  PvlToXmlTranslationManager::resetElementValue(eastElement, toString(maxLon), "deg");
1272  PvlToXmlTranslationManager::resetElementValue(westElement, toString(minLon), "deg");
1273  }
1274  else {
1275  PvlToXmlTranslationManager::resetElementValue(eastElement, toString(minLon), "deg");
1276  PvlToXmlTranslationManager::resetElementValue(westElement, toString(maxLon), "deg");
1277  }
1278 
1279  PvlToXmlTranslationManager::resetElementValue(northElement, toString(maxLat), "deg");
1280  PvlToXmlTranslationManager::resetElementValue(southElement, toString(minLat), "deg");
1281 
1282  // longitude_of_central_meridian and latitude_of_projection_origin need to be converted to floats.
1283  xmlPath.clear();
1284  xmlPath << "Product_Observational"
1285  << "Observation_Area"
1286  << "Discipline_Area"
1287  << "cart:Cartography"
1288  << "cart:Spatial_Reference_Information"
1289  << "cart:Horizontal_Coordinate_System_Definition"
1290  << "cart:Planar"
1291  << "cart:Map_Projection";
1292 
1293  // The following is necessary because the full xmlPath differs depending on the projection used.
1294  QDomElement projectionElement = getElement(xmlPath, baseElement);
1295  QDomElement tempElement = projectionElement.firstChildElement();
1296  QDomElement nameElement = tempElement.nextSiblingElement();
1297 
1298  QDomElement longitudeElement = nameElement.firstChildElement("cart:longitude_of_central_meridian");
1299  QDomElement originElement = nameElement.firstChildElement("cart:latitude_of_projection_origin");
1300 
1301  double longitudeElementValue = longitudeElement.text().toDouble();
1302  double originElementValue = originElement.text().toDouble();
1303 
1304  // Only update the ouput formatting if there are no digits after the decimal point.
1305  if (!longitudeElement.text().contains('.')) {
1306  QString toset1 = QString::number(longitudeElementValue, 'f', 1);
1307  PvlToXmlTranslationManager::resetElementValue(longitudeElement, toset1, "deg");
1308  }
1309 
1310  if (!originElement.text().contains('.')) {
1311  QString toset2 = QString::number(originElementValue, 'f', 1);
1312  PvlToXmlTranslationManager::resetElementValue(originElement, toset2, "deg");
1313  }
1314  }
1315 
1316 
1333  QDomElement ProcessExportPds4::getElement(QStringList xmlPath, QDomElement parent) {
1334  QDomElement baseElement = parent;
1335  if (baseElement.isNull()) {
1336  baseElement = m_domDoc->documentElement();
1337  }
1338  if (baseElement.isNull()) {
1339  QString msg = "Unable to get element from empty XML document.";
1340  throw IException(IException::Programmer, msg, _FILEINFO_);
1341  }
1342  QString parentName = xmlPath[0];
1343  if (parentName != baseElement.tagName()) {
1344  QString msg = "The tag name of the parent element passed in "
1345  "must be the first value in the given XML path.";
1346  throw IException(IException::Programmer, msg, _FILEINFO_);
1347  }
1348  for (int i = 1; i < xmlPath.size(); i++) {
1349  QString elementName = xmlPath[i];
1350  QDomElement nextElement = baseElement.firstChildElement(elementName);
1351  baseElement = nextElement;
1352  }
1353  return baseElement;
1354  }
1355 
1356 
1365  QString ProcessExportPds4::PDS4PixelType(PixelType pixelType, ByteOrder endianType) {
1366  QString pds4Type("UNK");
1367  if(p_pixelType == Isis::UnsignedByte) {
1368  pds4Type = "UnsignedByte";
1369  }
1370  else if((p_pixelType == Isis::UnsignedWord) && (p_endianType == Isis::Msb)) {
1371  pds4Type = "UnsignedMSB2";
1372  }
1373  else if((p_pixelType == Isis::UnsignedWord) && (p_endianType == Isis::Lsb)) {
1374  pds4Type = "UnsignedLSB2";
1375  }
1376  else if((p_pixelType == Isis::SignedWord) && (p_endianType == Isis::Msb)) {
1377  pds4Type = "SignedMSB2";
1378  }
1379  else if((p_pixelType == Isis::SignedWord) && (p_endianType == Isis::Lsb)) {
1380  pds4Type = "SignedLSB2";
1381  }
1382  else if((p_pixelType == Isis::Real) && (p_endianType == Isis::Msb)) {
1383  pds4Type = "IEEE754MSBSingle";
1384  }
1385  else if((p_pixelType == Isis::Real) && (p_endianType == Isis::Lsb)) {
1386  pds4Type = "IEEE754LSBSingle";
1387  }
1388  else {
1389  QString msg = "Unsupported PDS pixel type or sample size";
1390  throw IException(IException::User, msg, _FILEINFO_);
1391  }
1392  return pds4Type;
1393  }
1394 
1395 
1405  void ProcessExportPds4::addHistory(QString description, QString date, QString version) {
1406  // Check that at least the "Identification_Area" element exists.
1407  QDomElement identificationElement;
1408  QStringList identificationPath;
1409  identificationPath.append("Product_Observational");
1410  identificationPath.append("Identification_Area");
1411  try {
1412  identificationElement = getElement(identificationPath);
1413  if( identificationElement.isNull() ) {
1414  throw IException(IException::Unknown, "", _FILEINFO_);
1415  }
1416  }
1417  catch(IException &e) {
1418  QString msg = "Could not find Identification_Area element "
1419  "to add modification history under.";
1420  throw IException(IException::Programmer, msg, _FILEINFO_);
1421  }
1422 
1423  // Check if the "Modification_History" element exists yet.
1424  // If not, create one.
1425  QDomElement historyElement = identificationElement.firstChildElement("Modification_History");
1426  if ( historyElement.isNull() ) {
1427  historyElement = m_domDoc->createElement("Modification_History");
1428  identificationElement.insertAfter( historyElement,
1429  identificationElement.lastChildElement() );
1430  }
1431 
1432  // Create the "Modification_Detail" element and add it to the end of the
1433  // "Modification_History" element.
1434  QDomElement detailElement = m_domDoc->createElement("Modification_Detail");
1435 
1436  QDomElement modDateElement = m_domDoc->createElement("modification_date");
1437  PvlToXmlTranslationManager::setElementValue(modDateElement, date);
1438  detailElement.appendChild(modDateElement);
1439 
1440  QDomElement versionIdElement = m_domDoc->createElement("version_id");
1441  PvlToXmlTranslationManager::setElementValue(versionIdElement, version);
1442  detailElement.appendChild(versionIdElement);
1443 
1444  QDomElement descriptionElement = m_domDoc->createElement("description");
1445  PvlToXmlTranslationManager::setElementValue(descriptionElement, description);
1446  detailElement.appendChild(descriptionElement);
1447 
1448  historyElement.insertAfter( detailElement,
1449  historyElement.lastChildElement() );
1450  }
1451 
1452 
1470  void ProcessExportPds4::translateUnits(QDomDocument &label, QString transMapFile) {
1471  Pvl configPvl;
1472  try {
1473  configPvl.read(transMapFile);
1474  }
1475  catch(IException &e) {
1476  QString msg = "Failed to read unit translation config file [" + transMapFile + "].";
1477  throw IException(e, IException::Io, msg, _FILEINFO_);
1478  }
1479 
1480  QMap<QString, QString> transMap;
1481  try {
1482  transMap = createUnitMap(configPvl);
1483  }
1484  catch(IException &e) {
1485  QString msg = "Failed to load unit translation config file [" + transMapFile + "].";
1486  throw IException(e, IException::Unknown, msg, _FILEINFO_);
1487  }
1488 
1489  // Now that the map is filled, recursively search through the XML document
1490  // for units and translate them.
1491  try {
1492  translateChildUnits( label.documentElement(), transMap );
1493  }
1494  catch(IException &e) {
1495  QString msg = "Failed to translate units with config file [" + transMapFile + "].";
1496  throw IException(e, IException::Unknown, msg, _FILEINFO_);
1497  }
1498  }
1499 
1500 
1511  QMap<QString, QString> ProcessExportPds4::createUnitMap(Pvl configPvl) {
1512  QMap<QString, QString> transMap;
1513  for (int i = 0; i < configPvl.objects(); i++) {
1514  PvlObject unitObject = configPvl.object(i);
1515  for (int j = 0; j < unitObject.groups(); j++) {
1516  PvlGroup unitGroup = unitObject.group(j);
1517  if (!unitGroup.hasKeyword("PDS4_Unit")) {
1518  QString msg = "No PDS4 standard specified for for [" + unitGroup.name() + "]";
1519  throw IException(IException::Programmer, msg, _FILEINFO_);
1520  }
1521  PvlKeyword pds4Key = unitGroup["PDS4_Unit"];
1522  // Add the PDS4 format for when the format is already correct.
1523  // This also handles case issues such as KM instead of km.
1524  transMap.insert(pds4Key[0].toLower(), pds4Key[0]);
1525 
1526  // If there are ISIS versions with different formats then add those.
1527  if (unitGroup.hasKeyword("ISIS_Units")) {
1528  PvlKeyword isisKey = unitGroup["ISIS_Units"];
1529  for (int k = 0; k < isisKey.size() ; k++) {
1530  transMap.insert(isisKey[k].toLower(), pds4Key[0]);
1531  }
1532  }
1533  }
1534  }
1535  return transMap;
1536  }
1537 
1538 
1551  void ProcessExportPds4::translateChildUnits(QDomElement parent, QMap<QString, QString> transMap) {
1552  QDomElement childElement = parent.firstChildElement();
1553 
1554  while( !childElement.isNull() ) {
1555  if ( childElement.hasAttribute("unit") ) {
1556  QString originalUnit = childElement.attribute("unit");
1557  if ( transMap.contains( originalUnit.toLower() ) ) {
1558  childElement.setAttribute("unit", transMap.value( originalUnit.toLower() ) );
1559  }
1560  else {
1561  QString msg = "Could not translate unit [" + originalUnit + "] to PDS4 format.";
1562  throw IException(IException::Unknown, msg, _FILEINFO_);
1563  }
1564  }
1565  translateChildUnits(childElement, transMap);
1566  childElement = childElement.nextSiblingElement();
1567  }
1568 
1569  // Base case: If there are no more children end return
1570  return;
1571  }
1572 
1573 
1574 } // End of Isis namespace
PvlObject & object(const int index)
Return the object at the specified index.
Definition: PvlObject.cpp:460
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
QString unit(const int index=0) const
Returns the units of measurement of the element of the array of values for the object at the specifie...
Definition: PvlKeyword.cpp:373
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:141
File name manipulation and expansion.
Definition: FileName.h:116
int objects() const
Returns the number of objects.
Definition: PvlObject.h:231
PvlObjectIterator findObject(const QString &name, PvlObjectIterator beg, PvlObjectIterator end)
Find the index of object with a specified name, between two indexes.
Definition: PvlObject.h:286
Namespace for the standard library.
ByteOrder
Tests the current architecture for byte order.
Definition: Endian.h:59
QString name() const
Returns the name of the file excluding the path and the attributes in the file name.
Definition: FileName.cpp:178
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
QString originalPath() const
Returns the path of the original file name.
Definition: FileName.cpp:100
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:164
PixelType
Enumerations for Isis Pixel Types.
Definition: PixelType.h:43
bool hasObject(const QString &name) const
Returns a boolean value based on whether the object exists in the current PvlObject or not...
Definition: PvlObject.h:335
QString name() const
Returns the container name.
Definition: PvlContainer.h:77
Base class for Map Projections.
Definition: Projection.h:171
int size() const
Returns the number of values stored in this keyword.
Definition: PvlKeyword.h:141
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
A single keyword-value pair.
Definition: PvlKeyword.h:98
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition: FileName.cpp:212
Container for cube-like labels.
Definition: Pvl.h:135
PvlKeyword & findKeyword(const QString &name)
Find a keyword with a specified name.
QString baseName() const
Returns the name of the file without the path and without extensions.
Definition: FileName.cpp:161
PvlGroup & group(const int index)
Return the group at the specified index.
Definition: PvlObject.cpp:423
Allows applications to translate simple text files.
void Auto(QDomDocument &outputLabel)
Automatically translate all the output names found in the translation table.
Isis exception class.
Definition: IException.h:107
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
QString extension() const
Returns the last extension of the file name.
Definition: FileName.cpp:194
int groups() const
Returns the number of groups contained.
Definition: PvlObject.h:87
Contains Pvl Groups and Pvl Objects.
Definition: PvlObject.h:74
void read(const QString &file)
Loads PVL information from a stream.
Definition: Pvl.cpp:76
virtual QString Name() const =0
This method returns the name of the map projection.