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