Skip to content

Latest commit

 

History

History
183 lines (140 loc) · 4.75 KB

ocean_mask.md

File metadata and controls

183 lines (140 loc) · 4.75 KB
import marc_analysis as ma
import xray

%matplotlib inline
import matplotlib.pyplot as plt
data = xray.open_dataset(
    "/Users/daniel/Desktop/MARC_AIE/F2000/"
    "arg_comp/arg_comp.cam2.h0.0008-04.nc")

Use the total precipitation data as a test dataset.

def extract_feature(ds, feature='ocean'):
    """ Extract a masked dataset with only the requested feature
    available for analysis.

    Parameters
    ----------
    ds : Dataset or DataArray
        The Dataset or DataArray to mask
    feature : str
        A string, either "ocean" or "land" indicating which
        feature to preserve in the dataset; the inverse of this feature
        will be masked.

    Returns
    -------
    masked_ds : Dataset or DataArray
        The original Dataset or DataArray with the inverse of the requested
        feature masked out.
    """
    
    _FEATURE_MAP = {
        'ocean': 0., 'land': 1., 'ice': 2.,
    }
    feature_key_str = " ".join(["'%s'" % s for s in _FEATURE_MAP])
    
    
    if not (feature in _FEATURE_MAP):
        raise ValueError("Expected one of [%s] as feature; got '%s'"
                            % (feature_key_str, feature))
    

    mask = (ma.masks['ORO'] == _FEATURE_MAP[feature])
    return ds.where(mask)
    
d = extract_feature(data, 'land')
ma.geo_plot(d['TS'].squeeze())
from marc_analysis.utilities import area_grid
from xray import DataArray
import numpy as np

def global_avg(data, weights=None, dims=['lon', 'lat']):
    """ Compute (area-weighted) global average over a DataArray
    or Dataset. If `weights` are not passed, they will be computed
    by using the areas of each grid cell in the dataset.

    .. note::
        Handles missing values (nans and infs).

    """

    if weights is None: # Compute gaussian weights in latitude
        weights = area_grid(data.lon, data.lat)
        gw = weights.sum('lon')
        weights = 2.*gw/gw.sum('lat')

    if isinstance(data, DataArray):

        is_null = ~np.isfinite(data)
        if np.any(is_null):
            data = np.ma.masked_where(is_null, data)
                                      
        # return np.average(data, weights=weights)
        # return np.sum(data*weights)/np.sum(weights)
        return (data*weights/weights.sum()).sum(dims)

ds = data['PRECC']

for f in ['land', 'ocean']:
    d = extract_feature(ds, f).squeeze()
    print (f)
    print (ds.mean()) 
#     print (global_avg(ds))
    print (d.mean()) 
#     print (global_avg(d))

Use the Natural Earth Data physical boundaries shapefiles to help extract the continent/land mask.

import geopandas
import os

continents = geopandas.read_file("/Users/daniel/Downloads/ne_110m_land/ne_110m_land.shp")
oceans = geopandas.read_file("/Users/daniel/Downloads/ne_110m_ocean/ne_110m_ocean.shp")

ocean_shapes = [(shape, n) for n, shape in enumerate(oceans.geometry)]
contient_shapes = [(shape, n) for n, shape in enumerate(continents.geometry)]
o1 = oceans.geometry.ix[1]
lons, lats = o1.exterior.coords.xy

lons

Rasterize example using rasterio and geopandas following Stephan Hoyer

from rasterio import features
from affine import Affine
import numpy as np


def shift_lons(lon):
    """ Convert a vector of longitudes in the 0 < lon < 360 coordinate
    system to longitudes in the -180 < 0 < 180 coordinate system. """
    lon = np.asarray(lon)
    mask = np.where(lon > 180)
    lon[mask] = -1.*(360. - lon[mask])

    return lon
    
def transform_from_latlon(lat, lon):
    lat = np.asarray(lat)
    lon = np.asarray(lon)
    trans = Affine.translation(lon[0], lat[0])
    scale = Affine.scale(lon[1] - lon[0], lat[1] - lat[0])
    return trans * scale

def rasterize(shapes, coords, fill=np.nan, **kwargs):
    """Rasterize a list of (geometry, fill_value) tuples onto the given
    xray coordinates. This only works for 1d latitude and longitude
    arrays.
    """
    lon = coords['lon'].data
    lat = coords['lat'].data
    
    # Check if in 0 < lon < 360 coordinates; if so, convert to
    # -180 < lon < 180
    if np.any(lon > 180): 
        lon = shift_lons(lon)
        
    transform = transform_from_latlon(lat, lon)
    out_shape = (len(lat), len(lon))
    raster = features.rasterize(shapes, out_shape=out_shape,
                                fill=fill, transform=transform,
                                dtype=float, **kwargs)
    return xray.DataArray(raster, coords=coords, dims=('lat', 'lon'))
ocean_mask = rasterize(ocean_shapes, ds.coords)
print(ocean_mask.lon)

%matplotlib inline
import matplotlib.pyplot as plt

plt.figure()
ma.geo_plot(data.where(~np.isnan(ocean_mask))['PRECC'].squeeze())
plt.figure()
ma.geo_plot(data['PRECC'].squeeze())
ds.lat.min()