Esempio n. 1
0
def solar_irradiance(latitude, longitude, dt=None, optical_depth=0.118):
    '''
    Calculate the solar irradiance for the specified time and location.

    latitude : scalar
        The latitude of the location on the Earth in degrees

    longitude : scalar
        The longitude of the location on the Earth in degrees

    dt : datetime.datetime instance
        The date and time for which the irradiance should be calculated.
        This defaults to the current date.

    optical_depth : scalar
        An overall optical depth value to assume for the atmosphere so
        that the effects of atmospheric absorption can be accounted for.
        Defaults to 0.118.

    Returns : scalar or array
        The solar irradiance in W / m^2 for each value in *hour*.
    '''
    if dt is None:
        dt = datetime.utcnow()

    if not iterable(dt):
        dt = [dt]

    lat_rad = latitude * degree
    lon_rad = longitude * degree

    cos_zenith = _get_cos_zenith(lat_rad, lon_rad, dt)

    s = solar_constant(dt[0])

    # Mask out values for cos < 0
    mask = np.array(cos_zenith < 0.)
    if mask.any():
        cos_zenith = masked_array(cos_zenith, mask=mask)

    return s * cos_zenith * np.exp(-optical_depth / cos_zenith)
Esempio n. 2
0
def solar_irradiance(latitude, longitude, dt=None, optical_depth=0.118):
    '''
    Calculate the solar irradiance for the specified time and location.

    latitude : scalar
        The latitude of the location on the Earth in degrees

    longitude : scalar
        The longitude of the location on the Earth in degrees

    dt : datetime.datetime instance
        The date and time for which the irradiance should be calculated.
        This defaults to the current date.

    optical_depth : scalar
        An overall optical depth value to assume for the atmosphere so
        that the effects of atmospheric absorption can be accounted for.
        Defaults to 0.118.

    Returns : scalar or array
        The solar irradiance in W / m^2 for each value in *hour*.
    '''
    if dt is None:
        dt = datetime.utcnow()

    if not iterable(dt):
        dt = [dt]

    lat_rad = latitude * degree
    lon_rad = longitude * degree

    cos_zenith = _get_cos_zenith(lat_rad, lon_rad, dt)

    s = solar_constant(dt[0])

    # Mask out values for cos < 0
    mask = np.array(cos_zenith < 0.)
    if mask.any():
        cos_zenith = masked_array(cos_zenith, mask=mask)

    return s * cos_zenith * np.exp(-optical_depth / cos_zenith)
Esempio n. 3
0
def grid_data(ob_data, grid_x, grid_y, ob_x, ob_y, weight_func, params):
    '''
    Calculates a value at each grid point based on the observed data in
    ob_data.

    ob_data : 1D array
        The observation data

    grid_x : 2D array
        The x locations of the grid points

    grid_y : 2D array
        The y locations of the grid points

    ob_x : 1D array
        The x locations of the observation points

    ob_y : 1D array
        The y locations of the observation points

    weight_func : callable
        Any function that returns weights for the observations given their
        distance from a grid point.

    params : any object or tuple of objects
        Appropriate parameters to pass to *weight_func* after the distances.

    Returns  : 2D array
        The values for the grid points
    '''
    if not iterable(params):
        params = (params, )
    # grid_point_dists calculates a 3D array containing the distance for each
    # grid point to every observation.
    weights = weight_func(grid_point_dists(grid_x, grid_y, ob_x, ob_y),
                          *params)
    total_weights = weights.sum(axis=2)
    final = (weights * ob_data).sum(axis=2) / total_weights
    final = np.ma.masked_array(final, mask=(total_weights == 0.))
    return final
Esempio n. 4
0
def grid_data(ob_data, grid_x, grid_y, ob_x, ob_y, weight_func, params):
    '''
    Calculates a value at each grid point based on the observed data in
    ob_data.

    ob_data : 1D array
        The observation data

    grid_x : 2D array
        The x locations of the grid points

    grid_y : 2D array
        The y locations of the grid points

    ob_x : 1D array
        The x locations of the observation points

    ob_y : 1D array
        The y locations of the observation points

    weight_func : callable
        Any function that returns weights for the observations given their
        distance from a grid point.

    params : any object or tuple of objects
        Appropriate parameters to pass to *weight_func* after the distances.

    Returns  : 2D array
        The values for the grid points
    '''
    if not iterable(params):
        params = (params,)
    # grid_point_dists calculates a 3D array containing the distance for each
    # grid point to every observation.
    weights = weight_func(grid_point_dists(grid_x, grid_y, ob_x, ob_y), *params)
    total_weights = weights.sum(axis=2)
    final = (weights * ob_data).sum(axis=2) / total_weights
    final = np.ma.masked_array(final, mask=(total_weights==0.))
    return final
Esempio n. 5
0
def meteogram(data, fig=None, num_panels=3, time_range=None, layout=None,
    styles=None, limits=None, units=None, tz=UTC):
    '''
    Plots a meteogram (collection of time series) for a data set. This
    is broken down into a series of panels (defaults to 3), each of which
    can plot multiple variables, with sensible defaults, but can also be
    specified using *layout*.

    *data* : numpy record array
        A numpy record array containing time series for individual variables
        in each field.

    *fig* : :class:`matplotlib.figure.Figure` instance or None.
        A matplotlib Figure on which to draw.  If None, a new figure
        will be created.

    *num_panels* : int
        The number of panels to use in the plot.

    *time_range* : sequence, datetime.timedetla, or *None*
        If a sequence, the starting and ending times for the x-axis.  If
        a :class:`datetime.timedelta` object, it represents the time span
        to plot, which will end with the last data point.  It defaults to
        the last 24 hours of data.

    *layout* : dictionary
        A dictionary that maps panel numbers to lists of variables.
        If a panel is not found in the dictionary, a default (up to panel 5)
        will be used.  *None* can be included in the list to denote that
        :func:`pyplot.twinx` should be called, and the remaining variables
        plotted.

    *styles* : dictionary
        A dictionary that maps variable names to dictionary of matplotlib
        style arguments.  Also, the keyword `fill` can be included, to
        indicate that a filled plot should be used.  Any variable not
        specified will use a default (if available).

    *limits* : dictionary
        A dictionary that maps variable names to plot limits.  These limits
        are given by tuples with at least two items, which specify the
        start and end limits.  Either can be *None* which implies using the
        automatically determined value.  Optional third and fourth values
        can be given in the tuple, which is a list of tick values and labels,
        respectively.

    *units* : dictionary
        A dictionary that maps variable names to unit strings for axis labels.

    *tz* : datetime.tzinfo instance
        A :class:`datetime.tzinfo instance specifying the timezone to use
        for plotting the x-axis.  See the docs for :module:`datetime` and
        :module:`pytz` for how to construct and use these objects.  The
        default is UTC.

    Returns : list
        A list of the axes objects that were created.
    '''

    if fig is None:
        fig = plt.figure()

    # Get the time variable
    time = data['datetime']

    # Process time_range.
    major_ticker = AutoDateLocator(tz=tz)
    minor_ticker = NullLocator()
    major_formatter = DateFormatter('%H', tz=tz)
    minor_formatter = NullFormatter()
    if time_range is None:
        time_range = timedelta(hours=24)
        major_ticker = HourLocator(byhour=np.arange(0, 25, 3), tz=tz)
        minor_ticker = HourLocator(tz=tz)

    if not iterable(time_range):
        end = time[-1]
        start = end - time_range
        time_range = (start, end)

    #List of variables in each panel.  None denotes that at that point, twinx
    #should be called and the remaining variables plotted on the other axis
    default_layout = {
        0:['temperature', 'dewpoint'],
        1:['wind gusts', 'wind speed', None, 'wind direction'],
        2:['pressure'],
        3:['rainfall'],
        4:['solar radiation']}

    if layout is not None:
        default_layout.update(layout)
    layout = default_layout

    #Default styles for each variable
    default_styles = {
        'relative humidity':dict(color='#255425', linestyle='--'),
        'dewpoint':dict(facecolor='#265425', edgecolor='None', fill=True),
        'temperature':dict(facecolor='#C14F53', edgecolor='None', fill=True),
        'pressure':dict(facecolor='#895125', edgecolor='None', fill=True),
        'dewpoint':dict(facecolor='#265425', edgecolor='None', fill=True),
        'wind speed':dict(facecolor='#1C2386', edgecolor='None', fill=True),
        'wind gusts':dict(facecolor='#8388FC', edgecolor='None', fill=True),
        'wind direction':dict(markeredgecolor='#A9A64B', marker='D',
            linestyle='', markerfacecolor='None', markeredgewidth=1,
            markersize=3),
        'rainfall':dict(facecolor='#37CD37', edgecolor='None', fill=True),
        'solar radiation':dict(facecolor='#FF8529', edgecolor='None',
            fill=True),
        'windchill':dict(color='#8388FC', linewidth=1.5),
        'heat index':dict(color='#671A5C')}

    if styles is not None:
        default_styles.update(styles)
    styles = default_styles

    #Default data limits
    default_limits = {
        'wind direction':(0, 360, np.arange(0,400,45,),
            ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N']),
        'wind speed':(0, None),
        'wind gusts':(0, None),
        'rainfall':(0, None),
        'solar radiation':(0, 1000, np.arange(0,1050,200))
        }
    if limits is not None:
        default_limits.update(limits)
    limits = default_limits

    #Set data units
    def_units = default_units.copy()
    if units is not None:
        def_units.update(units)
    units = def_units

    #Get the station name
    site = data['site'][0]

    #Get strings for the start and end times
    start = time_range[0].astimezone(tz).strftime('%H%M %d %b %Y')
    end = time_range[1].astimezone(tz).strftime('%H%M %d %b %Y %Z')

    axes = []
    for panel in range(num_panels):
        if panel > 0:
            ax = fig.add_subplot(num_panels, 1, panel+1, sharex=ax)
        else:
            ax = fig.add_subplot(num_panels, 1, panel+1)
            ax.set_title('%s\n%s to %s' % (site, start, end))

        panel_twinned = False

        var_min = []
        var_max = []
        for varname in layout[panel]:
            if varname is None:
                _rescale_yaxis(ax, var_min + var_max)
                ax = ax.twinx()
                panel_twinned = True
                var_min = []
                var_max = []
                continue

            # Get the linestyle for this variable
            style = styles.get(varname, dict())

            #Get the variable from the data and plot
            var = data[varname]

            #Set special limits if necessary
            lims = limits.get(varname, (None, None))

            #Store the max and min for auto scaling
            if var.max() is not np.ma.masked:
                var_max.append(var.max())
                var_min.append(var.min())

            if style.pop('fill', False):
                #Plot the filled area.  Need latest Matplotlib for date support
                #with fill_betweeen
                lower = -500 if lims[0] is None else lims[0]
                ax.fill_between(time, lower, var, where=~var.mask, **style)
                #TODO: Matplotlib SVN has support for turning off the
                #automatic scaling of the y-axis.  Can we use that somehow
                #to simplify our code??
                _rescale_yaxis(ax, var_min + var_max)
            else:
                ax.plot(time, var, **style)

            #If then length > 2, then we have ticks and (maybe) labels
            if len(lims) > 2:
                other = lims[2:]
                lims = lims[:2]
                #Separate out the ticks and perhaps labels
                if len(other) == 1:
                    ax.set_yticks(other[0])
                else:
                    ticks,labels = other
                    ax.set_yticks(ticks)
                    ax.set_yticklabels(labels)
            ax.set_ylim(*lims)

            # Set the label to the title-cased nice-version from the
            # field info with units, if given.
            if varname in units and units[varname]:
                unit_str = ' (%s)' % units[varname]
                if '^' in unit_str and '$' not in unit_str:
                    unit_str = '$' + unit_str + '$'
            else:
                unit_str = ''

            descr = get_title(data, varname)
            ax.set_ylabel(descr.title() + unit_str)

        ax.xaxis.set_major_locator(major_ticker)
        ax.xaxis.set_major_formatter(major_formatter)
        ax.xaxis.set_minor_locator(minor_ticker)
        ax.xaxis.set_minor_formatter(minor_formatter)
        if not panel_twinned:
            ax.yaxis.set_ticks_position('both')
            for tick in ax.yaxis.get_major_ticks():
                tick.label2On = True
        axes.append(ax)

    # Set the xlabel as appropriate depending on the timezone
    ax.set_xlabel('Hour (%s)' % time[-1].astimezone(tz).tzname())
    ax.set_xlim(*time_range)

    return axes
Esempio n. 6
0
def meteogram(data,
              fig=None,
              num_panels=3,
              time_range=None,
              layout=None,
              styles=None,
              limits=None,
              units=None,
              tz=UTC):
    '''
    Plots a meteogram (collection of time series) for a data set. This
    is broken down into a series of panels (defaults to 3), each of which
    can plot multiple variables, with sensible defaults, but can also be
    specified using *layout*.

    *data* : numpy record array
        A numpy record array containing time series for individual variables
        in each field.

    *fig* : :class:`matplotlib.figure.Figure` instance or None.
        A matplotlib Figure on which to draw.  If None, a new figure
        will be created.

    *num_panels* : int
        The number of panels to use in the plot.

    *time_range* : sequence, datetime.timedetla, or *None*
        If a sequence, the starting and ending times for the x-axis.  If
        a :class:`datetime.timedelta` object, it represents the time span
        to plot, which will end with the last data point.  It defaults to
        the last 24 hours of data.

    *layout* : dictionary
        A dictionary that maps panel numbers to lists of variables.
        If a panel is not found in the dictionary, a default (up to panel 5)
        will be used.  *None* can be included in the list to denote that
        :func:`pyplot.twinx` should be called, and the remaining variables
        plotted.

    *styles* : dictionary
        A dictionary that maps variable names to dictionary of matplotlib
        style arguments.  Also, the keyword `fill` can be included, to
        indicate that a filled plot should be used.  Any variable not
        specified will use a default (if available).

    *limits* : dictionary
        A dictionary that maps variable names to plot limits.  These limits
        are given by tuples with at least two items, which specify the
        start and end limits.  Either can be *None* which implies using the
        automatically determined value.  Optional third and fourth values
        can be given in the tuple, which is a list of tick values and labels,
        respectively.

    *units* : dictionary
        A dictionary that maps variable names to unit strings for axis labels.

    *tz* : datetime.tzinfo instance
        A :class:`datetime.tzinfo instance specifying the timezone to use
        for plotting the x-axis.  See the docs for :module:`datetime` and
        :module:`pytz` for how to construct and use these objects.  The
        default is UTC.

    Returns : list
        A list of the axes objects that were created.
    '''

    if fig is None:
        fig = plt.figure()

    # Get the time variable
    time = data['datetime']

    # Process time_range.
    major_ticker = AutoDateLocator(tz=tz)
    minor_ticker = NullLocator()
    major_formatter = DateFormatter('%H', tz=tz)
    minor_formatter = NullFormatter()
    if time_range is None:
        time_range = timedelta(hours=24)
        major_ticker = HourLocator(byhour=np.arange(0, 25, 3), tz=tz)
        minor_ticker = HourLocator(tz=tz)

    if not iterable(time_range):
        end = time[-1]
        start = end - time_range
        time_range = (start, end)

    #List of variables in each panel.  None denotes that at that point, twinx
    #should be called and the remaining variables plotted on the other axis
    default_layout = {
        0: ['temperature', 'dewpoint'],
        1: ['wind gusts', 'wind speed', None, 'wind direction'],
        2: ['pressure'],
        3: ['rainfall'],
        4: ['solar radiation']
    }

    if layout is not None:
        default_layout.update(layout)
    layout = default_layout

    #Default styles for each variable
    default_styles = {
        'relative humidity':
        dict(color='#255425', linestyle='--'),
        'dewpoint':
        dict(facecolor='#265425', edgecolor='None', fill=True),
        'temperature':
        dict(facecolor='#C14F53', edgecolor='None', fill=True),
        'pressure':
        dict(facecolor='#895125', edgecolor='None', fill=True),
        'dewpoint':
        dict(facecolor='#265425', edgecolor='None', fill=True),
        'wind speed':
        dict(facecolor='#1C2386', edgecolor='None', fill=True),
        'wind gusts':
        dict(facecolor='#8388FC', edgecolor='None', fill=True),
        'wind direction':
        dict(markeredgecolor='#A9A64B',
             marker='D',
             linestyle='',
             markerfacecolor='None',
             markeredgewidth=1,
             markersize=3),
        'rainfall':
        dict(facecolor='#37CD37', edgecolor='None', fill=True),
        'solar radiation':
        dict(facecolor='#FF8529', edgecolor='None', fill=True),
        'windchill':
        dict(color='#8388FC', linewidth=1.5),
        'heat index':
        dict(color='#671A5C')
    }

    if styles is not None:
        default_styles.update(styles)
    styles = default_styles

    #Default data limits
    default_limits = {
        'wind direction': (0, 360, np.arange(
            0,
            400,
            45,
        ), ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N']),
        'wind speed': (0, None),
        'wind gusts': (0, None),
        'rainfall': (0, None),
        'solar radiation': (0, 1000, np.arange(0, 1050, 200))
    }
    if limits is not None:
        default_limits.update(limits)
    limits = default_limits

    #Set data units
    def_units = default_units.copy()
    if units is not None:
        def_units.update(units)
    units = def_units

    #Get the station name
    site = data['site'][0]

    #Get strings for the start and end times
    start = time_range[0].astimezone(tz).strftime('%H%M %d %b %Y')
    end = time_range[1].astimezone(tz).strftime('%H%M %d %b %Y %Z')

    axes = []
    for panel in range(num_panels):
        if panel > 0:
            ax = fig.add_subplot(num_panels, 1, panel + 1, sharex=ax)
        else:
            ax = fig.add_subplot(num_panels, 1, panel + 1)
            ax.set_title('%s\n%s to %s' % (site, start, end))

        panel_twinned = False

        var_min = []
        var_max = []
        for varname in layout[panel]:
            if varname is None:
                _rescale_yaxis(ax, var_min + var_max)
                ax = ax.twinx()
                panel_twinned = True
                var_min = []
                var_max = []
                continue

            # Get the linestyle for this variable
            style = styles.get(varname, dict())

            #Get the variable from the data and plot
            var = data[varname]

            #Set special limits if necessary
            lims = limits.get(varname, (None, None))

            #Store the max and min for auto scaling
            if var.max() is not np.ma.masked:
                var_max.append(var.max())
                var_min.append(var.min())

            if style.pop('fill', False):
                #Plot the filled area.  Need latest Matplotlib for date support
                #with fill_betweeen
                lower = -500 if lims[0] is None else lims[0]
                ax.fill_between(time, lower, var, where=~var.mask, **style)
                #TODO: Matplotlib SVN has support for turning off the
                #automatic scaling of the y-axis.  Can we use that somehow
                #to simplify our code??
                _rescale_yaxis(ax, var_min + var_max)
            else:
                ax.plot(time, var, **style)

            #If then length > 2, then we have ticks and (maybe) labels
            if len(lims) > 2:
                other = lims[2:]
                lims = lims[:2]
                #Separate out the ticks and perhaps labels
                if len(other) == 1:
                    ax.set_yticks(other[0])
                else:
                    ticks, labels = other
                    ax.set_yticks(ticks)
                    ax.set_yticklabels(labels)
            ax.set_ylim(*lims)

            # Set the label to the title-cased nice-version from the
            # field info with units, if given.
            if varname in units and units[varname]:
                unit_str = ' (%s)' % units[varname]
                if '^' in unit_str and '$' not in unit_str:
                    unit_str = '$' + unit_str + '$'
            else:
                unit_str = ''

            descr = get_title(data, varname)
            ax.set_ylabel(descr.title() + unit_str)

        ax.xaxis.set_major_locator(major_ticker)
        ax.xaxis.set_major_formatter(major_formatter)
        ax.xaxis.set_minor_locator(minor_ticker)
        ax.xaxis.set_minor_formatter(minor_formatter)
        if not panel_twinned:
            ax.yaxis.set_ticks_position('both')
            for tick in ax.yaxis.get_major_ticks():
                tick.label2On = True
        axes.append(ax)

    # Set the xlabel as appropriate depending on the timezone
    ax.set_xlabel('Hour (%s)' % time[-1].astimezone(tz).tzname())
    ax.set_xlim(*time_range)

    return axes