Пример #1
0
def test_cfprojection_api():
    """Test the basic API of the projection interface."""
    attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'earth_radius': 6367000}
    proj = CFProjection(attrs)

    assert proj['earth_radius'] == 6367000
    assert proj.to_dict() == attrs
    assert str(proj) == 'Projection: lambert_conformal_conic'
Пример #2
0
def test_cfprojection_api():
    """Test the basic API of the projection interface."""
    attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'earth_radius': 6367000}
    proj = CFProjection(attrs)

    assert proj['earth_radius'] == 6367000
    assert proj.to_dict() == attrs
    assert str(proj) == 'Projection: lambert_conformal_conic'
Пример #3
0
def test_lcc():
    """Test handling lambert conformal conic projection."""
    attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'earth_radius': 6367000,
             'standard_parallel': [25, 30]}
    proj = CFProjection(attrs)

    crs = proj.to_cartopy()
    assert isinstance(crs, ccrs.LambertConformal)
    assert crs.proj4_params['lat_1'] == 25
    assert crs.proj4_params['lat_2'] == 30
    assert crs.globe.to_proj4_params()['ellps'] == 'sphere'
Пример #4
0
def test_lcc():
    """Test handling lambert conformal conic projection."""
    attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'earth_radius': 6367000,
             'standard_parallel': [25, 30]}
    proj = CFProjection(attrs)

    crs = proj.to_cartopy()
    assert isinstance(crs, ccrs.LambertConformal)
    assert crs.proj4_params['lat_1'] == 25
    assert crs.proj4_params['lat_2'] == 30
    assert crs.globe.to_proj4_params()['ellps'] == 'sphere'
Пример #5
0
def test_globe():
    """Test handling building a cartopy globe."""
    attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'earth_radius': 6367000,
             'standard_parallel': 25}
    proj = CFProjection(attrs)

    crs = proj.to_cartopy()
    globe_params = crs.globe.to_proj4_params()

    assert globe_params['ellps'] == 'sphere'
    assert globe_params['a'] == 6367000
    assert globe_params['b'] == 6367000
Пример #6
0
def test_globe_spheroid():
    """Test handling building a cartopy globe that is not spherical."""
    attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'semi_major_axis': 6367000,
             'semi_minor_axis': 6360000}
    proj = CFProjection(attrs)

    crs = proj.to_cartopy()
    globe_params = crs.globe.to_proj4_params()

    assert 'ellps' not in globe_params
    assert globe_params['a'] == 6367000
    assert globe_params['b'] == 6360000
Пример #7
0
def test_globe():
    """Test handling building a cartopy globe."""
    attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'earth_radius': 6367000,
             'standard_parallel': 25}
    proj = CFProjection(attrs)

    crs = proj.to_cartopy()
    globe_params = crs.globe.to_proj4_params()

    assert globe_params['ellps'] == 'sphere'
    assert globe_params['a'] == 6367000
    assert globe_params['b'] == 6367000
Пример #8
0
def test_globe_spheroid():
    """Test handling building a cartopy globe that is not spherical."""
    attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'semi_major_axis': 6367000,
             'semi_minor_axis': 6360000}
    proj = CFProjection(attrs)

    crs = proj.to_cartopy()
    globe_params = crs.globe.to_proj4_params()

    assert 'ellps' not in globe_params
    assert globe_params['a'] == 6367000
    assert globe_params['b'] == 6360000
Пример #9
0
def test_aea():
    """Test handling albers equal area projection."""
    attrs = {
        'grid_mapping_name': 'albers_conical_equal_area',
        'earth_radius': 6367000,
        'standard_parallel': [20, 50]
    }
    proj = CFProjection(attrs)

    crs = proj.to_cartopy()
    assert isinstance(crs, ccrs.AlbersEqualArea)
    assert crs.proj4_params['lat_1'] == 20
    assert crs.proj4_params['lat_2'] == 50
    assert crs.globe.to_proj4_params()['ellps'] == 'sphere'
Пример #10
0
def test_geostationary_fixed_angle():
    """Test handling geostationary information that gives fixed angle instead of sweep."""
    attrs = {'grid_mapping_name': 'geostationary', 'fixed_angle_axis': 'y'}
    crs = CFProjection(attrs).to_cartopy()

    assert isinstance(crs, ccrs.Geostationary)
    assert crs.proj4_params['sweep'] == 'x'
Пример #11
0
def test_bad_projection_raises():
    """Test behavior when given an unknown projection."""
    attrs = {'grid_mapping_name': 'unknown'}
    with pytest.raises(ValueError) as exc:
        CFProjection(attrs).to_cartopy()

    assert 'Unhandled projection' in str(exc.value)
Пример #12
0
def test_inverse_flattening_0():
    """Test new code for dealing the case where inverse_flattening = 0."""
    attrs = {
        'grid_mapping_name': 'lambert_conformal_conic',
        'semi_major_axis': 6367000,
        'semi_minor_axis': 6367000,
        'inverse_flattening': 0
    }
    proj = CFProjection(attrs)

    crs = proj.to_cartopy()
    globe_params = crs.globe.to_proj4_params()

    assert globe_params['ellps'] == 'sphere'
    assert globe_params['a'] == 6367000
    assert globe_params['b'] == 6367000
Пример #13
0
def test_coord_helper_da_latlon():
    """Provide a DataArray with lat/lon coords for coord helpers."""
    return xr.DataArray(
        np.arange(9).reshape((3, 3)),
        dims=('y', 'x'),
        coords={
            'latitude': xr.DataArray(
                np.array(
                    [[34.99501239, 34.99875307, 35.],
                     [35.44643155, 35.45019292, 35.45144675],
                     [35.89782579, 35.90160784, 35.90286857]]
                ),
                dims=('y', 'x')
            ),
            'longitude': xr.DataArray(
                np.array(
                    [[-101.10219213, -100.55111288, -100.],
                     [-101.10831414, -100.55417417, -100.],
                     [-101.11450453, -100.55726965, -100.]]
                ),
                dims=('y', 'x')
            ),
            'crs': CFProjection(sample_cf_attrs)
        }
    )
Пример #14
0
def test_coord_helper_da_yx():
    """Provide a DataArray with y/x coords for coord helpers."""
    return xr.DataArray(np.arange(9).reshape((3, 3)),
                        dims=('y', 'x'),
                        coords={'y': np.linspace(0, 1e5, 3),
                                'x': np.linspace(-1e5, 0, 3),
                                'crs': CFProjection(sample_cf_attrs)})
Пример #15
0
def generate_rectangular_grid(nx, ny, dx, dy, cf_attrs, x0=None, y0=None):
    """Generate an xarray Dataset representing a regular horizontal grid in projected space.

    Parameters
    ----------
    nx : int
        Number of grid points in x direction
    ny : int
        Numver of grid points in y direction
    dx : float
        Grid spacing in projected space in x direction
    dy : float
        Grid spacing in projected space in y direction
    cf_attrs : dict
        Dictionary of attributes describing the projection following the CF Conventions
    x0 : float, optional
        x coordinate of lower-left corner of grid. If None, defaults to centering the grid on
        the projection origin.
    y0 : float, optional
        y coordinate of lower-left corner of grid. If None, defaults to centering the grid on
        the projection origin.
    
    Returns
    -------
    xarray.Dataset
        Dataset describing grid, following CF Conventions
    """
    crs = CFProjection(cf_attrs).to_cartopy()
    if x0 is None or y0 is None:
        x0 = -(nx - 1) / 2 * dx
        y0 = -(ny - 1) / 2 * dy

    # Generate grid
    x = np.arange(nx) * dx + x0
    y = np.arange(ny) * dy + y0
    xx, yy = np.meshgrid(x, y)
    lonlats = ccrs.PlateCarree().transform_points(crs, xx, yy)
    lon = lonlats[..., 0]
    lat = lonlats[..., 1]

    # Create Dataset
    ds = xr.Dataset(coords={
        'lon': (['y', 'x'], lon),
        'lat': (['y', 'x'], lat),
        'y': y,
        'x': x
    })
    ds['lon'].attrs = {'standard_name': 'longitude', 'units': 'degrees_east'}
    ds['lat'].attrs = {'standard_name': 'latitude', 'units': 'degrees_north'}
    ds['y'].attrs = {
        'standard_name': 'projection_y_coordinate',
        'units': 'meter'
    }
    ds['x'].attrs = {
        'standard_name': 'projection_x_coordinate',
        'units': 'meter'
    }
    ds['projection'] = xr.DataArray(np.array(0), attrs=cf_attrs)

    return ds
Пример #16
0
def test_aea_single_std_parallel():
    """Test albers equal area with one standard parallel."""
    attrs = {
        'grid_mapping_name': 'albers_conical_equal_area',
        'standard_parallel': 20
    }
    crs = CFProjection(attrs).to_cartopy()
    assert isinstance(crs, ccrs.AlbersEqualArea)
    assert crs.proj4_params['lat_1'] == 20
Пример #17
0
def test_polar_stereographic_std_parallel():
    """Test handling a polar stereographic projection that gives a standard parallel."""
    attrs = {'grid_mapping_name': 'polar_stereographic', 'latitude_of_projection_origin': -90,
             'standard_parallel': 60}
    crs = CFProjection(attrs).to_cartopy()

    assert isinstance(crs, ccrs.Stereographic)
    assert crs.proj4_params['lat_0'] == -90
    assert crs.proj4_params['lat_ts'] == 60
Пример #18
0
def test_cfprojection_arg_mapping():
    """Test the projection mapping arguments."""
    source = {'source': 'a', 'longitude_of_projection_origin': -100}

    # 'dest' should be the argument in the output, with the value from source
    mapping = [('dest', 'source')]

    kwargs = CFProjection.build_projection_kwargs(source, mapping)
    assert kwargs == {'dest': 'a', 'central_longitude': -100}
Пример #19
0
def test_cfprojection_arg_mapping():
    """Test the projection mapping arguments."""
    source = {'source': 'a', 'longitude_of_projection_origin': -100}

    # 'dest' should be the argument in the output, with the value from source
    mapping = [('dest', 'source')]

    kwargs = CFProjection.build_projection_kwargs(source, mapping)
    assert kwargs == {'dest': 'a', 'central_longitude': -100}
Пример #20
0
def test_lcc_single_std_parallel():
    """Test lambert conformal projection with one standard parallel."""
    attrs = {
        'grid_mapping_name': 'lambert_conformal_conic',
        'standard_parallel': 25
    }
    crs = CFProjection(attrs).to_cartopy()
    assert isinstance(crs, ccrs.LambertConformal)
    assert crs.proj4_params['lat_1'] == 25
Пример #21
0
def test_mercator_scale_factor():
    """Test handling a mercator projection with a scale factor."""
    attrs = {
        'grid_mapping_name': 'mercator',
        'scale_factor_at_projection_origin': 0.9
    }
    crs = CFProjection(attrs).to_cartopy()

    assert isinstance(crs, ccrs.Mercator)
    assert crs.proj4_params['k_0'] == 0.9
Пример #22
0
def test_stereographic():
    """Test handling a stereographic projection."""
    attrs = {'grid_mapping_name': 'stereographic', 'scale_factor_at_projection_origin': 0.9,
             'longitude_of_projection_origin': -100, 'latitude_of_projection_origin': 60}
    crs = CFProjection(attrs).to_cartopy()

    assert isinstance(crs, ccrs.Stereographic)
    assert crs.proj4_params['lon_0'] == -100
    assert crs.proj4_params['lat_0'] == 60
    assert crs.proj4_params['k_0'] == 0.9
Пример #23
0
def test_mercator():
    """Test handling a mercator projection."""
    attrs = {'grid_mapping_name': 'mercator', 'standard_parallel': 25,
             'longitude_of_projection_origin': -100, 'false_easting': 0, 'false_westing': 0,
             'central_latitude': 0}
    crs = CFProjection(attrs).to_cartopy()

    assert isinstance(crs, ccrs.Mercator)
    assert crs.proj4_params['lat_ts'] == 25
    assert crs.proj4_params['lon_0'] == -100
Пример #24
0
def test_geostationary():
    """Test handling a geostationary projection."""
    attrs = {'grid_mapping_name': 'geostationary', 'perspective_point_height': 35000000,
             'longitude_of_projection_origin': -100, 'sweep_angle_axis': 'x',
             'latitude_of_projection_origin': 0}
    crs = CFProjection(attrs).to_cartopy()

    assert isinstance(crs, ccrs.Geostationary)
    assert crs.proj4_params['h'] == 35000000
    assert crs.proj4_params['lon_0'] == -100
    assert crs.proj4_params['sweep'] == 'x'
Пример #25
0
def test_lcc_minimal():
    """Test handling lambert conformal conic projection with minimal attributes."""
    attrs = {'grid_mapping_name': 'lambert_conformal_conic'}
    crs = CFProjection(attrs).to_cartopy()
    assert isinstance(crs, ccrs.LambertConformal)
Пример #26
0
def test_ne():
    """Test that two CFProjection instances are not equal when attrs differs."""
    cf_proj_1 = CFProjection({'grid_mapping_name': 'latitude_longitude'})
    cf_proj_2 = CFProjection({'grid_mapping_name': 'lambert_conformal_conic'})
    assert cf_proj_1 != cf_proj_2
Пример #27
0
def test_eq():
    """Test that two CFProjection instances are equal given that they have the same attrs."""
    attrs = {'grid_mapping_name': 'latitude_longitude'}
    cf_proj_1 = CFProjection(attrs)
    cf_proj_2 = CFProjection(attrs.copy())
    assert cf_proj_1 == cf_proj_2
Пример #28
0
def test_lat_lon():
    """Test handling basic lat/lon projection."""
    attrs = {'grid_mapping_name': 'latitude_longitude'}
    crs = CFProjection(attrs).to_cartopy()
    assert isinstance(crs, ccrs.PlateCarree)
Пример #29
0
def to_stations(da: Var,
                *,
                stations: pd.DataFrame,
                datastore: DataStore = None,
                chunks: dict = None,
                **kwargs) -> xr.DataArray:

    if isinstance(da, camps.Variable):
        da = da(datastore=datastore, chunks=chunks, **kwargs)
    elif isinstance(da, xr.DataArray):
        if chunks:
            da = da.camps.chunk(chunks)

    # Determine x and y
    # Use Projected crs as common crs for grid and station

    try:
        x = da.camps.projx.name
        y = da.camps.projy.name
    except KeyError:
        # projected coordinates do not exist
        if da.camps.grid_mapping:
            # try to make them from grid_mapping and lat/lons
            da = da.metpy.assign_crs(da.camps.grid_mapping)
            da = da.metpy.assign_y_x()
            da = da.drop('metpy_crs')
            x = da.camps.projx.name
            y = da.camps.projy.name
        else:
            # exception for mercator data without grid_mapping
            lat = da.camps.latitude
            if lat.ndim == 1:
                y = lat.name
            lon = da.camps.longitude
            if lon.ndim == 1:
                x = lon.name

            # ensure longitude expressed as degrees east from prime meridian with -179 and 180 bounds
            lon_attrs = lon.attrs
            da[lon.name] = xr.where(lon > 180, lon - 360, lon)
            da[lon.name].attrs = lon_attrs

            # Add the lat lon grid mapping since it didn't have one
            # Latitude and longitude on the WGS 1984 datum
            lat_lon_wgs84 = {
                'grid_mapping_name': "latitude_longitude",
                'longitude_of_prime_meridian': 0.0,
                'semi_major_axis': 6378137.0,
                'inverse_flattening': 298.257223563
            }
            gm = xr.DataArray()
            gm.attrs.update(lat_lon_wgs84)
            da = da.assign_coords({'lat_lon_wgs84': gm})
            da.attrs['grid_mapping'] = 'lat_lon_wgs84'

    pyproj_crs = CFProjection(da.camps.grid_mapping).to_pyproj()
    stations['x'], stations['y'] = Proj(pyproj_crs)(stations.lon.values,
                                                    stations.lat.values)

    # rechunk so that multiple chunks don't span x and y dims
    if da.chunks is not None:
        da = da.chunk({x: -1, y: -1})
        da = da.unify_chunks()

    # load x,y data
    da[x].load()
    da[y].load()

    # make horizonontal space 1-D by stacking x and y
    stacked = da.stack(xy=(x, y))
    gridxy = np.column_stack((stacked[x].data, stacked[y].data))

    stationxy = np.column_stack((stations.x, stations.y))

    tree = cKDTree(gridxy)  # fast nearest neighbor search algorith
    dist_ix = tree.query(
        stationxy)  # find distance to nearest and index of nearest

    # make a grid polygon
    from shapely.geometry import Polygon, MultiPoint, Point
    unit_y = xr.DataArray(np.ones(da[y].shape), dims=da[y].dims)
    unit_x = xr.DataArray(np.ones(da[x].shape), dims=da[x].dims)
    edge_x = edge(da[x] * unit_y)  # broadcast constant x along the y dim
    edge_y = edge(unit_x * da[y])  # broadcast constant y along the x dim

    xy = zip(edge_x, edge_y)
    grid_polygon = Polygon(xy)

    # make station points
    xy = zip(stations.x.values, stations.y.values)
    station_points = MultiPoint(list(xy))

    # determine which stations lie outside grid domain (polygon)
    stations['point_str'] = pd.Series([str(p) for p in station_points])
    stations['ix'] = stations.index
    points_outside_grid = station_points - station_points.intersection(
        grid_polygon)
    if not points_outside_grid.is_empty:
        if isinstance(points_outside_grid, Point):
            points_outside_grid = [points_outside_grid]
        ix_stations_outside_grid = stations.set_index('point_str').loc[[
            str(p) for p in points_outside_grid
        ]].ix
    else:
        ix_stations_outside_grid = list()  # let be empty list

    print(len(ix_stations_outside_grid))

    def nearest_worker(da: xr.DataArray, *, x, y, ix, ix_nan) -> xr.DataArray:
        da = da.stack(station=(x,
                               y))  # squash the horizontal space dims into one

        da = da.isel(station=ix)
        da = da.drop_vars('station')  # remove station coord
        da.loc[{
            'station': ix_nan
        }] = np.nan  # use integer index location to set stations outside grid to missing

        return da

    # make template for expressing the change in shape in map_blocks
    template = da.copy()
    # reshape action
    template = template.stack(
        station=(x, y))  # combine the lat/lon dims into one dim called station
    template = template.isel(
        station=[0] *
        len(stations))  # select only the first; this removes the dim station
    template = template.drop(
        'station'
    )  # drop the multiindex lat/lon coord associated with 'station' from the 0th grid point

    mb_kwargs = dict(x=x, y=y, ix=dist_ix[1], ix_nan=ix_stations_outside_grid)
    da = xr.map_blocks(nearest_worker, da, kwargs=mb_kwargs, template=template)

    # remove any metadata that may be leftover from the grid
    da = da.drop_vars([x, y], errors='ignore')

    # configure station metadata
    # prep station coord with numeric index called 'station'
    stations = stations.reset_index()
    stations.index.set_names('station', inplace=True)
    # assign the new coords
    da = da.assign_coords({'platform_id': stations.call})
    da.platform_id.attrs['standard_name'] = 'platform_id'

    # assign the new coords with numeric index
    da = da.assign_coords({'lat': stations.lat})
    da.lat.attrs['standard_name'] = 'latitude'
    da.lat.attrs['units'] = 'degrees_north'
    da = da.assign_coords({'lon': stations.lon})
    da.lon.attrs['standard_name'] = 'longitude'
    da.lon.attrs['units'] = 'degrees_east'
    # drop the numeric index;
    da = da.reset_index('station', drop=True)

    return da
Пример #30
0
def test_assign_crs_dataset_by_kwargs(test_ds_generic):
    """Test assigning CRS to Dataset by projection kwargs."""
    new_ds = test_ds_generic.metpy.assign_crs(**sample_cf_attrs)
    assert isinstance(new_ds['test'].metpy.cartopy_crs, ccrs.LambertConformal)
    assert new_ds['crs'] == CFProjection(sample_cf_attrs)
Пример #31
0
def test_assign_crs_dataarray_by_argument(test_ds_generic):
    """Test assigning CRS to DataArray by projection dict."""
    da = test_ds_generic['test']
    new_da = da.metpy.assign_crs(sample_cf_attrs)
    assert isinstance(new_da.metpy.cartopy_crs, ccrs.LambertConformal)
    assert new_da['crs'] == CFProjection(sample_cf_attrs)
Пример #32
0
def test_aea_minimal():
    """Test handling albers equal area projection with minimal attributes."""
    attrs = {'grid_mapping_name': 'albers_conical_equal_area'}
    crs = CFProjection(attrs).to_cartopy()
    assert isinstance(crs, ccrs.AlbersEqualArea)