Merge branch 'master' of https://github.com/apache/incubator-sdap-nexus into SDAP-40
diff --git a/analysis/webservice/algorithms/DataInBoundsSearch.py b/analysis/webservice/algorithms/DataInBoundsSearch.py
index 2942649..80f0416 100644
--- a/analysis/webservice/algorithms/DataInBoundsSearch.py
+++ b/analysis/webservice/algorithms/DataInBoundsSearch.py
@@ -46,7 +46,7 @@
             "name": "Bounding box",
             "type": "comma-delimited float",
             "description": "Minimum (Western) Longitude, Minimum (Southern) Latitude, "
-                           "Maximum (Eastern) Longitude, Maximum (Northern) Latitude. Required"
+                           "Maximum (Eastern) Longitude, Maximum (Northern) Latitude. Required if 'metadataFilter' not provided"
         },
         "startTime": {
             "name": "Start Time",
@@ -57,6 +57,11 @@
             "name": "End Time",
             "type": "string",
             "description": "Ending time in format YYYY-MM-DDTHH:mm:ssZ or seconds since EPOCH. Required"
+        },
+        "metadataFilter": {
+            "name": "Metadata Filter",
+            "type": "string",
+            "description": "Filter in format key:value. Required if 'b' not provided"
         }
     }
     singleton = True
@@ -100,27 +105,36 @@
                     request.get_start_datetime().strftime(ISO_8601), request.get_end_datetime().strftime(ISO_8601)),
                 code=400)
 
+        bounding_polygon = metadata_filter = None
         try:
             bounding_polygon = request.get_bounding_polygon()
         except:
-            raise NexusProcessingException(
-                reason="'b' argument is required. Must be comma-delimited float formatted as Minimum (Western) Longitude, Minimum (Southern) Latitude, Maximum (Eastern) Longitude, Maximum (Northern) Latitude",
-                code=400)
+            metadata_filter = request.get_metadata_filter()
+            if 0 == len(metadata_filter):
+                raise NexusProcessingException(
+                    reason="'b' or 'metadataFilter' argument is required. 'b' must be comma-delimited float formatted "
+                           "as Minimum (Western) Longitude, Minimum (Southern) Latitude, Maximum (Eastern) Longitude, "
+                           "Maximum (Northern) Latitude. 'metadataFilter' must be in the form key:value",
+                    code=400)
 
-        return ds, parameter_s, start_time, end_time, bounding_polygon
+        return ds, parameter_s, start_time, end_time, bounding_polygon, metadata_filter
 
     def calc(self, computeOptions, **args):
-        ds, parameter, start_time, end_time, bounding_polygon = self.parse_arguments(computeOptions)
+        ds, parameter, start_time, end_time, bounding_polygon, metadata_filter = self.parse_arguments(computeOptions)
 
         includemeta = computeOptions.get_include_meta()
 
-        min_lat = bounding_polygon.bounds[1]
-        max_lat = bounding_polygon.bounds[3]
-        min_lon = bounding_polygon.bounds[0]
-        max_lon = bounding_polygon.bounds[2]
+        min_lat = max_lat = min_lon = max_lon = None
+        if bounding_polygon:
+            min_lat = bounding_polygon.bounds[1]
+            max_lat = bounding_polygon.bounds[3]
+            min_lon = bounding_polygon.bounds[0]
+            max_lon = bounding_polygon.bounds[2]
 
-        tiles = self._tile_service.get_tiles_bounded_by_box(min_lat, max_lat, min_lon, max_lon, ds, start_time,
-                                                            end_time)
+            tiles = self._tile_service.get_tiles_bounded_by_box(min_lat, max_lat, min_lon, max_lon, ds, start_time,
+                                                                end_time)
+        else:
+            tiles = self._tile_service.get_tiles_by_metadata(metadata_filter, ds, start_time, end_time)
 
         data = []
         for tile in tiles:
@@ -148,7 +162,7 @@
                     except (KeyError, IndexError):
                         pass
                 else:
-                    pass
+                    point['variable'] = nexus_point.data_val
 
                 data.append({
                     'latitude': nexus_point.latitude,
diff --git a/analysis/webservice/webmodel.py b/analysis/webservice/webmodel.py
index 3f01eac..e75ac01 100644
--- a/analysis/webservice/webmodel.py
+++ b/analysis/webservice/webmodel.py
@@ -51,6 +51,7 @@
     PLOT_SERIES = "plotSeries"
     PLOT_TYPE = "plotType"
     SPARK_CFG = "spark"
+    METADATA_FILTER = "metadataFilter"
 
 
 class StandardNexusErrors:
@@ -246,6 +247,9 @@
         else:
             return ds.split(",")
 
+    def get_metadata_filter(self):
+        return self.requestHandler.get_arguments(RequestParameters.METADATA_FILTER)
+
     def get_environment(self):
         env = self.get_argument(RequestParameters.ENVIRONMENT, None)
         if env is None and "Origin" in self.requestHandler.request.headers:
diff --git a/data-access/nexustiles/nexustiles.py b/data-access/nexustiles/nexustiles.py
index 6ecb033..1330c03 100644
--- a/data-access/nexustiles/nexustiles.py
+++ b/data-access/nexustiles/nexustiles.py
@@ -166,7 +166,7 @@
     @tile_data()
     def find_tiles_by_metadata(self, metadata, ds=None, start_time=0, end_time=-1, **kwargs):
         """
-        Return list of tiles that matches the specified metadata, start_time, end_time.
+        Return list of tiles whose metadata matches the specified metadata, start_time, end_time.
         :param metadata: List of metadata values to search for tiles e.g ["river_id_i:1", "granule_s:granule_name"]
         :param ds: The dataset name to search
         :param start_time: The start time to search for tiles
@@ -174,6 +174,20 @@
         :return: A list of tiles
         """
         tiles = self._metadatastore.find_all_tiles_by_metadata(metadata, ds, start_time, end_time, **kwargs)
+
+        return tiles
+
+    def get_tiles_by_metadata(self, metadata, ds=None, start_time=0, end_time=-1, **kwargs):
+        """
+        Return list of tiles that matches the specified metadata, start_time, end_time with tile data outside of time
+        range properly masked out.
+        :param metadata: List of metadata values to search for tiles e.g ["river_id_i:1", "granule_s:granule_name"]
+        :param ds: The dataset name to search
+        :param start_time: The start time to search for tiles
+        :param end_time: The end time to search for tiles
+        :return: A list of tiles
+        """
+        tiles = self.find_tiles_by_metadata(metadata, ds, start_time, end_time, **kwargs)
         tiles = self.mask_tiles_to_time_range(start_time, end_time, tiles)
 
         return tiles
diff --git a/data-access/tests/nexustiles_test.py b/data-access/tests/nexustiles_test.py
index 9b2901e..9f533a8 100644
--- a/data-access/tests/nexustiles_test.py
+++ b/data-access/tests/nexustiles_test.py
@@ -76,6 +76,11 @@
         for tile in tiles:
             print tile.get_summary()
 
+    def test_get_tiles_by_metadata(self):
+        tiles = self.tile_service.get_tiles_by_metadata(['id:60758e00-5721-3a6e-bf57-78448bb0aeeb'],
+                                                        "MUR-JPL-L4-GLOB-v4.1", 1514764800, 1514764800)
+        for tile in tiles:
+            print tile.get_summary()
 
 # from nexustiles.model.nexusmodel import get_approximate_value_for_lat_lon
 # import numpy as np