Example #1
0
def longitude_slicer(lons, query):
    """
    Returns a slice object that will slice out the smallest chunk of lons
    that covers the query domain defined by query['domain']['W'] and
    query['domain']['E'].

    The resulting slice will result in longitudes which increase from west
    to east, with a grid delta that is closest to the request grid delta,
    query['resolution'].
    """
    domain = query['domain']
    lons = np.asarray(lons, dtype=np.float32)
    if 'grid_delta' in query:
        warnings.warn('grid_delta in queries is obsolete, use resolution')

    lon_delta = query.get('resolution', None)
    slicer = angle_slicer(lons, domain['W'], domain['E'], lon_delta)

    # at least one point west of the eastern edge
    assert np.any(angles.angle_diff(lons[slicer], domain['E']) <= 0.)
    # east of the eastern edge
    assert np.any(angles.angle_diff(lons[slicer], domain['E']) >= 0.)
    # east of the western edge
    assert np.any(angles.angle_diff(lons[slicer], domain['W']) >= 0.)
    # west of the western edge
    assert np.any(angles.angle_diff(lons[slicer], domain['W']) <= 0.)

    return slicer
Example #2
0
def bounding_box(fcst, pad=0.1, lon_pad=None, lat_pad=None):
    lons = np.unique(fcst['longitude'].values)
    lats = np.unique(fcst['latitude'].values)

    lon_diffs = angles.angle_diff(lons[:, None], lons)
    lat_diffs = angles.angle_diff(lats[:, None], lats)

    western_most_ind = np.nonzero(np.all(lon_diffs >= 0., axis=0))[0]
    western_most = np.unique(lons[western_most_ind]).item()
    eastern_most_ind = np.nonzero(np.all(lon_diffs <= 0., axis=0))[0]
    eastern_most = np.unique(lons[eastern_most_ind]).item()

    northern_most_ind = np.nonzero(np.all(lat_diffs <= 0., axis=0))[0]
    northern_most = np.unique(lats[northern_most_ind]).item()
    southern_most_ind = np.nonzero(np.all(lat_diffs >= 0., axis=0))[0]
    southern_most = np.unique(lats[southern_most_ind]).item()

    # count the number of lons greater than and less than each lon
    # and take the difference.  The longitude (or pair of lons) that
    # minimize this help us determine the median.  This allows different
    # definitions of longitude.
    lon_rel_loc = np.abs(np.sum(lon_diffs >= 0., axis=0) -
                         np.sum(lon_diffs <= 0., axis=0))
    central_lons = lons[lon_rel_loc == np.min(lon_rel_loc)]
    # make sure the central two aren't too far apart.
    assert np.max(central_lons) - np.min(central_lons) < 90
    median_lon = np.median(central_lons)

    lat_rel_loc = np.abs(np.sum(lat_diffs >= 0., axis=0) -
                         np.sum(lat_diffs <= 0., axis=0))
    central_lats = lats[lat_rel_loc == np.min(lat_rel_loc)]
    median_lat = np.median(central_lats)

    width = angles.geographic_distance(western_most, median_lat,
                                       eastern_most, median_lat)
    height = angles.geographic_distance(median_lon, northern_most,
                                        median_lon, southern_most)
    if lon_pad is None:
        lon_pad = pad * np.abs(angles.angle_diff(eastern_most,
                                                 western_most))
    if lat_pad is None:
        lat_pad = pad * np.abs(angles.angle_diff(northern_most,
                                                 southern_most))

    return {'llcrnrlon': western_most - lon_pad,
            'urcrnrlon': eastern_most + lon_pad,
            'urcrnrlat': northern_most + lat_pad,
            'llcrnrlat': southern_most - lat_pad,
            'width': width * (1. + 2 * pad),
            'height': height * (1. + 2 * pad),
            'lon_0': median_lon,
            'lat_0': median_lat}
Example #3
0
def angle_slicer(x, low, high, delta=None, tolerance=4):
    """
    A function which returns a slicer that will extract all
    the values in x that fall between low and high with a prescribed
    grid delta.
    """
    assert x.ndim == 1
    # determine the native grid delta.
    diffs = angles.angle_diff(x)
    diffs = np.unique(np.round(diffs, tolerance))
    # assume angles are equally spaced.
    assert diffs.size == 1
    native_delta = np.abs(diffs[0])
    # round to the nearest native stride but make sure we dont hit zero
    delta = delta or native_delta
    stride = max(1, int(np.round(delta / native_delta)))

    def fully_contained(y):
        """Returns true if the range low/high is contained in y"""
        return (np.any(angles.angle_diff(low, y) >= 0) and
                np.any(angles.angle_diff(high, y) <= 0))

    high_diff = angles.angle_diff(x, high)
    low_diff = angles.angle_diff(x, low)
    inside_domain = np.logical_and(high_diff <= 0, low_diff >= 0)
    # if the edges are in the domain, make sure everything is
    # otherwise we will have to use multiple slicers.
    if inside_domain[0] and inside_domain[-1]:
        assert np.all(inside_domain)
    # returns the index which minimizes x conditional on 'cond' being true
    def argmin_conditional(x, cond):
        x = x.copy()
        x[np.logical_not(cond)] = np.nan
        return np.nanargmin(x)
    # find the indices of the values that fall on or just outside the
    # lower and higher limits.
    low_ind = argmin_conditional(-low_diff, low_diff <= 0.)
    high_ind = argmin_conditional(high_diff, high_diff >= 0.)
    # if the angles in x are descending we flip the indices around
    if low_ind > high_ind:
        high_ind, low_ind = low_ind, high_ind

    possible_slicers = [slice(low_ind - i, high_ind - i + 1, stride)
                        for i in range(stride)]
    # we strive to find a slicer that yield the fewest number of point
    # that fully contains the domain.
    possible_slicers = [s for s in possible_slicers if fully_contained(x[s])]
    if not len(possible_slicers):
        raise ValueError("Couldn't find a slice that would provide contain "
                         "the desired domain")
    return possible_slicers[0]
Example #4
0
    def test_angle_diff(self):

        pairs = [((180, 180), 0.),
                 ((181, 181), 0.),
                 ((-1, -1), 0.),
                 ((180, -180), 0.),
                 # note, wrapped values get sent to -180
                 ((180, 0), -180.),
                 ((0, 180), -180.),
                 ((360, 180), -180.),
                 ((361, 181), -180.),
                 ((179, 0), 179.),
                 ((0, 179), -179.),
                 ((360, 181), 179.),
                 ((1, 182), 179.),
                 ((181, 179), 2.),
                 ((179, 181), -2.),
                 ((361, 359), 2.),
                 ((-1, 1.), -2.),
                 ((np.pi - 0.1, 0., False), np.pi - 0.1),
                 ((np.pi - 0.1, -np.pi + 0.1, False), -0.2)]

        for args, expected in pairs:
            actual = angles.angle_diff(*args)
            self.assertAlmostEqual(expected,
                                   actual,
                                   places=6,
                                   msg=("actual: %f "
                                        "expected: %f "
                                        "args: %s"
                                        % (actual, expected,
                                           args)))
Example #5
0
 def test_maximum_error(self):
     from slocum.lib import angles
     ds = self.get_data()
     scheme = self.get_scheme()
     actual = roundtrip(scheme, ds)
     diff = angles.angle_diff(ds[scheme.variable_name].values,
                              actual[scheme.variable_name].values)
     self.assertTrue(np.all(np.abs(diff) <= np.pi/16))
Example #6
0
    def __init__(self, fcst, velocity_variable,
                 speed_units='knots', ax=None, fig=None,
                  **kwdargs):
        self.variable = velocity_variable
        self.speed_units = speed_units
        # set the default colormap if needed
        kwdargs['cmap'] = kwdargs.get('cmap', velocity_cmap)

        # convert the bins to knots
        bins = xray.Variable('bins',
                             velocity_variable.speed_bins.copy(),
                             {'units': velocity_variable.units})
        _, self.bins, _ = units.convert_units(bins, speed_units)

        default_norm = plt.cm.colors.BoundaryNorm(self.bins, self.bins.size)
        kwdargs['norm'] = kwdargs.get('norm', default_norm)

        self.variable = velocity_variable
        self.ax, self.fig = utils.axis_figure(ax, fig)

        # add the color bar
        self.cax = self.fig.add_axes([0.92, 0.05, 0.03, 0.9])
        cbar = mpl.colorbar.ColorbarBase(self.cax,
                                         cmap=kwdargs['cmap'],
                                         norm=kwdargs['norm'])
        cbar.set_label("Knots")

        fcst = self.normalize(fcst)
        # use the longitude grid to define the radius of the circles
        sorted_lats = np.sort(fcst['latitude'].values)
        resol = np.median(angles.angle_diff(sorted_lats[1:],
                                            sorted_lats[:-1]))

        # create the map
        self.m = utils.get_basemap(fcst, ax=self.ax,
                                   lon_pad=0.75 * resol,
                                   lat_pad = 0.75 * resol)
        def create_circle(one_loc):
            # determine the circle center
            x, y = self.m(one_loc['longitude'].values,
                          one_loc['latitude'].values)
            speeds = one_lonlat[self.variable.speed_name].values
            dirs = one_lonlat[self.variable.direction_name].values
            orientation = self.variable.direction_orientation
            return DirectionCircle(x, y,
                                   speeds=np.atleast_1d(speeds),
                                   directions=np.atleast_1d(dirs),
                                   orientation=orientation,
                                   radius=0.4 * resol,
                                   ax=self.ax, **kwdargs)

        self.circles = [[create_circle(one_lonlat)
                         for lo, one_lonlat in one_lat.groupby('longitude')]
                        for la, one_lat in fcst.groupby('latitude')]
        self.set_title(fcst)
Example #7
0
 def fully_contained(y):
     """Returns true if the range low/high is contained in y"""
     return (np.any(angles.angle_diff(low, y) >= 0) and
             np.any(angles.angle_diff(high, y) <= 0))