From b130ce76ba7ca4258b3964224eb46167ba520846 Mon Sep 17 00:00:00 2001 From: dcookastro <34168267+dcookastro@users.noreply.github.com> Date: Fri, 1 Mar 2019 16:34:44 -0700 Subject: [PATCH] Lro lidar infrastructure updated to repo dev. (#3043) * Updated the cmake version to 3.10 * Adding configurations for gtest * Tweaking things for gtest * Got gtest working and made a small example test * Updated the cmake version to be 3.10 or greater * Added test file discovery support and seperated the main into another file * Added more tests for FileName * Updated with the new testing guidelines and added new tests. * Added gmock * PixelTest (#536) * initial Pixel test * Add more test cases for Pixel * testing parameterization in Pixel test * testing function parameterization * parameterize static vs object methods * basic PixelTests implementation * clean up and more static tests * update PixelTests with float/dbl expects * Refactored stats to a functions * Modified to have cubeit remove mosaic pixel tracking info from labels. Fixes #5533 * Update cubeit.xml Fixed a typo * Fixes conda channels in Jenkinsfile & adds a MacOS node (#633) * Merged dev and cleaned up header conflicts * Update for #619. * Fix libtiff dependency (#636) (#644) * Upgrade libtiff to 4.0.10 (#636) * Switch libtiff to 4.0.9 or higher to remove geotiff conflict * Moved ISIS3 conda-build recipe from ISIS3_deps repository (#650) * Fixed warning in Pixel unit tests * Removing build numbers from external libraries (#660) * Moved ISIS3 conda-build recipe from ISIS3_deps repository * Un-pinned non-astro build numbers * Removing build numbers from external libraries in the environment and meta.yeml files * Final merging * Added pixel type attribute to the output image of program shadow. Fixes #5187 (#659) * Removed bolding of some text to decrease distraction. * Fixed some typos. * Reworded documentation. * Added section for Environment and PreferemcesSetup in the Getting Started Section. (#663) * Updated .gitmodules to use https rather than ssh (#673) * Added build type release to conda recipe (#676) * Updates README with Discourse (#690) * Updates README with Discourse * Update README.md * Update README.md * Added fix to Spice Server to accept clients with version nums >=3.5.*.* (#780) * Fixed error when trying to export a compressed image using pds2hideal. Fixes #5525. (#2059) * Added capability to export a compressed image with tables missing RECORD_BYTES keyword. Added test for compressed image. * Changed names of test output cubes. * Adding explanatory comments to the meta.yaml file (#2358) * Adding explanatory comments to the recipes/meta.yaml * Replacing accidentally erased line * Added option to sort the output for consistent order in testing and added a test case * Cleaned up commented out lines in LidarData.h and .cpp * Added additional documentation for LidarData, LidarControlPoint, and lrolola2isis * Multisegment DSK support for Bullet engine (#2791) * Merged multisegment DSKs for Bullet * basic tests * removed original tests * Minor updates to download sizes and some text. (#2790) * Removed debug lines from LidarData.cpp and old comments from the lrolola2isis twoImage test * Added an additional comment about the Test format to LidarData.cpp * Phocube will export RA and declination planes even if the point is not on the planet. Fixes #4446 (#657) * Made it so that RA and Dec are added last. * Updated code to be up to standards. * Added RA and dec test. * Updated history comment. * Renamed test. * Fixed typo in documentation * Refactored code so that RA and dec are placed in the correct bands instead of moving them to the last two bands. * Updated documentation, test now checks cube label. * Fixed SummingMode keyword being set in tgocassis2isis (#2847) * Adding Expanded keyword to cassis label and modifying SummingMode keyword * Updating the window count value * Adding capacity to handle imports without Expanded tag * Setting tgocassismos to have tracking turned-on (#2856) * Adding parameter "tracking=true" to tgocassismos call to automos * Adding documentation * Fixing indentation in documentation * Updates to tgocassisrdrgen and ProcessExportPds4 to add content to exported CaSSIS Label. (#2858) * Add 32-bit ISIS Special Pixel Constants to ProcessExportPds4 * Add ability to set title and version id in exported PDS4 produce in tgocassisrdrgen and ProcessExportPds4 API * Fix bug that occurs when exporting mosaics with Archive groups and fixed min/max lat/lon to east/west bounding box positive east flip bug * Added docs. * Update schema to most recent versions and comment-out schema that isn't used (temporarily) in tgocassisrdrgen. --- .github/pull_request_template.md | 1 + .gitmodules | 2 +- Jenkinsfile | 60 ++- README.md | 11 +- environment.yml | 61 ++- environment_gcc4.yml | 59 ++- isis/CMakeLists.txt | 5 +- .../apollo/apps/apollopanstitcher/Trans2d3p.h | 57 +-- isis/src/base/apps/crop/crop.h | 5 + isis/src/base/apps/cubeit/cubeit.xml | 3 + isis/src/base/apps/cubeit/main.cpp | 7 + isis/src/base/apps/phocube/main.cpp | 256 ++++++++---- isis/src/base/apps/phocube/phocube.xml | 393 +++++++++--------- .../base/apps/phocube/tsts/off_body/Makefile | 12 + isis/src/base/apps/shadow/shadow.xml | 131 +++--- isis/src/base/apps/smtk/PointPlot.h | 70 ++-- isis/src/base/apps/smtk/Stereo.cpp | 200 --------- isis/src/base/apps/smtk/Stereo.h | 74 ---- isis/src/base/apps/spiceserver/main.cpp | 17 +- .../src/base/apps/spiceserver/spiceserver.xml | 7 +- isis/src/base/apps/stats/main.cpp | 122 +----- isis/src/base/apps/stats/stats.cpp | 175 ++++++++ isis/src/base/apps/stats/stats.h | 24 ++ .../objs/BulletDskShape/BulletDskShape.cpp | 190 +++++---- .../objs/BulletDskShape/BulletDskShape.truth | 29 -- .../src/base/objs/BulletDskShape/unitTest.cpp | 90 ---- isis/src/base/objs/Cube/Cube.h | 17 +- .../objs/ImportPdsTable/ImportPdsTable.cpp | 9 + .../base/objs/ImportPdsTable/ImportPdsTable.h | 2 + .../ProcessExportPds4/ProcessExportPds4.cpp | 107 ++++- .../ProcessExportPds4/ProcessExportPds4.h | 7 + isis/src/clementine/apps/clem2isis/jpeg_c.h | 4 + isis/src/clementine/apps/clem2isis/pds.h | 4 + isis/src/docsys/build/UserDocs.xsl | 26 +- .../apps/mrf2pds/ProcessExportMiniRFLroPds.h | 5 + isis/src/mgs/apps/mocuncompress/array.h | 5 + isis/src/mgs/apps/mocuncompress/image_io.h | 5 + isis/src/mgs/apps/mocuncompress/initBlock.cpp | 6 +- .../src/mgs/apps/mocuncompress/invFdct16x16.h | 5 + .../src/mgs/apps/mocuncompress/invFwht16x16.h | 7 +- .../{limits.h => mocuncompresslimits.h} | 4 +- isis/src/mgs/apps/mocuncompress/msdp.h | 5 + .../pds2hideal/tsts/compressedImage/Makefile | 17 + isis/src/tgo/apps/tgocassis2isis/main.cpp | 79 ++-- isis/src/tgo/apps/tgocassismos/main.cpp | 3 +- .../tgo/apps/tgocassismos/tgocassismos.xml | 21 +- .../apps/tgocassismos/tsts/default/Makefile | 1 + isis/src/tgo/apps/tgocassisrdrgen/main.cpp | 30 +- .../apps/tgocassisrdrgen/tgocassisrdrgen.xml | 24 ++ isis/src/tgo/tsts/coloredMosaic/Makefile | 1 + .../tsts/singleColorMosaicReingest/Makefile | 2 +- .../uncontrolledSingleColorMosaic/Makefile | 2 +- isis/tests/BulletDskShapeTests.cpp | 65 +++ isis/tests/PixelTests.cpp | 4 +- isis/tests/statsTests.cpp | 209 ++++++++++ recipe/build.sh | 5 + recipe/meta.yaml | 174 ++++++++ 57 files changed, 1725 insertions(+), 1191 deletions(-) create mode 100644 isis/src/base/apps/phocube/tsts/off_body/Makefile delete mode 100644 isis/src/base/apps/smtk/Stereo.cpp delete mode 100644 isis/src/base/apps/smtk/Stereo.h create mode 100644 isis/src/base/apps/stats/stats.cpp create mode 100644 isis/src/base/apps/stats/stats.h delete mode 100644 isis/src/base/objs/BulletDskShape/BulletDskShape.truth delete mode 100644 isis/src/base/objs/BulletDskShape/unitTest.cpp rename isis/src/mgs/apps/mocuncompress/{limits.h => mocuncompresslimits.h} (98%) create mode 100644 isis/src/mro/apps/pds2hideal/tsts/compressedImage/Makefile create mode 100644 isis/tests/BulletDskShapeTests.cpp create mode 100644 isis/tests/statsTests.cpp create mode 100644 recipe/build.sh create mode 100644 recipe/meta.yaml diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e8abd6034e..63686ef867 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -23,6 +23,7 @@ - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) +- [ ] Documentation change (update to the documentation; no code change) - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Checklist: diff --git a/.gitmodules b/.gitmodules index 110ccc43ce..48294b183d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "gtest"] path = gtest - url = git@github.com:google/googletest.git + url = https://github.com/google/googletest.git diff --git a/Jenkinsfile b/Jenkinsfile index 21a76b5c62..67aec8e33c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,9 +9,11 @@ nodes["isis-fedora-25"] = { git branch: 'dev', url: 'https://github.com/USGS-Astrogeology/ISIS3.git' // sh """ // git clone https://github.com/abseil/googletest.git gtest -// sed -i "s|usgs-astrogeology|http://astro-bin.wr.usgs.gov/conda-usgs-astrogeology|" environment.yml -// sed -i "s|conda-forge|http://astro-bin.wr.usgs.gov/conda-forge|" environment.yml -// sed -i "s|defaults|http://astro-bin.wr.usgs.gov/conda|" environment.yml +// conda config --set channel_alias http://astro-bin.wr.usgs.gov/conda +// conda config --prepend channels anaconda +// conda config --append channels conda-forge +// conda config --append channels usgs-astrogeology +// conda config --prepend default_channels anaconda // conda env create -n isis3 -f environment.yml // source activate isis3 // mkdir -p ./install ./build && cd build @@ -35,9 +37,11 @@ nodes["isis-centos-7"] = { git branch: 'dev', url: 'https://github.com/USGS-Astrogeology/ISIS3.git' // sh """ // git clone https://github.com/abseil/googletest.git gtest -// sed -i "s|usgs-astrogeology|http://astro-bin.wr.usgs.gov/conda-usgs-astrogeology|" environment.yml -// sed -i "s|conda-forge|http://astro-bin.wr.usgs.gov/conda-forge|" environment.yml -// sed -i "s|defaults|http://astro-bin.wr.usgs.gov/conda|" environment.yml +// conda config --set channel_alias http://astro-bin.wr.usgs.gov/conda +// conda config --prepend channels anaconda +// conda config --append channels conda-forge +// conda config --append channels usgs-astrogeology +// conda config --prepend default_channels anaconda // conda env create -n isis3 -f environment.yml // source activate isis3 // mkdir -p ./install ./build && cd build @@ -61,9 +65,11 @@ nodes["isis-debian-9"] = { git branch: 'dev', url: 'https://github.com/USGS-Astrogeology/ISIS3.git' // sh """ // git clone https://github.com/abseil/googletest.git gtest -// sed -i "s|usgs-astrogeology|http://astro-bin.wr.usgs.gov/conda-usgs-astrogeology|" environment.yml -// sed -i "s|conda-forge|http://astro-bin.wr.usgs.gov/conda-forge|" environment.yml -// sed -i "s|defaults|http://astro-bin.wr.usgs.gov/conda|" environment.yml +// conda config --set channel_alias http://astro-bin.wr.usgs.gov/conda +// conda config --prepend channels anaconda +// conda config --append channels conda-forge +// conda config --append channels usgs-astrogeology +// conda config --prepend default_channels anaconda // conda env create -n isis3 -f environment.yml // source activate isis3 // mkdir -p ./install ./build && cd build @@ -87,9 +93,39 @@ nodes["isis-ubuntu-1804"] = { git branch: 'dev', url: 'https://github.com/USGS-Astrogeology/ISIS3.git' // sh """ // git clone https://github.com/abseil/googletest.git gtest -// sed -i "s|usgs-astrogeology|http://astro-bin.wr.usgs.gov/conda-usgs-astrogeology|" environment.yml -// sed -i "s|conda-forge|http://astro-bin.wr.usgs.gov/conda-forge|" environment.yml -// sed -i "s|defaults|http://astro-bin.wr.usgs.gov/conda|" environment.yml +// conda config --set channel_alias http://astro-bin.wr.usgs.gov/conda +// conda config --prepend channels anaconda +// conda config --append channels conda-forge +// conda config --append channels usgs-astrogeology +// conda config --prepend default_channels anaconda +// conda env create -n isis3 -f environment.yml +// source activate isis3 +// mkdir -p ./install ./build && cd build +// cmake -GNinja -DJP2KFLAG=OFF -Dpybindings=OFF -DCMAKE_INSTALL_PREFIX=../install -Disis3Data=/usgs/cpkgs/isis3/data -Disis3TestData=/usgs/cpkgs/isis3/testData ../isis +// set +e +// ninja -j8 && ninja install +// source /usgs/cpkgs/isis3/isis3mgr_scripts/initIsisCmake.sh . +// ctest -V -R _unit_ --timeout 500 +// ctest -V -R _app_ --timeout 500 +// ctest -V -R _module_ --timeout 500 +// """ + } + } + } +} + +nodes["mac1013"] = { + node("mac1013") { + withEnv(["ISISROOT=" + "${workspace}" + "/build/", "ISIS3TESTDATA=/usgs/cpkgs/isis3/testData/", "ISIS3DATA=/usgs/cpkgs/isis3/data/"]) { + stage ("MacOS 10.13 (High Sierra)") { + git branch: 'dev', url: 'https://github.com/USGS-Astrogeology/ISIS3.git' +// sh """ +// git clone https://github.com/abseil/googletest.git gtest +// conda config --set channel_alias http://astro-bin.wr.usgs.gov/conda +// conda config --prepend channels anaconda +// conda config --append channels conda-forge +// conda config --append channels usgs-astrogeology +// conda config --prepend default_channels anaconda // conda env create -n isis3 -f environment.yml // source activate isis3 // mkdir -p ./install ./build && cd build diff --git a/README.md b/README.md index b40b5a3ad1..1ba7646505 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # ISIS3 [![Join the chat at https://gitter.im/USGS-Astrogeology/isis3_cmake](https://badges.gitter.im/USGS-Astrogeology/isis3_cmake.svg)](https://gitter.im/USGS-Astrogeology/isis3_cmake?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Join the discourse at https://astrodiscuss.usgs.gov](https://img.shields.io/discourse/https/astrodiscuss.usgs.gov/topics.svg?style=flat)](https://astrodiscuss.usgs.gov/) ## Table of Contents @@ -110,19 +111,19 @@ To build and compile ISIS3 requires following the instructions listed below, whi ### Mission Requirements -ISIS3 supports many planetary missions; in fact, over 40 different instruments including some flown as early as the 1960s. Ancillary data are required to process images from these instruments. For example, translation definition files to help convert from PDS format to ISIS cubes, dark current and flat file images for radiometric calibration, and large quantities of SPICE files (spacecraft pointing and position) for map projecting images. If you plan to work with data from all missions, then the download will require about 180 GB for all the ancillary data. However, most of this volume is taken up by SPICE files. We have a SPICE Web service that can be used in lieu of downloading all of the SPICE files which can reduce the download size to 10 GB. When downloading ISIS, you will have the option of choosing which mission data to acquire as well as if you only want the translation and calibration files and not SPICE files. +ISIS3 supports many planetary missions; in fact, over 40 different instruments including some flown as early as the 1960s. Ancillary data are required to process images from these instruments. For example, translation definition files to help convert from PDS format to ISIS cubes, dark current and flat file images for radiometric calibration, and large quantities of SPICE files (spacecraft pointing and position) for map projecting images. If you plan to work with data from all missions, then the download will require about 530 GB for all the ancillary data. However, most of this volume is taken up by SPICE files. We have a SPICE Web service that can be used in lieu of downloading all of the SPICE files which can reduce the download size to 10 GB. When downloading ISIS, you will have the option of choosing which mission data to acquire as well as if you only want the translation and calibration files and not SPICE files. ### DTM Requirements -The strength of ISIS3 lies in its capabilities for planetary cartography. The image orthorectification process is improved if a digital terrain model (DTM) is used. The DTMs can be quite large and take some time to download. They exist for many planetary bodies (e.g., the Moon, Mars, etc.). Therefore, there are options for selecting which DTMs to download if you are only working with a particular target body. +The strength of ISIS3 lies in its capabilities for planetary cartography. The image orthorectification process requires a digital terrain model (DTM). The DTMs can be quite large and take some time to download. They exist for many planetary bodies (e.g., the Moon, Mars, etc.). Therefore, there are options for selecting which DTMs to download if you are only working with a particular target body. ### Full ISIS3 Data Download -Mission data is hosted on rsync servers and not through conda channels like the ISIS3 distribution. This requires using the rsync command from within a terminal window within your Unix distribution, or from within WSL if running Windows 10. Downloading all mission data requires over 130 GB of disk space. If you want to acquire only certain mission data [click here](#MissionSpecific). To download all ISIS3 data files, continue reading. +Mission data is hosted on rsync servers and not through conda channels like the ISIS3 distribution. This requires using the rsync command from within a terminal window within your Unix distribution, or from within WSL if running Windows 10. Downloading all mission data requires over 520 GB of disk space. If you want to acquire only certain mission data [click here](#MissionSpecific). To download all ISIS3 data files, continue reading. -To download all ISIS3 data (approximately 180 GB), enter the following commands at the command prompt: +To download all ISIS3 data (approximately 520 GB), enter the following commands at the command prompt: cd $ISIS3DATA rsync -azv --delete --partial isisdist.astrogeology.usgs.gov::isis3data/data . @@ -149,7 +150,7 @@ ISIS can now use a service to retrieve the SPICE data for all instruments ISIS s For example: `rsync -azv **--exclude='kernels'** --delete --partial isisdist.astrogeology.usgs.gov::isis3data/data/cassini data/` - WARNING: Some instruments require mission data to be present for calibration, which may not be supported by the SPICE Web Server exclusively, and some programs that are designed to run an image from ingestion through the mapping phase do not have an option to use the SPICE Web Service. For information specific to an instrument, see the documentation for radiometric callobration programs. + WARNING: Some instruments require mission data to be present for radiometric calibration, which may not be supported by the SPICE Web Server exclusively, and some programs that are designed to run an image from ingestion through the mapping phase do not have an option to use the SPICE Web Service. For information specific to an instrument, see the documentation for radiometric callobration programs. #### Apollo Mission (kernels can be excluded): diff --git a/environment.yml b/environment.yml index 6aec932d69..6ce26f1563 100644 --- a/environment.yml +++ b/environment.yml @@ -6,65 +6,62 @@ channels: dependencies: - armadillo==8.200.0 - - blas==1.1=openblas - - bullet==2.86.1=0 + - blas==1.1 + - bullet==2.86.1 - bz2file==0.98 - - bzip2==1.0.6=1 + - bzip2==1.0.6 - cmake>=3.10 - - cspice==66=h470a237_3 - - curl==7.60.0=0 - - doxygen==1.8.14=0 - - eigen==3.3.3=0 - - embree==2.14.0=0 + - cspice==66 + - curl==7.60.0 + - doxygen==1.8.14 + - eigen==3.3.3 + - embree==2.14.0 - geos==3.5.1 - - geotiff==1.4.2=1 + - geotiff==1.4.2 - gmm==5.0 - - gmp==6.1.2=0 - - gsl==2.2.1=blas_openblas_3 - - hdf5==1.8.18=2 - - icu==58.2=0 + - gmp==6.1.2 + - gsl==2.2.1 + - hdf5==1.8.18 + - icu==58.2 - jama==125 - - jpeg==9b=2 + - jpeg==9b - kakadu==1 - - krb5==1.14.2=0 + - krb5==1.14.2 - libpng>=1.6.34 - libprotobuf==3.5.2 - - libtiff==4.0.9=0 - - libxml2==2.9.7=0 + - libtiff>=4.0.9 + - libxml2==2.9.7 - make - - mesalib==17.2.0=0 + - mesalib==17.2.0 - mysql==5.7.20 - - mysql-connector-c==6.1.6=0 + - mysql-connector-c==6.1.6 - nanoflann==1.2.2 - - ninja==1.7.2=0 + - ninja==1.7.2 - conda-forge/label/gcc7::nn - # - numpy==1.13.3=py36_blas_openblas_200 - - openblas==0.2.19=2 + - openblas==0.2.19 - opencv - - openssl==1.0.2n=0 + - openssl==1.0.2n - patchelf==0.9 - pcl==1.8.1 - pip==9.0.1 - protobuf==3.5.2 - # - pyqt==5.6.0 - python==3.6 - qhull==7.2.0=0 - qt=5.9.6 - qwt=6.1.3 - setuptools=38.5.1 - sip==4.18 - - sqlite==3.13.0=1 - - suitesparse==4.5.4=blas_openblas_200 - - superlu==5.2.1=blas_openblas_201 + - sqlite==3.13.0 + - suitesparse==4.5.4 + - superlu==5.2.1 - tnt==126=0 - wheel==0.30.0 - x264==20131218 - xalan-c==1.11 - - xerces-c==3.1.4=0 - - xorg-kbproto==1.0.7=1 + - xerces-c==3.1.4 + - xorg-kbproto==1.0.7 - xorg-libice - xorg-libsm - - xorg-libx11==1.6.4=6 + - xorg-libx11==1.6.4 - xorg-libxi - - zlib==1.2.11=0 - + - zlib==1.2.11 diff --git a/environment_gcc4.yml b/environment_gcc4.yml index 6a671b8318..92be4d2037 100644 --- a/environment_gcc4.yml +++ b/environment_gcc4.yml @@ -8,64 +8,61 @@ channels: dependencies: - armadillo==8.200.0 - blas==1.1=openblas - - bullet==2.86.1=0 + - bullet==2.86.1 - bz2file==0.98 - - bzip2==1.0.6=1 + - bzip2==1.0.6 - cmake>=3.10 - - cspice==66=h470a237_3 - - curl==7.60.0=0 - - doxygen==1.8.14=0 - - eigen==3.3.3=0 - - embree==2.14.0=0 + - cspice==66 + - curl==7.60.0 + - doxygen==1.8.14 + - eigen==3.3.3 + - embree==2.14.0 - geos==3.5.1 - - geotiff==1.4.2=1 + - geotiff==1.4.2 - gmm==5.0 - - gmp==6.1.2=0 - - gsl==2.2.1=blas_openblas_3 - - hdf5==1.8.18=2 - - icu==58.2=0 + - gmp==6.1.2 + - gsl==2.2.1 + - hdf5==1.8.18 + - icu==58.2 - jama==125 - - jpeg==9b=2 + - jpeg==9b - kakadu==1 - - krb5==1.14.2=0 + - krb5==1.14.2 - libpng>=1.6.34 - libprotobuf==3.5.2 - - libtiff==4.0.9=0 - - libxml2==2.9.7=0 + - libtiff>=4.0.9 + - libxml2==2.9.7 - make - - mesalib==17.2.0=0 + - mesalib==17.2.0 - mysql==5.7.20 - - mysql-connector-c==6.1.6=0 + - mysql-connector-c==6.1.6 - nanoflann==1.2.2 - - ninja==1.7.2=0 + - ninja==1.7.2 - nn==1.86.0 - # - numpy==1.13.3=py36_blas_openblas_200 - - openblas==0.2.19=2 + - openblas==0.2.19 - opencv - - openssl==1.0.2n=0 + - openssl==1.0.2n - patchelf==0.9 - pcl==1.8.1 - pip==9.0.1 - protobuf==3.5.2 - # - pyqt==5.6.0 - python==3.6 - qhull==7.2.0=0 - qt=5.9.6 - qwt=6.1.3 - setuptools=38.5.1 - sip==4.18 - - sqlite==3.13.0=1 - - suitesparse==4.5.4=blas_openblas_200 - - superlu==5.2.1=blas_openblas_201 + - sqlite==3.13.0 + - suitesparse==4.5.4 + - superlu==5.2.1 - tnt==126=0 - wheel==0.30.0 - x264==20131218 - xalan-c==1.11 - - xerces-c==3.1.4=0 - - xorg-kbproto==1.0.7=1 + - xerces-c==3.1.4 + - xorg-kbproto==1.0.7 - xorg-libice - xorg-libsm - - xorg-libx11==1.6.4=6 + - xorg-libx11==1.6.4 - xorg-libxi - - zlib==1.2.11=0 - + - zlib==1.2.11 diff --git a/isis/CMakeLists.txt b/isis/CMakeLists.txt index 6827f60298..5239c8fdbd 100644 --- a/isis/CMakeLists.txt +++ b/isis/CMakeLists.txt @@ -252,7 +252,7 @@ find_package(Armadillo REQUIRED) find_package(Threads) -# Im this case, we specify the version numbers being searched for in the non-traditional installs. +# In this case, we specify the version numbers being searched for in the non-traditional installs. if(APPLE) find_package(OpenGL REQUIRED) endif(APPLE) @@ -443,7 +443,8 @@ add_custom_target(docs COMMAND ${CMAKE_COMMAND} # will be executed when running "ninja install" # On a clean build, all files will be copied over. add_custom_target(incs ALL COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_SOURCE_DIR}/src/*/objs/*/*.h ${CMAKE_SOURCE_DIR}/src/*/objs/*/*.hpp ${CMAKE_BINARY_DIR}/inc) + ${CMAKE_SOURCE_DIR}/src/*/objs/*/*.h ${CMAKE_SOURCE_DIR}/src/*/objs/*/*.hpp + ${CMAKE_SOURCE_DIR}/src/*/apps/*/*.h ${CMAKE_BINARY_DIR}/inc) add_dependencies(isis3 incs) # Add a custom build target to clean out everything that gets added to the source diff --git a/isis/src/apollo/apps/apollopanstitcher/Trans2d3p.h b/isis/src/apollo/apps/apollopanstitcher/Trans2d3p.h index f79883e736..4b6bcbfe97 100644 --- a/isis/src/apollo/apps/apollopanstitcher/Trans2d3p.h +++ b/isis/src/apollo/apps/apollopanstitcher/Trans2d3p.h @@ -1,45 +1,48 @@ +#ifndef trans2d3p_h +#define trans2d3p_h + #include "Cube.h" #include "Transform.h" #include -/** - * @file - * $Revision: 1.7 $ - * $Date: 2005/10/03 22:43:39 $ - * - * Unless noted otherwise, the portions of Isis written by the USGS are - * public domain. See individual third-party library and package descriptions - * for intellectual property information, user agreements, and related - * information. - * - * Although Isis has been used by the USGS, no warranty, expressed or - * implied, is made by the USGS as to the accuracy and functioning of such - * software and related material nor shall the fact of distribution +/** + * @file + * $Revision: 1.7 $ + * $Date: 2005/10/03 22:43:39 $ + * + * Unless noted otherwise, the portions of Isis written by the USGS are + * public domain. See individual third-party library and package descriptions + * for intellectual property information, user agreements, and related + * information. + * + * Although Isis has been used by the USGS, no warranty, expressed or + * implied, is made by the USGS as to the accuracy and functioning of such + * software and related material nor shall the fact of distribution * constitute any such warranty, and no responsibility is assumed by the - * USGS in connection therewith. - * - * For additional information, launch - * $ISISROOT/doc//documents/Disclaimers/Disclaimers.html + * USGS in connection therewith. + * + * For additional information, launch + * $ISISROOT/doc//documents/Disclaimers/Disclaimers.html * in a browser or see the Privacy & Disclaimers page on the Isis website, * http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on - * http://www.usgs.gov/privacy.html. - */ + * http://www.usgs.gov/privacy.html. + */ using namespace Isis; -/** +/** * @brief Brief coming soon - * + * * Description coming soon - * + * * @author 2011-09-19 Orrin Thomas - * - * @internal + * + * @internal * @history 2011-09-19 Orrin Thomas - Original version - */ + */ class Trans2d3p : public Transform { -public: +public: Trans2d3p(double theta, double sampOffset, double lineOffset,int samples, int lines) { m_lines = lines; m_samples = samples; @@ -74,3 +77,5 @@ class Trans2d3p : public Transform { int m_lines; int m_samples; }; + +#endif diff --git a/isis/src/base/apps/crop/crop.h b/isis/src/base/apps/crop/crop.h index bbe9fbd549..d6142d72bb 100644 --- a/isis/src/base/apps/crop/crop.h +++ b/isis/src/base/apps/crop/crop.h @@ -1,3 +1,6 @@ +#ifndef crop_h +#define crop_h + #include "Cube.h" #include "ProcessByLine.h" #include "SpecialPixel.h" @@ -11,3 +14,5 @@ #include "UserInterface.h" extern Isis::PvlGroup crop(Isis::UserInterface &ui); + +#endif diff --git a/isis/src/base/apps/cubeit/cubeit.xml b/isis/src/base/apps/cubeit/cubeit.xml index c6f69f541b..139d378463 100644 --- a/isis/src/base/apps/cubeit/cubeit.xml +++ b/isis/src/base/apps/cubeit/cubeit.xml @@ -79,6 +79,9 @@ Modified Makefile of badinputs app test to truncate paths before data directory. Allows tests to pass when not using the default data area. Fixes #4783. + + Removed the tracking label group if it exists in the output cube. Fixes #5533. + diff --git a/isis/src/base/apps/cubeit/main.cpp b/isis/src/base/apps/cubeit/main.cpp index 35e677f8c2..971245e190 100644 --- a/isis/src/base/apps/cubeit/main.cpp +++ b/isis/src/base/apps/cubeit/main.cpp @@ -183,6 +183,13 @@ void IsisMain() { ocube->deleteBlob("Table", "InputImages"); } + // Delete the Tracking group if it exists (3.6.0 and up) + // The tracking group could be transfered from the first input cube, but it does not + // represent the images used in any other band after cubeit. + if(ocube->hasGroup("Tracking")) { + ocube->deleteGroup("Tracking"); + } + p2.EndProcess(); // Now loop and mosaic in each cube diff --git a/isis/src/base/apps/phocube/main.cpp b/isis/src/base/apps/phocube/main.cpp index 8ca656e2e6..61cbdfd548 100644 --- a/isis/src/base/apps/phocube/main.cpp +++ b/isis/src/base/apps/phocube/main.cpp @@ -23,7 +23,7 @@ int nbands; bool noCamera; bool dn; -bool phase; +bool phase; bool emission; bool incidence; bool localEmission; @@ -49,13 +49,15 @@ bool bodyFixedX; bool bodyFixedY; bool bodyFixedZ; +int raBandNum; + void phocubeDN(Buffer &in, Buffer &out); void phocube(Buffer &out); // Function to create a keyword with same values of a specified count -template PvlKeyword makeKey(const QString &name, - const int &nvals, +template PvlKeyword makeKey(const QString &name, + const int &nvals, const T &value); // Structure containing new mosaic planes @@ -68,7 +70,7 @@ struct MosData { // Computes the special MORPHOLOGYRANK and ALBEDORANK planes MosData *getMosaicIndicies(Camera &camera, MosData &md); // Updates BandBin keyword -void UpdateBandKey(const QString &keyname, PvlGroup &bb, const int &nvals, +void UpdateBandKey(const QString &keyname, PvlGroup &bb, const int &nvals, const QString &default_value = "Null"); @@ -95,7 +97,7 @@ void IsisMain() { QString msg = "Mosaic files must contain mapping labels"; throw IException(e, IException::User, msg, _FILEINFO_); } - } + } else { try { cam = icube->camera(); @@ -134,6 +136,8 @@ void IsisMain() { bodyFixedX = false; bodyFixedY = false; bodyFixedZ = false; + raBandNum = 0; // 0 based, if RA is 5th band, raBandNum will be 4 + if (!noCamera) { if ((phase = ui.GetBoolean("PHASE"))) nbands++; if ((emission = ui.GetBoolean("EMISSION"))) nbands++; @@ -149,27 +153,27 @@ void IsisMain() { if ((offnadirAngle = ui.GetBoolean("OFFNADIRANGLE"))) nbands++; if ((subSpacecraftGroundAzimuth = ui.GetBoolean("SUBSPACECRAFTGROUNDAZIMUTH"))) nbands++; if ((subSolarGroundAzimuth = ui.GetBoolean("SUBSOLARGROUNDAZIMUTH"))) nbands++; - if ((morphologyRank = ui.GetBoolean("MORPHOLOGYRANK"))) nbands++; - if ((albedoRank = ui.GetBoolean("ALBEDORANK"))) nbands++; - if ((northAzimuth = ui.GetBoolean("NORTHAZIMUTH"))) nbands++; + if ((morphologyRank = ui.GetBoolean("MORPHOLOGYRANK"))) nbands++; + if ((albedoRank = ui.GetBoolean("ALBEDORANK"))) nbands++; + if ((northAzimuth = ui.GetBoolean("NORTHAZIMUTH"))) nbands++; if ((ra = ui.GetBoolean("RADEC"))) nbands++; if ((declination = ui.GetBoolean("RADEC"))) nbands++; if ((bodyFixedX = ui.GetBoolean("BODYFIXED"))) nbands++; if ((bodyFixedY = ui.GetBoolean("BODYFIXED"))) nbands++; if ((bodyFixedZ = ui.GetBoolean("BODYFIXED"))) nbands++; } - if((dn = ui.GetBoolean("DN"))) nbands++; - if((latitude = ui.GetBoolean("LATITUDE"))) nbands++; - if((longitude = ui.GetBoolean("LONGITUDE"))) nbands++; - if((pixelResolution = ui.GetBoolean("PIXELRESOLUTION"))) nbands++; + if ((dn = ui.GetBoolean("DN"))) nbands++; + if ((latitude = ui.GetBoolean("LATITUDE"))) nbands++; + if ((longitude = ui.GetBoolean("LONGITUDE"))) nbands++; + if ((pixelResolution = ui.GetBoolean("PIXELRESOLUTION"))) nbands++; - if(nbands < 1) { + if (nbands < 1) { QString message = "At least one photometry parameter must be entered" "[PHASE, EMISSION, INCIDENCE, LATITUDE, LONGITUDE...]"; throw IException(IException::User, message, _FILEINFO_); } - // If outputting a a dn band, retrieve the orignal values for the filter name from the input cube, + // If outputting a dn band, retrieve the orignal values for the filter name from the input cube, // if it exists. Otherwise, the default will be "DN" QString bname = "DN"; if ( dn && icube->hasGroup("BandBin") ) { @@ -184,41 +188,113 @@ void IsisMain() { // Create a bandbin group for the output label PvlKeyword name("Name"); - if (dn) name += bname; - if (phase) name += "Phase Angle"; - if (emission) name += "Emission Angle"; - if (incidence) name += "Incidence Angle"; - if (localEmission) name += "Local Emission Angle"; - if (localIncidence) name += "Local Incidence Angle"; - if (latitude) name += "Latitude"; - if (longitude) name += "Longitude"; - if (pixelResolution) name += "Pixel Resolution"; - if (lineResolution) name += "Line Resolution"; - if (sampleResolution) name += "Sample Resolution"; - if (detectorResolution) name += "Detector Resolution"; - if (obliqueDetectorResolution) name += "Oblique Detector Resolution"; - if (northAzimuth) name += "North Azimuth"; - if (sunAzimuth) name += "Sun Azimuth"; - if (spacecraftAzimuth) name += "Spacecraft Azimuth"; - if (offnadirAngle) name += "OffNadir Angle"; - if (subSpacecraftGroundAzimuth) name += "Sub Spacecraft Ground Azimuth"; - if (subSolarGroundAzimuth) name += "Sub Solar Ground Azimuth"; - if (morphologyRank) name += "Morphology Rank"; - if (albedoRank) name += "Albedo Rank"; - if (ra) name += "Right Ascension"; - if (declination) name += "Declination"; - if (bodyFixedX) name += "Body Fixed X"; - if (bodyFixedY) name += "Body Fixed Y"; - if (bodyFixedZ) name += "Body Fixed Z"; - + if (dn) { + name += bname; + raBandNum++; + } + if (phase) { + name += "Phase Angle"; + raBandNum++; + } + if (emission) { + name += "Emission Angle"; + raBandNum++; + } + if (incidence) { + name += "Incidence Angle"; + raBandNum++; + } + if (localEmission) { + name += "Local Emission Angle"; + raBandNum++; + } + if (localIncidence) { + name += "Local Incidence Angle"; + raBandNum++; + } + if (latitude) { + name += "Latitude"; + raBandNum++; + } + if (longitude) { + name += "Longitude"; + raBandNum++; + } + if (pixelResolution) { + name += "Pixel Resolution"; + raBandNum++; + } + if (lineResolution) { + name += "Line Resolution"; + raBandNum++; + } + if (sampleResolution) { + name += "Sample Resolution"; + raBandNum++; + } + if (detectorResolution) { + name += "Detector Resolution"; + raBandNum++; + } + if (obliqueDetectorResolution) { + name += "Oblique Detector Resolution"; + raBandNum++; + } + if (northAzimuth) { + name += "North Azimuth"; + raBandNum++; + } + if (sunAzimuth) { + name += "Sun Azimuth"; + raBandNum++; + } + if (spacecraftAzimuth) { + name += "Spacecraft Azimuth"; + raBandNum++; + } + if (offnadirAngle) { + name += "OffNadir Angle"; + raBandNum++; + } + if (subSpacecraftGroundAzimuth) { + name += "Sub Spacecraft Ground Azimuth"; + raBandNum++; + } + if (subSolarGroundAzimuth) { + name += "Sub Solar Ground Azimuth"; + raBandNum++; + } + if (morphologyRank) { + name += "Morphology Rank"; + raBandNum++; + } + if (albedoRank) { + name += "Albedo Rank"; + raBandNum++; + } + if (ra) { + name += "Right Ascension"; + } + if (declination) { + name += "Declination"; + } + if (bodyFixedX) { + name += "Body Fixed X"; + } + if (bodyFixedY) { + name += "Body Fixed Y"; + } + if (bodyFixedZ) { + name += "Body Fixed Z"; + } // Create the output cube. Note we add the input cube to expedite propagation // of input cube elements (label, blobs, etc...). It will be cleared // prior to systematic processing only if the DN option is not selected. - // If DN is chosen by the user, then we propagate the input buffer with a + // If DN is chosen by the user, then we propagate the input buffer with a // different function - one that accepts both input and output buffers. (void) p.SetInputCube("FROM", OneBand); - Cube *ocube = p.SetOutputCube("TO", icube->sampleCount(), + Cube *ocube = p.SetOutputCube("TO", icube->sampleCount(), icube->lineCount(), nbands); p.SetBrickSize(64, 64, nbands); @@ -238,7 +314,7 @@ void IsisMain() { // exists, remove all existing keywords and add the keywords for this app. // Otherwise, just put the group in. PvlObject &cobj = ocube->label()->findObject("IsisCube"); - if(!cobj.hasGroup("BandBin")) { + if (!cobj.hasGroup("BandBin")) { cobj.addGroup(PvlGroup("BandBin")); } @@ -283,8 +359,8 @@ void phocube(Buffer &out) { // function. We must compute the offset to start at the second band. int skipDN = (dn) ? 64 * 64 : 0; - for(int i = 0; i < 64; i++) { - for(int j = 0; j < 64; j++) { + for (int i = 0; i < 64; i++) { + for (int j = 0; j < 64; j++) { MosData mosd, *p_mosd(0); // For special mosaic angles @@ -292,7 +368,8 @@ void phocube(Buffer &out) { double samp = out.Sample(index); double line = out.Line(index); - bool isGood=false; + // Checks to see if the point is in outer space + bool isGood = false; if (noCamera) { isGood = projection->SetWorld(samp, line); } @@ -301,19 +378,20 @@ void phocube(Buffer &out) { } if (isGood) { - if(phase) { + + if (phase) { out[index] = cam->PhaseAngle(); index += 64 * 64; } - if(emission) { + if (emission) { out[index] = cam->EmissionAngle(); index += 64 * 64; } - if(incidence) { + if (incidence) { out[index] = cam->IncidenceAngle(); index += 64 * 64; } - if(localEmission || localIncidence) { + if (localEmission || localIncidence) { Angle phase; Angle incidence; Angle emission; @@ -330,66 +408,66 @@ void phocube(Buffer &out) { index += 64 * 64; } } - if(latitude) { - if(noCamera) { - out[index] = projection->UniversalLatitude(); + if (latitude) { + if (noCamera) { + out[index] = proj->UniversalLatitude(); } else { out[index] = cam->UniversalLatitude(); } index += 64 * 64; } - if(longitude) { - if(noCamera) { - out[index] = projection->UniversalLongitude(); + if (longitude) { + if (noCamera) { + out[index] = proj->UniversalLongitude(); } else { out[index] = cam->UniversalLongitude(); } index += 64 * 64; } - if(pixelResolution) { - if(noCamera) { - out[index] = projection->Resolution(); + if (pixelResolution) { + if (noCamera) { + out[index] = proj->Resolution(); } else { out[index] = cam->PixelResolution(); } index += 64 * 64; } - if(lineResolution) { + if (lineResolution) { out[index] = cam->LineResolution(); index += 64 * 64; } - if(sampleResolution) { + if (sampleResolution) { out[index] = cam->SampleResolution(); index += 64 * 64; } - if(detectorResolution) { + if (detectorResolution) { out[index] = cam->DetectorResolution(); index += 64 * 64; } - if(obliqueDetectorResolution) { + if (obliqueDetectorResolution) { out[index] = cam->ObliqueDetectorResolution(); index += 64 * 64; } - if(northAzimuth) { + if (northAzimuth) { out[index] = cam->NorthAzimuth(); index += 64 * 64; } - if(sunAzimuth) { + if (sunAzimuth) { out[index] = cam->SunAzimuth(); index += 64 * 64; } - if(spacecraftAzimuth) { + if (spacecraftAzimuth) { out[index] = cam->SpacecraftAzimuth(); index += 64 * 64; } - if(offnadirAngle) { + if (offnadirAngle) { out[index] = cam->OffNadirAngle(); index += 64 * 64; } - if(subSpacecraftGroundAzimuth) { + if (subSpacecraftGroundAzimuth) { double ssplat, ssplon; ssplat = ssplon = 0.0; cam->subSpacecraftPoint(ssplat, ssplon); @@ -397,7 +475,7 @@ void phocube(Buffer &out) { cam->UniversalLongitude(), ssplat, ssplon); index += 64 * 64; } - if(subSolarGroundAzimuth) { + if (subSolarGroundAzimuth) { double sslat, sslon; sslat = sslon = 0.0; cam->subSolarPoint(sslat,sslon); @@ -408,36 +486,39 @@ void phocube(Buffer &out) { // Special Mosaic indexes if (morphologyRank) { - if (!p_mosd) { p_mosd = getMosaicIndicies(*cam, mosd); } + if (!p_mosd) { + p_mosd = getMosaicIndicies(*cam, mosd); + } out[index] = mosd.m_morph; index += 64 * 64; } if (albedoRank) { - if (!p_mosd) { p_mosd = getMosaicIndicies(*cam, mosd); } + if (!p_mosd) { + p_mosd = getMosaicIndicies(*cam, mosd); + } out[index] = mosd.m_albedo; index += 64 * 64; } - + if (ra) { out[index] = cam->RightAscension(); index += 64 * 64; } - + if (declination) { out[index] = cam->Declination(); index += 64 * 64; } - - + if (!noCamera) { double pB[3]; cam->Coordinate(pB); if (bodyFixedX) { - out[index] = pB[0]; - index += 64 * 64; + out[index] = pB[0]; + index += 64 * 64; } - + if (bodyFixedY) { out[index] = pB[1]; index += 64 * 64; @@ -447,12 +528,20 @@ void phocube(Buffer &out) { index += 64 * 64; } } - } - // Trim outerspace + + // Trim outer space except RA and dec bands else { - for(int b = (skipDN) ? 1 : 0; b < nbands; b++) { - out[index] = Isis::NULL8; + for (int b = (skipDN) ? 1 : 0; b < nbands; b++) { + if(ra && b == raBandNum) { + out[index] = cam->RightAscension(); + } + else if (declination && b == raBandNum + 1) { + out[index] = cam->Declination(); + } + else { + out[index] = Isis::NULL8; + } index += 64 * 64; } } @@ -525,4 +614,3 @@ void UpdateBandKey(const QString &keyname, PvlGroup &bb, const int &nvals, bb.addKeyword(makeKey(keyname, nvals, defVal), PvlContainer::Replace); return; } - diff --git a/isis/src/base/apps/phocube/phocube.xml b/isis/src/base/apps/phocube/phocube.xml index a04dc72ffe..39474d5636 100644 --- a/isis/src/base/apps/phocube/phocube.xml +++ b/isis/src/base/apps/phocube/phocube.xml @@ -10,21 +10,21 @@ xsi:noNamespaceSchemaLocation=

- This program, phocube, creates backplane bands that contain photometric, geometric, and + This program, phocube, creates backplane bands that contain photometric, geometric, and spacecraft instrument information for an image file. The parameter options - range from photometric angles (incidence, emission, and phase) to various - azimuth angles, and options based on spatial + range from photometric angles (incidence, emission, and phase) to various + azimuth angles, and options based on spatial (latitude, longitude, and resolution) information. This program will - not work on Level1 images without a camera model, or on mosaics. - The input image pixels are not propagated to the output file unless the - user selects the "DN" option. The following is a partial list of how users have made use of + not work on Level1 images without a camera model, or on mosaics. + The input image pixels are not propagated to the output file unless the + user selects the "DN" option. The following is a partial list of how users have made use of band output:

    -
  • Evaluate the individual bands in order to establish subsequent - image processing steps
  • +
  • Evaluate the individual bands in order to establish subsequent + image processing steps
  • Specify as input to other ISIS3 programs such as fx and photomet
  • Demonstrate the result of each selected option
  • -
  • Determine how the images are mosaicked together after the +
  • Determine how the images are mosaicked together after the Level2 images are created from the Level1 images with backplanes

@@ -37,32 +37,32 @@ xsi:noNamespaceSchemaLocation=

- There are instances where the local emission angle and the local incidence angle will have - values over 90 degrees. ISIS allows the computation of emission and incidence angles greater - than 90 degrees. This feature allows representation of viewing and illumination conditions - where there is actual target body surface data beyond the limb or deep terminator boundary - areas. Applications such as photomet that applies photometric functions honor the 90-degree - boundary. Applications such as photrim can be applied to the phocube output to replace the - angle values above 90 degrees to NULL. There are certain processes that need these data, - therefore it is allowed. + There are instances where the local emission angle and the local incidence angle will have + values over 90 degrees. ISIS allows the computation of emission and incidence angles greater + than 90 degrees. This feature allows representation of viewing and illumination conditions + where there is actual target body surface data beyond the limb or deep terminator boundary + areas. Applications such as photomet that applies photometric functions honor the 90-degree + boundary. Applications such as photrim can be applied to the phocube output to replace the + angle values above 90 degrees to NULL. There are certain processes that need these data, + therefore it is allowed.

- This program requires a Level1 file that has a successful - "spiceinit" applied to it, or a Level2 image - cube file. For every valid input pixel, an output pixel is computed - based on either the SPICE information, or the - map projected spatial information, - or a pre-defined equation. + This program requires a Level1 file that has a successful + "spiceinit" applied to it, or a Level2 image + cube file. For every valid input pixel, an output pixel is computed + based on either the SPICE information, or the + map projected spatial information, + or a pre-defined equation.

-

+

The parameters "morphologyRank" and "albedoRank" are specifically designed to be used - by the ISIS3 mosaic programs. A mosaic program will automatically compare - two pixel values to determine how each pixel is mosaicked into an output - file, which depends on whether a morphology-based or an albedo-based - product is desired. The program computes a - DN value for every input pixel based on - the formulas listed below and outputs the value to a backplane band. - These backplane bands are used by the ISIS3 mosaic programs. The following are + by the ISIS3 mosaic programs. A mosaic program will automatically compare + two pixel values to determine how each pixel is mosaicked into an output + file, which depends on whether a morphology-based or an albedo-based + product is desired. The program computes a + DN value for every input pixel based on + the formulas listed below and outputs the value to a backplane band. + These backplane bands are used by the ISIS3 mosaic programs. The following are equations for "morphologyRank" and "albedoRank" options:

@@ -73,41 +73,41 @@ xsi:noNamespaceSchemaLocation= (1/cos(IncidenceAngle))]
-

+

- All the options in phocube are applicable if the input file is a Level1 - image and has a camera model associated with the file. If the input file - is a map-projected Level2 image, only a few options are appropriate + All the options in phocube are applicable if the input file is a Level1 + image and has a camera model associated with the file. If the input file + is a map-projected Level2 image, only a few options are appropriate and available for selection.

- + The following options are available for Level1 images that contain a camera model:

    - +
  • DN
  • -
  • EMISSION
  • +
  • EMISSION
  • INCIDENCE
  • PHASE
  • OFFNADIRANGLE
  • - +
  • LOCALEMISSION
  • LOCALINCIDENCE
  • - -
  • LATITUDE
  • + +
  • LATITUDE
  • LONGITUDE
  • -
  • DETECTORRESOLUTION
  • +
  • DETECTORRESOLUTION
  • OBLIQUEDETECTORRESOLUTION
  • PIXELRESOLUTION
  • LINERESOLUTION
  • -
  • SAMPLERESOLUTION
  • -
  • NORTHAZIMUTH
  • +
  • SAMPLERESOLUTION
  • +
  • NORTHAZIMUTH
  • SUNAZIMUTH
  • -
  • SPACECRAFTAZIMUTH
  • +
  • SPACECRAFTAZIMUTH
  • SUBSPACECRAFTGROUNDAZIMUTH
  • SUBSOLARGROUNDAZIMUTH
  • MORPHOLOGYRANK
  • ALBEDORANK
  • -
  • RADEC (Right Ascension, Declination )
  • BODYFIXED
  • +
  • RADEC (Right Ascension, Declination )
The following options are available for Level2 images:
    @@ -118,26 +118,26 @@ xsi:noNamespaceSchemaLocation=

- The BandBin group keywords are updated in the labels of the output cube + The BandBin group keywords are updated in the labels of the output cube file. The keyword "Name" within the BandBin group, - shown below, is populated with the name of each option selected by the - user as bands. These bands can be referenced by their names in - applications such as + shown below, is populated with the name of each option selected by the + user as bands. These bands can be referenced by their names in + applications such as "mapmos" - and - "qview." + and + "qview."

     Example:
-    
+
     phocube from=EW0131773041G_cal.cub to=EW0131773041G_cal.pho.cub morph=true dn=true
-     
+
     Sample of image label:
-    
+
     Group = Dimensions
       Samples = 1024
       Lines   = 1024
-      Bands   = 7   
+      Bands   = 7 
     End_Group
     
     Group = BandBin
@@ -148,7 +148,7 @@ xsi:noNamespaceSchemaLocation=
       Width  = (5.1, 5.1, 5.1, 5.1, 5.1, 5.1, 5.1)
     End_Group
     
-    
+
     Note:  The first band retained the BandBin Name value from the input file.
     The program has "Phase Angle," "Emission Angle," "Incidence Angle,"
     "Latitude," and "Longitude" options pre-selected.
@@ -156,14 +156,14 @@ xsi:noNamespaceSchemaLocation=
    

- If the backplane bands generated by phocube are used in the mosaic - programs and the mosaic requires the input image pixel, the "DN" - parameter name must be set to "true" in phocube. When backplane + If the backplane bands generated by phocube are used in the mosaic + programs and the mosaic requires the input image pixel, the "DN" + parameter name must be set to "true" in phocube. When backplane bands are used in the - "fx" - or - "photomet" - program, it is not necessary to propagate the input image to the output + "fx" + or + "photomet" + program, it is not necessary to propagate the input image to the output file.

@@ -202,7 +202,7 @@ xsi:noNamespaceSchemaLocation= Modified filename parameters to be cube parameters where necessary. - Fixed problem where the program threw an error when it tried to propagate + Fixed problem where the program threw an error when it tried to propagate the pixel type from the input file. @@ -225,19 +225,19 @@ xsi:noNamespaceSchemaLocation= Added new parameters: PIXELRESOLUTION, LINERESOLUTION, - SAMPLERESOLUTION, DETECTORRESOLUTION, NORTHAZIMUTH, + SAMPLERESOLUTION, DETECTORRESOLUTION, NORTHAZIMUTH, SUNAZIMUTH, SPACECRAFTAZIMUTH, OFFNADIRANGLE. Fixed a bug that occurred when processing band-dependent cubes. - Added the propagation of the input cube labels, objects, blobs, etc..., so + Added the propagation of the input cube labels, objects, blobs, etc..., so the pedigree of the input source is retained. Set the defaults for PIXELRESOLUTION, LINERESOLUTION, - SAMPLERESOLUTION, DETECTORRESOLUTION, NORTHAZIMUTH, + SAMPLERESOLUTION, DETECTORRESOLUTION, NORTHAZIMUTH, SUNAZIMUTH, SPACECRAFTAZIMUTH, and OFFNADIRANGLE to FALSE. @@ -249,21 +249,21 @@ xsi:noNamespaceSchemaLocation= Added the option to propagate the input pixel value (DN) - as the first band (however, this, by default, will not be invoked as default so as to - preserve pre-existing behavior). Ensure the Center, OriginalBand, Name, and - Width BandBin keyword values are populated properly and propagated to - the output label (should support camera models). Added MORPHOLOGY and + as the first band (however, this, by default, will not be invoked as default so as to + preserve pre-existing behavior). Ensure the Center, OriginalBand, Name, and + Width BandBin keyword values are populated properly and propagated to + the output label (should support camera models). Added MORPHOLOGY and ALBEDO backplane options for special mosaic options. Added the capability to run this program on image files where the camera - information is missing. The ability to generate lat/lon bands for + information is missing. The ability to generate lat/lon bands for image files is highly useful when performing fx operations on the files. Other bands that can be created for a mosaic file include: Dn and PixelResolution. A new parameter, SOURCE, was added so that the user can specify if their image does not contain a camera model. This will result in the - appropriate parameter choices being greyed out. If the user does not specify - that their file does not contain a camera model, then they will receive an error + appropriate parameter choices being greyed out. If the user does not specify + that their file does not contain a camera model, then they will receive an error if they choose to create a band that cannot be created for the file. @@ -285,14 +285,14 @@ xsi:noNamespaceSchemaLocation= Fixes mantis ticket #1525. - The error thrown when the input is a projected image was misleading. It has been fixed to + The error thrown when the input is a projected image was misleading. It has been fixed to be more helpful for the user. Fixes #923. Updated documentation. Fixes #1001. - Added Ra/Dec and Body Fixed Coordinates options to help create new camera models and help + Added Ra/Dec and Body Fixed Coordinates options to help create new camera models and help with mission team operations. References #2277. @@ -302,6 +302,9 @@ xsi:noNamespaceSchemaLocation= Renamed albedo to albedoRank, and morph (or morphology) to morphRank (or MorphologyRank). Ref #4008. + + Allowed RA and DEC to be exported regardless if the pixel is off body. Fixes #4446. + @@ -319,7 +322,7 @@ xsi:noNamespaceSchemaLocation= This is the input filename. The input image cube can be a Level1 - or Level2 file. For a Level1 image, spiceinit + or Level2 file. For a Level1 image, spiceinit must be successfully applied before running phocube. @@ -336,9 +339,9 @@ xsi:noNamespaceSchemaLocation= This is the output file name. The cube file will contain a band for - each of the selected options. The BandBin Group in the image labels - of the output cube file will be updated with the "Name" keyword - containing the output band names (options) in the order that they are + each of the selected options. The BandBin Group in the image labels + of the output cube file will be updated with the "Name" keyword + containing the output band names (options) in the order that they are stacked within the cube. @@ -349,28 +352,28 @@ xsi:noNamespaceSchemaLocation= CAMERA Specifies whether the geometric information will be obtained from the - camera model or from the map projection. If this parameter is set to CAMERA, - all band options are available for the user to select. If this - parameter is set to PROJECTION, then only DN, LATITUDE, LONGITUDE, and - PIXELRESOLUTION band options are available and all other options - will be greyed out. If the user sets this parameter to CAMERA and - the input file does not contain a camera model or SPICE information, + camera model or from the map projection. If this parameter is set to CAMERA, + all band options are available for the user to select. If this + parameter is set to PROJECTION, then only DN, LATITUDE, LONGITUDE, and + PIXELRESOLUTION band options are available and all other options + will be greyed out. If the user sets this parameter to CAMERA and + the input file does not contain a camera model or SPICE information, then an error message will occur.
- + Create latitude and morphologyRank backplane bands - Within this example, phocube creates an output file with two bands - that contain latitude information and computed morphologyRank - values, which are shown below as separate snapshots under "Output - Images." The input image file shown below under "Input Image" was - not propagated to the phocube output file because DN is set to - false. If the phocube output file is projected and mosaicked, - the mosaic file will contain the latitude information for the first - band and the "morphologyRank" values for the second band, but no image - information from the input image will be included. The "DN" - parameter must be set to "true" if the output product is expected + Within this example, phocube creates an output file with two bands + that contain latitude information and computed morphologyRank + values, which are shown below as separate snapshots under "Output + Images." The input image file shown below under "Input Image" was + not propagated to the phocube output file because DN is set to + false. If the phocube output file is projected and mosaicked, + the mosaic file will contain the latitude information for the first + band and the "morphologyRank" values for the second band, but no image + information from the input image will be included. The "DN" + parameter must be set to "true" if the output product is expected to contain the input image. - from=EW0131773041G_cal.cub to=EW0131773041G_cal.pho.cub phase=no emission=no incidence=no + from=EW0131773041G_cal.cub to=EW0131773041G_cal.pho.cub phase=no emission=no incidence=no longitude=no morphologyRank=yes Run phocube to create latitude and morphologyRank bands. @@ -749,9 +752,9 @@ xsi:noNamespaceSchemaLocation= Example GUI Screenshot of GUI version of the application.
For this example, - only "LATITUDE" and "MORPHOLOGYRANK" are selected. The pre-selected - options "PHASE," "EMISSION," "INCIDENCE," and "LONGITUDE" are - deselected to avoid creating these unwanted backplanes. + only "LATITUDE" and "MORPHOLOGYRANK" are selected. The pre-selected + options "PHASE," "EMISSION," "INCIDENCE," and "LONGITUDE" are + deselected to avoid creating these unwanted backplanes.
@@ -761,7 +764,7 @@ xsi:noNamespaceSchemaLocation= Input file - Screenshot of the input cube file. + Screenshot of the input cube file. @@ -772,9 +775,9 @@ xsi:noNamespaceSchemaLocation= Output file band 1 (Latitude) - + Screenshot of the first band in the output file. This file contains the - latitude information with -72.2 degree as the minimum, and 6.27 degree + latitude information with -72.2 degree as the minimum, and 6.27 degree as the maximum value. @@ -784,9 +787,9 @@ xsi:noNamespaceSchemaLocation= Output file band 2 (MorphologyRank) - + Screenshot of the second band in the output file. This file contains the - computed morphologyRank values with 0.0 as the minimum, and 15.0 as the maximum + computed morphologyRank values with 0.0 as the minimum, and 15.0 as the maximum value. The minimum and maximum values were manually selected to brighten the image for visual presentation. The actual range is between 2.8 and 41.5 DN values. @@ -797,6 +800,6 @@ xsi:noNamespaceSchemaLocation= -
+
diff --git a/isis/src/base/apps/phocube/tsts/off_body/Makefile b/isis/src/base/apps/phocube/tsts/off_body/Makefile new file mode 100644 index 0000000000..19e792ae73 --- /dev/null +++ b/isis/src/base/apps/phocube/tsts/off_body/Makefile @@ -0,0 +1,12 @@ +# Tests RA and declination bands to make sure values are displayed even if not on the planet. +APPNAME = phocube + +include $(ISISROOT)/make/isismake.tsts + +commands: + $(APPNAME) from=$(INPUT)/EW0131773041G.cal.cub \ + to=$(OUTPUT)/radec_bands.cub \ + pixelresolution=true \ + radec=true > /dev/null; + catlab from= $(OUTPUT)/radec_bands.cub \ + to=$(OUTPUT)/output.pvl > /dev/null; diff --git a/isis/src/base/apps/shadow/shadow.xml b/isis/src/base/apps/shadow/shadow.xml index b18e54baaa..f6f14fc0da 100644 --- a/isis/src/base/apps/shadow/shadow.xml +++ b/isis/src/base/apps/shadow/shadow.xml @@ -2,26 +2,28 @@ - High accuracy hillshade with shadow casting + Create a high accuracy hillshade with shadow casting - This program will create a shaded-relief cube from a DEM and match cube.

+ This program, shadow, will create a shaded-relief cube from a digital + elevation model (DEM) and a MATCH cube.

- This program operates much like 'shade' but instead of an azimuth/elevation input, we use the - sun's position at the center of the given cube or at a given time and, by default, factor in - shadows cast by features.

+ We use the sun's position at the center of the input cube or a user-defined + observation time. By default, we factor in shadows cast by features. + This program operates much like the ISIS3 'shade' program which instead requires azimuth/elevation as input.

- Using the sun's position allows much higher precision shading and enables the possibility of - computing shadowed areas. The algorithm description below is provided to help understand the + However, with the shadow application, using the sun's position allows much + higher precision shading and enables the possibility of computing shadowed areas. + The algorithm description below is provided to help understand the optimization settings.

User-Requirements
The user must supply an elevation model (DEM) and either an observation time or a cube - with raw camera geometry (see spiceinit).

+ with raw camera geometry (see 'spiceinit').

Understanding the Algorithm
- The shade program's algorithm is called 'hillshade' in this algorithm description. + The 'shade' program's algorithm is called 'hillshade' in this algorithm description.
  1. @@ -30,35 +32,32 @@
  2. - For every pixel in the input elevation model
    + For every pixel in the input elevation model, compute the hillshade value for the pixel, and
    1. - Compute the hillshade value for the pixel, and -
    2. -
    3. - if the hillshade result is positive (facing towards the sun) then estimate if the pixel is + if the hillshade result is positive (facing towards the sun), then estimate if the pixel is in shadow;
    4. - if the hillshade value is positive and the pixel is in shadow then the result is an LRS; + if the hillshade value is positive and the pixel is in shadow, then the result is an LRS;
    5. - if the hillshade value is positive and the pixel is not in shadow then the result is the + if the hillshade value is positive and the pixel is not in shadow, then the result is the hillshade result and
    6. - if the hillshade value is negative the result is an LRS + if the hillshade value is negative, the result is low resolution saturation (LRS).
- The algorithm to estimate if a pixel is in shadow + The algorithm to estimate if a pixel is in shadow:
  1. - Optimization: If SHADOW, and this elevation model pixel is known to be shadowed, + Optimization: If SHADOW, and this elevation model pixel is known to be shadowed, consider this pixel to be shadowed and stop (no pixels are initially known to be - shadowed). + shadowed).
  2. Compute the pixel's body-fixed coordinate (XYZ) position. @@ -68,7 +67,7 @@ the sun.
  3. - Subtract the sun's position from the elevation model pixel's position, giving you a vector + Subtract the sun's position from the elevation model pixel's position, providing a vector to the center (or edge) of the sun.
  4. @@ -84,10 +83,10 @@
  5. Update estimate of how far along the 3D ray is equivalent to the full resolution of the elevation model.
    - Optimization: Multiply the step by PRECISION.
    - Optimization : If SKIPOVERSHADOW, while the next linearly-extrapolated elevation + Optimization: Multiply the step by PRECISION.
    + Optimization : If SKIPOVERSHADOW, while the next linearly-extrapolated elevation model position is known to be in shadow, increase the next step size by the - estimate up to MAXSKIPOVERSHADOWSTEPS times
    . + estimate up to MAXSKIPOVERSHADOWSTEPS times.
  6. Check for a solution
    • @@ -98,9 +97,9 @@
    • If the ray's elevation is higher than the highest point on the elevation model, the originating pixel is in light.
      - Optimization: If LIGHTCURTAIN, and the ray's elevation is higher than a previous ray + Optimization: If LIGHTCURTAIN, and the ray's elevation is higher than a previous ray that intersected this pixel in the elevation model, consider the - originating pixel in light. + originating pixel in light.
  7. @@ -109,18 +108,18 @@
  8. If LIGHTCURTAIN, and the pixel was determined to be in light, record the elevations of the ray where it projected onto the elevation model.
    - Optimization: If LOWERLIGHTCURTAIN, lower the elevations along the ray by + Optimization: If LOWERLIGHTCURTAIN, lower the elevations along the ray by subtracting the minimum difference between the ray and the elevation model while - the ray was being walked..
    - Optimization: If CACHEINTERPOLATEDVALUES, linearly interpolate the points where the - ray would have intersected the elevation model to one pixel accuracy. + the ray was being walked.
    + Optimization: If CACHEINTERPOLATEDVALUES, linearly interpolate the points where the + ray would have intersected the elevation model to one pixel accuracy.
  9. If SHADOWMAP, and the pixel was determined to be in shadow, record all points where the ray projected onto the elevation model to be known shadowed points, excluding the actual intersection point.
    - Optimization: If CACHEINTERPOLATEDVALUES, linearly interpolate the points where the - ray would have intersected the elevation model to one pixel accuracy. + Optimization: If CACHEINTERPOLATEDVALUES, linearly interpolate the points where the + ray would have intersected the elevation model to one pixel accuracy.
  10. @@ -137,8 +136,8 @@ cache, and 16 bytes for the shadow cache, but the caches are hash-based causing a large amount of potential overhead. The caches are not limited to their specified sizes, only reduced to them periodically, and larger cache sizes result in this program consuming more memory. - Although the larger the cache, the longer takes to lookup a single value (which happens often), - in general larger caches mean less CPU time for the cost of memory. + The larger the cache, the longer it takes to lookup a single value (which happens often). + In general, larger caches mean less CPU time for the cost of memory. @@ -152,6 +151,10 @@ Added NaifStatus::CheckErrors() to see if any NAIF errors were signaled. References #2248. + + Added the PixelType attribute to the output cube and set it to real. + Documentation updated by editor. Fixes #5187. + @@ -170,6 +173,7 @@ cube + real output Shaded and shadowed DEM @@ -267,8 +271,8 @@ This is the estimated radius of the sun in solar radii. Since the unit "solar radius" is not our best guess of the sun's radius, the default is slightly different than 1. - A larger number has the end effect of lessening shadows, a smaller number increases - shadows. The sun's radius is only used for shadow computations, hillshade always uses the + A larger number has the end effect of lessening shadows; a smaller number increases + shadows. The sun's radius is only used for shadow computations. Hillshade always uses the sun's center. 0.0 @@ -291,8 +295,8 @@ This should be the time of the observer to use for the sun's position. The entered time - will be adjusted for light-time between the sun and the observer. The format should be: - "YYYY-MM-DDTHH:MM:SS.SSS" - for example: "2012-01-01T14:25:15.36" + will be adjusted for light-time between the sun and the observer. The format should be + "YYYY-MM-DDTHH:MM:SS.SSS"; for example, "2012-01-01T14:25:15.36" @@ -306,7 +310,7 @@ This is a list of quick settings for the other parameters in the Optimizations group. This also includes the ability to disable the shadow computations entirely. These options are - provided for those who don't need a lot of customization or don't want to calculate + provided for those who do not need a lot of customization or do not want to calculate shadow positions. @@ -417,10 +421,10 @@ When a ray is determined to be in shadow, every DEM pixel between the original position and - the point at which the DEM intersected the ray will be marked as in shadow. We then don't - have to do any significant work when processing a pixel we previously determined was in + the point at which the DEM intersected the ray will be marked as in shadow. We then perform no + significant work when processing a pixel that we previously determined was in shadow. This also helps avoid unnecessary ray-DEM intersection checks (because surfaces - aren't shadowed by surfaces already in shadow). + are not shadowed by surfaces already in shadow). BASESHADOWCACHESIZE @@ -436,8 +440,8 @@ The shadow cache is allowed to grow to an unlimited size while the shadowing algorithm is - processing. However, periodically the caches are shrunk - this is the approximate number - of elements to shrink the shadow cache to. The shrinking is optimized in a way that + processing. This is the approximate number of elements to shrink the + shadow cache to, as periodically the caches are shrunk. The shrinking is optimized in a way that is mostly respected, but not guaranteed. 0 @@ -480,8 +484,8 @@ The light curtain cache is allowed to grow to an unlimited size while the shadowing - algorithm is processing. However, periodically the caches are shrunk - this is the - approximate number of elements to shrink the light caches to. The shrinking is optimized + algorithm is processing. This is the approximate number of elements to + shrink the light caches to, as periodically the caches are shrunk. The shrinking is optimized in a way that this is mostly respected, but not guaranteed. 0 @@ -515,7 +519,7 @@ This is a means to lessen the number of ray-DEM intersection checks by guessing the - next ray-DEM intersection location and checking if it's in shadow. If it is, the ray + next ray-DEM intersection location and checking if it is in shadow. If it is, the ray is stepped farther before the next intersection test (up to MAXSKIPOVERSHADOWSTEPS farther). @@ -530,7 +534,7 @@ Distance to consider SKIPOVERSHADOW valid - Since the ray won't make a perfectly straight line across the DEM (DEMs are + Since the ray will not make a perfectly straight line across the DEM (DEMs are projected onto a flat surface) a linear guess as to the next intersection point degrades in accuracy (depending on a number of factors, such as ray elevation, projection type, and DEM accuracy). This controls how far the algorithm can guess the next intersection point @@ -547,7 +551,7 @@ This example will cover running this program in balanced mode. - The input file "localdem.cub" was produced with the commands: + The input file "localdem.cub" was produced with the following commands:
               spiceinit from=ab102401.cub
               fx from=ab102401.cub to=ab102401.radii.cub equation="radius(f1)"
    @@ -565,10 +569,10 @@
           
             
               
    -            Example's parameters in the graphical interface
    +            Example of parameters in the graphical interface
                 
    -              Run this program given a high resolution DEM 'localdem.cub' and the lighting
    -              characteristics from 'ab102401.cub' to create 'shadowed.cub'
    +              Run this program using the high resolution DEM 'localdem.cub' and the lighting
    +              characteristics from 'ab102401.cub' to create the 'shadowed.cub'
                 
                 
    @@ -579,8 +583,8 @@
             
               FROM DEM to do shaded relief and shadow calculations on
               
    -            This is the elevation model for which we're computing a shaded relief and shadow
    -            positions for. This is the FROM cube.
    +            This is the elevation model for which the shaded relief and shadow
    +            positions are to be computed. This is the FROM cube.
               
               
    @@ -588,7 +592,7 @@
             
               MATCH image for computing the sun position
               
    -            This is the image we're gathering the sun position from. In other words, we're trying
    +            We gather the sun position from this image. In other words, we are trying
                 to make the DEM look like this image.
               
               Output shaded relief with shadows
               
                 This is the result of the shadow program. This is the TO cube.
    -
                 The shadows are not as big as the shadows we see in the projected MATCH image because
    -            we're computing the fully shadowed areas in this program. Areas of the image that are
    -            getting less light, but aren't fully shadowed, look like they are in shadow but still
    -            have detail in them. To extend the shadow computations to encompass the visible
    +            we have computed the fully shadowed areas in this program. Areas of the image that are
    +            getting less light, but not fully shadowed, still appear in the shadow, but
    +            show more detail in them. To extend the shadow computations to encompass the visible
                 shadow completely you can turn off SUNEDGE.
               
               
                 Example's parameters in the graphical interface
                 
    -              Run this program given a the DEM 'dem.cub' and the lighting
    -              characteristics from midnight March 1st, 2012 to create 'shadowed_dem.cub'
    +              Run this program given the DEM 'dem.cub' and the lighting
    +              characteristics from midnight March 1st, 2012 to create 'shadowed_dem.cub'.
                 
                 
    @@ -661,8 +664,8 @@
             
               FROM DEM to do shaded relief and shadow calculations on
               
    -            This is the elevation model for which we're computing a shaded relief and shadow
    -            positions for. This is the FROM cube.
    +            This is the elevation model for which the shaded relief and shadow
    +            positions are to be computed. This is the FROM cube.
               
               
    @@ -674,7 +677,7 @@
               
                 This is the result of the shadow program. This is the TO cube.
     
    -            This is what a mosaic of the entire moon would look like, approximately, if all of the
    +            This is what a mosaic of the entire moon would look like, approximately, if all
                 images were taken at one time.
               
               getRight().getSample();
                 if (rhcam.SetImage(rhSamp, rhLine) && rhcam.InCube()) {
                   double radius, lat, lon, sepang, error;
    -              if (Stereo::Elevation(lhcam, rhcam, radius, lat, lon, sepang, error)) {
    +              if (Stereo::elevation(lhcam, rhcam, radius, lat, lon, sepang, error)) {
                     int index;
                     if (WithinTile(lhcam, lat, lon, radius, dem, index)) {
                       double elevation = radius - lhcam.LocalRadius().meters();
    @@ -136,7 +136,7 @@ namespace Isis {
           }
     
           /** Checks a latitude/longitude coordinate if it falls within a buffer */
    -      bool WithinTile(Camera &cam, const double &latitude, const double &longitude, 
    +      bool WithinTile(Camera &cam, const double &latitude, const double &longitude,
                           const double radius, Buffer &obuf, int &index) {
             if (!(cam.SetUniversalGround(latitude, longitude) && cam.InCube())) {
               return (false);
    @@ -147,7 +147,7 @@ namespace Isis {
             if (!inBuffer(Coordinate(line,samp), 0.5)) {
               return (false);
             }
    -        index = obuf.Index(static_cast(samp+0.5), 
    +        index = obuf.Index(static_cast(samp+0.5),
                                static_cast(line+0.5),1);
             return (true);
           }
    diff --git a/isis/src/base/apps/smtk/Stereo.cpp b/isis/src/base/apps/smtk/Stereo.cpp
    deleted file mode 100644
    index 5c4beb08bc..0000000000
    --- a/isis/src/base/apps/smtk/Stereo.cpp
    +++ /dev/null
    @@ -1,200 +0,0 @@
    -/**                                                                       
    - * @file                                                                  
    - * $Revision: 1.1 $                                                             
    - * $Date: 2009/09/09 23:42:41 $                                                 
    -                
    - *                                                                        
    - *   Unless noted otherwise, the portions of Isis written by the USGS are 
    - *   public domain. See individual third-party library and package descriptions 
    - *   for intellectual property information, user agreements, and related  
    - *   information.                                                         
    - *                                                                        
    - *   Although Isis has been used by the USGS, no warranty, expressed or   
    - *   implied, is made by the USGS as to the accuracy and functioning of such 
    - *   software and related material nor shall the fact of distribution     
    - *   constitute any such warranty, and no responsibility is assumed by the
    - *   USGS in connection therewith.                                        
    - *                                                                        
    - *   For additional information, launch                                   
    - *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html                
    - *   in a browser or see the Privacy & Disclaimers page on the Isis website,
    - *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
    - *   http://www.usgs.gov/privacy.html.                                    
    - */                                                                       
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -
    -#include 
    -#include 
    -#include 
    -
    -#include "Camera.h"
    -#include "SpecialPixel.h"
    -#include "Stereo.h"
    -#include "TProjection.h"
    -
    -namespace Isis {
    -
    -
    -  bool Stereo::Elevation(Camera &cam1, Camera &cam2, double &radius,
    -                         double &latitude, double &longitude, 
    -                         double &sepang, double &error) {
    -
    -    // Gut check on input Camera points
    -    radius = latitude = longitude = Isis::Null;
    -    if ( !cam1.HasSurfaceIntersection() ) return (false);
    -    if ( !cam2.HasSurfaceIntersection() ) return (false);
    -
    -    // Get spacecraft position from target
    -    double TC1[3], TC2[3];
    -    TargetToSpacecraft(cam1, TC1);
    -    TargetToSpacecraft(cam2, TC2);
    -
    -
    -    // Get surface vectors from center of body to surface
    -    double TP1[3], TP2[3];
    -    TargetToSurface(cam1, TP1);
    -    TargetToSurface(cam2, TP2);
    -
    -    // Stereo angle
    -    sepang = vsep_c(TC1, TC2) * dpr_c();
    -
    -    SpiceDouble CP1[3], CP2[3];
    -    vsub_c(TC1, TP1, CP1);
    -    vsub_c(TC2, TP2, CP2);
    -
    -    sepang = vsep_c(CP1, CP2) * dpr_c();
    -
    -    double DR1, DR2;
    -    DR1 = vnorm_c(CP1);
    -    DR2 = vnorm_c(CP2);
    -
    -    vscl_c(1.0/DR1, CP1, CP1);
    -    vscl_c(1.0/DR2, CP2, CP2);
    -
    -    // Do stereo intersections
    -    double aa = CP2[0];
    -    double bb = CP2[1];
    -    double cc = CP2[2];
    -    double xx = CP1[0];
    -    double yy = CP1[1];
    -    double zz = CP1[2];
    -
    -    // Vector between both spacecraft
    -    double dd = TC2[0] - TC1[0];
    -    double ee = TC2[1] - TC1[1];
    -    double ff = TC2[2] - TC1[2];
    -
    -    //  Do the stereo intersection 
    -    double bzcy = bb*zz - cc*yy;
    -    double cebf = cc*ee - bb*ff;
    -    double cxaz = cc*xx - aa*zz;
    -    double afcd = aa*ff - cc*dd;
    -    double aybx = aa*yy - bb*xx;
    -    double bdae = bb*dd - aa*ee;
    -
    -    // Get fraction `T' along left vector to "intersection point"
    -    double T=-(bzcy*cebf+cxaz*afcd+aybx*bdae)/
    -              (bzcy*bzcy+cxaz*cxaz+aybx*aybx);
    -    double lx=TC1[0] + T * CP1[0];
    -    double ly=TC1[1] + T * CP1[1];
    -    double lz=TC1[2] + T * CP1[2];
    -
    -    //  Find the Perp. vector between both lines (at shortest sep.)
    -    double x = TC2[0] - lx;
    -    double y = TC2[1] - ly;
    -    double z = TC2[2] - lz;
    -
    -    // Find the separation distance - useful later
    -    double rx = y * CP2[2] - CP2[1] * z;
    -    double ry = CP2[0] * z - x * CP2[2];
    -    double rz = x * CP2[1] - CP2[0] * y;
    -    double dr = std::sqrt(rx*rx+ry*ry+rz*rz);
    -
    -    // Find position of intersection on lower line
    -    rx = CP1[1] * CP2[2] - CP2[1] * CP1[2];
    -    ry = CP2[0] * CP1[2] - CP1[0] * CP2[2];
    -    rz = CP1[0] * CP2[1] - CP2[0] * CP1[1];
    -    double raa = std::sqrt(rx*rx+ry*ry+rz*rz);
    -
    -    // Normalize our new Perpendicular vector
    -    rx = rx/raa;
    -    ry = ry/raa;
    -    rz = rz/raa;
    -
    -    // Get the other intersection position
    -    rx = lx - rx*dr;
    -    ry = ly - ry*dr;
    -    rz = lz - rz*dr;
    -
    -    // Compute the mid point of the intersection on the planets
    -    // surface
    -    double mx = (lx+rx)/2.0;
    -    double my = (ly+ry)/2.0;
    -    double mz = (lz+rz)/2.0;
    -    Rectangular(mx, my, mz, latitude, longitude, radius);
    -    radius *= 1000.0 ;  // convert to meters
    -    error = dr * 1000.0;
    -    return (true);
    -  }
    -
    -
    -  void Stereo::Spherical(const double latitude, const double longitude, 
    -                         const double radius, 
    -                         double &x, double &y, double &z) { 
    -    SpiceDouble rec[3];
    -    latrec_c(radius/1000.0, longitude*rpd_c(), latitude*rpd_c(), &rec[0]);
    -    x = rec[0];
    -    y = rec[1];
    -    z = rec[2];
    -    return;
    -  }
    -
    -   void Stereo::Rectangular(const double x, const double y, const double z, 
    -                            double &latitude, double &longitude,
    -                            double &radius) { 
    -     SpiceDouble rec[3];
    -     rec[0] = x;
    -     rec[1] = y;
    -     rec[2] = z;
    -     reclat_c(&rec[0], &radius, &longitude, &latitude);
    -     longitude *= dpr_c();
    -     latitude *= dpr_c();
    -     longitude = TProjection::To360Domain(longitude);
    -     return;
    -     return;
    -   }
    -
    -   std::vector Stereo::Array2StdVec(const double d[3]) {
    -     std::vector v;
    -     for ( int  i = 0 ; i < 3 ; i++ ) {
    -       v.push_back(d[i]);
    -     }
    -     return (v);
    -   }
    -
    -   double *Stereo::StdVec2Array(const std::vector &v, double *d) {
    -     if ( d == NULL ) {
    -       d = new double[v.size()];
    -     }
    -
    -     for ( unsigned int i = 0 ; i < v.size() ; i++ ) {
    -       d[i] = v[i];
    -     }
    -     return (d);
    -   }
    -
    -   void Stereo::TargetToSpacecraft(Camera &camera, double TP[3]) {
    -     camera.instrumentPosition(TP);
    -     return;
    -   }
    -
    -   void Stereo::TargetToSurface(Camera &camera, double TC[3]) {
    -     camera.Coordinate(TC);
    -     return;
    -   }
    -
    -}
    diff --git a/isis/src/base/apps/smtk/Stereo.h b/isis/src/base/apps/smtk/Stereo.h
    deleted file mode 100644
    index 64a4e44d6c..0000000000
    --- a/isis/src/base/apps/smtk/Stereo.h
    +++ /dev/null
    @@ -1,74 +0,0 @@
    -#ifndef Stereo_h
    -#define Stereo_h
    -
    -/**
    - * @file
    - * $Revision: 1.1 $
    - * $Date: 2009/09/09 23:42:41 $
    - *
    - *   Unless noted otherwise, the portions of Isis written by the USGS are
    - *   public domain. See individual third-party library and package descriptions
    - *   for intellectual property information, user agreements, and related
    - *   information.
    - *
    - *   Although Isis has been used by the USGS, no warranty, expressed or
    - *   implied, is made by the USGS as to the accuracy and functioning of such
    - *   software and related material nor shall the fact of distribution
    - *   constitute any such warranty, and no responsibility is assumed by the
    - *   USGS in connection therewith.
    - *
    - *   For additional information, launch
    - *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html
    - *   in a browser or see the Privacy & Disclaimers page on the Isis website,
    - *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
    - *   http://www.usgs.gov/privacy.html.
    - */
    -
    -#include "Camera.h"
    -
    -namespace Isis {
    -
    -  /**
    -   * @brief Provide stereo information/data for a point or relationship
    -   *
    -   * @author  2009-09-11 Kris Becker
    -   *
    -   * @internal
    -   *  @history 2012-07-06 Debbie A. Cook, Updated Spice members to be more compliant with Isis 
    -   *                     coding standards. References #972.
    -   *  @history 2012-12-06 Debbie A. Cook - Changed to use TProjection instead of Projection.
    -   *                     References #775.
    -   */
    -  class Stereo  {
    -    public:
    -
    -      /**
    -       * @brief Construct a Stereo object
    -       */
    -      Stereo () { }
    -
    -      /** Destructor for Stereo */
    -      virtual ~Stereo() {}
    -
    -      static bool Elevation(Camera &cam1, Camera &cam2, double &radius,
    -                            double &latitude, double &longitude,
    -                            double &sepang, double &error);
    -
    -      static void Spherical(const double latitude, const double longitude,
    -                            const double radius, double &x, double &y,
    -                            double &z);
    -
    -      static void Rectangular(const double x, const double y, const double z,
    -                              double &latitude, double &longitude,
    -                              double &radius);
    -
    -  private:
    -    static std::vector Array2StdVec(const double d[3]);
    -    static double *StdVec2Array(const std::vector &v, double *d = 0);
    -    static void TargetToSpacecraft(Camera &camera, double TP[3]);
    -    static void TargetToSurface(Camera &camera, double TC[3]);
    -
    -  };
    -};
    -
    -#endif
    diff --git a/isis/src/base/apps/spiceserver/main.cpp b/isis/src/base/apps/spiceserver/main.cpp
    index 1e4639207a..c28148b5f4 100644
    --- a/isis/src/base/apps/spiceserver/main.cpp
    +++ b/isis/src/base/apps/spiceserver/main.cpp
    @@ -149,13 +149,18 @@ void IsisMain() {
           throw IException(IException::User, msg, _FILEINFO_);
         }
     
    -    if ( ui.GetBoolean("CHECKVERSION") && otherVersion != Application::Version() ) {
    -      QString msg = "The SPICE server only supports the latest Isis version [" +
    -                    Application::Version() + "], version [" + otherVersion +
    -                    "] is not compatible";
    -      throw IException(IException::User, msg, _FILEINFO_);
    -    }
     
    +    if (ui.GetBoolean("CHECKVERSION") ) {
    +      QStringList remoteVersion = otherVersion.split(QRegExp("\\s+"))[0].split(QRegExp("\\."));
    +      if ( remoteVersion[1].toInt() < 5) {
    +
    +       QString msg ="The SPICE server only supports Isis versions greater than or equal to 3.5.*.*.";
    +               msg += "Your version:   [" + otherVersion + "] is not compatible";
    +        throw IException(IException::User, msg, _FILEINFO_);
    +
    +      }
    +
    +    }
         // This next section looks a lot like spiceinit, its semi-duplicated because
         //   I did not want users to be able to spiceinit a label without cube
         //   data.
    diff --git a/isis/src/base/apps/spiceserver/spiceserver.xml b/isis/src/base/apps/spiceserver/spiceserver.xml
    index af5d520959..0ab63bc58f 100644
    --- a/isis/src/base/apps/spiceserver/spiceserver.xml
    +++ b/isis/src/base/apps/spiceserver/spiceserver.xml
    @@ -1,6 +1,7 @@
     
     
    -
    +
       
         Spiceinit Server
       
    @@ -50,6 +51,10 @@
         
           Modified to open the temporary cube correctly. Fixes #2213.
         
    +    
    +      Modified the version check.  Now all versions of ISIS3 >= 3.5.*.* will be acceptable
    +      to the application.
    +    
       
     
       
    diff --git a/isis/src/base/apps/stats/main.cpp b/isis/src/base/apps/stats/main.cpp
    index 55ae598b81..150cec80e5 100644
    --- a/isis/src/base/apps/stats/main.cpp
    +++ b/isis/src/base/apps/stats/main.cpp
    @@ -1,127 +1,11 @@
     #include "Isis.h"
     
    -#include 
    -#include 
    +#include "UserInterface.h"
    +#include "stats.h"
     
    -#include "Cube.h"
    -#include "Process.h"
    -#include "Histogram.h"
    -#include "Pvl.h"
    -
    -using namespace std;
     using namespace Isis;
     
     void IsisMain() {
    -
       UserInterface &ui = Application::GetUserInterface();
    -  Process process;
    -
    -  // Get the histogram
    -  Cube *inputCube = process.SetInputCube("FROM");
    -
    -  double validMin = Isis::ValidMinimum;
    -  double validMax = Isis::ValidMaximum;
    -
    -  if ( ui.WasEntered("VALIDMIN") ) {
    -    validMin = ui.GetDouble("VALIDMIN");
    -  }
    -
    -  if ( ui.WasEntered("VALIDMAX") ) {
    -    validMax = ui.GetDouble("VALIDMAX");
    -  }
    -  
    -  // Set a global Pvl for storing results
    -  Pvl mainPvl;
    -  
    -  // Get the number of bands to process
    -  int bandCount = inputCube->bandCount();
    -  
    -  for (int i = 1; i <= bandCount; i++) {
    -    Histogram *stats = inputCube->histogram(i, validMin, validMax);
    -
    -    // Construct a label with the results
    -    PvlGroup results("Results");  
    -    results += PvlKeyword("From", inputCube->fileName());
    -    results += PvlKeyword("Band", toString(inputCube->physicalBand(i)));
    -    if ( stats->ValidPixels() != 0 ) {
    -      results += PvlKeyword("Average", toString(stats->Average()));
    -      results += PvlKeyword("StandardDeviation", toString(stats->StandardDeviation()));
    -      results += PvlKeyword("Variance", toString(stats->Variance()));
    -      // These statistics only worked on a histogram
    -      results += PvlKeyword("Median", toString(stats->Median()));
    -      results += PvlKeyword("Mode", toString(stats->Mode()));
    -      results += PvlKeyword("Skew", toString(stats->Skew()));
    -      results += PvlKeyword("Minimum", toString(stats->Minimum()));
    -      results += PvlKeyword("Maximum", toString(stats->Maximum()));
    -      results += PvlKeyword("Sum", toString(stats->Sum()));
    -    }
    -    results += PvlKeyword("TotalPixels", toString(stats->TotalPixels()));
    -    results += PvlKeyword("ValidPixels", toString(stats->ValidPixels()));
    -    results += PvlKeyword("OverValidMaximumPixels", toString(stats->OverRangePixels()));
    -    results += PvlKeyword("UnderValidMinimumPixels", toString(stats->UnderRangePixels()));
    -    results += PvlKeyword("NullPixels", toString(stats->NullPixels()));
    -    results += PvlKeyword("LisPixels", toString(stats->LisPixels()));
    -    results += PvlKeyword("LrsPixels", toString(stats->LrsPixels()));
    -    results += PvlKeyword("HisPixels", toString(stats->HisPixels()));
    -    results += PvlKeyword("HrsPixels", toString(stats->HrsPixels()));
    -    
    -    mainPvl.addGroup(results);
    -    
    -    delete stats;
    -    stats = NULL;
    -
    -    // Write the results to the log
    -    Application::Log(results);
    -  }
    -  
    -  // Write the results to the output file if the user specified one
    -  if ( ui.WasEntered("TO") ) {
    -    QString outFile = FileName(ui.GetFileName("TO")).expanded();
    -    bool exists = FileName(outFile).fileExists();
    -    bool append = ui.GetBoolean("APPEND");
    -    ofstream os;
    -    bool writeHeader = false;
    -    //write the results in the requested format.
    -    if ( ui.GetString("FORMAT") == "PVL" ) {
    -      if (append) {
    -        mainPvl.append(outFile);
    -      }
    -      else {
    -        mainPvl.write(outFile);
    -      }
    -    }
    -    else {
    -      //if the format was not PVL, write out a flat file.
    -      if (append) {
    -        os.open(outFile.toLatin1().data(), ios::app);
    -        if (!exists) {
    -          writeHeader = true;
    -        }
    -      }
    -      else {
    -        os.open(outFile.toLatin1().data(), ios::out);
    -        writeHeader = true;
    -      }
    -
    -      if (writeHeader) {
    -        for (int i = 0; i < mainPvl.group(0).keywords(); i++) {
    -          os << mainPvl.group(0)[i].name();
    -          if ( i < mainPvl.group(0).keywords() - 1 ) {
    -            os << ",";
    -          }
    -        }
    -        os << endl;
    -      }
    -      
    -      for (int i = 0; i < mainPvl.groups(); i++) {
    -        for (int j = 0; j < mainPvl.group(i).keywords(); j++) {
    -          os << (QString) mainPvl.group(i)[j];
    -          if ( j < mainPvl.group(i).keywords() - 1 ) {
    -            os << ",";
    -          }
    -        }
    -        os << endl;
    -      }
    -    }
    -  }
    +  stats(ui);
     }
    diff --git a/isis/src/base/apps/stats/stats.cpp b/isis/src/base/apps/stats/stats.cpp
    new file mode 100644
    index 0000000000..4c94087bf2
    --- /dev/null
    +++ b/isis/src/base/apps/stats/stats.cpp
    @@ -0,0 +1,175 @@
    +#include "stats.h"
    +
    +#include 
    +#include 
    +
    +#include "Application.h"
    +#include "CubeAttribute.h"
    +#include "Cube.h"
    +#include "FileName.h"
    +#include "Histogram.h"
    +#include "Pvl.h"
    +#include "UserInterface.h"
    +
    +using namespace std;
    +using namespace Isis;
    +
    +namespace Isis {
    +
    +  /**
    +   * Compute the stats for an ISIS cube. This is the programmatic interface to
    +   * the ISIS3 stats application.
    +   *
    +   * @param ui The User Interface to parse the parameters from
    +   */
    +  void stats(UserInterface &ui) {
    +
    +    Cube *inputCube = new Cube();
    +    CubeAttributeInput inAtt(ui.GetAsString("FROM"));
    +    inputCube->setVirtualBands(inAtt.bands());
    +    inputCube->open(ui.GetFileName("FROM"));
    +
    +    double validMin = Isis::ValidMinimum;
    +    double validMax = Isis::ValidMaximum;
    +
    +    if ( ui.WasEntered("VALIDMIN") ) {
    +      validMin = ui.GetDouble("VALIDMIN");
    +    }
    +
    +    if ( ui.WasEntered("VALIDMAX") ) {
    +      validMax = ui.GetDouble("VALIDMAX");
    +    }
    +
    +    Pvl statsPvl = stats(inputCube, validMin, validMax);
    +
    +    for (int resultIndex = 0; resultIndex < statsPvl.groups(); resultIndex++) {
    +      if (statsPvl.group(resultIndex).name() == "Results") {
    +        Application::Log(statsPvl.group(resultIndex));
    +      }
    +    }
    +
    +    delete inputCube;
    +    inputCube = nullptr;
    +
    +    if ( ui.WasEntered("TO") ) {
    +      QString outFile = FileName(ui.GetFileName("TO")).expanded();
    +      bool append = ui.GetBoolean("APPEND");
    +      //write the results in the requested format.
    +      if ( ui.GetString("FORMAT") == "PVL" ) {
    +        if (append) {
    +          statsPvl.append(outFile);
    +        }
    +        else {
    +          statsPvl.write(outFile);
    +        }
    +      }
    +      else {
    +        bool exists = FileName(outFile).fileExists();
    +        bool writeHeader = false;
    +        ofstream *os = new ofstream;
    +        if (append) {
    +          os->open(outFile.toLatin1().data(), ios::app);
    +          if (!exists) {
    +            writeHeader = true;
    +          }
    +        }
    +        else {
    +          os->open(outFile.toLatin1().data(), ios::out);
    +          writeHeader = true;
    +        }
    +        writeStatsStream(statsPvl, writeHeader, os);
    +        delete os;
    +        os = nullptr;
    +      }
    +    }
    +  }
    +
    +
    +  /**
    +   * Compute statistics about a Cube and store them in a PVL object.
    +   *
    +   * @param cube The cube to compute the statistics of
    +   * @param validMin The minimum pixel value to include in the statistics
    +   * @param validMax The maximum pixel value to include in the statistics
    +   *
    +   * @return @b Pvl The statistics for the cube in a Pvl object
    +   *
    +   * @see Cube::histogram
    +   */
    +  Pvl stats(Cube *cube, double validMin, double validMax) {
    +
    +    // Set a global Pvl for storing results
    +    Pvl statsPvl;
    +
    +    // Get the number of bands to process
    +    int bandCount = cube->bandCount();
    +
    +    for (int i = 1; i <= bandCount; i++) {
    +      Histogram *stats = cube->histogram(i, validMin, validMax);
    +
    +      // Construct a label with the results
    +      PvlGroup results("Results");
    +      results += PvlKeyword("From", cube->fileName());
    +      results += PvlKeyword("Band", toString(cube->physicalBand(i)));
    +      if ( stats->ValidPixels() != 0 ) {
    +        results += PvlKeyword("Average", toString(stats->Average()));
    +        results += PvlKeyword("StandardDeviation", toString(stats->StandardDeviation()));
    +        results += PvlKeyword("Variance", toString(stats->Variance()));
    +        // These statistics only worked on a histogram
    +        results += PvlKeyword("Median", toString(stats->Median()));
    +        results += PvlKeyword("Mode", toString(stats->Mode()));
    +        results += PvlKeyword("Skew", toString(stats->Skew()));
    +        results += PvlKeyword("Minimum", toString(stats->Minimum()));
    +        results += PvlKeyword("Maximum", toString(stats->Maximum()));
    +        results += PvlKeyword("Sum", toString(stats->Sum()));
    +      }
    +      results += PvlKeyword("TotalPixels", toString(stats->TotalPixels()));
    +      results += PvlKeyword("ValidPixels", toString(stats->ValidPixels()));
    +      results += PvlKeyword("OverValidMaximumPixels", toString(stats->OverRangePixels()));
    +      results += PvlKeyword("UnderValidMinimumPixels", toString(stats->UnderRangePixels()));
    +      results += PvlKeyword("NullPixels", toString(stats->NullPixels()));
    +      results += PvlKeyword("LisPixels", toString(stats->LisPixels()));
    +      results += PvlKeyword("LrsPixels", toString(stats->LrsPixels()));
    +      results += PvlKeyword("HisPixels", toString(stats->HisPixels()));
    +      results += PvlKeyword("HrsPixels", toString(stats->HrsPixels()));
    +
    +      statsPvl.addGroup(results);
    +
    +      delete stats;
    +      stats = nullptr;
    +    }
    +
    +    return statsPvl;
    +  }
    +
    +
    +  /**
    +   * Write a statistics Pvl to an output stream in a CSV format.
    +   *
    +   * @param statsPvl The Pvl to write out
    +   * @param writeHeader If a header line should be written
    +   * @param stram The stream to write to
    +   */
    +  void writeStatsStream(const Pvl &statsPvl, bool writeHeader, ostream *stream) {
    +    if (writeHeader) {
    +      for (int i = 0; i < statsPvl.group(0).keywords(); i++) {
    +        *stream << statsPvl.group(0)[i].name();
    +        if ( i < statsPvl.group(0).keywords() - 1 ) {
    +          *stream << ",";
    +        }
    +      }
    +      *stream << endl;
    +    }
    +
    +    for (int i = 0; i < statsPvl.groups(); i++) {
    +      for (int j = 0; j < statsPvl.group(i).keywords(); j++) {
    +        *stream << (QString) statsPvl.group(i)[j];
    +        if ( j < statsPvl.group(i).keywords() - 1 ) {
    +          *stream << ",";
    +        }
    +      }
    +      *stream << endl;
    +    }
    +  }
    +
    +}
    diff --git a/isis/src/base/apps/stats/stats.h b/isis/src/base/apps/stats/stats.h
    new file mode 100644
    index 0000000000..f751914004
    --- /dev/null
    +++ b/isis/src/base/apps/stats/stats.h
    @@ -0,0 +1,24 @@
    +#ifndef stats_h
    +#define stats_h
    +
    +#include 
    +#include 
    +
    +#include 
    +
    +#include "Cube.h"
    +#include "UserInterface.h"
    +
    +namespace Isis {
    +  extern void stats(UserInterface &ui);
    +  extern Pvl stats(
    +        Cube *cube,
    +        double validMin,
    +        double validMax);
    +  extern void writeStatsStream(
    +        const Pvl &statsPvl,
    +        bool writeHeader,
    +        std::ostream *stream);
    +}
    +
    +#endif
    diff --git a/isis/src/base/objs/BulletDskShape/BulletDskShape.cpp b/isis/src/base/objs/BulletDskShape/BulletDskShape.cpp
    index d950a138cc..3959eef40b 100644
    --- a/isis/src/base/objs/BulletDskShape/BulletDskShape.cpp
    +++ b/isis/src/base/objs/BulletDskShape/BulletDskShape.cpp
    @@ -1,25 +1,25 @@
    -/**                                                                       
    - * @file                                                                  
    +/**
    + * @file
      * $Revision$
      * $Date$
      * $Id$
    - * 
    - *   Unless noted otherwise, the portions of Isis written by the USGS are 
    - *   public domain. See individual third-party library and package descriptions 
    - *   for intellectual property information, user agreements, and related  
    - *   information.                                                         
    - *                                                                        
    - *   Although Isis has been used by the USGS, no warranty, expressed or   
    - *   implied, is made by the USGS as to the accuracy and functioning of such 
    - *   software and related material nor shall the fact of distribution     
    + *
    + *   Unless noted otherwise, the portions of Isis written by the USGS are
    + *   public domain. See individual third-party library and package descriptions
    + *   for intellectual property information, user agreements, and related
    + *   information.
    + *
    + *   Although Isis has been used by the USGS, no warranty, expressed or
    + *   implied, is made by the USGS as to the accuracy and functioning of such
    + *   software and related material nor shall the fact of distribution
      *   constitute any such warranty, and no responsibility is assumed by the
    - *   USGS in connection therewith.                                        
    - *                                                                        
    - *   For additional information, launch                                   
    - *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html                
    + *   USGS in connection therewith.
    + *
    + *   For additional information, launch
    + *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html
      *   in a browser or see the Privacy & Disclaimers page on the Isis website,
      *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
    - *   http://www.usgs.gov/privacy.html.                                    
    + *   http://www.usgs.gov/privacy.html.
      */
     
     #include "BulletDskShape.h"
    @@ -53,7 +53,7 @@ namespace Isis {
     
       /**
        * Construct a BulletDskShape from a DSK file.
    -   * 
    +   *
        * @param dskfile The DSK file to load into a Bullet target shape.
        */
       BulletDskShape::BulletDskShape(const QString &dskfile) : m_mesh()  {
    @@ -70,7 +70,7 @@ namespace Isis {
     
       /**
        * Return the number of triangles in the shape
    -   * 
    +   *
        * @return @b int The number of triangles. If nothing has been loaded, then 0 is returned.
        */
       int BulletDskShape::getNumTriangles() const {
    @@ -82,9 +82,9 @@ namespace Isis {
       }
     
     
    -  /** 
    +  /**
        * Return the number of verticies in the shape
    -   * 
    +   *
        * @return @b int The number of verticies. If nothing has been loaded, then 0 is returned.
        */
       int BulletDskShape::getNumVertices() const {
    @@ -98,14 +98,14 @@ namespace Isis {
     
       /**
       * @brief Return normal for a given triangle index
    -  *  
    -  * This method is particularly useful to return the normal of a triangle plate 
    -  * in a mesh-based target body.  
    -  * 
    -  * @author 2017-03-28 Kris Becker 
    -  *  
    +  *
    +  * This method is particularly useful to return the normal of a triangle plate
    +  * in a mesh-based target body.
    +  *
    +  * @author 2017-03-28 Kris Becker
    +  *
       * @param indexId The index of the triangle in the mesh.
    -  * 
    +  *
       * @return @b btVector3 The local normal for the triangle.
       */
       btVector3 BulletDskShape::getNormal(const int indexId) const {
    @@ -118,9 +118,9 @@ namespace Isis {
     
       /**
        * Get the vertices of a triangle in the mesh.
    -   * 
    +   *
        * @param index The index of the triangle in the mesh.
    -   * 
    +   *
        * @return @b btMatrix3x3 Matrix with each row containing the coordinate of a
        *                        vertex. The vertices are ordered counter-clockwise
        *                        around the surface normal of the triangle.
    @@ -140,7 +140,7 @@ namespace Isis {
     
         const btScalar *t_vertex = static_cast ((void *) v_mesh.m_vertexBase);
     
    -    btMatrix3x3 triangle(t_vertex[vndx0+0], t_vertex[vndx0+1], t_vertex[vndx0+2], 
    +    btMatrix3x3 triangle(t_vertex[vndx0+0], t_vertex[vndx0+1], t_vertex[vndx0+2],
                              t_vertex[vndx1+0], t_vertex[vndx1+1], t_vertex[vndx1+2],
                              t_vertex[vndx2+0], t_vertex[vndx2+1], t_vertex[vndx2+2]);
         return ( triangle );
    @@ -148,21 +148,16 @@ namespace Isis {
     
     
     /**
    - * @brief Load the contents of a NAIF DSK and create a Bullet triangle mesh  
    - * 
    - * @author 2017-03-28 Kris Becker 
    - * 
    + * @brief Load the contents of a NAIF DSK and create a Bullet triangle mesh
    + *
    + * @author 2017-03-28 Kris Becker
    + *
      * @param dskfile The DSK file to load.
      */
       void BulletDskShape::loadFromDsk(const QString &dskfile) {
     
         /** NAIF DSK parameter setup   */
    -    SpiceInt      v_handle;   //!< The DAS file handle of the DSK file.
    -    SpiceDLADescr v_dladsc;   /**< The DLA descriptor of the DSK segment representing the 
    -                                   target surface.*/
    -    SpiceDSKDescr v_dskdsc;   //!< The DSK descriptor.
    -    SpiceInt      v_plates;   //!< Number of Plates in the model.
    -    SpiceInt      v_vertices; //!< Number of vertices defining the plate.
    +    SpiceInt                   handle;   //!< The DAS file handle of the DSK file.
     
         // Sanity check
         FileName dskFile(dskfile);
    @@ -170,71 +165,90 @@ namespace Isis {
           QString mess = "NAIF DSK file [" + dskfile + "] does not exist.";
           throw IException(IException::User, mess, _FILEINFO_);
         }
    -  
    +
         // Open the NAIF Digital Shape Kernel (DSK)
    -    dasopr_c( dskFile.expanded().toLatin1().data(), &v_handle );
    +    dasopr_c( dskFile.expanded().toLatin1().data(), &handle );
         NaifStatus::CheckErrors();
    -  
    +
         // Search to the first DLA segment
    -    SpiceBoolean found;
    -    dlabfs_c( v_handle, &v_dladsc, &found );
    +    SpiceBoolean  found;
    +    SpiceDLADescr segment;
    +    dlabfs_c( handle, &segment, &found );
         NaifStatus::CheckErrors();
         if ( !found ) {
    -      QString mess = "No segments found in DSK file " + dskfile ; 
    +      QString mess = "No segments found in DSK file " + dskfile ;
           throw IException(IException::User, mess, _FILEINFO_);
         }
     
    -    dskgd_c( v_handle, &v_dladsc, &v_dskdsc );
    -    NaifStatus::CheckErrors();
    -
    -    // Get size/counts
    -    dskz02_c( v_handle, &v_dladsc, &v_vertices, &v_plates );
    -    NaifStatus::CheckErrors();
    -
    -    // Now allocate a new indexed mesh to contain all the DSK data
    -    btIndexedMesh i_mesh;
    -    m_mesh.reset( new btTriangleIndexVertexArray());
    -    m_mesh->addIndexedMesh(i_mesh, PHY_INTEGER);
    -
    -    // Get internal mesh reference and set parameters appropriately
    -    btIndexedMesh &v_mesh = m_mesh->getIndexedMeshArray()[0];
    -    v_mesh.m_vertexType = PHY_DOUBLE;
    +    std::vector segments;
    +    segments.push_back(segment);
     
    -    // Set and allocate data for triangle indexes
    -    v_mesh.m_numTriangles = v_plates;
    -    v_mesh.m_triangleIndexBase = new unsigned char[v_plates * 3 * sizeof(int)];
    -    v_mesh.m_triangleIndexStride = (sizeof(int) * 3);
    +    // Iterate until you find no more segments.
    +    while(found) {
    +      dlafns_c(handle, &segments.back(), &segment, &found);
    +      NaifStatus::CheckErrors();
     
    -    // Set and allocate vertex data
    -    v_mesh.m_numVertices = v_vertices;
    -    v_mesh.m_vertexBase = new unsigned char[v_vertices * 3 * sizeof(double)];
    -    v_mesh.m_vertexStride = (sizeof(double) * 3);
    +      segments.push_back(segment);
    +    }
     
    -    SpiceInt n;
    -    (void) dskv02_c(v_handle, &v_dladsc, 1, v_vertices, &n, 
    -                    ( SpiceDouble(*)[3] ) (v_mesh.m_vertexBase));
    -    NaifStatus::CheckErrors();
    +    // dskgd_c( v_handle, &v_dladsc, &v_dskdsc );
    +    // NaifStatus::CheckErrors();
     
    -    // Read the indexes from the DSK
    -    (void) dskp02_c(v_handle, &v_dladsc, 1, v_plates, &n, 
    -                    ( SpiceInt(*)[3] ) (v_mesh.m_triangleIndexBase));
    -    NaifStatus::CheckErrors();
    -
    -    // Ok, close the DSK...
    -    dascls_c(v_handle);
    +    // Now allocate a new indexed mesh to contain all the DSK data
    +    m_mesh.reset( new btTriangleIndexVertexArray());
     
    -    // Got to reset the vertex indexes to 0-based
    -    int *pindex = static_cast ((void *) v_mesh.m_triangleIndexBase);
    -    int nverts = v_plates * 3;
    -    for (int i = 0 ; i < nverts ; i++) {
    -      pindex[i] -= 1;
    -      btAssert ( pindex[i] >= 0 );
    -      btAssert ( pindex[i] < v_vertices );
    +    for (size_t i = 0; i < segments.size(); i++) {
    +      SpiceInt nplates;
    +      SpiceInt nvertices;
    +
    +      btIndexedMesh i_mesh;
    +
    +      // Get size/counts
    +      dskz02_c( handle, &segments[i], &nvertices, &nplates);
    +      NaifStatus::CheckErrors();
    +
    +      m_mesh->addIndexedMesh(i_mesh, PHY_INTEGER);
    +
    +      // Get internal mesh reference and set parameters appropriately
    +      btIndexedMesh &v_mesh = m_mesh->getIndexedMeshArray()[i];
    +      v_mesh.m_vertexType = PHY_DOUBLE;
    +
    +      // Set and allocate data for triangle indexes
    +      v_mesh.m_numTriangles = nplates;
    +      v_mesh.m_triangleIndexBase = new unsigned char[nplates * 3 * sizeof(int)];
    +      v_mesh.m_triangleIndexStride = (sizeof(int) * 3);
    +
    +      // Set and allocate vertex data
    +      v_mesh.m_numVertices = nvertices;
    +      v_mesh.m_vertexBase = new unsigned char[nvertices * 3 * sizeof(double)];
    +      v_mesh.m_vertexStride = (sizeof(double) * 3);
    +
    +      SpiceInt n;
    +      (void) dskv02_c(handle, &segments[i], 1, nvertices, &n,
    +                      ( SpiceDouble(*)[3] ) (v_mesh.m_vertexBase));
    +      NaifStatus::CheckErrors();
    +
    +      // Read the indexes from the DSK
    +      (void) dskp02_c(handle, &segments[i], 1, nplates, &n,
    +                      ( SpiceInt(*)[3] ) (v_mesh.m_triangleIndexBase));
    +      NaifStatus::CheckErrors();
    +
    +      // Got to reset the vertex indexes to 0-based
    +      int *pindex = static_cast ((void *) v_mesh.m_triangleIndexBase);
    +      int nverts = nplates * 3;
    +      for (int i = 0 ; i < nverts ; i++) {
    +        pindex[i] -= 1;
    +        btAssert ( pindex[i] >= 0 );
    +        btAssert ( pindex[i] < nvertices );
    +      }
         }
     
    +    // Close DSK
    +    dascls_c(handle);
    +
         bool useQuantizedAabbCompression = true;
         // bool useQuantizedAabbCompression = false;
    -    btBvhTriangleMeshShape *v_triShape = new btBvhTriangleMeshShape(m_mesh.data(), 
    +    btBvhTriangleMeshShape *v_triShape = new btBvhTriangleMeshShape(m_mesh.data(),
                                                                         useQuantizedAabbCompression);
         v_triShape->setUserPointer(this);
         btCollisionObject *vbody = new btCollisionObject();
    diff --git a/isis/src/base/objs/BulletDskShape/BulletDskShape.truth b/isis/src/base/objs/BulletDskShape/BulletDskShape.truth
    deleted file mode 100644
    index c58d8e0118..0000000000
    --- a/isis/src/base/objs/BulletDskShape/BulletDskShape.truth
    +++ /dev/null
    @@ -1,29 +0,0 @@
    -Testing BulletDskShape
    -
    -
    -Testing default constructor
    -
    -Target name:  ""
    -Maximum distance in kilometers:  0
    -btCollisionBody pointer is valid?  false
    -Number of triangles:  0
    -Number of vertices:  0
    -
    -
    -Testing DSK file constructor
    -Testing with  "$base/testData/hay_a_amica_5_itokawashape_v1_0_64q.bds" ...
    -
    -Target name:  ""
    -Maximum distance in kilometers:  0.683956
    -btCollisionBody pointer is valid?  true
    -Number of triangles:  49152
    -Number of vertices:  25350
    -
    -Vertices for triangle 0: ( -0.15153 ,  0.08183 ,  0.07523 )
    -                         ( -0.05653 ,  0.08832 ,  0.08776 )
    -                         ( 0.08183 ,  0.07523 ,  -0.14726 )
    -Normal for triangle 0: ( -0.00136126 ,  0.0240606 ,  -0.00214151 )
    -
    -
    -Testing loading non-existant file
    -**USER ERROR** NAIF DSK file [not_a_file] does not exist.
    diff --git a/isis/src/base/objs/BulletDskShape/unitTest.cpp b/isis/src/base/objs/BulletDskShape/unitTest.cpp
    deleted file mode 100644
    index 84ddfa9b40..0000000000
    --- a/isis/src/base/objs/BulletDskShape/unitTest.cpp
    +++ /dev/null
    @@ -1,90 +0,0 @@
    -/**
    - * @file
    - *
    - *   Unless noted otherwise, the portions of Isis written by the USGS are public
    - *   domain. See individual third-party library and package descriptions for
    - *   intellectual property information,user agreements, and related information.
    - *
    - *   Although Isis has been used by the USGS, no warranty, expressed or implied,
    - *   is made by the USGS as to the accuracy and functioning of such software
    - *   and related material nor shall the fact of distribution constitute any such
    - *   warranty, and no responsibility is assumed by the USGS in connection
    - *   therewith.
    - *
    - *   For additional information, launch
    - *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html in a browser or see
    - *   the Privacy & Disclaimers page on the Isis website,
    - *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
    - *   http://www.usgs.gov/privacy.html.
    - */
    -
    -#include 
    -
    -#include "BulletDskShape.h"
    -#include "IException.h"
    -#include "Preference.h"
    -
    -using namespace Isis;
    -
    -/**
    - * Unit test for the BulletDskShape class
    - */
    -int main(int argc, char *argv[]) {
    -  try {
    -    Preference::Preferences(true);
    -
    -    qDebug() << "Testing BulletDskShape";
    -    qDebug() << endl;
    -
    -    qDebug() << "Testing default constructor";
    -    qDebug() << "";
    -    BulletDskShape defaultDskShape;
    -    qDebug() << "Target name: " << defaultDskShape.name();
    -    qDebug() << "Maximum distance in kilometers: " << defaultDskShape.maximumDistance();
    -    qDebug() << "btCollisionBody pointer is valid? " << (bool) defaultDskShape.body();
    -    qDebug() << "Number of triangles: " << defaultDskShape.getNumTriangles();
    -    qDebug() << "Number of vertices: " << defaultDskShape.getNumVertices();
    -    qDebug() << endl;
    -
    -    qDebug() << "Testing DSK file constructor";
    -    QString dskfile("$base/testData/hay_a_amica_5_itokawashape_v1_0_64q.bds");
    -    qDebug() << "Testing with " << dskfile << "...";
    -    qDebug() << "";
    -    BulletDskShape itokawaShape(dskfile);
    -    qDebug() << "Target name: " << itokawaShape.name();
    -    qDebug() << "Maximum distance in kilometers: " << itokawaShape.maximumDistance();
    -    qDebug() << "btCollisionBody pointer is valid? " << (bool) itokawaShape.body();
    -    qDebug() << "Number of triangles: " << itokawaShape.getNumTriangles();
    -    qDebug() << "Number of vertices: " << itokawaShape.getNumVertices();
    -    qDebug() << "";
    -    btMatrix3x3 triangle = itokawaShape.getTriangle(0);
    -    qDebug() << "Vertices for triangle 0: (" << triangle[0].x() << ", "
    -                                            << triangle[0].y() << ", "
    -                                            << triangle[0].z() << ")";
    -    qDebug() << "                         (" << triangle[1].x() << ", "
    -                                            << triangle[1].y() << ", "
    -                                            << triangle[1].z() << ")";
    -    qDebug() << "                         (" << triangle[2].x() << ", "
    -                                            << triangle[2].y() << ", "
    -                                            << triangle[2].z() << ")";
    -    btVector3 normal = itokawaShape.getNormal(0);
    -    qDebug() << "Normal for triangle 0: (" << normal.x() << ", "
    -                                          << normal.y() << ", "
    -                                          << normal.z() << ")";
    -    qDebug() << endl;
    -
    -    qDebug() << "Testing loading non-existant file";
    -    try {
    -      BulletDskShape badShape("not_a_file");
    -    }
    -    catch (IException &e) {
    -      e.print();
    -    }
    -  }
    -  catch (IException &e) {
    -    qDebug() << "";
    -    qDebug() << "";
    -    QString msg = "**************** UNIT TEST FAILED! **************** ";
    -    IException(e, IException::Unknown, msg, _FILEINFO_).print();
    -  }
    -}
    diff --git a/isis/src/base/objs/Cube/Cube.h b/isis/src/base/objs/Cube/Cube.h
    index 7632d4d83e..6a131722f7 100644
    --- a/isis/src/base/objs/Cube/Cube.h
    +++ b/isis/src/base/objs/Cube/Cube.h
    @@ -164,6 +164,7 @@ namespace Isis {
        *                           make sure we get absolute path.  Fixes #5276.
        *   @history 2018-01-18 Summer Stapleton - Updated error message in ::create() to address when
        *                           an IsisPreference file cannot be found. Fixes #5145.
    +   *   @history 2018-11-16 Jesse Mapel - Made several methods virtual for mocking.
        */
       class Cube {
         public:
    @@ -265,24 +266,24 @@ namespace Isis {
           void relocateDnData(FileName dnDataFile);
     //       static void relocateDnData(FileName externalLabelFile, FileName dnDataFile);
     
    -      int bandCount() const;
    +      virtual int bandCount() const;
           double base() const;
           ByteOrder byteOrder() const;
           Camera *camera();
           FileName externalCubeFileName() const;
    -      QString fileName() const;
    +      virtual QString fileName() const;
           Format format() const;
    -      Histogram *histogram(const int &band = 1,
    -                           QString msg = "Gathering histogram");
    -      Histogram *histogram(const int &band, const double &validMin,
    -                           const double &validMax,
    -                           QString msg = "Gathering histogram");
    +      virtual Histogram *histogram(const int &band = 1,
    +                                   QString msg = "Gathering histogram");
    +      virtual Histogram *histogram(const int &band, const double &validMin,
    +                                   const double &validMax,
    +                                   QString msg = "Gathering histogram");
           Pvl *label() const;
           int labelSize(bool actual = false) const;
           int lineCount() const;
           double multiplier() const;
           PixelType pixelType() const;
    -      int physicalBand(const int &virtualBand) const;
    +      virtual int physicalBand(const int &virtualBand) const;
           Projection *projection();
           int sampleCount() const;
           Statistics *statistics(const int &band = 1,
    diff --git a/isis/src/base/objs/ImportPdsTable/ImportPdsTable.cpp b/isis/src/base/objs/ImportPdsTable/ImportPdsTable.cpp
    index 15fe83db75..c80a22957c 100644
    --- a/isis/src/base/objs/ImportPdsTable/ImportPdsTable.cpp
    +++ b/isis/src/base/objs/ImportPdsTable/ImportPdsTable.cpp
    @@ -465,9 +465,18 @@ namespace Isis {
         }
         //  Get some pertinent information from the label
         PvlObject &tabObj = label.findObject(tableName);
    +    // The table description contains the actual "RECORD_BYTES"
         if (tabObj.hasKeyword("RECORD_BYTES")) {
           m_recordBytes = (int) tabObj.findKeyword("RECORD_BYTES");
         }
    +    // The table description has "ROW_BYTES" and "ROW_SUFFIX_BYTES". These summed is the 
    +    // record length. Can be for detached and attached labels
    +    else if (tabObj.hasKeyword("ROW_BYTES") && tabObj.hasKeyword("ROW_SUFFIX_BYTES")) {
    +      m_recordBytes = (int) tabObj.findKeyword("ROW_BYTES") +
    +                      (int) tabObj.findKeyword("ROW_SUFFIX_BYTES");
    +    }
    +    // The table record length is defined by the file record
    +    // length (i.e., table is in with the image)
         else {
           m_recordBytes = (int) label.findKeyword("RECORD_BYTES");
         }
    diff --git a/isis/src/base/objs/ImportPdsTable/ImportPdsTable.h b/isis/src/base/objs/ImportPdsTable/ImportPdsTable.h
    index 0a0be6db76..b8351847c9 100644
    --- a/isis/src/base/objs/ImportPdsTable/ImportPdsTable.h
    +++ b/isis/src/base/objs/ImportPdsTable/ImportPdsTable.h
    @@ -103,6 +103,8 @@ namespace Isis {
        *   @history 2016-02-24 Ian Humphrey - Updated documentation and unit test. Added edrindex.lbl
        *                           and edrindex.tab files to data directory (for tests). Fixes #2397.
        *   @history 2016-03-10 Jeannie Backer - Removed non-UTF8 character. References #2397.
    +   *   @history 2018-02-12 Stuart Sides - Added detached table capabilities for label files
    +   *                                      without a "RECORD_BYTES" keyword. References #5525.
        *  
        *  
        * @todo The binary table import methods were written after the ascii table 
    diff --git a/isis/src/base/objs/ProcessExportPds4/ProcessExportPds4.cpp b/isis/src/base/objs/ProcessExportPds4/ProcessExportPds4.cpp
    index f449f51f13..f6830d10cf 100644
    --- a/isis/src/base/objs/ProcessExportPds4/ProcessExportPds4.cpp
    +++ b/isis/src/base/objs/ProcessExportPds4/ProcessExportPds4.cpp
    @@ -47,6 +47,9 @@ namespace Isis {
       ProcessExportPds4::ProcessExportPds4() {
     
         m_lid = "";
    +    m_versionId = "";
    +    m_title = "";
    +
         m_imageType = StandardImage;
     
         qSetGlobalQHashSeed(1031); // hash seed to force consistent output
    @@ -61,10 +64,10 @@ namespace Isis {
         m_domDoc->appendChild(xmlHeader);
     
         // base pds4 schema location
    -    m_schemaLocation = "http://pds.nasa.gov/pds4/pds/v1 http://pds.nasa.gov/pds4/pds/v1/PDS4_PDS_1800.xsd"; 
    +    m_schemaLocation = "http://pds.nasa.gov/pds4/pds/v1 http://pds.nasa.gov/pds4/pds/v1/PDS4_PDS_1B00.xsd"; 
     
         QString xmlModel;
    -    xmlModel += "href=\"http://pds.nasa.gov/pds4/pds/v1/PDS4_PDS_1800.sch\" ";
    +    xmlModel += "href=\"http://pds.nasa.gov/pds4/pds/v1/PDS4_PDS_1B00.sch\" ";
         xmlModel += "schematypens=\"http://purl.oclc.org/dsdl/schematron\"";
         QDomProcessingInstruction header =
             m_domDoc->createProcessingInstruction("xml-model", xmlModel);
    @@ -319,6 +322,39 @@ namespace Isis {
       }
     
     
    +  /**
    +   * Allows mission specific programs to set version_id
    +   * required for PDS4 labels. This value is added to the xml file 
    +   * by the identificationArea() method. 
    +   *  
    +   * The input string should be colon separated string with 6 
    +   * identifiers: 
    +   *  
    +   * @author 2019-03-01 Kristin Berry
    +   * 
    +   * @param versiondId The version_id value required for PDS4 
    +   *            compliant labels.
    +   */
    +   void ProcessExportPds4::setVersionId(QString versionId) {
    +    m_versionId = versionId;
    +  }
    +
    +
    +  /**
    +   * Allows mission specific programs to set the title
    +   * required for PDS4 labels. This value is added to the xml file 
    +   * by the identificationArea() method. 
    +   *  
    +   * @author 2019-03-01 Kristin Berry
    +   * 
    +   * @param title The title value required for PDS4 
    +   *            compliant labels.
    +   */
    +   void ProcessExportPds4::setTitle(QString title) {
    +    m_title = title;
    +  }
    +
    +
       /**
        * Allows mission specific programs to use specified 
        * versions of dictionaries. 
    @@ -366,6 +402,16 @@ namespace Isis {
         QDomElement lidElement = identificationElement.firstChildElement("logical_identifier");
         PvlToXmlTranslationManager::resetElementValue(lidElement, m_lid);
     
    +    if (m_versionId != "") {
    +      QDomElement versionElement = identificationElement.firstChildElement("version_id"); 
    +      PvlToXmlTranslationManager::resetElementValue(versionElement, m_versionId);
    +    }
    +
    +    if (m_title != "") {
    +      QDomElement titleElement = identificationElement.firstChildElement("title"); 
    +      PvlToXmlTranslationManager::resetElementValue(titleElement, m_title);
    +    }
    +
         // Get export history and add  element.
         // These regular expressions match the pipe followed by the date from
         // the Application::Version() return value.
    @@ -386,18 +432,17 @@ namespace Isis {
        * the PDS4 labels. 
        */
       void ProcessExportPds4::displaySettings() {
    +    // Add header info
    +    addSchema("PDS4_DISP_1B00.sch", 
    +              "PDS4_DISP_1B00.xsd",
    +              "xmlns:disp", 
    +              "http://pds.nasa.gov/pds4/disp/v1"); 
     
         Pvl *inputLabel = InputCubes[0]->label(); 
         FileName translationFileName;
         translationFileName = "$base/translations/pds4ExportDisplaySettings.trn";
         PvlToXmlTranslationManager xlator(*inputLabel, translationFileName.expanded());
         xlator.Auto(*m_domDoc);
    -
    -    // Add header info
    -    addSchema("PDS4_DISP_1700.sch", 
    -              "PDS4_DISP_1700.xsd",
    -              "xmlns:disp", 
    -              "http://pds.nasa.gov/pds4/disp/v1"); 
       }
     
       
    @@ -409,8 +454,8 @@ namespace Isis {
         Pvl *inputLabel = InputCubes[0]->label(); 
         if ( !inputLabel->findObject("IsisCube").hasGroup("BandBin") ) return;
         // Add header info
    -    addSchema("PDS4_IMG_1900.sch", 
    -              "PDS4_IMG_1900.xsd",
    +    addSchema("PDS4_IMG_1A10_1510.sch", 
    +              "PDS4_IMG_1A10_1510.xsd",
                   "xmlns:img", 
                   "http://pds.nasa.gov/pds4/img/v1"); 
         
    @@ -754,6 +799,33 @@ namespace Isis {
                                                         toString(base));
             elementArrayElement.appendChild(offsetElement);
           }
    +
    +      // Add the Special_Constants class to define ISIS special pixel values: 
    +
    +      // Assume 32-bit/Real for CaSSIS
    +      QDomElement specialConstantElement = m_domDoc->createElement("Special_Constants");
    +      arrayImageElement.insertAfter(specialConstantElement,
    +                                    arrayImageElement.lastChildElement("Axis_Array"));
    +
    +      QDomElement nullElement = m_domDoc->createElement("missing_constant");
    +      PvlToXmlTranslationManager::setElementValue(nullElement, QString::number(NULL8, 'g', 18)); //toString(NULL8));
    +      specialConstantElement.appendChild(nullElement);
    +
    +      QDomElement highInstrumentSatElement = m_domDoc->createElement("high_instrument_saturation");
    +      PvlToXmlTranslationManager::setElementValue(highInstrumentSatElement, QString::number(HIGH_INSTR_SAT8, 'g', 18));
    +      specialConstantElement.appendChild(highInstrumentSatElement);
    +
    +      QDomElement highRepresentationSatElement = m_domDoc->createElement("high_representation_saturation");
    +      PvlToXmlTranslationManager::setElementValue(highRepresentationSatElement, QString::number(HIGH_REPR_SAT8, 'g', 18));
    +      specialConstantElement.appendChild(highRepresentationSatElement);
    +
    +      QDomElement lowInstrumentSatElement = m_domDoc->createElement("low_instrument_saturation");
    +      PvlToXmlTranslationManager::setElementValue(lowInstrumentSatElement, QString::number(LOW_INSTR_SAT8, 'g', 18));
    +      specialConstantElement.appendChild(lowInstrumentSatElement);
    +
    +      QDomElement lowRepresentationSatElement = m_domDoc->createElement("low_representation_saturation");
    +      PvlToXmlTranslationManager::setElementValue(lowRepresentationSatElement, QString::number(LOW_REPR_SAT8, 'g', 18));
    +      specialConstantElement.appendChild(lowRepresentationSatElement);
         }
       }
     
    @@ -904,8 +976,8 @@ namespace Isis {
             !(inputLabel->findObject("IsisCube").hasGroup("Mapping"))) return;
         PvlGroup &inputMapping = inputLabel->findGroup("Mapping", Pvl::Traverse);
     
    -    addSchema("PDS4_CART_1700.sch", 
    -              "PDS4_CART_1700.xsd",
    +    addSchema("PDS4_CART_1900.sch", 
    +              "PDS4_CART_1900.xsd",
                   "xmlns:cart", 
                   "http://pds.nasa.gov/pds4/cart/v1"); 
     
    @@ -980,11 +1052,10 @@ namespace Isis {
           double maxLon = inputMapping.findKeyword("MaximumLongitude");
           double minLon = inputMapping.findKeyword("MinimumLongitude");
           xmlPath.clear();
    -      xmlPath << "Product_Observational" 
    +      xmlPath << "Product_Observational"
                   << "Observation_Area" 
    -              << "Discipline_Area" 
    +              << "Discipline_Area"
                   << "cart:Cartography" 
    -              << "cart:Map_Projection" 
                   << "cart:Spatial_Domain"
                   << "cart:Bounding_Coordinates";
           QDomElement boundingCoordElement = getElement(xmlPath, baseElement);
    @@ -993,18 +1064,16 @@ namespace Isis {
     
           // translation files currently handles Positive West case where east = min, west = max
           // so if positive east, swap min/max
    -      if(QString::compare(lonDir, "Positive East", Qt::CaseInsensitive) == 0) {
    +      if(QString::compare(lonDir, "PositiveEast", Qt::CaseInsensitive) == 0) {
             // west min, east max
             PvlToXmlTranslationManager::resetElementValue(eastElement, toString(maxLon), "deg");
             PvlToXmlTranslationManager::resetElementValue(westElement, toString(minLon), "deg");
           }
         }
    -
    -
       }
     
     
    - /**
    +/**
       * Convenience method to get an element given a path and its parent. 
       * 
       * @param xmlPath The XML path to the element to retrieve, 
    diff --git a/isis/src/base/objs/ProcessExportPds4/ProcessExportPds4.h b/isis/src/base/objs/ProcessExportPds4/ProcessExportPds4.h
    index 8dd72fdbdc..5631803c23 100644
    --- a/isis/src/base/objs/ProcessExportPds4/ProcessExportPds4.h
    +++ b/isis/src/base/objs/ProcessExportPds4/ProcessExportPds4.h
    @@ -75,6 +75,9 @@ namespace Isis {
        *                           attributes to elements. Matches pds validate tool specifations.
        *   @history 2018-06-12 Kristin Berry - Added schema associated with the img class when it is
        *                           used.
    +   *   @history 2019-03-01 Kristin Berry - Added ability to set version_id and title, added
    +   *                           Special_Constants to define ISIS special pixel values, fixed east/west
    +   *                           bounding coordinates swap bug. Fixes git issue #2635.
        */
     
       class ProcessExportPds4: public Isis::ProcessExport {
    @@ -108,6 +111,8 @@ namespace Isis {
           QDomElement getElement(QStringList xmlPath, QDomElement parent=QDomElement());
           void addHistory(QString description, QString date = "tbd", QString version = "1.0");
           void setLogicalId(QString lid);
    +      void setVersionId(QString versionId);
    +      void setTitle(QString title);
           void setSchemaLocation(QString schema);
           void setImageType(ImageType imageType);
     
    @@ -131,6 +136,8 @@ namespace Isis {
           QDomDocument *m_domDoc;               //!< XML label.
           QString m_schemaLocation;             //!< QString with all schema locations required.
           QString m_lid;                        //!< QString with specified logical identifier.
    +      QString m_versionId;                  //!< QString with specified version id.
    +      QString m_title;                      //!< QString with specified title. 
           ImageType m_imageType;                //!< Type of image data to be written.
     
       };
    diff --git a/isis/src/clementine/apps/clem2isis/jpeg_c.h b/isis/src/clementine/apps/clem2isis/jpeg_c.h
    index e6a54bdfc1..1aeaeb79db 100644
    --- a/isis/src/clementine/apps/clem2isis/jpeg_c.h
    +++ b/isis/src/clementine/apps/clem2isis/jpeg_c.h
    @@ -1,3 +1,5 @@
    +#ifndef jpeg_c_h
    +#define jpeg_c_h
     
     /* Structure Definitions */
     enum FmodeDef { INPUT, OUTPUT };
    @@ -57,3 +59,5 @@ extern void inithuffcode();
     extern void encode(short *, BitStream *);
     extern void decode(short *, BitStream *);
     extern void decomp(BitStream *bs, CHARH *Image, long rows, long cols);
    +
    +#endif
    diff --git a/isis/src/clementine/apps/clem2isis/pds.h b/isis/src/clementine/apps/clem2isis/pds.h
    index 42f8d4adf4..79d316f7d4 100644
    --- a/isis/src/clementine/apps/clem2isis/pds.h
    +++ b/isis/src/clementine/apps/clem2isis/pds.h
    @@ -1,3 +1,5 @@
    +#ifndef pds_h
    +#define pds_h
     
     #if defined(__BORLANDC__) && !defined(__WIN32__)
     #define CHARH   unsigned char huge
    @@ -19,3 +21,5 @@ typedef struct {
     //extern FILE     *qparm;          //removed qparm references BMG 2006-07-18
     
     extern PDSINFO *PDSR(char *fname, long *rows, long *cols);
    +
    +#endif
    diff --git a/isis/src/docsys/build/UserDocs.xsl b/isis/src/docsys/build/UserDocs.xsl
    index f0d384b09b..243485b417 100644
    --- a/isis/src/docsys/build/UserDocs.xsl
    +++ b/isis/src/docsys/build/UserDocs.xsl
    @@ -1,7 +1,7 @@
     
    -
     
     
     
     
    -  
             
    
             
    @@ -129,11 +129,22 @@ Deborah Lee Soltesz
                 ISIS Workshop
               
               
    -          Interactive and hands-on tutorials designed to guide users through the basics of using ISIS 
    +          Interactive and hands-on tutorials designed to guide users through the basics of using ISIS
     	  to advanced processing techniques for creating mosaics from mission data,
               correcting and enhancing problem data, and other techniques.
               
             
    +        
    +          
    +            
    +            Environment and Preference Setup
    +          
    +          
    +          A guide on how to setup your Unix environment in order to run ISIS.
    +          It also discusses the ISIS Preference files, which gives the user the
    +          ability to customize the operation of ISIS.
    +          
    +        
     
           
           code;
       c2 = e2->code;
    diff --git a/isis/src/mgs/apps/mocuncompress/invFdct16x16.h b/isis/src/mgs/apps/mocuncompress/invFdct16x16.h
    index 33dc04c50a..8c0551f658 100644
    --- a/isis/src/mgs/apps/mocuncompress/invFdct16x16.h
    +++ b/isis/src/mgs/apps/mocuncompress/invFdct16x16.h
    @@ -1,3 +1,6 @@
    +#ifndef mocuncompress_invfdct16x16_h
    +#define mocuncompress_invfdct16x16_h
    +
     /*
     NOTICE
     
    @@ -47,3 +50,5 @@ SCCSID @(#)invFdct16x16.h  1.1 10/04/99
     extern void invFdct16x16(int16 *in, int16 *out);
     
     #endif
    +
    +#endif
    diff --git a/isis/src/mgs/apps/mocuncompress/invFwht16x16.h b/isis/src/mgs/apps/mocuncompress/invFwht16x16.h
    index a58fe392d9..b069258513 100644
    --- a/isis/src/mgs/apps/mocuncompress/invFwht16x16.h
    +++ b/isis/src/mgs/apps/mocuncompress/invFwht16x16.h
    @@ -1,9 +1,12 @@
    +#ifndef mocuncompress_invfwht16x16_h
    +#define mocuncompress_invfwht16x16_h
    +
     /*
     
     ==================================================
     2018-OCT-19 Kaitlyn Lee - US Geological Survey
     
    -Removed the register keyword because it is deprecated in C++17. 
    +Removed the register keyword because it is deprecated in C++17.
     ==================================================
     
     NOTICE
    @@ -54,3 +57,5 @@ SCCSID @(#)invFwht16x16.h  1.1 10/04/99
     extern void invFwht16x16(int16 *in, int16 *out);
     
     #endif
    +
    +#endif
    diff --git a/isis/src/mgs/apps/mocuncompress/limits.h b/isis/src/mgs/apps/mocuncompress/mocuncompresslimits.h
    similarity index 98%
    rename from isis/src/mgs/apps/mocuncompress/limits.h
    rename to isis/src/mgs/apps/mocuncompress/mocuncompresslimits.h
    index 1f8f19b16e..69474bb754 100644
    --- a/isis/src/mgs/apps/mocuncompress/limits.h
    +++ b/isis/src/mgs/apps/mocuncompress/mocuncompresslimits.h
    @@ -40,9 +40,9 @@ SCCSID @(#)limits.h  1.1 10/04/99
     */
     /* SCCShid @(#)limits.h (limits.h) 1.4 */
     
    -#ifndef limits_h
    +#ifndef mocuncompress_limits_h
     
    -#define limits_h
    +#define mocuncompress_limits_h
     
     /*
     * The transform block size.  IMPORTANT NOTE:  If changed to another number
    diff --git a/isis/src/mgs/apps/mocuncompress/msdp.h b/isis/src/mgs/apps/mocuncompress/msdp.h
    index 241b934fe3..5a9464b018 100644
    --- a/isis/src/mgs/apps/mocuncompress/msdp.h
    +++ b/isis/src/mgs/apps/mocuncompress/msdp.h
    @@ -1,3 +1,6 @@
    +#ifndef mocuncompress_msdp_h
    +#define mocuncompress_msdp_h
    +
     /*
     NOTICE
     
    @@ -101,3 +104,5 @@ struct msdp_header {
     #define BYTE3(i) (((i)&0xff000000)>>24)
     
     #define STUFFSHORT(p,v) ((p)[0] = (v)&0xff, (p)[1] = (v)>>8)
    +
    +#endif
    diff --git a/isis/src/mro/apps/pds2hideal/tsts/compressedImage/Makefile b/isis/src/mro/apps/pds2hideal/tsts/compressedImage/Makefile
    new file mode 100644
    index 0000000000..6cf427e46d
    --- /dev/null
    +++ b/isis/src/mro/apps/pds2hideal/tsts/compressedImage/Makefile
    @@ -0,0 +1,17 @@
    +# This tests the following conditions:
    +#
    +# -- We can export a compressed image.
    +# -- The tables in the compressed image do not have the keyword RECORD_BYTES.
    +# -- When exported, the compressed and uncompressed input data produce the same output.
    +APPNAME = pds2hideal
    +
    +include $(ISISROOT)/make/isismake.tsts
    +
    +commands:
    +	$(APPNAME) FROM=$(INPUT)/PSP_009492_1280_RED.NOPROJ.lbl \
    +	  to=$(OUTPUT)/uncompressed_lbl.cub > /dev/null;
    +	$(APPNAME) FROM=$(INPUT)/PSP_009492_1280_RED_COMPRESSED.NOPROJ.IMG \
    +	  to=$(OUTPUT)/compressed_img.cub > /dev/null;
    +	cubediff from=$(OUTPUT)/uncompressed_lbl.cub from2=$(OUTPUT)/compressed_img.cub \
    +	  to=$(OUTPUT)/comparisonTruth.txt > /dev/null;
    +
    diff --git a/isis/src/tgo/apps/tgocassis2isis/main.cpp b/isis/src/tgo/apps/tgocassis2isis/main.cpp
    index d02dd341fe..6b1a6fcbd0 100644
    --- a/isis/src/tgo/apps/tgocassis2isis/main.cpp
    +++ b/isis/src/tgo/apps/tgocassis2isis/main.cpp
    @@ -33,10 +33,10 @@ void IsisMain() {
       try {
         ProcessImport importer;
         translateCoreInfo(xmlFileName, importer);
    -    
    +
         if(xmlFileName.removeExtension().addExtension("dat").fileExists()){
           importer.SetInputFile(xmlFileName.removeExtension().addExtension("dat").expanded());
    -    } 
    +    }
         else if (xmlFileName.removeExtension().addExtension("img").fileExists()) {
           importer.SetInputFile(xmlFileName.removeExtension().addExtension("img").expanded());
         }
    @@ -45,7 +45,7 @@ void IsisMain() {
             ".dat or .img file for this XML exists and is located in the same directory.";
           throw IException(IException::User, msg, _FILEINFO_);
         }
    -    
    +
         Cube *outputCube = importer.SetOutputCube("TO");
     
         QString transRawFile = "/translations/tgoCassisInstrument.trn";
    @@ -55,8 +55,8 @@ void IsisMain() {
         Pvl *outputLabel = outputCube->label();
         QString target = "";
         try {
    -      translateLabels(xmlFileName, outputCube, transRawFile); 
    -    } 
    +      translateLabels(xmlFileName, outputCube, transRawFile);
    +    }
         catch (IException &e) {
     
           if (translateMappingLabel(xmlFileName, outputCube)) {
    @@ -65,13 +65,13 @@ void IsisMain() {
             }
             else {
               if(outputLabel->findObject("IsisCube").hasGroup("Instrument")) {
    -            outputLabel->findObject("IsisCube").deleteGroup("Instrument"); 
    +            outputLabel->findObject("IsisCube").deleteGroup("Instrument");
               }
             }
           }
           else {
             if(outputLabel->findObject("IsisCube").hasGroup("Mapping")) {
    -          outputLabel->findObject("IsisCube").deleteGroup("Mapping"); 
    +          outputLabel->findObject("IsisCube").deleteGroup("Mapping");
             }
             translateLabels(xmlFileName, outputCube, transExportFile);
           }
    @@ -101,13 +101,13 @@ void IsisMain() {
     
     
     /**
    - * Translate core info from labels and set ProcessImport object with 
    + * Translate core info from labels and set ProcessImport object with
      * these values.
      *
      * @param inputLabel Reference to the xml label file name from the input image.
      * @param importer Reference to the ProcessImport object to which core info will
      *                 be set.
    - *  
    + *
      * @internal
      *   @history 2017-01-20 Jeannie Backer - Original Version
      */
    @@ -117,15 +117,15 @@ void translateCoreInfo(FileName &inputLabel, ProcessImport &importer) {
       QString missionDir = (QString) dataDir["Tgo"];
     
       // Get the translation manager ready
    -  FileName transFile; 
    +  FileName transFile;
       try {
    -    transFile = FileName(missionDir + "/translations/tgoCassis.trn"); 
    +    transFile = FileName(missionDir + "/translations/tgoCassis.trn");
         XmlToPvlTranslationManager labelXlater(inputLabel, transFile.expanded());
         translateCoreInfo(labelXlater, importer);
    -  } 
    +  }
       catch (IException &e) {
         // if exported, use this!
    -    transFile = FileName(missionDir + "/translations/tgoCassisRdr.trn"); 
    +    transFile = FileName(missionDir + "/translations/tgoCassisRdr.trn");
         XmlToPvlTranslationManager labelXlater(inputLabel, transFile.expanded());
         translateCoreInfo(labelXlater, importer);
       }
    @@ -133,13 +133,13 @@ void translateCoreInfo(FileName &inputLabel, ProcessImport &importer) {
     
     
     /**
    - * Translate core info from labels and set ProcessImport object with 
    + * Translate core info from labels and set ProcessImport object with
      * these values.
      *
      * @param labelXlater Reference to the XmlToPvlTranslationManager objcet to use for the translation.
      * @param importer Reference to the ProcessImport object to which core info will
      *                 be set.
    - *  
    + *
      * @internal
      *   @history 2017-01-20 Jeannie Backer - Original Version
      *   @history 2017-01-21 Krisitn Berry - Flipped ns & nl. They're flipped in the CaSSIS header.
    @@ -158,7 +158,7 @@ void translateCoreInfo(XmlToPvlTranslationManager labelXlater, ProcessImport &im
       str = labelXlater.Translate("CoreType");
       importer.SetPixelType(PixelTypeEnumeration(str));
     
    -  str = labelXlater.Translate("CoreByteOrder");    
    +  str = labelXlater.Translate("CoreByteOrder");
       importer.SetByteOrder(ByteOrderEnumeration(str));
     
       importer.SetFileHeaderBytes(0);
    @@ -172,15 +172,15 @@ void translateCoreInfo(XmlToPvlTranslationManager labelXlater, ProcessImport &im
     
     /**
      * Translate the cartographic info from the xml.
    - * 
    + *
      * @param xmlFileName The xml label file name for the input image.
    - * @param outputCube Pointer to output cube where ISIS3 labels will be added and 
    + * @param outputCube Pointer to output cube where ISIS3 labels will be added and
      *                   updated.
      */
     bool translateMappingLabel(FileName xmlFileName, Cube *outputCube) {
       //Translate the Mapping Group
       try {
    -    PvlGroup &dataDir = Preference::Preferences().findGroup("DataDirectory"); 
    +    PvlGroup &dataDir = Preference::Preferences().findGroup("DataDirectory");
         QString missionDir = (QString) dataDir["Tgo"];
         FileName mapTransFile(missionDir + "/translations/tgoCassisMapping.trn");
     
    @@ -195,7 +195,7 @@ bool translateMappingLabel(FileName xmlFileName, Cube *outputCube) {
       catch (IException &e) {
         Pvl *outputLabel = outputCube->label();
         if(outputLabel->hasGroup("Mapping")) {
    -      outputLabel->deleteGroup("Mapping"); 
    +      outputLabel->deleteGroup("Mapping");
         }
         return false;
       }
    @@ -205,14 +205,14 @@ bool translateMappingLabel(FileName xmlFileName, Cube *outputCube) {
     
     /**
      * Translate the Mosaic group info from the xml.
    - * 
    + *
      * @param xmlFileName The xml label file name for the input image.
    - * @param outputCube Pointer to output cube where ISIS3 labels will be added and 
    + * @param outputCube Pointer to output cube where ISIS3 labels will be added and
      *                   updated.
      */
     bool translateMosaicLabel(FileName xmlFileName, Cube *outputCube) {
       QDomDocument xmlDoc;
    -    
    +
       QFile xmlFile(xmlFileName.expanded());
       if ( !xmlFile.open(QIODevice::ReadOnly) ) {
         QString msg = "Could not open label file [" + xmlFileName.expanded() +
    @@ -242,7 +242,7 @@ bool translateMosaicLabel(FileName xmlFileName, Cube *outputCube) {
             QStringList logicalIdStringList = logicalIdText.split(":");
             if (logicalIdStringList.contains("data_mosaic")) {
               try {
    -            PvlGroup &dataDir = Preference::Preferences().findGroup("DataDirectory"); 
    +            PvlGroup &dataDir = Preference::Preferences().findGroup("DataDirectory");
                 QString missionDir = (QString) dataDir["Tgo"];
                 FileName bandBinTransFile(missionDir + "/translations/tgoCassisMosaicBandBin.trn");
                 // Get the translation manager ready for translating the band bin label
    @@ -260,10 +260,10 @@ bool translateMosaicLabel(FileName xmlFileName, Cube *outputCube) {
               catch (IException &e) {
                 Pvl *outputLabel = outputCube->label();
                 if(outputLabel->hasGroup("Mosaic")) {
    -              outputLabel->deleteGroup("Mosaic"); 
    +              outputLabel->deleteGroup("Mosaic");
                 }
                 if(outputLabel->hasGroup("BandBin")) {
    -              outputLabel->deleteGroup("BandBin"); 
    +              outputLabel->deleteGroup("BandBin");
                 }
                 return false;
               }
    @@ -276,13 +276,13 @@ bool translateMosaicLabel(FileName xmlFileName, Cube *outputCube) {
     
     
     /**
    - * Translate instrument, bandbin, and archive info from xml label into ISIS3 
    - * label and add kernels group. 
    + * Translate instrument, bandbin, and archive info from xml label into ISIS3
    + * label and add kernels group.
      *
      * @param inputLabel Reference to the xml label file name for the input image.
    - * @param outputCube Pointer to output cube where ISIS3 labels will be added and 
    + * @param outputCube Pointer to output cube where ISIS3 labels will be added and
      *                   updated.
    - *  
    + *
      * @internal
      *   @history 2017-01-20 Jeannie Backer - Original Version
      *   @history 2017-01-23 Kristin Berry - Added support for bandBin group and archive group
    @@ -338,9 +338,20 @@ void translateLabels(FileName &inputLabel, Cube *outputCube, QString instTransFi
         startTime->setValue(startTimeString);
       }
       iTime stime(startTimeString);
    -  
    +
       PvlGroup &archive = outputLabel->findGroup("Archive", Pvl::Traverse);
    -                                                  
    +
    +  // Calculate SummingMode keyword and add to label
    +  QString sumMode;
    +  if (inst.hasKeyword("Expanded") && (int)inst.findKeyword("Expanded") == 1) {
    +    sumMode = "0";
    +  }
    +  else {
    +    sumMode = (QString)archive["Window" + (QString)archive["WindowCount"] + "Binning"];
    +  }
    +  PvlKeyword summingMode("SummingMode", sumMode);
    +  outputLabel->findGroup("Instrument", Pvl::Traverse).addKeyword(summingMode);
    +
       PvlKeyword yeardoy("YearDoy", toString(stime.Year()*1000 + stime.DayOfYear()));
       archive.addKeyword(yeardoy);
     
    @@ -400,7 +411,7 @@ void translateLabels(FileName &inputLabel, Cube *outputCube, QString instTransFi
           spacecraftCode = -143424;
         }
         else {
    -      QString msg = "Unrecognized filter name [" 
    +      QString msg = "Unrecognized filter name ["
             + filter
             + "].";
             throw IException(IException::User, msg, _FILEINFO_);
    @@ -410,7 +421,7 @@ void translateLabels(FileName &inputLabel, Cube *outputCube, QString instTransFi
         bandBin.addKeyword(PvlKeyword("NaifIkCode", toString(spacecraftCode)));
       }
       else {
    -    QString msg = "Unrecognized Spacecraft name [" 
    +    QString msg = "Unrecognized Spacecraft name ["
           + spcName
           + "] and instrument ID ["
           + instId
    diff --git a/isis/src/tgo/apps/tgocassismos/main.cpp b/isis/src/tgo/apps/tgocassismos/main.cpp
    index e5f9d03357..e8915de0a4 100644
    --- a/isis/src/tgo/apps/tgocassismos/main.cpp
    +++ b/isis/src/tgo/apps/tgocassismos/main.cpp
    @@ -209,7 +209,8 @@ void IsisMain() {
         QString toMosaic = ui.GetFileName("TO");
         QString mosaicPriority = ui.GetString("PRIORITY");
     
    -    QString parameters = "FROMLIST=" + list + " MOSAIC=" + toMosaic + " PRIORITY=" + mosaicPriority;
    +    QString parameters = "FROMLIST=" + list + " MOSAIC=" + toMosaic + " PRIORITY=" + mosaicPriority 
    +                          + " TRACK=TRUE";
     
         if (QString::compare(ui.GetString("GRANGE"), "USER", Qt::CaseInsensitive) == 0) {
           parameters += " GRANGE=USER";
    diff --git a/isis/src/tgo/apps/tgocassismos/tgocassismos.xml b/isis/src/tgo/apps/tgocassismos/tgocassismos.xml
    index 872297f8eb..abc5c7154a 100644
    --- a/isis/src/tgo/apps/tgocassismos/tgocassismos.xml
    +++ b/isis/src/tgo/apps/tgocassismos/tgocassismos.xml
    @@ -7,14 +7,19 @@
     
       
         

    - If the final product wil not be an RDR, mapmos or automos can be used to produce a mosaic. - This program produces a mosaic and adds keywords to the label group "Mosaic".
    + If the final product wil not be an RDR, mapmos or automos can be used to produce a mosaic. + This program produces a mosaic and adds keywords to the label group "Mosaic".
    - The input cubes to this program must be from the same observation, same filter, and map projected.
    + The input cubes to this program must be from the same observation, same filter, and map projected.
    - A new label group, required by tgocassisrdrgen, called "Mosaic" is added to the image labels of - the output cube. The original label blob is also propagated from the first file in the input list.
    -

    + A new label group, required by tgocassisrdrgen, called "Mosaic" is added to the image labels of + the output cube. The original label blob is also propagated from the first file in the input list.
    + + This application will produce two cubes. The first cube (that you set with the "TO" parameter) + will be your mosaic. A tracking cube will also be created at the same level with tracking + information for the original cubes (that you set with the "FROMLIST" parameter). This cube will + have the same base name as the mosaic, but will end in "_tracking.cub".
    +

    processing sequence for single filter products that will become RDRs
    @@ -45,6 +50,10 @@ Added option to allow user to choose ground range. + + Added the "tracking=true" parameter to tgocassismos' call to automos resulting in automatic + tracking cube creation. + diff --git a/isis/src/tgo/apps/tgocassismos/tsts/default/Makefile b/isis/src/tgo/apps/tgocassismos/tsts/default/Makefile index 03ee1c91ff..1440520112 100644 --- a/isis/src/tgo/apps/tgocassismos/tsts/default/Makefile +++ b/isis/src/tgo/apps/tgocassismos/tsts/default/Makefile @@ -7,4 +7,5 @@ commands: $(APPNAME) fromlist=$(OUTPUT)/mosaic.lis \ to=$(OUTPUT)/tgocassismos.cub >& /dev/null; catlab from=$(OUTPUT)/tgocassismos.cub to=$(OUTPUT)/tgocassismos.pvl >& /dev/null; + catlab from=$(OUTPUT)/tgocassismos_tracking.cub to=$(OUTPUT)/tgocassismos_tracking.pvl >& /dev/null; $(RM) $(OUTPUT)/mosaic.lis; diff --git a/isis/src/tgo/apps/tgocassisrdrgen/main.cpp b/isis/src/tgo/apps/tgocassisrdrgen/main.cpp index a2f17a8b71..0619ae215b 100644 --- a/isis/src/tgo/apps/tgocassisrdrgen/main.cpp +++ b/isis/src/tgo/apps/tgocassisrdrgen/main.cpp @@ -69,16 +69,16 @@ void IsisMain() { } else { // Get the observationId from the Archive Group, or the Mosaic group, if the input is a mosaic - QString observationId; + QString observationId; - if(label->findObject("IsisCube").hasGroup("Archive")){ - PvlGroup archiveGroup = label->findObject("IsisCube").findGroup("Archive"); - observationId = archiveGroup.findKeyword("ObservationId")[0]; - } - else if (label->findObject("IsisCube").hasGroup("Mosaic")) { + if (label->findObject("IsisCube").hasGroup("Mosaic")) { PvlGroup mosaicGroup = label->findObject("IsisCube").findGroup("Mosaic"); observationId = mosaicGroup.findKeyword("ObservationId")[0]; } + else if(label->findObject("IsisCube").hasGroup("Archive")){ + PvlGroup archiveGroup = label->findObject("IsisCube").findGroup("Archive"); + observationId = archiveGroup.findKeyword("ObservationId")[0]; + } productId.setValue(observationId); } @@ -86,10 +86,20 @@ void IsisMain() { logicalId += productId[0]; process.setLogicalId(logicalId); + // Set Title + if ( ui.WasEntered("TITLE") ) { + process.setTitle( ui.GetString("TITLE") ); + } + + // Set Version ID + if ( ui.WasEntered("VERSIONID") ) { + process.setVersionId( ui.GetString("VERSIONID") ); + } + // std PDS4 label process.StandardPds4Label(); - process.addSchema("PDS4_PSA_1000.sch", +/* process.addSchema("PDS4_PSA_1000.sch", "PDS4_PSA_1000.xsd", "xmlns:psa", "http://psa.esa.int/psa/v1"); @@ -97,12 +107,12 @@ void IsisMain() { process.addSchema("PDS4_PSA_EM16_CAS_1000.sch", "PDS4_PSA_EM16_CAS_1000.xsd", "xmlns", - "http://psa.esa.int/psa/em16/cas/v1"); + "http://psa.esa.int/psa/em16/cas/v1");*/ // Add geometry schema for mosaics if (label->findObject("IsisCube").hasGroup("Mosaic")) { - process.addSchema("PDS4_GEOM_1900_1510.sch", - "PDS4_GEOM_1900_1510.xsd", + process.addSchema("PDS4_GEOM_1B00_1610.sch", + "PDS4_GEOM_1B00_1610.xsd", "xmlns:geom", "https://pds.jpl.nasa.gov/datastandards/schema/released/geom/v1"); } diff --git a/isis/src/tgo/apps/tgocassisrdrgen/tgocassisrdrgen.xml b/isis/src/tgo/apps/tgocassisrdrgen/tgocassisrdrgen.xml index bde3b3deae..f363e57e6a 100644 --- a/isis/src/tgo/apps/tgocassisrdrgen/tgocassisrdrgen.xml +++ b/isis/src/tgo/apps/tgocassisrdrgen/tgocassisrdrgen.xml @@ -41,6 +41,10 @@ Updated to use the tgoCassisExportMosaic.trn translation file when the input cube is a mosaic. + + Added TITLE and VERSIONID optional parameters to specify these values in the exported PDS4 label. + Fixed bug that disabled reading of mosaics with Archive groups. + @@ -89,6 +93,26 @@ Update the default Product ID value. This value will be the last section of the PDS4 logical_identifier value. + + string + None + + Product ID value + + + Update the default Title value. + + + + string + None + + Product ID value + + + Update the default Version ID value. + + diff --git a/isis/src/tgo/tsts/coloredMosaic/Makefile b/isis/src/tgo/tsts/coloredMosaic/Makefile index 927fca70fb..e84808b40e 100644 --- a/isis/src/tgo/tsts/coloredMosaic/Makefile +++ b/isis/src/tgo/tsts/coloredMosaic/Makefile @@ -132,4 +132,5 @@ commands: $(RM) $(OUTPUT)/nirCassisMosaic.cub; $(RM) $(OUTPUT)/panCassisMosaic.cub; $(RM) $(OUTPUT)/redCassisMosaic.cub; + $(RM) $(OUTPUT)/*_tracking*.cub; $(MV) $(OUTPUT)/equi.map $(OUTPUT)/equi.pvl; diff --git a/isis/src/tgo/tsts/singleColorMosaicReingest/Makefile b/isis/src/tgo/tsts/singleColorMosaicReingest/Makefile index 2fcbeaab98..81e92691b1 100644 --- a/isis/src/tgo/tsts/singleColorMosaicReingest/Makefile +++ b/isis/src/tgo/tsts/singleColorMosaicReingest/Makefile @@ -69,4 +69,4 @@ commands: $(RM) $(OUTPUT)/*.lis $(RM) $(OUTPUT)/*equi* $(RM) $(OUTPUT)/*B1.cub - + $(RM) $(OUTPUT)/*_tracking* diff --git a/isis/src/tgo/tsts/uncontrolledSingleColorMosaic/Makefile b/isis/src/tgo/tsts/uncontrolledSingleColorMosaic/Makefile index 7552136195..241b8db5d2 100644 --- a/isis/src/tgo/tsts/uncontrolledSingleColorMosaic/Makefile +++ b/isis/src/tgo/tsts/uncontrolledSingleColorMosaic/Makefile @@ -60,4 +60,4 @@ commands: $(RM) $(OUTPUT)/*.lis $(RM) $(OUTPUT)/*equi.cub; $(RM) $(OUTPUT)/*B1.cub; - + $(RM) $(OUTPUT)/*_tracking*; diff --git a/isis/tests/BulletDskShapeTests.cpp b/isis/tests/BulletDskShapeTests.cpp new file mode 100644 index 0000000000..a95839b39d --- /dev/null +++ b/isis/tests/BulletDskShapeTests.cpp @@ -0,0 +1,65 @@ +#include "BulletDskShape.h" +#include "IException.h" +#include "SpecialPixel.h" + +#include + +using namespace Isis; + +TEST(BulletDskShapeTests, DefaultConstructor) { + BulletDskShape defaultDskShape; + EXPECT_EQ(defaultDskShape.name(), ""); + EXPECT_FALSE((bool) defaultDskShape.body()); + EXPECT_EQ(defaultDskShape.getNumTriangles(), 0); + EXPECT_EQ(defaultDskShape.getNumVertices(), 0); +} + +TEST(BulletDskShapeTests, SingleSegment) { + QString dskfile("$base/testData/hay_a_amica_5_itokawashape_v1_0_64q.bds"); + + BulletDskShape itokawaShape(dskfile); + EXPECT_EQ(itokawaShape.name(), ""); + EXPECT_DOUBLE_EQ(itokawaShape.maximumDistance(), 0.68395571742620886); + EXPECT_TRUE((bool) itokawaShape.body()); + EXPECT_EQ(itokawaShape.getNumTriangles(), 49152); + EXPECT_EQ(itokawaShape.getNumVertices(), 25350); + + btMatrix3x3 truthTriangle(-0.15153, 0.08183, 0.07523, + -0.05653, 0.08832, 0.08776, + 0.08183, 0.07523, -0.14726); + btMatrix3x3 triangle = itokawaShape.getTriangle(0); + EXPECT_TRUE(triangle == truthTriangle); + + btVector3 truthNormal(-0.0013612620999999992, + 0.024060550800000004, + -0.0021415063999999989); + btVector3 normal = itokawaShape.getNormal(0); + EXPECT_TRUE(normal == truthNormal); +} + +TEST(BulletDskShapeTests, BadFile) { + EXPECT_THROW(BulletDskShape("not_a_file"), IException); +} + + +TEST(BulletDskShapeTests, MutiSegment) { + QString dskfile("$base/testData/test_shape.bds"); + + BulletDskShape multiseg(dskfile); + EXPECT_EQ(multiseg.name(), ""); + EXPECT_DOUBLE_EQ(multiseg.maximumDistance(), 7.3484692283495345); + EXPECT_TRUE((bool) multiseg.body()); + EXPECT_EQ(multiseg.getNumTriangles(), 14); + EXPECT_EQ(multiseg.getNumVertices(), 13); + + btMatrix3x3 truthTriangle(0, 0, 6, + 0, 6, 1, + 0, 5, 2); + btMatrix3x3 triangle = multiseg.getTriangle(0); + EXPECT_TRUE(triangle == truthTriangle); + + + btVector3 truthNormal(1, 0, 0); + btVector3 normal = multiseg.getNormal(0); + EXPECT_TRUE(normal == truthNormal); +} diff --git a/isis/tests/PixelTests.cpp b/isis/tests/PixelTests.cpp index 46ac5b0f7b..308cf7fac0 100644 --- a/isis/tests/PixelTests.cpp +++ b/isis/tests/PixelTests.cpp @@ -2,8 +2,8 @@ #include #include +#include #include -// #include #include #include @@ -465,7 +465,6 @@ TEST(Pixel, static_ToString) { EXPECT_EQ(std::string("Lis"), Pixel::ToString(Isis::Lis)); EXPECT_EQ(std::string("Lrs"), Pixel::ToString(Isis::Lrs)); EXPECT_EQ(std::string("Null"), Pixel::ToString(Isis::Null)); - EXPECT_EQ(std::string("Invalid"), Pixel::ToString(-1.0e+1000)); } TEST(Pixel, ToString) { @@ -477,5 +476,4 @@ TEST(Pixel, ToString) { EXPECT_EQ(std::string("Lis"), Pixel(1,2,3,Isis::Lis).ToString()); EXPECT_EQ(std::string("Lrs"), Pixel(1,2,3,Isis::Lrs).ToString()); EXPECT_EQ(std::string("Null"), Pixel(1,2,3,Isis::Null).ToString()); - EXPECT_EQ(std::string("Invalid"), Pixel(1,2,3,-1.0e+1000).ToString()); } diff --git a/isis/tests/statsTests.cpp b/isis/tests/statsTests.cpp new file mode 100644 index 0000000000..09edc877c7 --- /dev/null +++ b/isis/tests/statsTests.cpp @@ -0,0 +1,209 @@ +#include "stats.h" + +#include + +#include "gmock/gmock.h" + +#include "Cube.h" +#include "FileName.h" +#include "Histogram.h" +#include "Pvl.h" +#include "SpecialPixel.h" + +using namespace Isis; + +class MockCube : public Cube { + public: + MOCK_CONST_METHOD0(bandCount, int()); + MOCK_CONST_METHOD0(fileName, QString()); + MOCK_CONST_METHOD1(physicalBand, int(const int &virtualBand)); + MOCK_METHOD4(histogram, Histogram*( + const int &band, const double &validMin, + const double &validMax, + QString msg)); +}; + +class stats_FlatFileTest : public ::testing::Test { + protected: + Pvl testPvl; + + void SetUp() override { + PvlGroup firstGroup("FirstGroup"); + firstGroup += PvlKeyword("NumberKey", "0.0"); + firstGroup += PvlKeyword("StringKey", "Hello"); + testPvl += firstGroup; + + PvlGroup secondGroup("SecondGroup"); + PvlKeyword dupKey("DuplicateKey", "stats here"); + secondGroup += dupKey; + secondGroup += dupKey; + testPvl += secondGroup; + } +}; + +class stats_MockHist : public ::testing::Test { + protected: + MockCube *mockCube; + + void SetUp() override { + mockCube = nullptr; + + Histogram *testBand1Stats = new Histogram(-10, 10, 21); + for (int val = -10; val <=10; val++) { + testBand1Stats->AddData(val); + } + testBand1Stats->AddData(0.0); + + Histogram *testBand2Stats = new Histogram(-10, 10, 21); + testBand2Stats->AddData(Null); + testBand2Stats->AddData(Lrs); + testBand2Stats->AddData(Lis); + testBand2Stats->AddData(His); + testBand2Stats->AddData(Hrs); + + mockCube = new MockCube(); + EXPECT_CALL(*mockCube, bandCount()) + .Times(1) + .WillOnce(::testing::Return(2)); + EXPECT_CALL(*mockCube, histogram(1, ::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Return(testBand1Stats)); + EXPECT_CALL(*mockCube, histogram(2, ::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Return(testBand2Stats)); + EXPECT_CALL(*mockCube, fileName()) + .Times(2) + .WillRepeatedly(::testing::Return("TestCube.cub")); + EXPECT_CALL(*mockCube, physicalBand(1)) + .Times(1) + .WillOnce(::testing::Return(1)); + EXPECT_CALL(*mockCube, physicalBand(2)) + .Times(1) + .WillOnce(::testing::Return(2)); + } + + void TearDown() override { + if (mockCube) { + delete mockCube; + mockCube = nullptr; + } + // The histograms will be cleaned up by the stats function + } +}; + + +TEST_F(stats_MockHist, TestStats) { + Pvl statsPvl = stats( + mockCube, + Isis::ValidMinimum, + Isis::ValidMaximum); + + ASSERT_EQ(statsPvl.groups(), 2); + + PvlGroup band1Stats = statsPvl.group(0); + EXPECT_EQ("TestCube.cub", (QString) (band1Stats.findKeyword("From"))); + EXPECT_EQ(1, (int) (band1Stats.findKeyword("Band"))); + EXPECT_EQ(22, (int) (band1Stats.findKeyword("ValidPixels"))); + EXPECT_EQ(22, (int) (band1Stats.findKeyword("TotalPixels"))); + EXPECT_EQ(0, (int) (band1Stats.findKeyword("OverValidMaximumPixels"))); + EXPECT_EQ(0, (int) (band1Stats.findKeyword("UnderValidMinimumPixels"))); + EXPECT_EQ(0, (int) (band1Stats.findKeyword("NullPixels"))); + EXPECT_EQ(0, (int) (band1Stats.findKeyword("LisPixels"))); + EXPECT_EQ(0, (int) (band1Stats.findKeyword("LrsPixels"))); + EXPECT_EQ(0, (int) (band1Stats.findKeyword("HisPixels"))); + EXPECT_EQ(0, (int) (band1Stats.findKeyword("HrsPixels"))); + EXPECT_EQ(0.0, (double) (band1Stats.findKeyword("Average"))); + EXPECT_NEAR(6.0553, (double) (band1Stats.findKeyword("StandardDeviation")), 0.0001); + EXPECT_NEAR(36.6667, (double) (band1Stats.findKeyword("Variance")), 0.0001); + EXPECT_EQ(0.0, (double) (band1Stats.findKeyword("Median"))); + EXPECT_EQ(0.0, (double) (band1Stats.findKeyword("Mode"))); + EXPECT_EQ(0.0, (double) (band1Stats.findKeyword("Skew"))); + EXPECT_EQ(-10, (double) (band1Stats.findKeyword("Minimum"))); + EXPECT_EQ(10.0, (double) (band1Stats.findKeyword("Maximum"))); + EXPECT_EQ(0.0, (double) (band1Stats.findKeyword("Sum"))); + + PvlGroup band2Stats = statsPvl.group(1); + EXPECT_EQ("TestCube.cub", (QString) (band2Stats.findKeyword("From"))); + EXPECT_EQ(2, (int) (band2Stats.findKeyword("Band"))); + EXPECT_EQ(0, (int) (band2Stats.findKeyword("ValidPixels"))); + EXPECT_EQ(5, (int) (band2Stats.findKeyword("TotalPixels"))); + EXPECT_EQ(0, (int) (band2Stats.findKeyword("OverValidMaximumPixels"))); + EXPECT_EQ(0, (int) (band2Stats.findKeyword("UnderValidMinimumPixels"))); + EXPECT_EQ(1, (int) (band2Stats.findKeyword("NullPixels"))); + EXPECT_EQ(1, (int) (band2Stats.findKeyword("LisPixels"))); + EXPECT_EQ(1, (int) (band2Stats.findKeyword("LrsPixels"))); + EXPECT_EQ(1, (int) (band2Stats.findKeyword("HisPixels"))); + EXPECT_EQ(1, (int) (band2Stats.findKeyword("HrsPixels"))); +} + +TEST(stats, ValidMinimum) { + Histogram *testStats = new Histogram(-1000,1000); + + MockCube *mockCube = new MockCube(); + EXPECT_CALL(*mockCube, bandCount()) + .Times(1) + .WillOnce(::testing::Return(1)); + EXPECT_CALL(*mockCube, histogram(1, 0.0, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Return(testStats)); + EXPECT_CALL(*mockCube, fileName()) + .Times(1) + .WillRepeatedly(::testing::Return("TestCube.cub")); + EXPECT_CALL(*mockCube, physicalBand(1)) + .Times(1) + .WillOnce(::testing::Return(1)); + + Pvl statsPvl = stats( + dynamic_cast(mockCube), + 0.0, + Isis::ValidMaximum); + + delete mockCube; + mockCube = nullptr; + // The histogram will be cleaned up in the stats function +} + +TEST(stats, ValidMaximum) { + Histogram *testStats = new Histogram(-1000,1000); + + MockCube *mockCube = new MockCube(); + EXPECT_CALL(*mockCube, bandCount()) + .Times(1) + .WillOnce(::testing::Return(1)); + EXPECT_CALL(*mockCube, histogram(1, ::testing::_, 0.0, ::testing::_)) + .Times(1) + .WillOnce(::testing::Return(testStats)); + EXPECT_CALL(*mockCube, fileName()) + .Times(1) + .WillRepeatedly(::testing::Return("TestCube.cub")); + EXPECT_CALL(*mockCube, physicalBand(1)) + .Times(1) + .WillOnce(::testing::Return(1)); + + Pvl statsPvl = stats( + dynamic_cast(mockCube), + Isis::ValidMinimum, + 0.0); + + delete mockCube; + mockCube = nullptr; + // The histogram will be cleaned up in the stats function +} + +TEST_F(stats_FlatFileTest, FlatFile) { + std::ostringstream *testStream = new std::ostringstream(); + writeStatsStream(testPvl, false, testStream); + EXPECT_EQ(testStream->str(), "0.0,Hello\nstats here,stats here\n"); + + delete testStream; + testStream = nullptr; +} + +TEST_F(stats_FlatFileTest, FlatFileHeader) { + std::ostringstream *testStream = new std::ostringstream(); + writeStatsStream(testPvl, true, testStream); + EXPECT_EQ(testStream->str(), "NumberKey,StringKey\n0.0,Hello\nstats here,stats here\n"); + + delete testStream; + testStream = nullptr; +} diff --git a/recipe/build.sh b/recipe/build.sh new file mode 100644 index 0000000000..ef8368ae31 --- /dev/null +++ b/recipe/build.sh @@ -0,0 +1,5 @@ +mkdir build +cd build +cmake -GNinja -DJP2KFLAG=ON -Dpybindings=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$PREFIX -Disis3Data=/usgs/cpkgs/isis3/data -Disis3TestData=/usgs/cpkgs/isis3/testData ../isis +ninja install +ninja docs diff --git a/recipe/meta.yaml b/recipe/meta.yaml new file mode 100644 index 0000000000..c220d7649f --- /dev/null +++ b/recipe/meta.yaml @@ -0,0 +1,174 @@ +# When building ISIS for a public release, be sure that these variables are properly set to reflect +# your current build. Keep in mind that these values are how conda build names its .tar.bz2 build +# file, and so must be unique to other builds sitting in the USGS-Astrogeology channel on Anaconda +# Cloud, or they will be overwritten when you upload this current build. It is always a good idea to +# confirm that you will not be overwriting a file that has already been uploaded by checking the +# channel before building. + +# This is the version of ISIS that you are building. (Please refer to +# RFC2 (https://github.com/USGS-Astrogeology/ISIS3/wiki/RFC2:-Release-Process) if you are not sure +# about what version you are building.) +# Examples: +# A Public Release for ISIS3.6.1: {% set version = "3.6.1" %} +# A Release Candidate for ISIS3.6.1: {% set version = "3.6.1_RC" %} +# A custom build of ISIS3.6.1 for the CaSSIS mission: {% set version = "3.6.1_cassis" %} +{% set version = "3.6.1" %} + +# This is the branch that conda build will pull from the ISIS repository. If you are building for a +# general public release, leave the branch as "release". If you are building a custom build, like +# for a mission for example, simply change the value to the branch of choice. +{% set git_branch = "release" %} + +# This is the build number for the current version you are building. If this is the first build of +# this version, the build number will be 0. It is incremented by 1 with every consecutive build of +# the same version. +{% set build_number = "0" %} + +package: + name: isis3 + version: {{ version }} + +source: + git_url: 'https://github.com/USGS-Astrogeology/ISIS3.git' + git_branch: {{ git_branch }} + +build: + number: {{ build_number }} + +# Shotgun strat on requirements until we can narrow them down +requirements: + build: + - armadillo==8.200.0 + - blas==1.1 + - bullet==2.86.1 + - bz2file==0.98 + - bzip2==1.0.6 + - cmake=>3.10 + - cspice==66 + - curl==7.60.0 + - doxygen==1.8.14 + - eigen==3.3.3 + - embree==2.14.0 + - geos==3.5.1 + - geotiff==1.4.2 + - gmm==5.0 + - gmp==6.1.2 + - gsl==2.2.1 + - hdf5==1.8.18 + - icu==58.2 + - jama==125 + - jpeg==9b + - kakadu==1 + - krb5==1.14.2 + - libpng>=1.6.34 + - libprotobuf==3.5.2 + - libtiff=>4.0.9 + - libxml2==2.9.7 + - make + - mesalib==17.2.0 + - mysql==5.7.20 + - mysql-connector-c==6.1.6 + - nanoflann==1.2.2 + - ninja==1.7.2 + - nn + - openblas==0.2.19 + - opencv==3.2.0|3.3.0 + - openssl==1.0.2n + - patchelf==0.9 + - pcl==1.8.1 + - pip==9.0.1 + - protobuf==3.5.2 + - python==3.6 + - qhull==7.2.0=0 + - qt=5.9.6 + - qwt=6.1.3 + - setuptools=38.5.1 + - sip==4.18 + - sqlite==3.13.0 + - suitesparse==4.5.4 + - superlu==5.2.1 + - tnt==126=0 + - wheel==0.30.0 + - x264==20131218 + - xalan-c==1.11 + - xerces-c==3.1.4 + - xorg-kbproto==1.0.7 + - xorg-libice + - xorg-libsm + - xorg-libx11==1.6.4 + - xorg-libxi + - zlib==1.2.11 + run: + - armadillo==8.200.0 + - blas==1.1 + - bullet==2.86.1 + - bz2file==0.98 + - bzip2==1.0.6 + - cmake==3.9.1 + - cspice==66 + - curl==7.60.0 + - doxygen==1.8.14 + - eigen==3.3.3 + - embree==2.14.0 + - geos==3.5.1 + - geotiff==1.4.2 + - gmm==5.0 + - gmp==6.1.2 + - gsl==2.2.1 + - hdf5==1.8.18 + - icu==58.2 + - jama==125 + - jasper=1 + - jpeg==9b + - kakadu==1 + - krb5==1.14.2 + - libpng>=1.6.34 + - libprotobuf==3.5.2 + - libtiff=>4.0.9 + - libxml2==2.9.7 + - make + - mesalib==17.2.0 + - mysql==5.7.20 + - mysql-connector-c==6.1.6 + - nanoflann==1.2.2 + - ninja==1.7.2 + - conda-forge/label/gcc7::nn + - openblas==0.2.19 + - opencv==3.2.0|3.3.0 + - openssl==1.0.2n + - patchelf==0.9 + - pcl==1.8.1 + - pip==9.0.1 + - protobuf==3.5.2 + - python==3.6 + - qhull==7.2.0=0 + - qt=5.9.6 + - qwt=6.1.3 + - setuptools=38.5.1 + - sip==4.18 + - sqlite==3.13.0 + - suitesparse==4.5.4 + - superlu==5.2.1 + - tnt==126=0 + - wheel==0.30.0 + - x264==20131218 + - xalan-c==1.11 + - xerces-c==3.1.4 + - xorg-kbproto==1.0.7 + - xorg-libice + - xorg-libsm + - xorg-libx11==1.6.4 + - xorg-libxi + - zlib==1.2.11 +# Add the tests eventually +# test: +# + +# about: +# home: https://developers.google.com/protocol-buffers/ +# license: BSD 3-Clause +# license_file: LICENSE +# summary: "Protocol Buffers - Google's data interchange format." + +# extra: +# recipe-maintainers: