Ejemplo n.º 1
0
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 (&#176;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}
Ejemplo n.º 2
0
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 (&#176;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}
Ejemplo n.º 3
0
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}