def updtChart(basinName, basinSites): basin = basinName print('Working on STO POR Chart for ' + basinName) statsData = [] minData = [] maxData = [] meanData = [] lowestData = [] highestData = [] lowData = [] highData = [] sliderDates = [] meanData = [] trace = [] plotData = [] basinPlotData = [] PORplotData = [] validTrip = [] networks = [r'SNTL', r'SCAN', r'SNTLT'] sensor = r"STO" depths = awdb.service.getHeightDepths() dataPath = path.join(this_dir, 'data', 'metaData', sensor, 'metaData.json') with open(dataPath, 'r') as j: meta = json.load(j) meta[:] = [ x for x in meta if str.split(x['stationTriplet'], ":")[2] in networks and str.split(x['stationTriplet'], ":")[0] in basinSites ] validTrip = [x['stationTriplet'] for x in meta] date_series = [ date(2015, 10, 1) + datetime.timedelta(days=x) for x in range(0, 366) ] #could use any year with a leap day if validTrip: beginDateDict = {} for siteMeta in meta: beginDateDict.update({ str(siteMeta['stationTriplet']): dt.strptime(str(siteMeta['beginDate']), "%Y-%m-%d %H:%M:%S") }) basinBeginDate = min(beginDateDict.values()) sYear = basinBeginDate.year if basinBeginDate.year > sYear: if basinBeginDate.month < 10: sYear = basinBeginDate.year else: if basinBeginDate.month == 10 and basinBeginDate.day == 1: sYear = basinBeginDate.year else: sYear = basinBeginDate.year + 1 sDate = date(sYear, 10, 1).strftime("%Y-%m-%d") eDate = (today.date() - datetime.timedelta(days=1)).strftime("%Y-%m-%d") dataDict = {} sensorDepths = [-2, -4, -8, -20, -40] for sensorDepth in sensorDepths: for depth in depths: if depth.value == sensorDepth and depth.unitCd == r'in': data = [] for triplet in validTrip: dataPath = path.join( this_dir, 'data', sensor, str(abs(sensorDepth)), triplet.replace(':', '_') + '.json') with open(dataPath, 'r') as j: jTemp = json.load(j) data.append(jTemp) depthData = {} for dataSite in data: siteData = [] if hasattr(dataSite, r'values'): if dataSite['values']: # sat = getSaturation( # sensorDepth, # str(dataSite['stationTriplet'])) # sat = np.nanmax([float(c) for c in dataSite.values if c != None]) padMissingData(dataSite, sDate, eDate) siteData = np.array(dataSite['values'], dtype=np.float) # siteData[:] = [100 if 100*(c/float(sat)) > 100 else # 100*(c/float(sat)) for c in siteData] depthData.update({ str(dataSite['stationTriplet']): list(siteData) }) dataDict.update({sensorDepth: dict(depthData)}) depthData.clear() plotData = {} plotData = calcSMSAvg(dataDict) # numDays = max(len(l) for l in plotData.values()) for siteID, smsValues in plotData.items(): plotData.update({siteID: fillMissingData(plotData[siteID], 30)}) smsPlotData = list(plotData.values()) with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) basinPlotData = list( np.nanmean(np.array([i for i in smsPlotData if i]), axis=0)) PORplotData = list([ basinPlotData[i:i + 366] for i in range(0, len(basinPlotData), 366) ]) allButCurrWY = list(PORplotData) del allButCurrWY[-1] statsData = list(map(list, zip(*allButCurrWY))) if len(statsData[0]) > 1: statsData[151] = statsData[150] with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) minData = [np.nanmin(a) for a in statsData] maxData = [np.nanmax(a) for a in statsData] meanData = [np.nanmean(a) for a in statsData] lowestData = [np.nanpercentile(a, 10) for a in statsData] highestData = [np.nanpercentile(a, 90) for a in statsData] lowData = [np.nanpercentile(a, 30) for a in statsData] highData = [np.nanpercentile(a, 70) for a in statsData] future_date_pad = 30 if len(PORplotData[-1]) > 335: future_date_pad = 366 - len(PORplotData[-1]) - 1 sliderDates = list( chain([(date_series[0])] + [date_series[len(PORplotData[-1]) + future_date_pad]])) else: sliderDates = list(chain([(date_series[0])] + [date_series[-1]])) if len(PORplotData) > 0: for index, i in enumerate(PORplotData): if index == len(PORplotData) - 1: trace.extend([ go.Scatter(x=date_series, y=i, name=str(sYear + index + 1), visible=True, connectgaps=True, line=dict(color='rgb(0,0,0)')) ]) elif np.nansum(i) > 0: trace.extend([ go.Scatter(x=date_series, y=i, name=str(sYear + index + 1), visible='legendonly', connectgaps=True) ]) if meanData: if lowestData: trace.extend([ go.Scatter(x=date_series, y=minData, legendgroup='centiles', name=r'Min', visible=True, mode='line', line=dict(width=0), fillcolor='rgba(237,0,1,0.15)', fill='none', showlegend=False, hoverinfo='none', connectgaps=True) ]) trace.extend([ go.Scatter(x=date_series, y=lowestData, legendgroup='centiles', name=r'10%', visible=True, mode='line', line=dict(width=0), fillcolor='rgba(237,0,1,0.15)', fill='tonexty', showlegend=False, hoverinfo='none', connectgaps=True) ]) if lowData: trace.extend([ go.Scatter(x=date_series, y=lowData, legendgroup='centiles', name=r'30%', visible=True, mode='line', line=dict(width=0), fillcolor='rgba(237,237,0,0.15)', fill='tonexty', showlegend=False, hoverinfo='none', connectgaps=True) ]) if highData: trace.extend([ go.Scatter(x=date_series, y=highData, legendgroup='centiles', name=r'Stats. Shading', visible=True, mode='line', line=dict(width=0), fillcolor='rgba(115,237,115,0.15)', fill='tonexty', showlegend=True, hoverinfo='none', connectgaps=True) ]) if highestData: trace.extend([ go.Scatter(x=date_series, y=highestData, legendgroup='centiles', name=r'90%', visible=True, mode='line', line=dict(width=0), fillcolor='rgba(0,237,237,0.15)', fill='tonexty', showlegend=False, hoverinfo='none', connectgaps=True) ]) trace.extend([ go.Scatter(x=date_series, y=maxData, legendgroup='centiles', name=r'Max', visible=True, mode='line', line=dict(width=0), fillcolor='rgba(1,0,237,0.15)', fill='tonexty', showlegend=False, hoverinfo='none', connectgaps=True) ]) if minData: trace.extend([ go.Scatter(x=date_series, y=minData, name=r'Min', visible=True, hoverinfo='none', connectgaps=True, line=dict(color='rgba(237,0,0,0.5)')) ]) if meanData: trace.extend([ go.Scatter(x=date_series, y=meanData, name=r'Normal (POR)', connectgaps=True, visible=True, hoverinfo='none', line=dict(color='rgba(0,237,0,0.4)')) ]) if maxData: trace.extend([ go.Scatter(x=date_series, y=maxData, name=r'Max', visible=True, hoverinfo='none', connectgaps=True, line=dict(color='rgba(0,0,237,0.4)')) ]) annoText = str( r"Statistical shading breaks at 10th, 30th, 50th, 70th, and 90th Percentiles<br>Normal (POR) - Unofficial mean calculated from Period of Record data <br>For more information visit: <a href='https://www.wcc.nrcs.usda.gov/normals/30year_normals_data.htm'>30 year normals calcuation description</a>" ) layout = go.Layout(images=[ dict( source= "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/US-NaturalResourcesConservationService-Logo.svg/2000px-US-NaturalResourcesConservationService-Logo.svg.png", xref="paper", yref="paper", x=0, y=0.9, xanchor="left", yanchor="bottom", sizex=0.4, sizey=0.1, opacity=0.5, layer="above") ], annotations=[ dict(font=dict(size=10), text=annoText, x=0, y=-0.41, yref='paper', xref='paper', align='left', showarrow=False) ], legend=dict(traceorder='reversed', tracegroupgap=1, bordercolor='#E2E2E2', borderwidth=2), showlegend=True, title='Average Soil Temperature in ' + str(basin), height=622, width=700, autosize=False, yaxis=dict(title=r'Avg. Temperature (°F)', hoverformat=".1f", tickformat="0f"), xaxis=dict(range=sliderDates, tickformat="%b %e", rangeselector=dict(buttons=list([ dict(count=9, label='Jan', step='month', stepmode='todate'), dict(count=6, label='Apr', step='month', stepmode='todate'), dict(count=3, label='July', step='month', stepmode='todate'), dict(label='WY', step='all') ])), rangeslider=dict(thickness=0.1), type='date')) return {'data': trace, 'layout': layout}
def updtChart(site_triplet, siteName): print('Working on TAVG POR Chart for ' + siteName) statsData = [] minData = [] maxData = [] meanData = [] lowestData = [] highestData = [] lowData = [] highData = [] sliderDates = [] meanData = [] trace = [] sitePlotData = [] PORplotData = [] sitePlotNormData = [] validTrip = [site_triplet] sensor = r"TAVG" date_series = [date(2015,10,1) + datetime.timedelta(days=x) for x in range(0, 366)] #could use any year with a leap day sitePlotNormData = [] beginDateDict = {} for siteMeta in meta: beginDateDict.update( {str(siteMeta['stationTriplet']) : dt.strptime(str(siteMeta['beginDate']), "%Y-%m-%d %H:%M:%S")}) siteBeginDate = min(beginDateDict.values()) sYear = siteBeginDate.year if siteBeginDate.year > sYear: if siteBeginDate.month < 10: sYear = siteBeginDate.year else: if siteBeginDate.month == 10 and siteBeginDate.day == 1: sYear = siteBeginDate.year else: sYear = siteBeginDate.year + 1 sDate = date(sYear, 10, 1).strftime("%Y-%m-%d") eDate = today.date().strftime("%Y-%m-%d") data = [] for triplet in validTrip: url = '/'.join([dataUrl,'DAILY', sensor, triplet.replace(':','_') + '.json']) with request.urlopen(url) as d: jTemp = json.loads(d.read().decode()) data.append(trimToOct1(jTemp)) for dataSite in data: if dataSite: padMissingData(dataSite,sDate,eDate) sitePlotData = np.array(data[0]['values'], dtype=np.float) PORplotData = list([sitePlotData[i:i+366] for i in range(0,len(sitePlotData),366)]) allButCurrWY = list(PORplotData) del allButCurrWY[-1] statsData = list(map(list,zip(*allButCurrWY))) if len(statsData[0]) > 1: statsData[151] = statsData[150] with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) minData = [np.nanmin(a) for a in statsData] maxData = [np.nanmax(a) for a in statsData] meanData = [np.nanpercentile(a,50) for a in statsData] lowestData = [np.nanpercentile(a,10) for a in statsData] highestData = [np.nanpercentile(a,90) for a in statsData] lowData = [np.nanpercentile(a,30) for a in statsData] highData = [np.nanpercentile(a,70) for a in statsData] future_date_pad = 14 if len(PORplotData[-1]) > 351: future_date_pad = 366 - len(PORplotData[-1]) - 1 sliderDates = list(chain([(date_series[0])] + [date_series[len(PORplotData[-1])+ future_date_pad]])) else: sliderDates = list(chain([(date_series[0])] + [date_series[-1]])) if len(PORplotData) > 0: for index, i in enumerate(PORplotData): if index == len(PORplotData)-1: trace.extend( [go.Scatter( x=date_series,y=i, name=str(sYear + index + 1), visible=True,connectgaps=True, line=dict(color='rgb(0,0,0)'))]) elif np.nansum(i) > 0: trace.extend( [go.Scatter(x=date_series,y=i, name=str(sYear + index + 1), visible='legendonly', connectgaps=True)]) if meanData: if lowestData: trace.extend( [go.Scatter(x=date_series,y=minData ,legendgroup='centiles',name=r'Min', visible=True,mode='line', line=dict(width=0),connectgaps=True, fillcolor='rgba(237,0,1,0.15)', fill='none',showlegend=False, hoverinfo='none')]) trace.extend( [go.Scatter(x=date_series,y=lowestData ,legendgroup='centiles',name=r'10%', visible=True,mode='line', line=dict(width=0),connectgaps=True, fillcolor='rgba(237,0,1,0.15)', fill='tonexty',showlegend=False, hoverinfo='none')]) if lowData: trace.extend( [go.Scatter(x=date_series,y=lowData, legendgroup='centiles',name=r'30%', visible=True,mode='line', line=dict(width=0),connectgaps=True, fillcolor='rgba(237,237,0,0.15)', fill='tonexty',showlegend=False, hoverinfo='none')]) if highData: trace.extend( [go.Scatter(x=date_series,y=highData, legendgroup='centiles', name=r'Stats. Shading', visible=True,mode='line', line=dict(width=0),connectgaps=True, fillcolor='rgba(115,237,115,0.15)', fill='tonexty',showlegend=True, hoverinfo='none')]) if highestData: trace.extend( [go.Scatter(x=date_series,y=highestData, legendgroup='centiles',connectgaps=True, name=r'90%',visible=True ,mode='line',line=dict(width=0), fillcolor='rgba(0,237,237,0.15)', fill='tonexty',showlegend=False, hoverinfo='none')]) trace.extend( [go.Scatter(x=date_series,y=maxData ,legendgroup='centiles',name=r'Max', visible=True,mode='line', line=dict(width=0),connectgaps=True, fillcolor='rgba(1,0,237,0.15)', fill='tonexty',showlegend=False, hoverinfo='none')]) if minData: trace.extend( [go.Scatter(x=date_series,y=minData, name=r'Min',visible=True, hoverinfo='none',connectgaps=True, line=dict(color='rgba(237,0,0,0.5)'))]) if len(sitePlotNormData) > 0: trace.extend( [go.Scatter(x=date_series, y=sitePlotNormData, name=r"Normal ('81-'10)",connectgaps=True, visible=True,hoverinfo='none', line=dict(color='rgba(0,237,0,0.4)'))]) if meanData: if len(sitePlotNormData) > 0: trace.extend( [go.Scatter(x=date_series, y=meanData,name=r'Normal (POR)', visible='legendonly', hoverinfo='none', connectgaps=True, line=dict(color='rgba(0,237,0,0.4)', dash='dash'))]) else: trace.extend( [go.Scatter(x=date_series,y=meanData, name=r'Normal (POR)',connectgaps=True, visible=True,hoverinfo='none', line=dict(color='rgba(0,237,0,0.4)'))]) if maxData: trace.extend( [go.Scatter(x=date_series,y=maxData, name=r'Max',visible=True, hoverinfo='none',connectgaps=True, line=dict(color='rgba(0,0,237,0.4)'))]) annoText = str(r"Statistical shading breaks at 10th, 30th, 50th, 70th, and 90th Percentiles<br>Normal ('81-'10) - Official median calculated from 1981 thru 2010 data <br>Normal (POR) - Unofficial mean calculated from Period of Record data <br>For more information visit: <a href='https://www.wcc.nrcs.usda.gov/normals/30year_normals_data.htm'>30 year normals calcuation description</a>") # asterisk = '' # if len(sitePlotNormData) == 0: # sitePlotNormData = meanData # annoText = annoText + '<br>*POR data used to calculate Normals since no published 30-year normals available for this site' # asterisk = '*' # jDay = len(PORplotData[-1])-1 # if len(sitePlotNormData) == 0: # perNorm = r'N/A' # else: # perNorm = str('{0:g}'.format(100*round( # PORplotData[-1][jDay]/sitePlotNormData[jDay],2))) # perPeak = str('{0:g}'.format(100*round( # PORplotData[-1][jDay]/max(sitePlotNormData),2))) # if not math.isnan(PORplotData[-1][jDay]): # centile = ordinal(int(round( # stats.percentileofscore( # statsData[jDay],PORplotData[-1][jDay]),0))) # else: # centile = 'N/A' # # dayOfPeak = sitePlotNormData.index(max(sitePlotNormData)) # if jDay > dayOfPeak: # tense = r'Since' # else: # tense = r'Until' # daysToPeak = str(abs(jDay-dayOfPeak)) annoData = ''#str(r"Current" + asterisk + ":<br>% of Normal - " + # perNorm + r"%<br>" + # r"% Normal Peak - " + perPeak + r"%<br>" + # r"Days " + tense + # r" Normal Peak - " + daysToPeak + r"<br>" # r"Percentile Rank- " + centile) layout = go.Layout( images= [dict( source= "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/US-NaturalResourcesConservationService-Logo.svg/2000px-US-NaturalResourcesConservationService-Logo.svg.png", xref="paper", yref="paper", x= 0, y= 0.9, xanchor="left", yanchor="bottom", sizex= 0.4, sizey= 0.1, opacity= 0.5, layer= "above" )], annotations=[dict( font=dict(size=10), text=annoText, x=0,y=-0.41, yref='paper',xref='paper', align='left', showarrow=False), dict(font=dict(size=10), text=annoData, x=0,y=0.9, yref='paper',xref='paper', align='left', xanchor="left", yanchor="top", showarrow=False)], legend=dict(traceorder='reversed',tracegroupgap=1, bordercolor='#E2E2E2',borderwidth=2), showlegend = True, title='Average Daily Temperature at<br>' + siteName, height=622, width=700, autosize=False, yaxis=dict(title=r'Avg. Daily Temperature (°F)',hoverformat='.1f', tickformat="0f"), xaxis=dict( range=sliderDates, tickformat="%b %e", rangeselector=dict( buttons=list([ dict(count=9, label='Jan', step='month', stepmode='todate'), dict(count=6, label='Apr', step='month', stepmode='todate'), dict(count=3, label='July', step='month', stepmode='todate'), dict(label='WY', step='all') ]) ), rangeslider=dict(thickness=0.1), type='date' ) ) return {'data': trace, 'layout': layout}
def updtChart(basinName, basinSites): basin = basinName print('Working on WTEQ POR Chart for ' + basinName) statsData = [] minData = [] maxData = [] meanData = [] lowestData = [] highestData = [] lowData = [] highData = [] sliderDates = [] meanData = [] trace = [] plotData = [] basinPlotData = [] PORplotData = [] basinNormData = [] basinPlotNormData = [] validTrip = [] networks = [r'SNTL', r'SCAN', r'SNTLT'] sensor = r"WTEQ" dataPath = path.join(this_dir, 'data', 'metaData', sensor, 'metaData.json') with open(dataPath, 'r') as j: meta = json.load(j) meta[:] = [ x for x in meta if str.split(x['stationTriplet'], ":")[2] in networks and str.split(x['stationTriplet'], ":")[0] in basinSites ] validTrip = [x['stationTriplet'] for x in meta] date_series = [ date(2015, 10, 1) + datetime.timedelta(days=x) for x in range(0, 366) ] #could use any year with a leap day if validTrip: normData = [] for triplet in validTrip: dataPath = dataPath = path.join( this_dir, 'data', 'norms', sensor, triplet.replace(':', '_') + '.json') with open(dataPath, 'r') as j: jTemp = json.load(j) normData.append(jTemp) basinNormData = [ np.array(x['values'], dtype=np.float) for x in normData if x['values'] ] if basinNormData: basinPlotNormData = list( np.nanmean(np.array([i for i in basinNormData]), axis=0)) validTrip[:] = [ x for index, x in enumerate(validTrip) if normData[index]['values'] ] beginDateDict = {} for siteMeta in meta: beginDateDict.update({ str(siteMeta['stationTriplet']): dt.strptime(str(siteMeta['beginDate']), "%Y-%m-%d %H:%M:%S") }) basinBeginDate = min(beginDateDict.values()) sYear = basinBeginDate.year if basinBeginDate.year > sYear: if basinBeginDate.month < 10: sYear = basinBeginDate.year else: if basinBeginDate.month == 10 and basinBeginDate.day == 1: sYear = basinBeginDate.year else: sYear = basinBeginDate.year + 1 sDate = date(sYear, 10, 1).strftime("%Y-%m-%d") eDate = today.date().strftime("%Y-%m-%d") data = [] for triplet in validTrip: dataPath = path.join(this_dir, 'data', sensor, triplet.replace(':', '_') + '.json') with open(dataPath, 'r') as j: jTemp = json.load(j) data.append(jTemp) for dataSite in data: if dataSite: padMissingData(dataSite, sDate, eDate) plotData = [np.array(x['values'], dtype=np.float) for x in data] with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) basinPlotData = list( np.nanmean(np.array([i for i in plotData]), axis=0)) PORplotData = list( [basinPlotData[i:i + 366] for i in range(0, len(basinPlotData), 366)]) allButCurrWY = list(PORplotData) del allButCurrWY[-1] statsData = list(map(list, zip(*allButCurrWY))) if len(statsData[0]) > 1: statsData[151] = statsData[150] with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) minData = [np.nanmin(a) for a in statsData] maxData = [np.nanmax(a) for a in statsData] meanData = [np.nanpercentile(a, 50) for a in statsData] lowestData = [np.nanpercentile(a, 10) for a in statsData] highestData = [np.nanpercentile(a, 90) for a in statsData] lowData = [np.nanpercentile(a, 30) for a in statsData] highData = [np.nanpercentile(a, 70) for a in statsData] future_date_pad = 14 if len(PORplotData[-1]) > 351: future_date_pad = 366 - len(PORplotData[-1]) - 1 sliderDates = list( chain([(date_series[0])] + [ date_series[get_last_non_zero_index(maxData[0:305]) + future_date_pad] ])) else: sliderDates = list(chain([(date_series[0])] + [date_series[-1]])) if len(PORplotData) > 0: for index, i in enumerate(PORplotData): if index == len(PORplotData) - 1: trace.extend([ go.Scatter(x=date_series, y=i, name=str(sYear + index + 1), visible=True, connectgaps=True, line=dict(color='rgb(0,0,0)')) ]) elif np.nansum(i) > 0: trace.extend([ go.Scatter(x=date_series, y=i, name=str(sYear + index + 1), visible='legendonly', connectgaps=True) ]) if meanData: if lowestData: trace.extend([ go.Scatter(x=date_series, y=minData, legendgroup='centiles', name=r'Min', visible=True, mode='line', line=dict(width=0), connectgaps=True, fillcolor='rgba(237,0,1,0.15)', fill='none', showlegend=False, hoverinfo='none') ]) trace.extend([ go.Scatter(x=date_series, y=lowestData, legendgroup='centiles', name=r'10%', visible=True, mode='line', line=dict(width=0), connectgaps=True, fillcolor='rgba(237,0,1,0.15)', fill='tonexty', showlegend=False, hoverinfo='none') ]) if lowData: trace.extend([ go.Scatter(x=date_series, y=lowData, legendgroup='centiles', name=r'30%', visible=True, mode='line', line=dict(width=0), connectgaps=True, fillcolor='rgba(237,237,0,0.15)', fill='tonexty', showlegend=False, hoverinfo='none') ]) if highData: trace.extend([ go.Scatter(x=date_series, y=highData, legendgroup='centiles', name=r'Stats. Shading', visible=True, mode='line', line=dict(width=0), connectgaps=True, fillcolor='rgba(115,237,115,0.15)', fill='tonexty', showlegend=True, hoverinfo='none') ]) if highestData: trace.extend([ go.Scatter(x=date_series, y=highestData, legendgroup='centiles', connectgaps=True, name=r'90%', visible=True, mode='line', line=dict(width=0), fillcolor='rgba(0,237,237,0.15)', fill='tonexty', showlegend=False, hoverinfo='none') ]) trace.extend([ go.Scatter(x=date_series, y=maxData, legendgroup='centiles', name=r'Max', visible=True, mode='line', line=dict(width=0), connectgaps=True, fillcolor='rgba(1,0,237,0.15)', fill='tonexty', showlegend=False, hoverinfo='none') ]) if minData: trace.extend([ go.Scatter(x=date_series, y=minData, name=r'Min', visible=True, hoverinfo='none', connectgaps=True, line=dict(color='rgba(237,0,0,0.5)')) ]) if basinPlotNormData: trace.extend([ go.Scatter(x=date_series, y=basinPlotNormData, name=r"Normal ('81-'10)", connectgaps=True, visible=True, hoverinfo='none', line=dict(color='rgba(0,237,0,0.4)')) ]) if meanData: if basinPlotNormData: trace.extend([ go.Scatter(x=date_series, y=meanData, name=r'Normal (POR)', visible='legendonly', hoverinfo='none', connectgaps=True, line=dict(color='rgba(0,237,0,0.4)', dash='dash')) ]) else: trace.extend([ go.Scatter(x=date_series, y=meanData, name=r'Normal (POR)', connectgaps=True, visible=True, hoverinfo='none', line=dict(color='rgba(0,237,0,0.4)')) ]) if maxData: trace.extend([ go.Scatter(x=date_series, y=maxData, name=r'Max', visible=True, hoverinfo='none', connectgaps=True, line=dict(color='rgba(0,0,237,0.4)')) ]) annoText = str( r"Statistical shading breaks at 10th, 30th, 50th, 70th, and 90th Percentiles<br>Normal ('81-'10) - Official median calculated from 1981 thru 2010 data <br>Normal (POR) - Unofficial mean calculated from Period of Record data <br>For more information visit: <a href='https://www.wcc.nrcs.usda.gov/normals/30year_normals_data.htm'>30 year normals calcuation description</a>" ) asterisk = '' if not basinPlotNormData: basinPlotNormData = meanData annoText = annoText + '<br>*POR data used to calculate Normals since no published 30-year normals available for this basin' asterisk = '*' jDay = len(PORplotData[-1]) - 1 # maxList = [np.nanmax(x) for x in allButCurrWY] # trace.extend([go.Box(y=maxList,x0=date_series[jDay],marker=dict(size=0))]) # if basinPlotNormData[jDay] == 0: perNorm = r'N/A' else: perNorm = str('{0:g}'.format( 100 * round(PORplotData[-1][jDay] / basinPlotNormData[jDay], 2))) perPeak = str('{0:g}'.format( 100 * round(PORplotData[-1][jDay] / max(basinPlotNormData), 2))) if not math.isnan(PORplotData[-1][jDay]): centile = ordinal( int( round( stats.percentileofscore(statsData[jDay], PORplotData[-1][jDay]), 0))) else: centile = 'N/A' dayOfPeak = basinPlotNormData.index(max(basinPlotNormData)) if jDay > dayOfPeak: tense = r'Since' else: tense = r'Until' daysToPeak = str(abs(jDay - dayOfPeak)) annoData = str(r"Current" + asterisk + ":<br>% of Normal - " + perNorm + r"%<br>" + r"% Normal Peak - " + perPeak + r"%<br>" + r"Days " + tense + r" Normal Peak - " + daysToPeak + r"<br>" r"Percentile Rank- " + centile) layout = go.Layout(images=[ dict( source= "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/US-NaturalResourcesConservationService-Logo.svg/2000px-US-NaturalResourcesConservationService-Logo.svg.png", xref="paper", yref="paper", x=0, y=0.9, xanchor="left", yanchor="bottom", sizex=0.4, sizey=0.1, opacity=0.5, layer="above") ], annotations=[ dict(font=dict(size=10), text=annoText, x=0, y=-0.41, yref='paper', xref='paper', align='left', showarrow=False), dict(font=dict(size=10), text=annoData, x=0, y=0.9, yref='paper', xref='paper', align='left', xanchor="left", yanchor="top", showarrow=False) ], legend=dict(traceorder='reversed', tracegroupgap=1, bordercolor='#E2E2E2', borderwidth=2), showlegend=True, title='Snow Water Equivalent in ' + str(basin), height=622, width=700, autosize=False, yaxis=dict(title=r'Snow Water Equivalent (in.)', hoverformat=".1f", tickformat="0f"), xaxis=dict(range=sliderDates, tickformat="%b %e", rangeselector=dict(buttons=list([ dict(count=9, label='Jan', step='month', stepmode='todate'), dict(count=6, label='Apr', step='month', stepmode='todate'), dict(count=3, label='July', step='month', stepmode='todate'), dict(label='WY', step='all') ])), rangeslider=dict(thickness=0.1), type='date')) return {'data': trace, 'layout': layout}