Pulling in changes from dataplumber/nexus image-gen

Project: http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/commit/9646e7c5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/tree/9646e7c5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/diff/9646e7c5

Branch: refs/heads/master
Commit: 9646e7c5bdf55f6eb43fdca42cc7b923f1320207
Parents: 0bbf01c
Author: fgreg <[email protected]>
Authored: Wed Jan 10 16:31:49 2018 -0800
Committer: Frank Greguska <[email protected]>
Committed: Wed Jan 10 17:00:20 2018 -0800

----------------------------------------------------------------------
 analysis/.DS_Store                              |  Bin 6148 -> 6148 bytes
 analysis/setup.py                               |    5 +-
 analysis/webservice/GenerateImageMRF.py         |  311 ++
 analysis/webservice/WorkflowDriver.py           |   67 +
 .../webservice/algorithms/ColorBarHandler.py    |  140 +
 .../webservice/algorithms/MapFetchHandler.py    |  345 ++
 analysis/webservice/algorithms/colortables.py   |  535 ++
 .../algorithms/imaging/Roboto/LICENSE.txt       |  202 +
 .../algorithms/imaging/Roboto/Roboto-Bold.ttf   |  Bin 0 -> 162464 bytes
 .../webservice/algorithms/imaging/__init__.py   |    5 +
 analysis/webservice/config/web.ini              |    2 +-
 analysis/webservice/resources/transparent.png   |  Bin 0 -> 1397 bytes
 analysis/webservice/resources/wkt.txt           |   10 +
 data-access/nexustiles/dao/SolrProxy.pyx        |   62 +
 data-access/nexustiles/nexustiles.py            |   14 +-
 docker/nexus-imaging/Dockerfile                 |   10 +
 docker/nexus-imaging/docker-entrypoint.sh       |    8 +
 docker/nexus-imaging/setupall.sh                |   18 +
 nexus-ingest/nexus-messages/build.gradle        |    2 +-
 .../reports/license/dependency-license.html     |   62 -
 .../reports/license/dependency-license.xml      |    6 -
 .../reports/license/license-dependency.html     |   69 -
 .../reports/license/license-dependency.xml      |    5 -
 .../build/reports/project/dependencies.txt      |   43 -
 .../project/dependencies/css/base-style.css     |  179 -
 .../reports/project/dependencies/css/style.css  |   84 -
 .../reports/project/dependencies/css/tree.css   |  102 -
 .../reports/project/dependencies/images/d.gif   |  Bin 2944 -> 0 bytes
 .../reports/project/dependencies/images/d.png   |  Bin 7635 -> 0 bytes
 .../project/dependencies/images/throbber.gif    |  Bin 1849 -> 0 bytes
 .../reports/project/dependencies/index.html     |   44 -
 .../project/dependencies/js/jquery.jstree.js    | 4564 ------------------
 .../dependencies/js/jquery.min-1.11.0.js        |    4 -
 .../reports/project/dependencies/js/script.js   |  202 -
 .../reports/project/dependencies/root.html      |   40 -
 .../build/reports/project/dependencies/root.js  |    1 -
 .../build/reports/project/properties.txt        |  128 -
 .../build/reports/project/tasks.txt             |   68 -
 38 files changed, 1732 insertions(+), 5605 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/.DS_Store
----------------------------------------------------------------------
diff --git a/analysis/.DS_Store b/analysis/.DS_Store
index ec4f52c..9dcef1c 100644
Binary files a/analysis/.DS_Store and b/analysis/.DS_Store differ

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/setup.py
----------------------------------------------------------------------
diff --git a/analysis/setup.py b/analysis/setup.py
index 93449e1..dfed266 100644
--- a/analysis/setup.py
+++ b/analysis/setup.py
@@ -34,7 +34,10 @@ setuptools.setup(
         'utm',
         'shapely',
         'mock',
-        'backports.functools-lru-cache==1.3'
+        'backports.functools-lru-cache==1.3',
+        'netcdf4',
+        'boto3',
+        'pyproj==1.9.5.1'
     ],
 
     classifiers=[

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/GenerateImageMRF.py
----------------------------------------------------------------------
diff --git a/analysis/webservice/GenerateImageMRF.py 
b/analysis/webservice/GenerateImageMRF.py
new file mode 100644
index 0000000..7d2060a
--- /dev/null
+++ b/analysis/webservice/GenerateImageMRF.py
@@ -0,0 +1,311 @@
+"""
+Copyright (c) 2017 Jet Propulsion Laboratory,
+California Institute of Technology.  All rights reserved
+"""
+import os
+import errno
+from shutil import copyfile
+from subprocess import call
+
+tmpdir = '/tmp/tmp/'
+gdal_dir = '/usr/local/anaconda2/envs/nexus/bin/'
+gdal_translate = 'gdal_translate'
+gdaladdo = 'gdaladdo'
+gdalwarp = 'gdalwarp'
+
+COMPRESSION = 'PNG'
+OUTWIDTH = 2560
+OUTHEIGHT = 1280
+OUTWIDTHPOLAR = 8192
+OUTHEIGHTPOLAR = 8192
+
+GEO_TILEMATRIXSET = 'EPSG4326_16km'
+GEO_PROJECTION = 'EPSG:4326'
+
+ANTARCTIC_TILEMATRIXSET = 'EPSG3031_1km'
+ANTARCTIC_PROJECTION = 'EPSG:3031'
+
+ARCTIC_TILEMATRIXSET = 'EPSG3413_1km'
+ARCTIC_PROJECTION = 'EPSG:3413'
+
+def create_geo_mrf_header(shortname):
+    header = """<MRF_META>
+  <Raster>
+    <Size x="${_OUTWIDTH}" y="${_OUTHEIGHT}" c="3" />
+    <Compression>${_COMPRESSION}</Compression>
+    <DataValues NoData="0 0 0 " />
+    <Quality>80</Quality>
+    <PageSize x="512" y="512" c="3" />
+  </Raster>
+  <Rsets model="uniform" scale="2" />
+  <GeoTags>
+    <BoundingBox minx="-180.00000000" miny="-90.00000000" maxx="180.00000000" 
maxy=" 90.00000000" />
+    <Projection>GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 
84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]</Projection>
+  </GeoTags>
+</MRF_META>"""
+
+    header = header.replace('${_OUTWIDTH}', str(OUTWIDTH))
+    header = header.replace('${_OUTHEIGHT}', str(OUTHEIGHT))
+    header = header.replace('${_COMPRESSION}', COMPRESSION)
+
+    path = shortname + '/MRF-GEO/'
+    create_path(path)
+    filename = path + shortname + '-geo.mrf'
+    write_to_file(filename, header)
+
+def create_geo_xml_config(shortname, prefix):
+    config = """<?xml version="1.0" encoding="UTF-8"?>
+<LayerConfiguration>
+ <Identifier>${shortname}</Identifier>
+ <Title>${shortname}</Title>
+ <FileNamePrefix>${prefix}_</FileNamePrefix>
+ <TiledGroupName>${prefix} tileset</TiledGroupName>
+ 
<HeaderFileName>/etc/onearth/config/headers/${shortname}-geo.mrf</HeaderFileName>
+ <Compression>${_COMPRESSION}</Compression>
+ <TileMatrixSet>${_GEO_TILEMATRIXSET}</TileMatrixSet>
+ <EmptyTileSize offset="0">0</EmptyTileSize>
+ <Projection>${_GEO_PROJECTION}</Projection>
+ 
<EnvironmentConfig>/etc/onearth/config/conf/environment_geographic.xml</EnvironmentConfig>
+ <ArchiveLocation static="false" year="true" 
root="geographic">${shortname}</ArchiveLocation>
+ <ColorMap>sample.xml</ColorMap>
+ <Time>DETECT/P1M</Time>
+</LayerConfiguration>"""
+
+    config = config.replace('${shortname}', shortname)
+    config = config.replace('${prefix}', prefix)
+    config = config.replace('${_COMPRESSION}', COMPRESSION)
+    config = config.replace('${_GEO_TILEMATRIXSET}', GEO_TILEMATRIXSET)
+    config = config.replace('${_GEO_PROJECTION}', GEO_PROJECTION)
+
+    path = shortname + '/MRF-GEO/'
+    create_path(path)
+    filename = path + shortname + '-geo.xml'
+    write_to_file(filename, config)
+
+def create_arctic_mrf_header(shortname):
+    header = """<MRF_META>
+  <Raster>
+  <Size x="${_OUTWIDTHPOLAR}" y="${_OUTHEIGHTPOLAR}" c="3" />
+  <Compression>${_COMPRESSION}</Compression>
+    <DataValues NoData="0 0 0" />
+    <Quality>80</Quality>
+    <PageSize x="512" y="512" c="3" />
+  </Raster>
+  <Rsets model="uniform" />
+  <GeoTags>
+    <BoundingBox minx="-4194300.00000000" miny="-4194200.00000000" 
maxx="4194200.00000000" maxy="4194300.00000000" />
+    <Projection>PROJCS["WGS 84 / NSIDC Sea Ice Polar Stereographic 
North",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 
84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Polar_Stereographic"],PARAMETER["latitude_of_origin",70],PARAMETER["central_meridian",-45],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],AUTHORITY["EPSG","3413"]]</Projection>
+  </GeoTags>
+</MRF_META>"""
+
+    header = header.replace('${_OUTWIDTHPOLAR}', str(OUTWIDTHPOLAR))
+    header = header.replace('${_OUTHEIGHTPOLAR}', str(OUTHEIGHTPOLAR))
+    header = header.replace('${_COMPRESSION}', COMPRESSION)
+
+    path = shortname + '/MRF-ARCTIC/'
+    create_path(path)
+    filename = path + shortname + '-arctic.mrf'
+    write_to_file(filename, header)
+
+def create_arctic_xml_config(shortname, prefix):
+    config = """<?xml version="1.0" encoding="UTF-8"?>
+<LayerConfiguration>
+ <Identifier>${shortname}</Identifier>
+ <Title>${shortname}</Title>
+ <FileNamePrefix>${prefix}_</FileNamePrefix>
+ <TiledGroupName>${prefix} tileset</TiledGroupName>
+ 
<HeaderFileName>/etc/onearth/config/headers/${shortname}-arctic.mrf</HeaderFileName>
+ <Compression>${_COMPRESSION}</Compression>
+ <TileMatrixSet>${_ARCTIC_TILEMATRIXSET}</TileMatrixSet>
+ <EmptyTileSize offset="0">0</EmptyTileSize>
+ <Projection>${_ARCTIC_PROJECTION}</Projection>
+ 
<EnvironmentConfig>/etc/onearth/config/conf/environment_arctic.xml</EnvironmentConfig>
+ <ArchiveLocation static="false" year="true" 
root="arctic">${shortname}</ArchiveLocation>
+ <ColorMap>sample.xml</ColorMap>
+ <Time>DETECT/P1M</Time>
+</LayerConfiguration>"""
+
+    config = config.replace('${shortname}', shortname)
+    config = config.replace('${prefix}', prefix)
+    config = config.replace('${_COMPRESSION}', COMPRESSION)
+    config = config.replace('${_ARCTIC_TILEMATRIXSET}', ARCTIC_TILEMATRIXSET)
+    config = config.replace('${_ARCTIC_PROJECTION}', ARCTIC_PROJECTION)
+
+    path = shortname + '/MRF-ARCTIC/'
+    create_path(path)
+    filename = path + shortname + '-arctic.xml'
+    write_to_file(filename, config)
+
+def create_antarctic_mrf_header(shortname):
+    header = """<MRF_META>
+  <Raster>
+  <Size x="${_OUTWIDTHPOLAR}" y="${_OUTHEIGHTPOLAR}" c="3" />
+  <Compression>${_COMPRESSION}</Compression>
+    <DataValues NoData="0 0 0" />
+    <Quality>80</Quality>
+    <PageSize x="512" y="512" c="3" />
+  </Raster>
+  <Rsets model="uniform" />
+  <GeoTags>
+    <BoundingBox minx="-4194300.00000000" miny="-4194200.00000000" 
maxx="4194200.00000000" maxy="4194300.00000000" />
+    <Projection>PROJCS["WGS 84 / Antarctic Polar Stereographic",GEOGCS["WGS 
84",DATUM["WGS_1984",SPHEROID["WGS 
84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Polar_Stereographic"],PARAMETER["latitude_of_origin",-71],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","3031"]]</Projection>
+  </GeoTags>
+</MRF_META>"""
+
+    header = header.replace('${_OUTWIDTHPOLAR}', str(OUTWIDTHPOLAR))
+    header = header.replace('${_OUTHEIGHTPOLAR}', str(OUTHEIGHTPOLAR))
+    header = header.replace('${_COMPRESSION}', COMPRESSION)
+
+    path = shortname + '/MRF-ANTARCTIC/'
+    create_path(path)
+    filename = path + shortname + '-antarctic.mrf'
+    write_to_file(filename, header)
+
+def create_antarctic_xml_config(shortname, prefix):
+    config = """<?xml version="1.0" encoding="UTF-8"?>
+<LayerConfiguration>
+ <Identifier>${shortname}</Identifier>
+ <Title>${shortname}</Title>
+ <FileNamePrefix>${prefix}_</FileNamePrefix>
+ <TiledGroupName>${prefix} tileset</TiledGroupName>
+ 
<HeaderFileName>/etc/onearth/config/headers/${shortname}-antarctic.mrf</HeaderFileName>
+ <Compression>${_COMPRESSION}</Compression>
+ <TileMatrixSet>${_ANTARCTIC_TILEMATRIXSET}</TileMatrixSet>
+ <EmptyTileSize offset="0">0</EmptyTileSize>
+ <Projection>${_ANTARCTIC_PROJECTION}</Projection>
+ 
<EnvironmentConfig>/etc/onearth/config/conf/environment_antarctic.xml</EnvironmentConfig>
+ <ArchiveLocation static="false" year="true" 
root="antarctic">${shortname}</ArchiveLocation>
+ <ColorMap>sample.xml</ColorMap>
+ <Time>DETECT/P1M</Time>
+</LayerConfiguration>"""
+
+    config = config.replace('${shortname}', shortname)
+    config = config.replace('${prefix}', prefix)
+    config = config.replace('${_COMPRESSION}', COMPRESSION)
+    config = config.replace('${_ANTARCTIC_TILEMATRIXSET}', 
ANTARCTIC_TILEMATRIXSET)
+    config = config.replace('${_ANTARCTIC_PROJECTION}', ANTARCTIC_PROJECTION)
+
+    path = shortname + '/MRF-ANTARCTIC/'
+    create_path(path)
+    filename = path + shortname + '-antarctic.xml'
+    write_to_file(filename, config)
+
+def geo_to_mrf(intiff, prefix, year, dt, shortname):
+    path = shortname + '/MRF-GEO/' + str(year)
+    create_path(path)
+
+    print('Creating Geographic MRF...')
+    src = os.getcwd() + '/resources/transparent.png'
+    dst = path + '/' + prefix + '_' + str(dt) + "_.ppg"
+    copyfile(src, dst)
+
+    output = path + '/' + prefix + '_' + str(dt) + '_.mrf'
+
+    retcode = call([gdal_dir + gdal_translate, "-of", "MRF", "-co", 
"COMPRESS=" + COMPRESSION, "-co", "BLOCKSIZE=512",
+                    "-outsize", str(OUTWIDTH), str(OUTHEIGHT), intiff, output])
+
+    if retcode == 0:
+        print("Creating Geographic Tiles...")
+        retcode = call([gdal_dir + gdaladdo, output, "-r", "nearest", "2", 
"4", "8", "16"])
+
+    return retcode
+
+def geo_to_arctic_mrf(intiff, prefix, year, dt, shortname):
+    path = shortname + '/MRF-ARCTIC/' + str(year)
+    create_path(path)
+
+    geo_wkt_file = os.getcwd() + '/resources/wkt.txt'
+    subsetnorthtiff = tmpdir + prefix + '-epsg3413_stage_0.tif'
+    outputnorthtiff = tmpdir + prefix + '-epsg3413_stage_1.tif'
+    output = path + '/' + prefix + '_' + str(dt) + "_.mrf"
+    tgt_proj4_north = '+proj=stere +lat_0=90 +lat_ts=52.6 +lon_0=-45 +k=1 
+x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs'
+
+    print('Creating Arctic Subset...')
+    retcode = call([gdal_dir + gdal_translate, "-projwin", "-180", "90", 
"180", "52.6", "-projwin_srs",
+                    geo_wkt_file, intiff, subsetnorthtiff])
+
+    if retcode == 0:
+        print('Reprojecting to Arctic...')
+        retcode = call([gdal_dir + gdalwarp, "-s_srs", geo_wkt_file, "-t_srs", 
tgt_proj4_north, "-wo",
+                    "SOURCE_EXTRA=125", "-dstnodata", "0", "-of", "GTiff", 
"-overwrite", subsetnorthtiff, outputnorthtiff])
+
+    if retcode == 0:
+        print("Creating Arctic MRF...")
+        src = os.getcwd() + '/resources/transparent.png'
+        dst = path + '/' + prefix + '_' + str(dt) + "_.ppg"
+        copyfile(src, dst)
+
+        retcode = call([gdal_dir + gdal_translate, "-of", "MRF", "-co", 
"COMPRESS=" + COMPRESSION, "-co", "BLOCKSIZE=512",
+             "-outsize", str(OUTWIDTHPOLAR), str(OUTHEIGHTPOLAR), 
outputnorthtiff, output])
+
+    if retcode == 0:
+        print("Creating Arctic Tiles...")
+        retcode = call([gdal_dir + gdaladdo, output, "-r", "nearest", "2", 
"4", "8", "16"])
+
+    return retcode
+
+def geo_to_antarctic_mrf(intiff, prefix, year, dt, shortname, interp):
+    if (interp == "") or (interp is None):
+        interp = "near"
+
+    path = shortname + '/MRF-ANTARCTIC/' + str(year)
+    create_path(path)
+
+    geo_wkt_file = os.getcwd() + '/resources/wkt.txt'
+    subsetsouthtiff = tmpdir + prefix + '-epsg3031_stage_0.tif'
+    outputsouthtiff = tmpdir + prefix + '-epsg3031_stage_1.tif'
+    output = path + '/' + prefix + '_' + str(dt) + "_.mrf"
+    tgt_proj4_south = '+proj=stere +lat_0=-90 +lat_ts=-52.6 +lon_0=0 +k=1 
+x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs'
+
+    print('Creating Antarctic Subset...')
+    retcode = call([gdal_dir + gdal_translate, "-projwin", "-180", "-52.6", 
"180", "-90", "-projwin_srs",
+                    geo_wkt_file, intiff, subsetsouthtiff])
+
+    if retcode == 0:
+        print("Reprojecting to Antarctic...")
+        retcode = call([gdal_dir + gdalwarp, "-s_srs", geo_wkt_file, "-t_srs", 
tgt_proj4_south, "-wo",
+                        "SOURCE_EXTRA=125", "-r", interp, "-dstnodata", "0", 
"-of", "GTiff", "-overwrite", subsetsouthtiff,
+                        outputsouthtiff])
+
+    if retcode == 0:
+        print("Creating Antarctic MRF...")
+        src = os.getcwd() + '/resources/transparent.png'
+        dst = path + '/' + prefix + '_' + str(dt) + "_.ppg"
+        copyfile(src, dst)
+
+        retcode = call([gdal_dir + gdal_translate, "-of", "MRF", "-co", 
"COMPRESS=" + COMPRESSION, "-co", "BLOCKSIZE=512",
+                        "-r", interp, "-outsize", str(OUTWIDTHPOLAR), 
str(OUTHEIGHTPOLAR), outputsouthtiff, output])
+
+    if retcode == 0:
+        print("Creating Antarctic Tiles...")
+        retcode = call([gdal_dir + gdaladdo, output, "-r", interp, "2", "4", 
"8", "16"])
+
+    return retcode
+
+def write_to_file(filename, data):
+    try:
+        f = open(filename, 'w')
+        f.write(data)
+        f.close()
+    except Exception as e:
+        print("Error creating " + filename + ":\n" + str(e))
+
+def create_path(path):
+    try:
+        os.makedirs(path)
+    except OSError as e:
+        if e.errno != errno.EEXIST:
+            raise
+
+def create_all(shortname, prefix):
+    create_geo_mrf_header(shortname)
+    create_geo_xml_config(shortname, prefix)
+    create_arctic_mrf_header(shortname)
+    create_arctic_xml_config(shortname, prefix)
+    create_antarctic_mrf_header(shortname)
+    create_antarctic_xml_config(shortname, prefix)
+
+def png_to_tif(input, output):
+    retcode = call([gdal_dir + gdal_translate, "-of", "GTiff", "-ot", "byte", 
"-a_ullr", "-180", "90", "180", "-90",
+                    input, output])
+    return retcode
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/WorkflowDriver.py
----------------------------------------------------------------------
diff --git a/analysis/webservice/WorkflowDriver.py 
b/analysis/webservice/WorkflowDriver.py
new file mode 100644
index 0000000..bb42927
--- /dev/null
+++ b/analysis/webservice/WorkflowDriver.py
@@ -0,0 +1,67 @@
+import argparse
+from algorithms.MapFetchHandler import MapFetchHandler
+
+def start(args):
+    dataset_shortname = args.ds
+    granule_name = args.g
+    prefix = args.p
+    ct = args.ct
+    _min = float(args.min)
+    _max = float(args.max)
+    width = int(args.w)
+    height = int(args.h)
+    interp = args.i
+    time_interval = args.t
+
+    map = MapFetchHandler()
+    map.generate(dataset_shortname, granule_name, prefix, ct, interp, _min, 
_max, width, height, time_interval)
+
+def parse_args():
+    parser = argparse.ArgumentParser(description='Automate NEXUS ingestion 
workflow',
+                                     
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+    parser.add_argument('--ds', '--datasetShortName',
+                        help='The shortname of the dataset',
+                        required=True)
+
+    parser.add_argument('--g', '--granuleName',
+                        help='The filename of the granule',
+                        required=True)
+
+    parser.add_argument('--p', '--prefix',
+                        help='The desired filename prefix',
+                        required=False)
+
+    parser.add_argument('--ct', '--colorTable',
+                        help='Identifier of a supported color table. DEFAULT: 
smap',
+                        required=False)
+
+    parser.add_argument('--i', '--interpolation',
+                        help="Interpolation filter to use when rescaling image 
data. Can be 'near', 'lanczos', 'bilinear', or 'bicubic'",
+                        required=False)
+
+    parser.add_argument('--min', '--minimum',
+                        help='Minimum value to use when computing color 
scales',
+                        required=False)
+
+    parser.add_argument('--max', '--maximum',
+                        help='Maximum value to use when computing color 
scales',
+                        required=False)
+
+    parser.add_argument('--w', '--width',
+                        help='Output image width (max: 8192). DEFAULT: 1024',
+                        required=False)
+
+    parser.add_argument('--h', '--height',
+                        help='Output image height (max: 8192). DEFAULT: 512',
+                        required=False)
+
+    parser.add_argument('--t', '--timeInterval',
+                        help="The time interval for imaging. Can be 'day' or 
'month'. DEFAULT: month",
+                        required=False)
+
+    return parser.parse_args()
+
+if __name__ == "__main__":
+    the_args = parse_args()
+    start(the_args)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/algorithms/ColorBarHandler.py
----------------------------------------------------------------------
diff --git a/analysis/webservice/algorithms/ColorBarHandler.py 
b/analysis/webservice/algorithms/ColorBarHandler.py
new file mode 100644
index 0000000..9509f58
--- /dev/null
+++ b/analysis/webservice/algorithms/ColorBarHandler.py
@@ -0,0 +1,140 @@
+import json
+import math
+import time
+
+import numpy as np
+from webservice.NexusHandler import NexusHandler as BaseHandler
+from webservice.NexusHandler import nexus_handler
+
+import colortables
+
+
+@nexus_handler
+class ColorBarHandler(BaseHandler):
+    name = "ColorBarHandler"
+    path = "/colorbar"
+    description = "Creates a CMC colorbar spec for a dataset"
+    params = {
+        "ds": {
+            "name": "Dataset",
+            "type": "string",
+            "description": "A supported dataset shortname identifier"
+        },
+        "t": {
+            "name": "Time",
+            "type": "int",
+            "description": "Data observation date if not specifying a min/max"
+        },
+        "min": {
+            "name": "Minimum Value",
+            "type": "float",
+            "description": "Minimum value to use when computing color scales. 
Will be computed if not specified"
+        },
+        "max": {
+            "name": "Maximum Value",
+            "type": "float",
+            "description": "Maximum value to use when computing color scales. 
Will be computed if not specified"
+        },
+        "ct": {
+            "name": "Color Table",
+            "type": "string",
+            "description": "Identifier of a supported color table"
+        }
+    }
+    singleton = True
+
+    def __init__(self):
+        BaseHandler.__init__(self)
+
+
+    def __get_dataset_minmax(self, ds, dataTime):
+        dataTimeStart = dataTime - 86400.0  # 
computeOptions.get_datetime_arg("t", None)
+        dataTimeEnd = dataTime
+
+        daysinrange = self._tile_service.find_days_in_range_asc(-90.0, 90.0, 
-180.0, 180.0, ds, dataTimeStart,
+                                                                dataTimeEnd)
+
+        ds1_nexus_tiles = 
self._tile_service.get_tiles_bounded_by_box_at_time(-90.0, 90.0, -180.0, 180.0,
+                                                                              
ds,
+                                                                              
daysinrange[0])
+
+        data_min = 100000
+        data_max = -1000000
+
+        for tile in ds1_nexus_tiles:
+            data_min = np.min((data_min, np.ma.min(tile.data)))
+            data_max = np.max((data_max, np.ma.max(tile.data)))
+
+        return data_min, data_max
+
+    def __produce_color_list(self, colorbarDef, numColors, min, max, units):
+        colors = []
+        labels = []
+        values = []
+        for i in range(0, numColors):
+            index = float(i) / float(numColors)
+            index = index * (len(colorbarDef) - 1)
+            prev = int(math.floor(index))
+            next = int(math.ceil(index))
+            f = index - prev
+            prevColor = colorbarDef[prev]
+            nextColor = colorbarDef[next]
+
+            color = [0, 0, 0, 255]
+            color[0] = nextColor[0] * f + (prevColor[0] * (1.0 - f))
+            color[1] = nextColor[1] * f + (prevColor[1] * (1.0 - f))
+            color[2] = nextColor[2] * f + (prevColor[2] * (1.0 - f))
+
+            colors.append('%02x%02x%02xFF' % (color[0], color[1], color[2]))
+
+            value = (float(i) / float(numColors - 1)) * (max - min) + min
+            valueHigh = (float(i + 1) / float(numColors - 1)) * (max - min) + 
min
+            labels.append("%3.2f %s" % (value, units))
+            values.append((value, valueHigh))
+
+        return colors, labels, values
+
+    def calc(self, computeOptions, **args):
+        ds = computeOptions.get_argument("ds", None)
+
+        dataTime = computeOptions.get_datetime_arg("t", None)
+        if dataTime is None:
+            raise Exception("Missing 't' option for time")
+
+        dataTime = time.mktime(dataTime.timetuple())
+
+        color_table_name = computeOptions.get_argument("ct", "smap")
+        color_table = colortables.__dict__[color_table_name]
+
+        min = computeOptions.get_float_arg("min", np.nan)
+        max = computeOptions.get_float_arg("max", np.nan)
+
+        num_colors = computeOptions.get_int_arg("num", 255)
+
+        units = computeOptions.get_argument("units", "")
+
+        if np.isnan(min) or np.isnan(max):
+            data_min, data_max = self.__get_dataset_minmax(ds, dataTime)
+
+            if np.isnan(min):
+                min = data_min
+            if np.isnan(max):
+                max = data_max
+
+        colors, labels, values = self.__produce_color_list(color_table, 
num_colors, min, max, units)
+
+        obj = {
+            "scale": {
+                "colors": colors,
+                "labels": labels,
+                "values": values
+            },
+            "id": ds
+        }
+
+        class SimpleResult(object):
+            def toJson(self):
+                return json.dumps(obj, indent=4)
+
+        return SimpleResult()
+

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/algorithms/MapFetchHandler.py
----------------------------------------------------------------------
diff --git a/analysis/webservice/algorithms/MapFetchHandler.py 
b/analysis/webservice/algorithms/MapFetchHandler.py
new file mode 100644
index 0000000..8cfcbfd
--- /dev/null
+++ b/analysis/webservice/algorithms/MapFetchHandler.py
@@ -0,0 +1,345 @@
+import io
+import json
+import math
+import time
+import errno
+import calendar
+from subprocess import call
+import webservice.GenerateImageMRF as MRF
+import numpy as np
+from PIL import Image
+from PIL import ImageDraw
+from PIL import ImageFont
+from dateutil.relativedelta import *
+import os
+import boto3
+import colortables
+
+from webservice.NexusHandler import NexusHandler as BaseHandler
+from webservice.NexusHandler import nexus_handler
+
+@nexus_handler
+class MapFetchHandler(BaseHandler):
+    name = "MapFetchHandler"
+    path = "/map"
+    description = "Creates a map image"
+    params = {
+        "ds": {
+            "name": "Dataset",
+            "type": "string",
+            "description": "A supported dataset shortname identifier"
+        },
+        "t": {
+            "name": "Time",
+            "type": "int",
+            "description": "Data observation date"
+        },
+        "output": {
+            "name": "Output Format",
+            "type": "string",
+            "description": "Output format. Use 'PNG' for this endpoint"
+        },
+        "min": {
+            "name": "Minimum Value",
+            "type": "float",
+            "description": "Minimum value to use when computing color scales"
+        },
+        "max": {
+            "name": "Maximum Value",
+            "type": "float",
+            "description": "Maximum value to use when computing color scales"
+        },
+        "ct": {
+            "name": "Color Table",
+            "type": "string",
+            "description": "Identifier of a supported color table"
+        },
+        "interp": {
+            "name": "Interpolation filter",
+            "type": "string",
+            "description": "Interpolation filter to use when rescaling image 
data. Can be 'nearest', 'lanczos', 'bilinear', or 'bicubic'."
+        },
+        "width": {
+            "name": "Width",
+            "type": "int",
+            "description": "Output image width (max: 8192)"
+        },
+        "height": {
+            "name": "Height",
+            "type": "int",
+            "description": "Output image height (max: 8192)"
+        }
+    }
+    singleton = True
+
+    NO_DATA_IMAGE = None
+
+    def __init__(self):
+        BaseHandler.__init__(self)
+
+    @staticmethod
+    def __tile_to_image(img_data, tile, min, max, table, x_res, y_res):
+        width = len(tile.longitudes)
+        height = len(tile.latitudes)
+
+        d = np.ma.filled(tile.data[0], np.nan)
+
+        for y in range(0, height):
+            for x in range(0, width):
+                value = d[y][x]
+                if not np.isnan(value) and value != 0:
+
+                    lat = tile.latitudes[y]
+                    lon = tile.longitudes[x]
+
+                    pixel_y = int(math.floor(180.0 - ((lat + 90.0) * y_res)))
+                    pixel_x = int(math.floor((lon + 180.0) * x_res))
+
+                    value = np.max((min, value))
+                    value = np.min((max, value))
+                    value255 = int(round((value - min) / (max - min) * 255.0))
+                    rgba = MapFetchHandler.__get_color(value255, table)
+                    img_data.putpixel((pixel_x, pixel_y), (rgba[0], rgba[1], 
rgba[2], 255))
+
+    @staticmethod
+    def __translate_interpolation(interp):
+        if interp.upper() == "LANCZOS":
+            return Image.LANCZOS
+        elif interp.upper() == "BILINEAR":
+            return Image.BILINEAR
+        elif interp.upper() == "BICUBIC":
+            return Image.BICUBIC
+        else:
+            return Image.NEAREST
+
+    @staticmethod
+    def __make_tile_img(tile):
+        width = len(tile.longitudes)
+        height = len(tile.latitudes)
+        img = Image.new("RGBA", (width, height), (0, 0, 0, 0))
+        return img
+
+    @staticmethod
+    def __get_xy_resolution(tile):
+        x_res = abs(tile.longitudes[0] - tile.longitudes[1])
+        y_res = abs(tile.latitudes[0] - tile.latitudes[1])
+        return x_res, y_res
+
+    @staticmethod
+    def __create_global(nexus_tiles, stats, width=2048, height=1024, 
force_min=np.nan, force_max=np.nan, table=colortables.grayscale, 
interpolation="nearest"):
+
+        data_min = stats["minValue"] if np.isnan(force_min) else force_min
+        data_max = stats["maxValue"] if np.isnan(force_max) else force_max
+
+        x_res, y_res = MapFetchHandler.__get_xy_resolution(nexus_tiles[0])
+        x_res = 1
+        y_res = 1
+
+        canvas_width = int(360.0 / x_res)
+        canvas_height = int(180.0 / y_res)
+        img = Image.new("RGBA", (canvas_width, canvas_height), (0, 0, 0, 0))
+        img_data = img.getdata()
+
+        for tile in nexus_tiles:
+            MapFetchHandler.__tile_to_image(img_data, tile, data_min, 
data_max, table, x_res, y_res)
+
+        final_image = img.resize((width, height), 
MapFetchHandler.__translate_interpolation(interpolation))
+
+        return final_image
+
+    @staticmethod
+    def __get_color(value, table):
+        index = (float(value) / float(255)) * (len(table) - 1)
+        prev = int(math.floor(index))
+        next = int(math.ceil(index))
+
+        f = index - prev
+        prevColor = table[prev]
+        nextColor = table[next]
+
+        r = int(round(nextColor[0] * f + (prevColor[0] * (1.0 - f))))
+        g = int(round(nextColor[1] * f + (prevColor[1] * (1.0 - f))))
+        b = int(round(nextColor[2] * f + (prevColor[2] * (1.0 - f))))
+
+        return (r, g, b, 255)
+
+    @staticmethod
+    def __colorize(img, table):
+        data = img.getdata()
+
+        for x in range(0, img.width):
+            for y in range(0, img.height):
+                if data[x + (y * img.width)][3] == 255:
+                    value = data[x + (y * img.width)][0]
+                    rgba = MapFetchHandler.__get_color(value, table)
+                    data.putpixel((x, y), (rgba[0], rgba[1], rgba[2], 255))
+
+    @staticmethod
+    def __create_no_data(width, height):
+
+        if MapFetchHandler.NO_DATA_IMAGE is None:
+            img = Image.new("RGBA", (width, height), (0, 0, 0, 0))
+            draw = ImageDraw.Draw(img)
+
+            fnt = 
ImageFont.truetype('webservice/algorithms/imaging/Roboto/Roboto-Bold.ttf', 40)
+
+            for x in range(0, width, 500):
+                for y in range(0, height, 500):
+                    draw.text((x, y), "NO DATA", (180, 180, 180), font=fnt)
+            MapFetchHandler.NO_DATA_IMAGE = img
+
+        return MapFetchHandler.NO_DATA_IMAGE
+
+    def calc(self, computeOptions, **args):
+        ds = computeOptions.get_argument("ds", None)
+
+        dataTimeEnd = computeOptions.get_datetime_arg("t", None)
+        if dataTimeEnd is None:
+            raise Exception("Missing 't' option for time")
+
+        dataTimeEnd = time.mktime(dataTimeEnd.timetuple())
+        dataTimeStart = dataTimeEnd - 86400.0
+
+        color_table_name = computeOptions.get_argument("ct", "smap")
+        color_table = colortables.__dict__[color_table_name]
+
+        interpolation = computeOptions.get_argument("interp", "nearest")
+
+        force_min = computeOptions.get_float_arg("min", np.nan)
+        force_max = computeOptions.get_float_arg("max", np.nan)
+
+        width = np.min([8192, computeOptions.get_int_arg("width", 1024)])
+        height = np.min([8192, computeOptions.get_int_arg("height", 512)])
+
+        stats = self._tile_service.get_dataset_overall_stats(ds)
+
+        daysinrange = self._tile_service.find_days_in_range_asc(-90.0, 90.0, 
-180.0, 180.0, ds, dataTimeStart,
+                                                                dataTimeEnd)
+
+        if len(daysinrange) > 0:
+            ds1_nexus_tiles = 
self._tile_service.get_tiles_bounded_by_box_at_time(-90.0, 90.0, -180.0, 180.0,
+                                                                               
   ds,
+                                                                               
   daysinrange[0])
+
+            img = self.__create_global(ds1_nexus_tiles, stats, width, height, 
force_min, force_max, color_table,
+                                       interpolation)
+        else:
+            img = self.__create_no_data(width, height)
+
+        imgByteArr = io.BytesIO()
+        img.save(imgByteArr, format='PNG')
+        imgByteArr = imgByteArr.getvalue()
+
+        class SimpleResult(object):
+            def toJson(self):
+                return json.dumps({"status": "Please specify output type as 
PNG."})
+
+            def toImage(self):
+                return imgByteArr
+
+        return SimpleResult()
+
+    def generate(self, ds, granule_name, prefix, ct, interp, _min, _max, 
width, height, time_interval):
+
+        color_table_name = ct
+        if ct is None:
+            color_table_name = "smap"
+        color_table = colortables.__dict__[color_table_name]
+
+        interpolation = interp
+        if interp is None:
+            interpolation = "near"
+
+        force_min = _min
+        force_max = _max
+        if _min is None:
+            force_min = np.nan
+        if _max is None:
+            force_max = np.nan
+
+        temp_width = width
+        temp_height = height
+        if width is None:
+            temp_width = 1024
+        if height is None:
+            temp_height = 512
+
+        width = np.min([8192, temp_width])
+        height = np.min([8192, temp_height])
+
+        if time_interval == 'day':
+            time_interval = relativedelta(days=+1)
+        else:
+            time_interval = relativedelta(months=+1)
+
+        stats = self._tile_service.get_dataset_overall_stats(ds)
+
+        start_time, end_time = 
self._tile_service.get_min_max_time_by_granule(ds, granule_name)
+
+        MRF.create_all(ds, prefix)
+
+        # Make a temporary directory for storing the .png and .tif files
+        temp_dir = '/tmp/tmp/'
+        try:
+            os.makedirs(temp_dir)
+        except OSError as e:
+            if e.errno != errno.EEXIST:
+                raise
+
+        while start_time <= end_time:
+            one_interval_later = start_time + time_interval
+            temp_end_time = one_interval_later - relativedelta(minutes=+1)  # 
prevent getting tiles for 2 intervals
+            ds1_nexus_tiles = self._tile_service.find_tiles_in_box(-90.0, 
90.0, -180.0, 180.0, ds, start_time, temp_end_time)
+
+            if ds1_nexus_tiles is not None:
+                img = self.__create_global(ds1_nexus_tiles, stats, width, 
height, force_min, force_max, color_table,
+                                           interpolation)
+            else:
+                img = self.__create_no_data(width, height)
+
+            imgByteArr = io.BytesIO()
+            img.save(imgByteArr, format='PNG')
+            imgByteArr = imgByteArr.getvalue()
+
+            arr = str(start_time).split()  # arr[0] should contain a string of 
the date in format 'YYYY-MM-DD'
+            fulldate = arr[0]
+            temp_png = temp_dir + fulldate + '.png'
+            temp_tif = temp_dir + fulldate + '.tif'
+            open(temp_png, 'wb').write(imgByteArr)
+
+            arr = fulldate.split('-')
+            year = arr[0]
+            month = calendar.month_abbr[int(arr[1])]
+            dt = month + '_' + year
+
+            retcode = MRF.png_to_tif(temp_png, temp_tif)
+            if retcode == 0:
+                retcode = MRF.geo_to_mrf(temp_tif, prefix, year, dt, ds)
+            if retcode == 0:
+                retcode = MRF.geo_to_arctic_mrf(temp_tif, prefix, year, dt, ds)
+            if retcode == 0:
+                retcode = MRF.geo_to_antarctic_mrf(temp_tif, prefix, year, dt, 
ds, interp)
+            if retcode != 0:
+                break
+
+            start_time = one_interval_later
+
+        tar_file = ds + '.tar.gz'
+        retcode = call(["tar", "-zcvf", tar_file, ds])
+        if retcode == 0:
+            # Delete temporary files/folders if tar.gz is created successfully
+            call(["rm", "-rf", ds])
+            call(["rm", "-rf", temp_dir])
+        else:
+            print("Error creating tar.gz")
+
+        # Upload the tar.gz file to the sea-level-mrf S3 bucket
+        s3bucket = 'sea-level-mrf'
+        s3client = boto3.client('s3')
+        try:
+            with open(tar_file, 'rb') as data:
+                s3client.upload_fileobj(data, s3bucket, tar_file)
+        except Exception as e:
+            print("Unable to add tar.gz to S3: \n" + str(e))
+
+        call(["rm", "-rf", tar_file])  # Delete the tar.gz from local storage
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/algorithms/colortables.py
----------------------------------------------------------------------
diff --git a/analysis/webservice/algorithms/colortables.py 
b/analysis/webservice/algorithms/colortables.py
new file mode 100644
index 0000000..ff24929
--- /dev/null
+++ b/analysis/webservice/algorithms/colortables.py
@@ -0,0 +1,535 @@
+grayscale = [
+    [0, 0, 0],
+    [255, 255, 255]
+]
+
+oceanography = [
+            [2, 3, 206],
+            [143, 226, 255],
+            [255, 255, 255],
+            [255, 241, 27],
+            [253, 0, 0]
+        ]
+
+rainbow = [
+            [125, 0, 255],
+            [0, 0, 255],
+            [0, 255, 0],
+            [255, 255, 0],
+            [255, 125, 0],
+            [255, 0, 0]
+        ]
+
+anomaly = [
+            [129, 31, 240],
+            [124, 30, 240],
+            [119, 29, 241],
+            [114, 27, 242],
+            [108, 26, 242],
+            [103, 24, 243],
+            [97, 23, 244],
+            [91, 21, 245],
+            [86, 20, 245],
+            [80, 18, 246],
+            [73, 17, 247],
+            [67, 15, 247],
+            [61, 14, 248],
+            [55, 12, 249],
+            [48, 11, 250],
+            [42, 9, 250],
+            [35, 7, 251],
+            [28, 6, 252],
+            [21, 4, 252],
+            [14, 3, 253],
+            [7, 1, 254],
+            [0, 0, 255],
+            [3, 6, 253],
+            [6, 12, 252],
+            [10, 18, 250],
+            [13, 24, 249],
+            [17, 30, 247],
+            [20, 36, 246],
+            [23, 42, 245],
+            [26, 47, 243],
+            [29, 52, 242],
+            [33, 57, 240],
+            [36, 62, 239],
+            [39, 67, 237],
+            [42, 72, 236],
+            [45, 76, 235],
+            [48, 81, 233],
+            [51, 85, 232],
+            [53, 89, 230],
+            [56, 93, 229],
+            [59, 97, 227],
+            [62, 101, 226],
+            [65, 105, 225],
+            [63, 106, 226],
+            [62, 107, 227],
+            [60, 108, 229],
+            [59, 110, 230],
+            [57, 111, 232],
+            [56, 113, 233],
+            [54, 115, 235],
+            [52, 116, 236],
+            [51, 118, 237],
+            [49, 120, 239],
+            [47, 122, 240],
+            [46, 123, 242],
+            [44, 125, 243],
+            [42, 127, 245],
+            [41, 130, 246],
+            [39, 132, 247],
+            [37, 134, 249],
+            [35, 136, 250],
+            [33, 139, 252],
+            [31, 141, 253],
+            [29, 144, 255],
+            [28, 145, 255],
+            [27, 147, 255],
+            [25, 149, 255],
+            [24, 151, 255],
+            [22, 153, 255],
+            [21, 155, 255],
+            [19, 158, 255],
+            [18, 160, 255],
+            [17, 162, 255],
+            [15, 164, 255],
+            [14, 166, 255],
+            [12, 169, 255],
+            [11, 171, 255],
+            [9, 173, 255],
+            [8, 176, 255],
+            [7, 178, 255],
+            [5, 180, 255],
+            [4, 183, 255],
+            [2, 185, 255],
+            [1, 188, 255],
+            [0, 191, 255],
+            [7, 191, 254],
+            [14, 191, 253],
+            [21, 191, 252],
+            [28, 191, 251],
+            [35, 192, 251],
+            [41, 192, 250],
+            [48, 193, 249],
+            [55, 193, 248],
+            [62, 194, 248],
+            [69, 194, 247],
+            [75, 195, 246],
+            [82, 196, 245],
+            [88, 196, 245],
+            [95, 197, 244],
+            [101, 198, 243],
+            [108, 199, 242],
+            [114, 200, 242],
+            [121, 201, 241],
+            [127, 202, 240],
+            [133, 203, 239],
+            [140, 205, 239],
+            [143, 206, 239],
+            [146, 208, 239],
+            [149, 209, 240],
+            [152, 211, 240],
+            [155, 212, 241],
+            [158, 214, 241],
+            [161, 215, 242],
+            [164, 217, 242],
+            [168, 218, 243],
+            [171, 220, 243],
+            [174, 221, 244],
+            [177, 222, 244],
+            [180, 224, 245],
+            [184, 225, 245],
+            [187, 227, 246],
+            [190, 228, 246],
+            [193, 230, 247],
+            [197, 231, 247],
+            [200, 233, 248],
+            [203, 234, 248],
+            [207, 236, 249],
+            [255, 255, 200],
+            [255, 254, 192],
+            [255, 254, 185],
+            [255, 253, 178],
+            [255, 252, 171],
+            [255, 251, 164],
+            [255, 250, 157],
+            [255, 249, 149],
+            [255, 248, 142],
+            [255, 247, 135],
+            [255, 246, 128],
+            [255, 244, 121],
+            [255, 243, 114],
+            [255, 241, 107],
+            [255, 239, 99],
+            [255, 238, 92],
+            [255, 236, 85],
+            [255, 234, 78],
+            [255, 231, 71],
+            [255, 229, 64],
+            [255, 227, 57],
+            [255, 225, 49],
+            [255, 222, 47],
+            [255, 220, 45],
+            [255, 218, 42],
+            [255, 215, 40],
+            [255, 213, 38],
+            [255, 211, 35],
+            [255, 208, 33],
+            [255, 206, 30],
+            [255, 203, 28],
+            [255, 201, 26],
+            [255, 198, 23],
+            [255, 195, 21],
+            [255, 193, 19],
+            [255, 190, 16],
+            [255, 187, 14],
+            [255, 184, 11],
+            [255, 181, 9],
+            [255, 179, 7],
+            [255, 176, 4],
+            [255, 173, 2],
+            [255, 170, 0],
+            [255, 167, 0],
+            [255, 164, 0],
+            [255, 161, 0],
+            [255, 158, 0],
+            [255, 155, 0],
+            [255, 152, 0],
+            [255, 149, 0],
+            [255, 147, 0],
+            [255, 144, 0],
+            [255, 141, 0],
+            [255, 138, 0],
+            [255, 135, 0],
+            [255, 132, 0],
+            [255, 129, 0],
+            [255, 127, 0],
+            [255, 124, 0],
+            [255, 121, 0],
+            [255, 118, 0],
+            [255, 115, 0],
+            [255, 112, 0],
+            [255, 110, 0],
+            [255, 104, 0],
+            [255, 99, 0],
+            [255, 94, 0],
+            [255, 89, 0],
+            [255, 83, 0],
+            [255, 78, 0],
+            [255, 73, 0],
+            [255, 68, 0],
+            [255, 62, 0],
+            [255, 57, 0],
+            [255, 52, 0],
+            [255, 47, 0],
+            [255, 41, 0],
+            [255, 36, 0],
+            [255, 31, 0],
+            [255, 26, 0],
+            [255, 20, 0],
+            [255, 15, 0],
+            [255, 10, 0],
+            [255, 5, 0],
+            [255, 0, 0],
+            [252, 0, 0],
+            [249, 0, 0],
+            [247, 0, 0],
+            [244, 0, 0],
+            [241, 0, 0],
+            [239, 0, 0],
+            [236, 0, 0],
+            [234, 0, 0],
+            [231, 0, 0],
+            [228, 0, 0],
+            [226, 0, 0],
+            [223, 0, 0],
+            [220, 0, 0],
+            [218, 0, 0],
+            [215, 0, 0],
+            [213, 0, 0],
+            [210, 0, 0],
+            [207, 0, 0],
+            [205, 0, 0],
+            [202, 0, 0],
+            [200, 0, 0],
+            [202, 6, 6],
+            [205, 13, 13],
+            [207, 20, 20],
+            [210, 27, 27],
+            [213, 35, 35],
+            [215, 43, 43],
+            [218, 50, 50],
+            [220, 58, 58],
+            [223, 66, 66],
+            [226, 75, 75],
+            [228, 83, 83],
+            [231, 92, 92],
+            [234, 101, 101],
+            [236, 110, 110],
+            [239, 119, 119],
+            [241, 128, 128],
+            [244, 138, 138],
+            [247, 147, 147],
+            [249, 157, 157],
+            [252, 167, 167],
+            [255, 178, 178]
+        ]
+
+hottemp = [
+            [255, 255, 255],
+            [255, 255, 0],
+            [255, 0, 0],
+            [0, 0, 0],
+            [0, 0, 0]
+        ]
+
+anomaly2 = [
+            [129, 31, 240],
+            [124, 30, 240],
+            [119, 29, 241],
+            [114, 27, 242],
+            [108, 26, 242],
+            [103, 24, 243],
+            [97, 23, 244],
+            [91, 21, 245],
+            [86, 20, 245],
+            [80, 18, 246],
+            [73, 17, 247],
+            [67, 15, 247],
+            [61, 14, 248],
+            [55, 12, 249],
+            [48, 11, 250],
+            [42, 9, 250],
+            [35, 7, 251],
+            [28, 6, 252],
+            [21, 4, 252],
+            [14, 3, 253],
+            [7, 1, 254],
+            [0, 0, 255],
+            [3, 6, 253],
+            [6, 12, 252],
+            [10, 18, 250],
+            [13, 24, 249],
+            [17, 30, 247],
+            [20, 36, 246],
+            [23, 42, 245],
+            [26, 47, 243],
+            [29, 52, 242],
+            [33, 57, 240],
+            [36, 62, 239],
+            [39, 67, 237],
+            [42, 72, 236],
+            [45, 76, 235],
+            [48, 81, 233],
+            [51, 85, 232],
+            [53, 89, 230],
+            [56, 93, 229],
+            [59, 97, 227],
+            [62, 101, 226],
+            [65, 105, 225],
+            [63, 106, 226],
+            [62, 107, 227],
+            [60, 108, 229],
+            [59, 110, 230],
+            [57, 111, 232],
+            [56, 113, 233],
+            [54, 115, 235],
+            [52, 116, 236],
+            [51, 118, 237],
+            [49, 120, 239],
+            [47, 122, 240],
+            [46, 123, 242],
+            [44, 125, 243],
+            [42, 127, 245],
+            [41, 130, 246],
+            [39, 132, 247],
+            [37, 134, 249],
+            [35, 136, 250],
+            [33, 139, 252],
+            [31, 141, 253],
+            [29, 144, 255],
+            [28, 145, 255],
+            [27, 147, 255],
+            [25, 149, 255],
+            [24, 151, 255],
+            [22, 153, 255],
+            [21, 155, 255],
+            [19, 158, 255],
+            [18, 160, 255],
+            [17, 162, 255],
+            [15, 164, 255],
+            [14, 166, 255],
+            [12, 169, 255],
+            [11, 171, 255],
+            [9, 173, 255],
+            [8, 176, 255],
+            [7, 178, 255],
+            [5, 180, 255],
+            [4, 183, 255],
+            [2, 185, 255],
+            [1, 188, 255],
+            [0, 191, 255],
+            [7, 191, 254],
+            [14, 191, 253],
+            [21, 191, 252],
+            [28, 191, 251],
+            [35, 192, 251],
+            [41, 192, 250],
+            [48, 193, 249],
+            [55, 193, 248],
+            [62, 194, 248],
+            [69, 194, 247],
+            [75, 195, 246],
+            [82, 196, 245],
+            [88, 196, 245],
+            [95, 197, 244],
+            [101, 198, 243],
+            [108, 199, 242],
+            [114, 200, 242],
+            [121, 201, 241],
+            [127, 202, 240],
+            [133, 203, 239],
+            [140, 205, 239],
+            [143, 206, 239],
+            [146, 208, 239],
+            [149, 209, 240],
+            [152, 211, 240],
+            [155, 212, 241],
+            [158, 214, 241],
+            [161, 215, 242],
+            [164, 217, 242],
+            [168, 218, 243],
+            [171, 220, 243],
+            [174, 221, 244],
+            [177, 222, 244],
+            [180, 224, 245],
+            [184, 225, 245],
+            [187, 227, 246],
+            [190, 228, 246],
+            [193, 230, 247],
+            [197, 231, 247],
+            [200, 233, 248],
+            [203, 234, 248],
+            [207, 236, 249],
+            [255, 255, 255],
+            [255, 255, 255],
+            [255, 255, 200],
+            [255, 254, 192],
+            [255, 254, 185],
+            [255, 253, 178],
+            [255, 252, 171],
+            [255, 251, 164],
+            [255, 250, 157],
+            [255, 249, 149],
+            [255, 248, 142],
+            [255, 247, 135],
+            [255, 246, 128],
+            [255, 244, 121],
+            [255, 243, 114],
+            [255, 241, 107],
+            [255, 239, 99],
+            [255, 238, 92],
+            [255, 236, 85],
+            [255, 234, 78],
+            [255, 231, 71],
+            [255, 229, 64],
+            [255, 227, 57],
+            [255, 225, 49],
+            [255, 222, 47],
+            [255, 220, 45],
+            [255, 218, 42],
+            [255, 215, 40],
+            [255, 213, 38],
+            [255, 211, 35],
+            [255, 208, 33],
+            [255, 206, 30],
+            [255, 203, 28],
+            [255, 201, 26],
+            [255, 198, 23],
+            [255, 195, 21],
+            [255, 193, 19],
+            [255, 190, 16],
+            [255, 187, 14],
+            [255, 184, 11],
+            [255, 181, 9],
+            [255, 179, 7],
+            [255, 176, 4],
+            [255, 173, 2],
+            [255, 170, 0],
+            [255, 167, 0],
+            [255, 164, 0],
+            [255, 161, 0],
+            [255, 158, 0],
+            [255, 155, 0],
+            [255, 152, 0],
+            [255, 149, 0],
+            [255, 147, 0],
+            [255, 144, 0],
+            [255, 141, 0],
+            [255, 138, 0],
+            [255, 135, 0],
+            [255, 132, 0],
+            [255, 129, 0],
+            [255, 127, 0],
+            [255, 124, 0],
+            [255, 121, 0],
+            [255, 118, 0],
+            [255, 115, 0],
+            [255, 112, 0],
+            [255, 110, 0],
+            [255, 104, 0],
+            [255, 99, 0],
+            [255, 94, 0],
+            [255, 89, 0],
+            [255, 83, 0],
+            [255, 78, 0],
+            [255, 73, 0],
+            [255, 68, 0],
+            [255, 62, 0],
+            [255, 57, 0],
+            [255, 52, 0],
+            [255, 47, 0],
+            [255, 41, 0],
+            [255, 36, 0],
+            [255, 31, 0],
+            [255, 26, 0],
+            [255, 20, 0],
+            [255, 15, 0],
+            [255, 10, 0],
+            [255, 5, 0],
+            [255, 0, 0],
+            [252, 0, 0],
+            [249, 0, 0],
+            [247, 0, 0],
+            [244, 0, 0],
+            [241, 0, 0],
+            [239, 0, 0],
+            [236, 0, 0],
+            [234, 0, 0],
+            [231, 0, 0],
+            [228, 0, 0],
+            [226, 0, 0],
+            [223, 0, 0],
+            [220, 0, 0],
+            [218, 0, 0],
+            [215, 0, 0],
+            [213, 0, 0],
+            [210, 0, 0],
+            [207, 0, 0],
+            [205, 0, 0],
+            [202, 0, 0],
+            [200, 0, 0],
+            [200, 6, 6]
+        ]
+
+
+smap = [
+            [125,    0,  255],
+            [0,    0,  255],
+            [0 , 255,    0],
+            [255,  255,    0],
+            [255,  125,    0],
+            [255,    0,    0]
+        ]

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/algorithms/imaging/Roboto/LICENSE.txt
----------------------------------------------------------------------
diff --git a/analysis/webservice/algorithms/imaging/Roboto/LICENSE.txt 
b/analysis/webservice/algorithms/imaging/Roboto/LICENSE.txt
new file mode 100755
index 0000000..75b5248
--- /dev/null
+++ b/analysis/webservice/algorithms/imaging/Roboto/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/algorithms/imaging/Roboto/Roboto-Bold.ttf
----------------------------------------------------------------------
diff --git a/analysis/webservice/algorithms/imaging/Roboto/Roboto-Bold.ttf 
b/analysis/webservice/algorithms/imaging/Roboto/Roboto-Bold.ttf
new file mode 100755
index 0000000..a355c27
Binary files /dev/null and 
b/analysis/webservice/algorithms/imaging/Roboto/Roboto-Bold.ttf differ

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/algorithms/imaging/__init__.py
----------------------------------------------------------------------
diff --git a/analysis/webservice/algorithms/imaging/__init__.py 
b/analysis/webservice/algorithms/imaging/__init__.py
new file mode 100644
index 0000000..8905e65
--- /dev/null
+++ b/analysis/webservice/algorithms/imaging/__init__.py
@@ -0,0 +1,5 @@
+"""
+Copyright (c) 2017 Jet Propulsion Laboratory,
+California Institute of Technology.  All rights reserved
+"""
+

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/config/web.ini
----------------------------------------------------------------------
diff --git a/analysis/webservice/config/web.ini 
b/analysis/webservice/config/web.ini
index ed9bbb7..77f77ce 100644
--- a/analysis/webservice/config/web.ini
+++ b/analysis/webservice/config/web.ini
@@ -8,4 +8,4 @@ static_enabled=true
 static_dir=static
 
 [modules]
-module_dirs=webservice.algorithms,webservice.algorithms_spark,webservice.algorithms.doms
\ No newline at end of file
+module_dirs=webservice.algorithms,webservice.algorithms_spark
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/resources/transparent.png
----------------------------------------------------------------------
diff --git a/analysis/webservice/resources/transparent.png 
b/analysis/webservice/resources/transparent.png
new file mode 100755
index 0000000..5f13d29
Binary files /dev/null and b/analysis/webservice/resources/transparent.png 
differ

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/analysis/webservice/resources/wkt.txt
----------------------------------------------------------------------
diff --git a/analysis/webservice/resources/wkt.txt 
b/analysis/webservice/resources/wkt.txt
new file mode 100644
index 0000000..c8b3f1d
--- /dev/null
+++ b/analysis/webservice/resources/wkt.txt
@@ -0,0 +1,10 @@
+GEOGCS["WGS 84",
+    DATUM["WGS_1984",
+        SPHEROID["WGS 84",6378137,298.257223563,
+            AUTHORITY["EPSG","7030"]],
+        AUTHORITY["EPSG","6326"]],
+    PRIMEM["Greenwich",0,
+        AUTHORITY["EPSG","8901"]],
+    UNIT["degree",0.01745329251994328,
+        AUTHORITY["EPSG","9122"]],
+    AUTHORITY["EPSG","4326"]]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/data-access/nexustiles/dao/SolrProxy.pyx
----------------------------------------------------------------------
diff --git a/data-access/nexustiles/dao/SolrProxy.pyx 
b/data-access/nexustiles/dao/SolrProxy.pyx
index 0d775e6..6ed9ffa 100644
--- a/data-access/nexustiles/dao/SolrProxy.pyx
+++ b/data-access/nexustiles/dao/SolrProxy.pyx
@@ -103,6 +103,36 @@ class SolrProxy(object):
 
         return results[0]['tile_max_time_dt']
 
+    def find_min_max_date_from_granule(self, ds, granule_name, **kwargs):
+        search = 'dataset_s:%s' % ds
+
+        kwargs['rows'] = 1
+        kwargs['fl'] = 'tile_min_time_dt'
+        kwargs['sort'] = ['tile_min_time_dt asc']
+        additionalparams = {
+            'fq': [
+                "granule_s:%s" % granule_name
+            ]
+        }
+
+        self._merge_kwargs(additionalparams, **kwargs)
+        results, start, found = self.do_query(*(search, None, None, False, 
None), **additionalparams)
+        start_time = results[0]['tile_min_time_dt']
+
+        kwargs['fl'] = 'tile_max_time_dt'
+        kwargs['sort'] = ['tile_max_time_dt desc']
+        additionalparams = {
+            'fq': [
+                "granule_s:%s" % granule_name
+            ]
+        }
+
+        self._merge_kwargs(additionalparams, **kwargs)
+        results, start, found = self.do_query(*(search, None, None, False, 
None), **additionalparams)
+        end_time = results[0]['tile_max_time_dt']
+
+        return start_time, end_time
+
     def get_data_series_list(self):
 
         datasets = self.get_data_series_list_simple()
@@ -133,6 +163,38 @@ class SolrProxy(object):
         l = sorted(l, key=lambda entry: entry["title"])
         return l
 
+    def get_data_series_stats(self, ds):
+        search = "dataset_s:%s" % ds
+        params = {
+            "facet": "true",
+            "facet.field": ["dataset_s", "tile_max_time_dt"],
+            "facet.limit": "-1",
+            "facet.mincount": "1",
+            "facet.pivot": "{!stats=piv1}dataset_s",
+            "stats": "on",
+            "stats.field": ["{!tag=piv1 min=true max=true 
sum=false}tile_max_time_dt","{!tag=piv1 min=true max=false 
sum=false}tile_min_val_d","{!tag=piv1 min=false max=true 
sum=false}tile_max_val_d"]
+        }
+
+        response = self.do_query_raw(*(search, None, None, False, None), 
**params)
+
+        stats = {}
+
+        for g in response.facet_counts["facet_pivot"]["dataset_s"]:
+            if g["value"] == ds:
+                stats["start"] = 
time.mktime(g["stats"]["stats_fields"]["tile_max_time_dt"]["min"].timetuple()) 
* 1000
+                stats["end"] = 
time.mktime(g["stats"]["stats_fields"]["tile_max_time_dt"]["max"].timetuple()) 
* 1000
+                stats["minValue"] = 
g["stats"]["stats_fields"]["tile_min_val_d"]["min"]
+                stats["maxValue"] = 
g["stats"]["stats_fields"]["tile_max_val_d"]["max"]
+
+
+        stats["availableDates"] = []
+        for dt in response.facet_counts["facet_fields"]["tile_max_time_dt"]:
+            stats["availableDates"].append(time.mktime(datetime.strptime(dt, 
"%Y-%m-%dT%H:%M:%SZ").timetuple()) * 1000)
+
+        stats["availableDates"] = sorted(stats["availableDates"])
+
+        return stats
+
     def find_tile_by_polygon_and_most_recent_day_of_year(self, 
bounding_polygon, ds, day_of_year):
 
         search = 'dataset_s:%s' % ds

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/data-access/nexustiles/nexustiles.py
----------------------------------------------------------------------
diff --git a/data-access/nexustiles/nexustiles.py 
b/data-access/nexustiles/nexustiles.py
index e97ecf6..39a1849 100644
--- a/data-access/nexustiles/nexustiles.py
+++ b/data-access/nexustiles/nexustiles.py
@@ -122,7 +122,11 @@ class NexusTileService(object):
     @tile_data()
     def find_tiles_in_box(self, min_lat, max_lat, min_lon, max_lon, ds=None, 
start_time=0, end_time=-1, **kwargs):
         # Find tiles that fall in the given box in the Solr index
-        return self._solr.find_all_tiles_in_box_sorttimeasc(min_lat, max_lat, 
min_lon, max_lon, ds, start_time,
+        if type(start_time) is datetime:
+            start_time = (start_time - EPOCH).total_seconds()
+        if type(end_time) is datetime:
+            end_time = (end_time - EPOCH).total_seconds()
+        return self._metadatastore.find_all_tiles_in_box_sorttimeasc(min_lat, 
max_lat, min_lon, max_lon, ds, start_time,
                                                             end_time, **kwargs)
 
     @tile_data()
@@ -171,6 +175,14 @@ class NexusTileService(object):
 
         return tiles
 
+    def get_min_max_time_by_granule(self, ds, granule_name):
+        start_time, end_time = self._solr.find_min_max_date_from_granule(ds, 
granule_name)
+
+        return start_time, end_time
+
+    def get_dataset_overall_stats(self, ds):
+        return self._solr.get_data_series_stats(ds)
+
     def get_tiles_bounded_by_box_at_time(self, min_lat, max_lat, min_lon, 
max_lon, dataset, time, **kwargs):
         tiles = self.find_all_tiles_in_box_at_time(min_lat, max_lat, min_lon, 
max_lon, dataset, time, **kwargs)
         tiles = self.mask_tiles_to_bbox(min_lat, max_lat, min_lon, max_lon, 
tiles)

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/docker/nexus-imaging/Dockerfile
----------------------------------------------------------------------
diff --git a/docker/nexus-imaging/Dockerfile b/docker/nexus-imaging/Dockerfile
new file mode 100644
index 0000000..d0bc916
--- /dev/null
+++ b/docker/nexus-imaging/Dockerfile
@@ -0,0 +1,10 @@
+FROM nexusjpl/spark-mesos-base
+
+RUN yum -y install unzip aws-cli
+
+RUN cd /tmp
+RUN git clone -b image-gen https://github.com/dataplumber/nexus.git
+COPY docker-entrypoint.sh /tmp/docker-entrypoint.sh
+
+WORKDIR /tmp
+ENTRYPOINT ["/tmp/docker-entrypoint.sh"]

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/docker/nexus-imaging/docker-entrypoint.sh
----------------------------------------------------------------------
diff --git a/docker/nexus-imaging/docker-entrypoint.sh 
b/docker/nexus-imaging/docker-entrypoint.sh
new file mode 100755
index 0000000..565b9d8
--- /dev/null
+++ b/docker/nexus-imaging/docker-entrypoint.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+#Activate Python environment
+source activate nexus
+
+cd /tmp/nexus/analysis/webservice
+python WorkflowDriver.py --ds ${DATASET_NAME} --g ${GRANULE_NAME} --p 
${PREFIX} --ct ${COLOR_TABLE} --min ${MIN} --max ${MAX} --h ${HEIGHT} --w 
${WIDTH} --t ${TIME_INTERVAL} --i ${INTERP}
+

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/docker/nexus-imaging/setupall.sh
----------------------------------------------------------------------
diff --git a/docker/nexus-imaging/setupall.sh b/docker/nexus-imaging/setupall.sh
new file mode 100644
index 0000000..7846fcf
--- /dev/null
+++ b/docker/nexus-imaging/setupall.sh
@@ -0,0 +1,18 @@
+cd /tmp/image-gen-nexus/nexus-ingest/nexus-messages
+./gradlew clean build install
+cd /build/python/nexusproto
+
+conda create --name nexus python
+source activate nexus
+
+conda install numpy
+python setup.py install
+
+cd /tmp/image-gen-nexus/data-access
+pip install cython
+python setup.py install
+
+cd /tmp/image-gen-nexus/analysis
+conda install numpy matplotlib mpld3 scipy netCDF4 basemap gdal pyproj=1.9.5.1 
libnetcdf=4.3.3.1
+pip install pillow
+python setup.py install
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/nexus-ingest/nexus-messages/build.gradle
----------------------------------------------------------------------
diff --git a/nexus-ingest/nexus-messages/build.gradle 
b/nexus-ingest/nexus-messages/build.gradle
index 290a338..5776362 100644
--- a/nexus-ingest/nexus-messages/build.gradle
+++ b/nexus-ingest/nexus-messages/build.gradle
@@ -75,7 +75,7 @@ if( project.hasProperty('artifactory_contextUrl') ) {
 
 }else {
     repositories {
-        mavenCentral()
+        maven { url 'http://repo1.maven.org/maven2' }
     }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/nexus-ingest/nexus-messages/build/reports/license/dependency-license.html
----------------------------------------------------------------------
diff --git 
a/nexus-ingest/nexus-messages/build/reports/license/dependency-license.html 
b/nexus-ingest/nexus-messages/build/reports/license/dependency-license.html
deleted file mode 100644
index 9c6d93a..0000000
--- a/nexus-ingest/nexus-messages/build/reports/license/dependency-license.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<html>
-  <head>
-    <title>HTML License report</title>
-  </head>
-  <style>table {
-                  width: 85%;
-                  border-collapse: collapse;
-                  text-align: center;
-                }
-                .dependencies {
-                  text-align: left;
-                }
-                tr {
-                  border: 1px solid black;
-                }
-                td {
-                  border: 1px solid black;
-                  font-weight: bold;
-                  color: #2E2E2E
-                }
-                th {
-                  border: 1px solid black;
-                }
-                h3 {
-                  text-align:center;
-                  margin:3px
-                }
-                .license {
-                    width:70%
-                }
-
-                .licenseName {
-                    width:15%
-                }
-                </style>
-  <body>
-    <table align='center'>
-      <tr>
-        <th>
-          <h3>Dependency</h3>
-        </th>
-        <th>
-          <h3>Jar</h3>
-        </th>
-        <th>
-          <h3>License name</h3>
-        </th>
-        <th>
-          <h3>License text URL</h3>
-        </th>
-      </tr>
-      <tr>
-        <td class='dependencies'>com.google.protobuf:protobuf-java:2.6.1</td>
-        <td class='licenseName'>protobuf-java-2.6.1.jar</td>
-        <td class='licenseName'>New BSD license</td>
-        <td class='license'>
-          <a href='http://www.opensource.org/licenses/bsd-license.php'>Show 
license agreement</a>
-        </td>
-      </tr>
-    </table>
-  </body>
-</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/nexus-ingest/nexus-messages/build/reports/license/dependency-license.xml
----------------------------------------------------------------------
diff --git 
a/nexus-ingest/nexus-messages/build/reports/license/dependency-license.xml 
b/nexus-ingest/nexus-messages/build/reports/license/dependency-license.xml
deleted file mode 100644
index 8ed0e97..0000000
--- a/nexus-ingest/nexus-messages/build/reports/license/dependency-license.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<dependencies>
-  <dependency name='com.google.protobuf:protobuf-java:2.6.1'>
-    <file>protobuf-java-2.6.1.jar</file>
-    <license name='New BSD license' 
url='http://www.opensource.org/licenses/bsd-license.php' />
-  </dependency>
-</dependencies>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/nexus-ingest/nexus-messages/build/reports/license/license-dependency.html
----------------------------------------------------------------------
diff --git 
a/nexus-ingest/nexus-messages/build/reports/license/license-dependency.html 
b/nexus-ingest/nexus-messages/build/reports/license/license-dependency.html
deleted file mode 100644
index d9f5e84..0000000
--- a/nexus-ingest/nexus-messages/build/reports/license/license-dependency.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<html>
-  <head>
-    <title>HTML License report</title>
-  </head>
-  <style>table {
-                  width: 85%;
-                  border-collapse: collapse;
-                  text-align: center;
-                }
-
-                .dependencies {
-                  text-align: left;
-                  width:15%;
-                }
-
-                tr {
-                  border: 1px solid black;
-                }
-
-                td {
-                  border: 1px solid black;
-                  font-weight: bold;
-                  color: #2E2E2E
-                }
-
-                th {
-                  border: 1px solid black;
-                }
-
-                h3 {
-                  text-align:center;
-                  margin:3px
-                }
-
-                .license {
-                    width:70%
-                }
-
-                .licenseName {
-                    width:15%
-                }
-                </style>
-  <body>
-    <table align='center'>
-      <tr>
-        <th>
-          <h3>License</h3>
-        </th>
-        <th>
-          <h3>License text URL</h3>
-        </th>
-        <th>
-          <h3>Dependency</h3>
-        </th>
-      </tr>
-      <tr>
-        <td class='licenseName'>New BSD license</td>
-        <td class='license'>
-          <a href='http://www.opensource.org/licenses/bsd-license.php'>License 
agreement</a>
-        </td>
-        <td class='dependencies'>
-          <ul>
-            <li>protobuf-java-2.6.1.jar</li>
-          </ul>
-        </td>
-      </tr>
-    </table>
-  </body>
-</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/nexus-ingest/nexus-messages/build/reports/license/license-dependency.xml
----------------------------------------------------------------------
diff --git 
a/nexus-ingest/nexus-messages/build/reports/license/license-dependency.xml 
b/nexus-ingest/nexus-messages/build/reports/license/license-dependency.xml
deleted file mode 100644
index 66df9d3..0000000
--- a/nexus-ingest/nexus-messages/build/reports/license/license-dependency.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<licenses>
-  <license name='New BSD license' 
url='http://www.opensource.org/licenses/bsd-license.php'>
-    <dependency>protobuf-java-2.6.1.jar</dependency>
-  </license>
-</licenses>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/nexus-ingest/nexus-messages/build/reports/project/dependencies.txt
----------------------------------------------------------------------
diff --git a/nexus-ingest/nexus-messages/build/reports/project/dependencies.txt 
b/nexus-ingest/nexus-messages/build/reports/project/dependencies.txt
deleted file mode 100644
index 6698ae1..0000000
--- a/nexus-ingest/nexus-messages/build/reports/project/dependencies.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-
-------------------------------------------------------------
-Root project
-------------------------------------------------------------
-
-archives - Configuration for archive artifacts.
-No dependencies
-
-compile - Dependencies for source set 'main'.
-\--- com.google.protobuf:protobuf-java:2.6.1
-
-compileClasspath - Compile classpath for source set 'main'.
-\--- com.google.protobuf:protobuf-java:2.6.1
-
-compileOnly - Compile dependencies for source set 'main'.
-\--- com.google.protobuf:protobuf-java:2.6.1
-
-default - Configuration for default artifacts.
-\--- com.google.protobuf:protobuf-java:2.6.1
-
-protobuf
-No dependencies
-
-protobufToolsLocator_protoc
-\--- com.google.protobuf:protoc:2.6.1
-
-runtime - Runtime dependencies for source set 'main'.
-\--- com.google.protobuf:protobuf-java:2.6.1
-
-testCompile - Dependencies for source set 'test'.
-\--- com.google.protobuf:protobuf-java:2.6.1
-
-testCompileClasspath - Compile classpath for source set 'test'.
-\--- com.google.protobuf:protobuf-java:2.6.1
-
-testCompileOnly - Compile dependencies for source set 'test'.
-\--- com.google.protobuf:protobuf-java:2.6.1
-
-testProtobuf
-No dependencies
-
-testRuntime - Runtime dependencies for source set 'test'.
-\--- com.google.protobuf:protobuf-java:2.6.1

http://git-wip-us.apache.org/repos/asf/incubator-sdap-nexus/blob/9646e7c5/nexus-ingest/nexus-messages/build/reports/project/dependencies/css/base-style.css
----------------------------------------------------------------------
diff --git 
a/nexus-ingest/nexus-messages/build/reports/project/dependencies/css/base-style.css
 
b/nexus-ingest/nexus-messages/build/reports/project/dependencies/css/base-style.css
deleted file mode 100644
index 4afa73e..0000000
--- 
a/nexus-ingest/nexus-messages/build/reports/project/dependencies/css/base-style.css
+++ /dev/null
@@ -1,179 +0,0 @@
-
-body {
-    margin: 0;
-    padding: 0;
-    font-family: sans-serif;
-    font-size: 12pt;
-}
-
-body, a, a:visited {
-    color: #303030;
-}
-
-#content {
-    padding-left: 50px;
-    padding-right: 50px;
-    padding-top: 30px;
-    padding-bottom: 30px;
-}
-
-#content h1 {
-    font-size: 160%;
-    margin-bottom: 10px;
-}
-
-#footer {
-    margin-top: 100px;
-    font-size: 80%;
-    white-space: nowrap;
-}
-
-#footer, #footer a {
-    color: #a0a0a0;
-}
-
-#line-wrapping-toggle {
-    vertical-align: middle;
-}
-
-#label-for-line-wrapping-toggle {
-    vertical-align: middle;
-}
-
-ul {
-    margin-left: 0;
-}
-
-h1, h2, h3 {
-    white-space: nowrap;
-}
-
-h2 {
-    font-size: 120%;
-}
-
-ul.tabLinks {
-    padding-left: 0;
-    padding-top: 10px;
-    padding-bottom: 10px;
-    overflow: auto;
-    min-width: 800px;
-    width: auto !important;
-    width: 800px;
-}
-
-ul.tabLinks li {
-    float: left;
-    height: 100%;
-    list-style: none;
-    padding-left: 10px;
-    padding-right: 10px;
-    padding-top: 5px;
-    padding-bottom: 5px;
-    margin-bottom: 0;
-    -moz-border-radius: 7px;
-    border-radius: 7px;
-    margin-right: 25px;
-    border: solid 1px #d4d4d4;
-    background-color: #f0f0f0;
-}
-
-ul.tabLinks li:hover {
-    background-color: #fafafa;
-}
-
-ul.tabLinks li.selected {
-    background-color: #c5f0f5;
-    border-color: #c5f0f5;
-}
-
-ul.tabLinks a {
-    font-size: 120%;
-    display: block;
-    outline: none;
-    text-decoration: none;
-    margin: 0;
-    padding: 0;
-}
-
-ul.tabLinks li h2 {
-    margin: 0;
-    padding: 0;
-}
-
-div.tab {
-}
-
-div.selected {
-    display: block;
-}
-
-div.deselected {
-    display: none;
-}
-
-div.tab table {
-    min-width: 350px;
-    width: auto !important;
-    width: 350px;
-    border-collapse: collapse;
-}
-
-div.tab th, div.tab table {
-    border-bottom: solid #d0d0d0 1px;
-}
-
-div.tab th {
-    text-align: left;
-    white-space: nowrap;
-    padding-left: 6em;
-}
-
-div.tab th:first-child {
-    padding-left: 0;
-}
-
-div.tab td {
-    white-space: nowrap;
-    padding-left: 6em;
-    padding-top: 5px;
-    padding-bottom: 5px;
-}
-
-div.tab td:first-child {
-    padding-left: 0;
-}
-
-div.tab td.numeric, div.tab th.numeric {
-    text-align: right;
-}
-
-span.code {
-    display: inline-block;
-    margin-top: 0em;
-    margin-bottom: 1em;
-}
-
-span.code pre {
-    font-size: 11pt;
-    padding-top: 10px;
-    padding-bottom: 10px;
-    padding-left: 10px;
-    padding-right: 10px;
-    margin: 0;
-    background-color: #f7f7f7;
-    border: solid 1px #d0d0d0;
-    min-width: 700px;
-    width: auto !important;
-    width: 700px;
-}
-
-span.wrapped pre {
-    word-wrap: break-word;
-    white-space: pre-wrap;
-    word-break: break-all;
-}
-
-label.hidden {
-    display: none;
-}
\ No newline at end of file


Reply via email to