Repository: azavea/raster-vision

Issue:
predict --channel-order not working
The `channel-order` option of `rastervision predict` does not work when the command is run as a stand-alone script.

```
root@122d4f0150f4:/opt/data/mar5# rastervision predict potsdam.zip example.jpg out.tif --channel-order "0 1 2"
Usage: python -m rastervision predict [OPTIONS] PREDICT_PACKAGE IMAGE_URI
                                      OUTPUT_URI
Try "python -m rastervision predict --help" for help.

Error: Got unexpected extra arguments (1 2)
```

However, when it is run as a python module, it works. 
```
root@122d4f0150f4:/opt/data/mar5# python -m rastervision.cli.main predict potsdam.zip example.jpg out.tif --channel-order "0 1 2"
/usr/local/lib/python3.5/dist-packages/pluginbase.py:439: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
  fromlist, level)
2019-03-06 18:46:21.105826: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
/usr/local/lib/python3.5/dist-packages/rasterio/__init__.py:217: NotGeoreferencedWarning: Dataset has no geotransform set. The identity matrix may be returned.
  s = DatasetReader(path, driver=driver, **kwargs)
[0, 1, 2]
[0]
2019-03-06 18:46:21:rastervision.task.semantic_segmentation: INFO - Making predictions for scene
/usr/local/lib/python3.5/dist-packages/rasterio/__init__.py:226: NotGeoreferencedWarning: Dataset has no geotransform set. The identity matrix may be returned.
  **kwargs)
.........................
```


Discussion:

Resolving patch:
diff --git a/rastervision/cli/main.py b/rastervision/cli/main.py
--- a/rastervision/cli/main.py
+++ b/rastervision/cli/main.py
@@ -211,6 +211,37 @@ def ls(experiment_module, arg):
         click.echo('{}'.format(e.id))
 
 
+# https://stackoverflow.com/questions/48391777/nargs-equivalent-for-options-in-click
+class OptionEatAll(click.Option):
+    def __init__(self, *args, **kwargs):
+        self.save_other_options = kwargs.pop('save_other_options', True)
+        nargs = kwargs.pop('nargs', -1)
+        assert nargs == -1, 'nargs, if set, must be -1 not {}'.format(nargs)
+        super(OptionEatAll, self).__init__(*args, **kwargs)
+        self._previous_parser_process = None
+        self._eat_all_parser = None
+
+    def add_to_parser(self, parser, ctx):
+        def parser_process(value, state):
+            value = str(value)
+            while state.rargs:
+                value = '{} {}'.format(value, state.rargs.pop(0))
+            self._previous_parser_process(value, state)
+
+        retval = super(OptionEatAll, self).add_to_parser(parser, ctx)
+
+        for name in self.opts:
+            our_parser = parser._long_opt.get(name) or parser._short_opt.get(
+                name)
+            if our_parser:
+                self._eat_all_parser = our_parser
+                self._previous_parser_process = our_parser.process
+                our_parser.process = parser_process
+                break
+
+        return retval
+
+
 @main.command(
     'predict', short_help='Make predictions using a predict package.')
 @click.argument('predict_package')
@@ -225,7 +256,8 @@ def ls(experiment_module, arg):
           'that exist in the prediction package'))
 @click.option(
     '--channel-order',
-    help='String containing channel_order. Example: \"2 1 0\"')
+    cls=OptionEatAll,
+    help='List of indices comprising channel_order. Example: 2 1 0')
 @click.option(
     '--export-config',
     type=click.Path(exists=False),


Test patch: