{"action":"created","comment":{"url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/comments/33585332","id":33585332,"diff_hunk":"@@ -0,0 +1,831 @@\n+#! /usr/bin/env python\n+'''\n+-----     -----     -----     -----     -----     -----     -----     -----\n+Author: ngenetzky@usgs.gov\n+        Underlying scripts' author are described with the class documentation\n+License: NASA Open Source Agreement 1.3\n+USGS Designation: EROS Science Processing Architecture (ESPA)\n+\n+Classes:\n+    Cmd\n+    ScriptArgParser\n+    ScriptHelper (Version:0.0.1)\n+    SI: Spectral Indices (Version:2.1.0)\n+\n+SI: Spectral Indices\n+    Author:  gschmidt@usgs.gov\n+    File: spectral_indices.py\n+    Version: 2.1.0\n+    Source: https://github.com/USGS-EROS/espa-spectral-indices\n+    Purpose: Master script for creating spectral indices products.\n+    Usage: See \"spectral_indices.py --help\"\n+    Dependencies: spectral_indices\n+    Description:\n+        spectral_indices produces the desired spectral index products for the\n+        input surface reflectance or TOA reflectance bands. The options\n+        include: NDVI, EVI, SAVI, MSAVI, NDMI (also known as NDWI or NDII),\n+        NBR, and NBR2. The user may specify one, some, or all of the\n+        supported indices for output.\n+-----     -----     -----     -----     -----     -----     -----     -----\n+'''\n+import sys\n+import os\n+import argparse\n+import logging\n+import commands\n+import metadata_api\n+\n+\n+def get_logger():\n+    # Setup the Logger with the proper configuration\n+    logging.basicConfig(format=('%(asctime)s.%(msecs)03d %(process)d'\n+                                ' %(levelname)-8s'\n+                                ' %(filename)s:%(lineno)d:'\n+                                '%(funcName)s -- %(message)s'),\n+                        datefmt='%Y-%m-%d %H:%M:%S',\n+                        level=logging.INFO)\n+    return logging.getLogger(__name__)\n+\n+\n+class Cmd:  # #################################################################\n+    '''Builds and executes command line statements\n+\n+    Description:\n+        Encompasses all of the functions associated with building and\n+        executing command line arguments. By using this class with each script\n+        it ensures consistency in the way that that command line is produced.\n+    Usage:\n+        # __init__(SCRIPTNAME, DIRECTORY)\n+        cmdScript = Cmd('my_script_name')\n+        # add_param(PARAMERTER_NAME,PARAMETER_VALUE)\n+        cmdScript.add_param('xml', 'my_xml.xml')\n+        # Will use commands.getstatusoutput to execute built cmd-line\n+        cmdScript.execute()\n+    '''\n+\n+    class EXECUTE_ERROR(Exception):\n+        def __init__(self, message, *args):\n+            self.message = message\n+            Exception.__init__(self, message, *args)\n+\n+    class INVALID_SCRIPT(Exception):\n+        def __init__(self, script_name, *args):\n+            self.script_name = script_name\n+            Exception.__init__(self, script_name, *args)\n+        pass\n+\n+    def __init__(self, script_name, basedir=''):\n+        '''Setup the base of the cmdline with required directory and script name.\n+\n+        Parameters:\n+            script_name: Name of the executable\n+            basedir: Location of the executable\n+        Raises:\n+            INVALID_SCRIPT\n+        '''\n+        try:\n+            self.cmdline = [os.path.join(basedir, script_name)]\n+        except AttributeError:\n+            raise Cmd.INVALID_SCRIPT(script_name)\n+\n+    def get_help(self):\n+        '''Creates and execute 'script_name --help', and ignores errors.'''\n+        self.add_param('--help')\n+        cmd_string = self.__str__()\n+        (status, output) = commands.getstatusoutput(cmd_string)\n+        return output\n+\n+    def add_param(self, name, *args):\n+        '''Allows a parameter to be added; optionally with list of arguments\n+\n+        Parameters:\n+            name: Name of argument preceeded with \"--\" or \"-\" if desired.\n+            args: Additional arguments to place after name\n+        '''\n+        self.cmdline.append(name)\n+        # Will only add an argument of the list if it is truthy\n+        self.cmdline.append(' '.join(arg for arg in args if arg))\n+\n+    def __repr__(self):\n+        '''Combine the script name (and path) with the arguments'''\n+        return ' '.join(self.cmdline)\n+\n+    def __str__(self):\n+        '''Combine the script name (and path) with the arguments'''\n+        return ' '.join(self.cmdline)\n+\n+    def execute(self):\n+        ''' Execute a command line and return the terminal output\n+\n+        Raises:\n+            EXECUTE_ERROR (Stdout/Stderr)\n+        Returns:\n+            output:The stdout and/or stderr from the executed command.\n+        '''\n+        cmd_string = self.__str__()\n+        (status, output) = commands.getstatusoutput(cmd_string)\n+\n+        if status < 0:\n+            message = \"Application terminated by signal [%s]\" % cmd_string\n+            if len(output) > 0:\n+                message = ' Stdout/Stderr is: '.join([message, output])\n+            raise Cmd.EXECUTE_ERROR(message)\n+\n+        if status != 0:\n+            message = \"Application failed to execute [%s]\" % cmd_string\n+            if len(output) > 0:\n+                message = ' Stdout/Stderr is: '.join([message, output])\n+            raise Cmd.EXECUTE_ERROR(message)\n+\n+        if os.WEXITSTATUS(status) != 0:\n+            message = (\"Application [%s] returned error code [%d]\"\n+                       % (cmd_string, os.WEXITSTATUS(status)))\n+            if len(output) > 0:\n+                message = ' Stdout/Stderr is: '.join([message, output])\n+            raise Cmd.EXECUTE_ERROR(message)\n+\n+        return output\n+\n+\n+class ScriptArgParser(argparse.ArgumentParser):  # ############################\n+        class HELP_REQUESTED(Exception):\n+            '''Indicates that -h or --help has been parsed\n+\n+            See argparse.ArgumentParser.print_help()\n+            '''\n+            pass\n+\n+        class INVALID_ARGUMENT(Exception):\n+            '''Indicates ArgParse has detected an error\n+\n+            See argparse.ArgumentParser.error()\n+            '''\n+            pass\n+\n+        def print_help(self, outfile=None):\n+            raise ScriptArgParser.HELP_REQUESTED\n+\n+        def error(self, message):\n+            raise ScriptArgParser.INVALID_ARGUMENT(message)\n+\n+\n+class ScriptHelper:  # ########################################################\n+    '''Parse arguments, check conditions and execute the proper executable.\n+\n+    Description:\n+        General functions and properties inherent to all scripts.\n+        Should never be directly instantiated, but instead should\n+        be a base class for other script classes.\n+\n+    To-Do list to properly subclass:\n+        extend __init__ and define self.title and self.exe_filename\n+            super.__init__() should be called at the beginning\n+            exe_filename can be set to none if a function is defined\n+            called get_exe_filename()\n+        override parse_arguments(). See parse_common_args().\n+        extend setup(). See check_common_prereq(). (optional)\n+            super.setup() should be called at the beginning\n+        override build_cmd_line(). See build_cmd_line().\n+        extend handle_exception(). (optional)\n+    '''\n+    __title__ = 'Script Helper'\n+    __version__ = '0.0.1 (July 2015)'\n+\n+    def __init__(self):\n+        '''Create an instance of Script Helper\n+\n+        To-Do for subclass:\n+            Extend. super.__init__() should be called at the beginning\n+            define the following instance variables:\n+                title: title of its module to be used in log statements\n+                exe_filename: filename of executable to be called\n+        Instance Variables:\n+            Logger: logs info, error, fatal and other messages (from logging).\n+            args: defined by parse_arguments().\n+            cmd: defined by build_cmd_line()\n+            config: defined by get_config_from_xml()\n+            parser: created by setup() and should be used\n+                by subclass's parse_arguments()\n+\n+        Instance Variables defined by subprogram:\n+            title: A title that describes all executables in the class.\n+            exe_filename: Should be defined in __init__() if possible.\n+                Otherwise a function get_exe_filename should be defined\n+                that will determine which exe will be used.\n+            description: A description for all executables in the class.\n+        '''\n+        self.logger = logging.getLogger(__name__)\n+        self.args = None\n+        self.cmd = None\n+        self.config = None\n+        # Should be defined by sub-class\n+        self.title = 'Title Not Defined'\n+        self.exe_filename = 'Program Name not defined'\n+        self.description = 'Description not defined'\n+        # Attempt to Pre-Parse XML so satellite can be used in logic\n+\n+    class NO_ACTION_REQUESTED(Exception):\n+        pass\n+\n+    class INVALID_FILE(Exception):\n+        def __init__(self, name, recieved, *args):\n+            self.name = name\n+            self.recieved = recieved\n+            Exception.__init__(self, name, recieved, *args)\n+\n+    class INVALID_SATELLITE_XML(Exception):\n+        '''Parsed file parameter does not meet specified format'''\n+        def __init__(self, sat_name, *args):\n+            self.sat_name = sat_name\n+            Exception.__init__(self, sat_name, *args)\n+\n+    class INVALID_SUBCLASS(Exception):\n+        '''Subclass failed a requirements for a subclasses of ScriptHelper'''\n+        pass\n+\n+    class EXECUTABLE_NOT_DEFINED(Exception):\n+        '''exe_filename has been requested before being defined.\n+\n+        Should either be defined in __init__() or in get_exe_filename()\n+        '''\n+        pass\n+\n+    def get_config_from_xml(self, xml_filename):\n+        ''' Stores desired information into config(dict) from xml file\n+\n+        Description:\n+            Provides access to information from xml file.\n+        Precondition:\n+            xml_filename is a string that refers to a xmlfile in the current\n+                working directory\n+            XML contains the following item:\n+                <global_metadata><satellite>\n+        Postcondition:\n+            'satellite' is in dictionary self.config\n+        '''\n+        # self.check_common_prereq(check_xml=True) Handled in metadata_api\n+        self.config = {}  # Empty dictionary\n+        xml = metadata_api.parse(xml_filename, silence=True)\n+        global_metadata = xml.get_global_metadata()\n+        self.config['satellite'] = global_metadata.get_satellite()\n+        # self.config['instrument'] = global_metadata.get_instrument()\n+        # self.config['date'] = xml_filename[9:16]\n+        del global_metadata  # Explicitly release memory from xml object\n+        del xml  # Explicitly release memory from xml object\n+\n+    def is_landsat8(self, config):\n+        '''Reads config dictionary for satellite name, checks if L8.\n+        Precondition:\n+            (1) 'satellite' is in config\n+            (2) config['satellite'] is in\n+                ['LANDSAT_4','LANDSAT_5','LANDSAT_7','LANDSAT_8']\n+        Postcondition:\n+            returns is_landsat8\n+        Raises:\n+            INVALID_SUBCLASS if fail Precondition (1)\n+            INVALID_SATELLITE_XML if fail Precondition (2)\n+        '''\n+        try:\n+            if config['satellite'] in ['LANDSAT_8']:\n+                return True\n+            elif config['satellite'] in ['LANDSAT_4',\n+                                         'LANDSAT_5',\n+                                         'LANDSAT_7']:\n+                return False\n+            else:\n+                raise ScriptHelper.INVALID_SATELLITE_XML(config['satellite'])\n+        except TypeError:\n+            raise\n+            raise ScriptHelper.INVALID_SUBCLASS('is_landsat8 must be called'\n+                                                ' after get_config_from_xml')\n+\n+    @staticmethod\n+    def str_to_bool(string):\n+        '''Converts common boolean-like string to boolean value\n+\n+        Supported Strings:['true', '1', 't', 'yes']\n+                          ['false', '0', 'f', 'no']\n+        Note: Strings are not Case Sensitive\n+        '''\n+        if(string.lower() in ['true', '1', 't', 'yes']):\n+            return True\n+        elif(string.lower() in ['false', '0', 'f', 'no']):\n+            return False\n+\n+    @staticmethod\n+    def parse_common_args(parser, xml=False, debug=False, verbose=False,\n+                          version=False):\n+        '''Allows common arguments to be added to the parser.\n+\n+        Description:\n+            Should be used by subclass when overriding build_parser()\n+        Precondition:\n+            parser must be a valid argparse.ArgumentParser\n+        Arguments to add to ArgumentParser (Default is False):\n+            xml==True: add argument \"--xml XML_FILENAME\"\n+            debug==True: add argument \"--debug\"\n+            verbose==True: add argument \"--verbose\"\n+            version==True: add argument \"--version\"\n+        Postcondition:\n+            returns parser: A valid argparse.ArgumentParser object with equal\n+             or more arguments specified than the parser passed in.\n+        '''\n+        if(xml):\n+            parser.add_argument('--xml', action='store',\n+                                dest='xml_filename', required=True,\n+                                help='Input XML metadata file',\n+                                metavar='FILE')\n+        if(debug):\n+            parser.add_argument('--debug',\n+                                action='store_true', dest='debug',\n+                                required=False, default=False,\n+                                help=\"Keep any debugging data\"\n+                                     \" (default is False)\")\n+        if(verbose):\n+            parser.add_argument('--verbose',\n+                                action='store_true', dest='verbose',\n+                                required=False, default=False,\n+                                help=('Should intermediate messages'\n+                                      ' be printed? (default is False)'))\n+        if(version):\n+            parser.add_argument('--version',\n+                                action='store_true', dest='version',\n+                                required=False, default=False,\n+                                help=('Should version message'\n+                                      ' be printed? (default is False)'))\n+        return parser\n+\n+    def parse_only_xml(self, list_ignored=False):\n+        ''' Will only parse --xml XML_FILENAME from cmdline.\n+\n+        See get_config_from_xml() and  get_exe_filename()\n+        Precondition:\n+            '--xml FILENAME' exists in command line arguments\n+        Postcondition:\n+            self.args.xml_filename exists\n+        '''\n+\n+        # Try to parse out the XML so the exe can be determined\n+        parse_xml = ScriptArgParser(add_help=False)\n+        parse_xml = self.parse_common_args(parse_xml, xml=True)\n+        (temp, extra_args) = parse_xml.parse_known_args()\n+\n+        if(list_ignored):\n+            print 'The following arguments were ignored:' + ''.join(extra_args)\n+        try:\n+            if(self.args is None):\n+                self.args = temp  # Args are empty. thus overwrite\n+            else:\n+                self.args.xml_filename = temp.xml_filename\n+        except:\n+            raise\n+\n+        self.get_config_from_xml(self.args.xml_filename)\n+        self.get_exe_filename()\n+        return self.args.xml_filename\n+\n+    def print_custom_help(self):\n+        ''' Used to override default help so underlying exe's help can be shown\n+\n+        Format\n+        If the exe_filename can be determined at the time of execution:\n+            SCRIPT_TITLE SCRIPT_VERSION\n+            USAGE\n+            DESCRIPTION\n+            ARGUMENTS\n+\n+            ----     EXE_TITLE EXE_VERSION     -----\n+            Help from executables under this script:\n+            EXE_HELP_OUTPUT\n+        Otherwise:\n+            SCRIPT_TITLE SCRIPT_VERSION\n+            USAGE\n+            DESCRIPTION\n+            ARGUMENTS\n+\n+            For additional help specify the xml with the help parameter\n+        '''\n+        try:\n+            self.parse_only_xml()\n+            self.get_config_from_xml(self.args.xml_filename)\n+            self.get_exe_filename()\n+            msg = (('{2} {3}\\n{4}'\n+                    '\\n\\n-----     {0} {1}     -----\\n'\n+                    'Help from executables under this script:\\n\\n{5}'\n+                    ).format(\n+                            # Title of the module\n+                            self.title,\n+                            # Version of the underlying executable\n+                            self.get_executables_version(),\n+                            # Name of the Sub Class\n+                            self.__class__.__title__,\n+                            # Version of the Sub Class\n+                            self.__class__.__version__,\n+                            self.parser.format_help(),\n+                            self.get_executables_help()\n+                  ))\n+        except ScriptArgParser.INVALID_ARGUMENT:  # Implies xml is missing\n+            # Print only for ScriptHelper\n+            msg = ('{0} {1}\\n{2}\\nFor additional help specify the xml with'\n+                   ' the help parameter').format(\n+                                             # Name of the Sub Class\n+                                             self.__class__.__title__,\n+                                             # Version of the Sub Class\n+                                             self.__class__.__version__,\n+                                             self.parser.format_help())\n+        print msg\n+\n+    def get_executables_version(self):\n+        '''Will execute 'exe_filename --version' and return response.\n+\n+        Precondition:\n+            (1) self.get_exe_filename() returns a string that is the name of a\n+                script in the current working directory.\n+            (2) application exits with success\n+        Postcondition:\n+            returns output if (2) is met\n+            returns '' if (2) is failed\n+        Raises\n+            Cmd.INVALID_SCRIPT if (1) is failed\n+            Cmd.EXECUTE_ERROR if (2) is failed\n+        '''\n+        try:\n+            cmd = Cmd(self.get_exe_filename())\n+            cmd.add_param('--version')\n+            version_msg = cmd.execute()\n+            return version_msg\n+        except Cmd.INVALID_SCRIPT:\n+            raise ScriptHelper.EXECUTABLE_NOT_DEFINED\n+        except Cmd.EXECUTE_ERROR:\n+            #  print('Application returned error when requesting --version')\n+            return ''  # If version can't be obtained then leave it empty\n+        else:\n+            raise\n+\n+    # Error Cause\n+    def get_executables_help(self):\n+        '''Will execute 'exe_filename --help' and return response.\n+\n+        Precondition:\n+            (1) self.get_exe_filename() returns a string that is the name of a\n+                script in the current working directory.\n+            (2) application exits with success\n+        Postcondition:\n+            returns output if (2) is met\n+            returns '' if (2) is failed\n+        Raises\n+            Cmd.INVALID_SCRIPT if (1) is failed\n+            Cmd.EXECUTE_ERROR if (2) is failed\n+        '''\n+        try:\n+            cmd = Cmd(self.get_exe_filename())\n+            help_msg = cmd.get_help()\n+            return help_msg\n+        except Cmd.INVALID_SCRIPT:\n+            raise ScriptHelper.EXECUTABLE_NOT_DEFINED\n+        except Cmd.EXECUTE_ERROR as e:\n+            #  print('Application returned error when requesting --help')\n+            return e.args[0]  # Message should contain Stdout, but also Stderr\n+        else:\n+            raise\n+\n+    def check_common_prereq(self, check_xml=True):\n+        '''Allow easy way to check conditions that are commonly required for a script.\n+\n+        Description:\n+        Raises:\n+            INVALID_FILE (Filetype/Description, received filename)\n+        '''\n+        if check_xml:\n+            # Verify that the XML filename provided is not an empty string\n+            if not os.path.isfile(self.args.xml_filename):\n+                raise Cmd.INVALID_FILE(name='XML', recieved=\n+                                       self.args.xml_filename\n+                                       )\n+        return True\n+\n+    def get_execute_header(self):\n+        '''Return String describing action taken by execute().\n+\n+        Precondition:\n+            self.args.xmlfilename exists\n+            self.cmd is valid Cmd object\n+        Postcondition:\n+            returns string that introduces the operations of the execution.\n+            '''\n+        return (\"{0} is processing Landsat data associated with xml file\"\n+                \" ({1}). Using the command line:{2}\"\n+                ).format(self.title,\n+                         self.args.xml_filename,\n+                         str(self.cmd)\n+                         )\n+\n+    def execute(self):\n+        '''Print the execute header, execute cmdline and print output\n+\n+        Description:\n+            Uses get_execute_header() to know what to print before execution\n+            Executes line built with build_cmd_line()\n+        '''\n+        header = self.get_execute_header()\n+        self.logger.info(header)\n+\n+        output = self.cmd.execute()\n+\n+        if len(output) > 0:\n+            self.logger.info(\"STDOUT/STDERR Follows:\"\n+                             \"{0}\".format(output))\n+        self.logger.info(\"Completion of {0}\".format(self.title))\n+\n+    def build_parser(self):\n+        '''Should parse command line arguments using argparse module.\n+\n+        To-Do for subclass:\n+            Override. Create argparse.ArgumentParser object.\n+                argparse.ArgumentParser(description=self.description)\n+        SubClass Note:\n+            If this method is not overridden it will raise exception.\n+        Raises:\n+            INVALID_SUBCLASS\n+        '''\n+        raise ScriptHelper.INVALID_SUBCLASS\n+\n+    def parse_arguments(self):  # SubClass should override.\n+        '''Stores arguments specified in build_parser() to self.args\n+\n+            If '--help' or '-h' is specified as an argument the request is\n+            handled here. Invalid arguments will also show help along with\n+            error message.\n+        '''\n+        try:\n+            self.args = self.parser.parse_args()\n+        except ScriptArgParser.INVALID_ARGUMENT as e:\n+            self.print_custom_help()\n+            self.logger.error(e.args[0])  # Error message from ArgParse\n+            exit(1)\n+        except ScriptArgParser.HELP_REQUESTED:\n+            self.print_custom_help()\n+            exit(0)\n+\n+    def get_exe_filename(self):  # SubClass should override.\n+        '''Should return a the name of the script.\n+\n+        Precondition:\n+            self.exe_filename exists\n+        Postcondition:\n+            returns self.exe_filename\n+        To-Do for subclass:\n+            Override if exe_filename can not be determined before parsing\n+                arguments\n+        Raises:\n+            EXECUTABLE_NOT_DEFINED\n+        '''\n+        if self.exe_filename:\n+            return self.exe_filename\n+        else:\n+            raise ScriptHelper.EXECUTABLE_NOT_DEFINED\n+\n+    def build_cmd_line(self):  # SubClass should override.\n+        '''Builds a Cmd object that can be executed to run the executable.\n+\n+        To-Do for subclass:\n+            Override. Create Cmd object and store in self.cmd\n+            Add additional parameters by using Cmd.add_param()\n+        SubClass Note:\n+            If this method is not overridden it will raise exception.\n+        Raises:\n+            INVALID_SUBCLASS\n+        '''\n+        raise ScriptHelper.INVALID_SUBCLASS\n+\n+    def handle_exception(self):\n+        '''Will handle any exception that the ScriptHelper understands.\n+\n+        See sys.exc_info() = [type,value,traceback]\n+        See ScriptHelper.__doc__ for Understood Exceptions\n+        Description:\n+            Typically the method will log a message related to the exception\n+            and then exit with error.\n+        To-Do for subclass:\n+            Optionally Extend. Subclass should return False if the\n+            exception was not understood.\n+        SubClass Note:\n+            Consider effects of calling super.handle_exception at beginning or\n+            end of the method that extends this method.\n+        Understood exceptions:\n+            Any Exception class defined in ScriptHelper directly\n+        Return:\n+            exceptionHandled: returns False if the exception was not understood\n+        '''\n+        # Gets the exception type of exception being handled (class object)\n+        e_type = sys.exc_info()[0]\n+        # Gets the exception parameter (the second argument to raise)\n+        e = sys.exc_info()[1]\n+        print(str(e.__class__)+' Exception:'+str(e))\n+\n+        if e_type is ScriptHelper.NO_ACTION_REQUESTED:\n+            self.logger.fatal(\"NO_ACTION_REQUESTED\")\n+            try:\n+                self.logger.fatal(e.args[0])\n+            except NameError:\n+                pass\n+            raise\n+\n+        elif e_type is ScriptHelper.INVALID_FILE:\n+            try:\n+                self.logger.fatal(\"Error: {0} file ({1}) does not exist or\"\n+                                  \" is not accessible.\"\n+                                  .format(e.name, e.recieved))\n+            except NameError:\n+                pass\n+            raise\n+\n+        elif e_type is ScriptHelper.INVALID_SATELLITE_XML:\n+            try:\n+                self.logger.fatal(\"Error: XML specifies invalid satellite({0})\"\n+                                  .format(e.sat_name))\n+            except NameError:\n+                pass\n+            raise\n+\n+        elif e_type is ScriptHelper.INVALID_SUBCLASS:\n+            self.logger.fatal(\"Script Helper was improperly subclassed\")\n+            raise\n+        else:\n+            # Return False because Script Helper does not understand Exception\n+            return False\n+\n+    def run(self):\n+        try:\n+            self.build_parser()\n+            self.parse_arguments()\n+            self.build_cmd_line()\n+            self.execute()\n+            sys.exit(0)\n+        except Exception:\n+            # Exceptions that were raised by the executable\n+            #  Handle any exceptions understood by script\n+            #  handle_exception may exit with error and not return.\n+            if not self.handle_exception():\n+                raise   # Unhandled/Unexpected exceptions will not be masked\n+\n+\n+class SI(ScriptHelper):  # ####################################################\n+    '''Parse request, check conditions, execute appropriate executable\n+\n+    SI: Spectral Indices\n+    Author:  gschmidt@usgs.gov\n+    File: spectral_indices.py\n+    Source: https://github.com/USGS-EROS/espa-spectral-indices\n+    Purpose: Master script for creating spectral indices products.\n+    Usage: See \"spectral_indices.py --help\"\n+    Dependencies: spectral_indices\n+    Description:\n+        spectral_indices produces the desired spectral index products for the\n+        input surface reflectance or TOA reflectance bands. The options\n+        include: NDVI, EVI, SAVI, MSAVI, NDMI (also known as NDWI or NDII),\n+        NBR, and NBR2. The user may specify one, some, or all of the\n+        supported indices for output.\n+    '''\n+    __title__ = 'Spectral Indices'\n+    __version__ = '2.1.0'\n+\n+    def __init__(self):\n+        ScriptHelper.__init__(self)\n+        self.exe_filename = 'spectral_indices'\n+        self.title = 'Spectral Indices'\n+        self.description = ('''\n+        spectral_indices produces the desired spectral index products for the\n+        input surface reflectance or TOA reflectance bands. The options\n+        include: NDVI, EVI, SAVI, MSAVI, NDMI (also known as NDWI or NDII),\n+        NBR, and NBR2. The user may specify one, some, or all of the\n+        supported indices for output.\n+        ''')\n+\n+    def build_parser(self):\n+        '''See ScriptHelper.build_parser() for more details'''\n+        self.parser = ScriptArgParser(description=self.description,\n+                                      add_help=True)\n+        self.parser = ScriptHelper.parse_common_args(self.parser, xml=True)\n+        # Additional parameters","path":"scripts/spectral_indices.py","position":710,"original_position":710,"commit_id":"c456026ea925c56c6dbd595a6c820b852e6d5a38","original_commit_id":"c456026ea925c56c6dbd595a6c820b852e6d5a38","user":{"login":"NGenetzky","id":12733783,"avatar_url":"https://avatars.githubusercontent.com/u/12733783?v=3","gravatar_id":"","url":"https://api.github.com/users/NGenetzky","html_url":"https://github.com/NGenetzky","followers_url":"https://api.github.com/users/NGenetzky/followers","following_url":"https://api.github.com/users/NGenetzky/following{/other_user}","gists_url":"https://api.github.com/users/NGenetzky/gists{/gist_id}","starred_url":"https://api.github.com/users/NGenetzky/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NGenetzky/subscriptions","organizations_url":"https://api.github.com/users/NGenetzky/orgs","repos_url":"https://api.github.com/users/NGenetzky/repos","events_url":"https://api.github.com/users/NGenetzky/events{/privacy}","received_events_url":"https://api.github.com/users/NGenetzky/received_events","type":"User","site_admin":false},"body":"Fixed.","created_at":"2015-06-30T15:25:22Z","updated_at":"2015-06-30T15:25:22Z","html_url":"https://github.com/USGS-EROS/espa-spectral-indices/pull/12#discussion_r33585332","pull_request_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/12","_links":{"self":{"href":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/comments/33585332"},"html":{"href":"https://github.com/USGS-EROS/espa-spectral-indices/pull/12#discussion_r33585332"},"pull_request":{"href":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/12"}}},"pull_request":{"url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/12","id":38577039,"html_url":"https://github.com/USGS-EROS/espa-spectral-indices/pull/12","diff_url":"https://github.com/USGS-EROS/espa-spectral-indices/pull/12.diff","patch_url":"https://github.com/USGS-EROS/espa-spectral-indices/pull/12.patch","issue_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/issues/12","number":12,"state":"open","locked":false,"title":"Peer Review Ready SI (script-wrapper)","user":{"login":"NGenetzky","id":12733783,"avatar_url":"https://avatars.githubusercontent.com/u/12733783?v=3","gravatar_id":"","url":"https://api.github.com/users/NGenetzky","html_url":"https://github.com/NGenetzky","followers_url":"https://api.github.com/users/NGenetzky/followers","following_url":"https://api.github.com/users/NGenetzky/following{/other_user}","gists_url":"https://api.github.com/users/NGenetzky/gists{/gist_id}","starred_url":"https://api.github.com/users/NGenetzky/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NGenetzky/subscriptions","organizations_url":"https://api.github.com/users/NGenetzky/orgs","repos_url":"https://api.github.com/users/NGenetzky/repos","events_url":"https://api.github.com/users/NGenetzky/events{/privacy}","received_events_url":"https://api.github.com/users/NGenetzky/received_events","type":"User","site_admin":false},"body":"Pull request generated to facilitate discussion about spectral_indices script wrapper.","created_at":"2015-06-25T18:19:49Z","updated_at":"2015-06-30T15:25:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"970fd59a6b3989a1e487df1af9ed81a199e2a908","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/12/commits","review_comments_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/12/comments","review_comment_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/comments{/number}","comments_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/issues/12/comments","statuses_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/statuses/c456026ea925c56c6dbd595a6c820b852e6d5a38","head":{"label":"NGenetzky:dev-wrapper-script","ref":"dev-wrapper-script","sha":"c456026ea925c56c6dbd595a6c820b852e6d5a38","user":{"login":"NGenetzky","id":12733783,"avatar_url":"https://avatars.githubusercontent.com/u/12733783?v=3","gravatar_id":"","url":"https://api.github.com/users/NGenetzky","html_url":"https://github.com/NGenetzky","followers_url":"https://api.github.com/users/NGenetzky/followers","following_url":"https://api.github.com/users/NGenetzky/following{/other_user}","gists_url":"https://api.github.com/users/NGenetzky/gists{/gist_id}","starred_url":"https://api.github.com/users/NGenetzky/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NGenetzky/subscriptions","organizations_url":"https://api.github.com/users/NGenetzky/orgs","repos_url":"https://api.github.com/users/NGenetzky/repos","events_url":"https://api.github.com/users/NGenetzky/events{/privacy}","received_events_url":"https://api.github.com/users/NGenetzky/received_events","type":"User","site_admin":false},"repo":{"id":36813247,"name":"espa-spectral-indices","full_name":"NGenetzky/espa-spectral-indices","owner":{"login":"NGenetzky","id":12733783,"avatar_url":"https://avatars.githubusercontent.com/u/12733783?v=3","gravatar_id":"","url":"https://api.github.com/users/NGenetzky","html_url":"https://github.com/NGenetzky","followers_url":"https://api.github.com/users/NGenetzky/followers","following_url":"https://api.github.com/users/NGenetzky/following{/other_user}","gists_url":"https://api.github.com/users/NGenetzky/gists{/gist_id}","starred_url":"https://api.github.com/users/NGenetzky/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NGenetzky/subscriptions","organizations_url":"https://api.github.com/users/NGenetzky/orgs","repos_url":"https://api.github.com/users/NGenetzky/repos","events_url":"https://api.github.com/users/NGenetzky/events{/privacy}","received_events_url":"https://api.github.com/users/NGenetzky/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/NGenetzky/espa-spectral-indices","description":"ESPA module for generating various spectral indices","fork":true,"url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices","forks_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/forks","keys_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/keys{/key_id}","collaborators_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/teams","hooks_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/hooks","issue_events_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/issues/events{/number}","events_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/events","assignees_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/assignees{/user}","branches_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/branches{/branch}","tags_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/tags","blobs_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/git/refs{/sha}","trees_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/git/trees{/sha}","statuses_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/statuses/{sha}","languages_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/languages","stargazers_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/stargazers","contributors_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/contributors","subscribers_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/subscribers","subscription_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/subscription","commits_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/commits{/sha}","git_commits_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/git/commits{/sha}","comments_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/comments{/number}","issue_comment_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/issues/comments{/number}","contents_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/contents/{+path}","compare_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/compare/{base}...{head}","merges_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/merges","archive_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/downloads","issues_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/issues{/number}","pulls_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/pulls{/number}","milestones_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/milestones{/number}","notifications_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/labels{/name}","releases_url":"https://api.github.com/repos/NGenetzky/espa-spectral-indices/releases{/id}","created_at":"2015-06-03T15:36:45Z","updated_at":"2015-06-03T15:36:45Z","pushed_at":"2015-06-25T18:14:17Z","git_url":"git://github.com/NGenetzky/espa-spectral-indices.git","ssh_url":"git@github.com:NGenetzky/espa-spectral-indices.git","clone_url":"https://github.com/NGenetzky/espa-spectral-indices.git","svn_url":"https://github.com/NGenetzky/espa-spectral-indices","homepage":"","size":260,"stargazers_count":0,"watchers_count":0,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"USGS-EROS:master","ref":"master","sha":"23bd66afaf2466cb6db1ee86ff97ba2010c486da","user":{"login":"USGS-EROS","id":12549192,"avatar_url":"https://avatars.githubusercontent.com/u/12549192?v=3","gravatar_id":"","url":"https://api.github.com/users/USGS-EROS","html_url":"https://github.com/USGS-EROS","followers_url":"https://api.github.com/users/USGS-EROS/followers","following_url":"https://api.github.com/users/USGS-EROS/following{/other_user}","gists_url":"https://api.github.com/users/USGS-EROS/gists{/gist_id}","starred_url":"https://api.github.com/users/USGS-EROS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/USGS-EROS/subscriptions","organizations_url":"https://api.github.com/users/USGS-EROS/orgs","repos_url":"https://api.github.com/users/USGS-EROS/repos","events_url":"https://api.github.com/users/USGS-EROS/events{/privacy}","received_events_url":"https://api.github.com/users/USGS-EROS/received_events","type":"Organization","site_admin":false},"repo":{"id":36030890,"name":"espa-spectral-indices","full_name":"USGS-EROS/espa-spectral-indices","owner":{"login":"USGS-EROS","id":12549192,"avatar_url":"https://avatars.githubusercontent.com/u/12549192?v=3","gravatar_id":"","url":"https://api.github.com/users/USGS-EROS","html_url":"https://github.com/USGS-EROS","followers_url":"https://api.github.com/users/USGS-EROS/followers","following_url":"https://api.github.com/users/USGS-EROS/following{/other_user}","gists_url":"https://api.github.com/users/USGS-EROS/gists{/gist_id}","starred_url":"https://api.github.com/users/USGS-EROS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/USGS-EROS/subscriptions","organizations_url":"https://api.github.com/users/USGS-EROS/orgs","repos_url":"https://api.github.com/users/USGS-EROS/repos","events_url":"https://api.github.com/users/USGS-EROS/events{/privacy}","received_events_url":"https://api.github.com/users/USGS-EROS/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/USGS-EROS/espa-spectral-indices","description":"ESPA module for generating various spectral indices","fork":false,"url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices","forks_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/forks","keys_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/keys{/key_id}","collaborators_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/teams","hooks_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/hooks","issue_events_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/issues/events{/number}","events_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/events","assignees_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/assignees{/user}","branches_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/branches{/branch}","tags_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/tags","blobs_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/git/refs{/sha}","trees_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/git/trees{/sha}","statuses_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/statuses/{sha}","languages_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/languages","stargazers_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/stargazers","contributors_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/contributors","subscribers_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/subscribers","subscription_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/subscription","commits_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/commits{/sha}","git_commits_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/git/commits{/sha}","comments_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/comments{/number}","issue_comment_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/issues/comments{/number}","contents_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/contents/{+path}","compare_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/compare/{base}...{head}","merges_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/merges","archive_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/downloads","issues_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/issues{/number}","pulls_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls{/number}","milestones_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/milestones{/number}","notifications_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/labels{/name}","releases_url":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/releases{/id}","created_at":"2015-05-21T18:41:23Z","updated_at":"2015-06-08T18:55:15Z","pushed_at":"2015-06-25T18:19:49Z","git_url":"git://github.com/USGS-EROS/espa-spectral-indices.git","ssh_url":"git@github.com:USGS-EROS/espa-spectral-indices.git","clone_url":"https://github.com/USGS-EROS/espa-spectral-indices.git","svn_url":"https://github.com/USGS-EROS/espa-spectral-indices","homepage":"","size":632,"stargazers_count":1,"watchers_count":1,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":5,"forks":1,"open_issues":5,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/12"},"html":{"href":"https://github.com/USGS-EROS/espa-spectral-indices/pull/12"},"issue":{"href":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/issues/12"},"comments":{"href":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/issues/12/comments"},"review_comments":{"href":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/12/comments"},"review_comment":{"href":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/pulls/12/commits"},"statuses":{"href":"https://api.github.com/repos/USGS-EROS/espa-spectral-indices/statuses/c456026ea925c56c6dbd595a6c820b852e6d5a38"}}}}