# Copyright (c) 2014, Vienna University of Technology (TU Wien), Department
# of Geodesy and Geoinformation (GEO).
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the Vienna University of Technology - Department of
# Geodesy and Geoinformation nor the names of its contributors may be used to
# endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VIENNA UNIVERSITY OF TECHNOLOGY,
# DEPARTMENT OF GEODESY AND GEOINFORMATION BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Author: Thomas Mistelbauer Thomas.Mistelbauer@geo.tuwien.ac.at
# Creation date: 2014-05-26
from cStringIO import StringIO
import os
from flask import Flask, render_template, jsonify, make_response
from flask.ext.cors import CORS
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from poets.timedate.dateindex import get_dtindex
from poets.web.overlays import bounds
import pytesmo.time_series as ts
[docs]def curpath():
"""
Gets the current path of the module.
Returns
-------
pth : str
Path of the module.
"""
pth, _ = os.path.split(os.path.abspath(__file__))
return pth
pd.DataFrame.to_dygraph_format = to_dygraph_format
dest = os.path.join(curpath(), 'static', 'temp')
app = Flask(__name__, static_folder='static', static_url_path='/static',
template_folder="templates")
app.config['CORS_HEADERS'] = 'Content-Type'
cors = CORS(app, resources={r"/*": {"origins": "*"}})
[docs]def start(poet, host='127.0.0.1', port=5000, debug=False):
"""
Starts application and sets global variables.
Parameters
----------
poet : Poet()
Instance of Poet class.
host : str, optional
Host that is used by the app, defaults to 127.0.0.1.
port : int, optional
Port where app runs on, defaults to 50000.
debug : bool, optional
Starts app in debug mode if set True, defaults to False.
"""
global regions
global sources
global variables
global dates
global vmin, vmax, cmap
global shapefile
global nan_value
global host_gl
global port_gl
global tmp_res
regions = poet.regions
sources = poet.sources
variables = poet.get_variables()
shapefile = poet.shapefile
nan_value = poet.nan_value
tmp_res = poet.temporal_resolution
host_gl = host
port_gl = port
if debug:
app.run(debug=True, use_debugger=True, use_reloader=True, host=host,
port=port)
else:
app.run(host=host, port=port)
@app.route('/', methods=['GET', 'POST'])
@app.route('/<reg>&<var>', methods=['GET', 'POST'])
[docs]def index(**kwargs):
"""
Renders main page of the web application. Generates image arguments needed
for OpenLayers overlay if parameters `reg` and `var` are set, renders
entry page if not set.
"""
global enddate
global dates
global ndate
global idxdates
if len(kwargs) > 0:
if 'reg' in kwargs:
region = kwargs['reg']
if 'var' in kwargs:
variable = kwargs['var']
for src in sources.keys():
if src in variable:
source = sources[src]
ndate = source._check_current_date()
begindate = ndate[region][variable][0]
enddate = ndate[region][variable][1]
if begindate is None and enddate is None:
error = 'No data available for this dataset.'
return render_template('index.html',
regions=regions,
sources=sources.keys(),
variables=variables,
error=error)
d = get_dtindex(tmp_res, begindate, enddate)
dates = d.to_pydatetime()
idxdates = len(dates) - 1
fdates = []
for i, d in enumerate(dates.tolist()):
dat = {'id': i, 'date': d.strftime('%Y-%m-%d')}
fdates.append(dat)
lon_min, lon_max, lat_min, lat_max, c_lat, c_lon, zoom = \
bounds(region, shapefile)
return render_template('app.html',
max=idxdates,
coord=[c_lon, c_lat],
zoom=zoom,
ex1=(lon_max, lat_min),
ex2=(lon_min, lat_max),
region=region,
source=source.name,
variable=variable,
regions=regions,
variables=variables,
dates=fdates,
host=host_gl,
port=port_gl)
else:
return render_template('index.html',
regions=regions,
sources=sources.keys(),
variables=variables)
@app.route('/_ts/<reg>&<src>&<var>&<loc>', methods=['GET', 'OPTIONS'])
@app.route('/_ts/<reg>&<src>&<var>&<loc>&<anom>', methods=['GET', 'OPTIONS'])
[docs]def get_ts(**kwargs):
"""
Gets time series for selected location, gets anomaly of time series if
`anom` parameter is passed.
Returns
-------
jsonified str
Time series (anomaly) in Dygraphs compatible json format.
"""
anomaly = False
if 'reg' in kwargs:
region = kwargs['reg']
if 'src' in kwargs:
source = sources[kwargs['src']]
if 'var' in kwargs:
variable = kwargs['var']
if 'loc' in kwargs:
loc = kwargs['loc']
if 'anom' in kwargs:
anomaly = True
loc = loc.split(',')
lonlat = (float(loc[0]), float(loc[1]))
df = source.read_ts(lonlat, region, variable, shapefile)
if anomaly:
df = ts.anomaly.calc_anomaly(df, window_size=100)
columns = []
for cols in df.columns:
columns.append(cols + '_anomaly')
df.columns = columns
labels, values = df.to_dygraph_format()
data = {'labels': labels, 'data': values}
return jsonify(data)
@app.route('/_tsdown/<reg>&<src>&<var>&<loc>')
@app.route('/_tsdown/<reg>&<src>&<var>&<loc>&<anom>')
[docs]def download_ts(**kwargs):
"""
Initiates download time series (anomaly) in comma separated values format.
Returns
-------
jsonified str
Time series (anomaly) in Dygraphs compatible json format.
"""
anomaly = False
if 'reg' in kwargs:
region = kwargs['reg']
if 'src' in kwargs:
source = sources[kwargs['src']]
if 'var' in kwargs:
variable = kwargs['var']
if 'loc' in kwargs:
loc = kwargs['loc']
if 'anom' in kwargs:
anomaly = True
loc = loc.split(',')
lonlat = (float(loc[0]), float(loc[1]))
filename = region + '_' + variable + '_' + loc[0][:6] + '_' + loc[1][:6]
df = source.read_ts(lonlat, region, variable, shapefile)
if anomaly:
df = ts.anomaly.calc_anomaly(df)
columns = []
for cols in df.columns:
columns.append(cols + '_anomaly')
df.columns = columns
filename += '_anomaly'
output = StringIO()
df.to_csv(output)
csv = output.getvalue()
response = make_response(csv)
response.headers["Content-Disposition"] = ("attachment; filename=" +
filename + ".csv")
return response
@app.route('/_rimg/<reg>&<src>&<var>&<idx>', methods=['GET', 'POST'])
[docs]def request_image(**kwargs):
"""
Creates image for OpenLayers overlay.
Returns
-------
StringIO
Image in StringIO.
"""
global vmin
global vmax
global metadata
global cmap
if 'reg' in kwargs:
region = kwargs['reg']
if 'src' in kwargs:
source = sources[kwargs['src']]
if 'var' in kwargs:
variable = kwargs['var']
if 'idx' in kwargs:
idx = kwargs['idx']
pidx = (dates[int(idx)])
img, _, _, metadata = source.read_img(pidx, region, variable)
if source.unit is not None:
if metadata is not None and 'unit' not in metadata:
metadata['unit'] = source.unit
elif metadata is None:
metadata = {}
metadata['unit'] = source.unit
if source.valid_range is not None:
vmin = source.valid_range[0]
vmax = source.valid_range[1]
else:
vmin = np.nanmin(img)
vmax = np.nanmax(img)
cmap = source.colorbar
# Resale the image
n = 10
img = np.kron(img, np.ones((n, n)))
img[img == nan_value] = np.NAN
buf = StringIO()
plt.imsave(buf, img, vmin=vmin, vmax=vmax, cmap=cmap)
image = buf.getvalue()
response = make_response(image)
response.headers["Content-Type"] = ("image/png; filename=data.png")
return response
@app.route('/_rlegend/<reg>&<var>', methods=['GET', 'POST'])
@app.route('/_rlegend/<reg>&<var>&<idx>', methods=['GET', 'POST'])
[docs]def request_legend(**kwargs):
"""
Creates Legend for OpenLayers overlay.
Returns
-------
StringIO
Legend in StringIO.
"""
global vmin
global vmax
global metadata
global cmap
fig = plt.figure(figsize=(4, 0.7))
ax1 = fig.add_axes([0.05, 0.7, 0.9, 0.10])
norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap=cmap, norm=norm,
orientation='horizontal')
plt.xticks(fontsize=9)
if metadata:
units = ['units', 'unit', 'UNITS', 'UNIT']
for unit in units:
if unit in metadata:
cb1.set_label(metadata[unit], fontsize=10)
buf = StringIO()
plt.savefig(buf)
plt.close()
image = buf.getvalue()
response = make_response(image)
response.headers["Content-Type"] = ("image/png; filename=legend.png")
return response
@app.route('/about')
[docs]def about():
"""
Creates the `about` page.
"""
return render_template('about.html')