In [1]:
import xarray as xr
import ndpyramid
import upath
import ipywidgets
import datatree
In [2]:
path = "s3://carbonplan-data-viewer/demo/leap-10-2023/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn.zarr"
ds = xr.open_dataset(path, engine='zarr', chunks={})
ds
Out[2]:
<xarray.Dataset>
Dimensions:  (lat: 600, lon: 1440, time: 73)
Coordinates:
  * lat      (lat) float64 -59.88 -59.62 -59.38 -59.12 ... 89.38 89.62 89.88
  * lon      (lon) float64 0.125 0.375 0.625 0.875 ... 359.1 359.4 359.6 359.9
  * time     (time) datetime64[ns] 1950-01-01T12:00:00 ... 1951-12-22T12:00:00
Data variables:
    tasmax   (time, lat, lon) float32 dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>
Attributes: (12/19)
    Conventions:           CF-1.7
    activity:              NEX-GDDP-CMIP6
    cmip6_institution_id:  CSIRO-ARCCSS
    cmip6_license:         CC-BY-SA 4.0
    cmip6_source_id:       ACCESS-CM2
    contact:               Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget...
    ...                    ...
    resolution_id:         0.25 degree
    scenario:              historical
    source:                BCSD
    title:                 ACCESS-CM2, r1i1p1f1, historical, global downscale...
    variant_label:         r1i1p1f1
    version:               1.0
xarray.Dataset
    • lat: 600
    • lon: 1440
    • time: 73
    • lat
      (lat)
      float64
      -59.88 -59.62 ... 89.62 89.88
      axis :
      Y
      long_name :
      latitude
      standard_name :
      latitude
      units :
      degrees_north
      array([-59.875, -59.625, -59.375, ...,  89.375,  89.625,  89.875])
    • lon
      (lon)
      float64
      0.125 0.375 0.625 ... 359.6 359.9
      axis :
      X
      long_name :
      longitude
      standard_name :
      longitude
      units :
      degrees_east
      array([1.25000e-01, 3.75000e-01, 6.25000e-01, ..., 3.59375e+02, 3.59625e+02,
             3.59875e+02])
    • time
      (time)
      datetime64[ns]
      1950-01-01T12:00:00 ... 1951-12-...
      axis :
      T
      long_name :
      time
      standard_name :
      time
      array(['1950-01-01T12:00:00.000000000', '1950-01-11T12:00:00.000000000',
             '1950-01-21T12:00:00.000000000', '1950-01-31T12:00:00.000000000',
             '1950-02-10T12:00:00.000000000', '1950-02-20T12:00:00.000000000',
             '1950-03-02T12:00:00.000000000', '1950-03-12T12:00:00.000000000',
             '1950-03-22T12:00:00.000000000', '1950-04-01T12:00:00.000000000',
             '1950-04-11T12:00:00.000000000', '1950-04-21T12:00:00.000000000',
             '1950-05-01T12:00:00.000000000', '1950-05-11T12:00:00.000000000',
             '1950-05-21T12:00:00.000000000', '1950-05-31T12:00:00.000000000',
             '1950-06-10T12:00:00.000000000', '1950-06-20T12:00:00.000000000',
             '1950-06-30T12:00:00.000000000', '1950-07-10T12:00:00.000000000',
             '1950-07-20T12:00:00.000000000', '1950-07-30T12:00:00.000000000',
             '1950-08-09T12:00:00.000000000', '1950-08-19T12:00:00.000000000',
             '1950-08-29T12:00:00.000000000', '1950-09-08T12:00:00.000000000',
             '1950-09-18T12:00:00.000000000', '1950-09-28T12:00:00.000000000',
             '1950-10-08T12:00:00.000000000', '1950-10-18T12:00:00.000000000',
             '1950-10-28T12:00:00.000000000', '1950-11-07T12:00:00.000000000',
             '1950-11-17T12:00:00.000000000', '1950-11-27T12:00:00.000000000',
             '1950-12-07T12:00:00.000000000', '1950-12-17T12:00:00.000000000',
             '1950-12-27T12:00:00.000000000', '1951-01-06T12:00:00.000000000',
             '1951-01-16T12:00:00.000000000', '1951-01-26T12:00:00.000000000',
             '1951-02-05T12:00:00.000000000', '1951-02-15T12:00:00.000000000',
             '1951-02-25T12:00:00.000000000', '1951-03-07T12:00:00.000000000',
             '1951-03-17T12:00:00.000000000', '1951-03-27T12:00:00.000000000',
             '1951-04-06T12:00:00.000000000', '1951-04-16T12:00:00.000000000',
             '1951-04-26T12:00:00.000000000', '1951-05-06T12:00:00.000000000',
             '1951-05-16T12:00:00.000000000', '1951-05-26T12:00:00.000000000',
             '1951-06-05T12:00:00.000000000', '1951-06-15T12:00:00.000000000',
             '1951-06-25T12:00:00.000000000', '1951-07-05T12:00:00.000000000',
             '1951-07-15T12:00:00.000000000', '1951-07-25T12:00:00.000000000',
             '1951-08-04T12:00:00.000000000', '1951-08-14T12:00:00.000000000',
             '1951-08-24T12:00:00.000000000', '1951-09-03T12:00:00.000000000',
             '1951-09-13T12:00:00.000000000', '1951-09-23T12:00:00.000000000',
             '1951-10-03T12:00:00.000000000', '1951-10-13T12:00:00.000000000',
             '1951-10-23T12:00:00.000000000', '1951-11-02T12:00:00.000000000',
             '1951-11-12T12:00:00.000000000', '1951-11-22T12:00:00.000000000',
             '1951-12-02T12:00:00.000000000', '1951-12-12T12:00:00.000000000',
             '1951-12-22T12:00:00.000000000'], dtype='datetime64[ns]')
    • tasmax
      (time, lat, lon)
      float32
      dask.array<chunksize=(1, 600, 1440), meta=np.ndarray>
      cell_measures :
      area: areacella
      cell_methods :
      area: mean time: maximum
      comment :
      maximum near-surface (usually, 2 meter) air temperature (add cell_method attribute 'time: max')
      long_name :
      Daily Maximum Near-Surface Air Temperature
      standard_name :
      air_temperature
      units :
      K
      Array Chunk
      Bytes 240.60 MiB 3.30 MiB
      Shape (73, 600, 1440) (1, 600, 1440)
      Dask graph 73 chunks in 2 graph layers
      Data type float32 numpy.ndarray
      1440 600 73
    • lat
      PandasIndex
      PandasIndex(Index([-59.875, -59.625, -59.375, -59.125, -58.875, -58.625, -58.375, -58.125,
             -57.875, -57.625,
             ...
              87.625,  87.875,  88.125,  88.375,  88.625,  88.875,  89.125,  89.375,
              89.625,  89.875],
            dtype='float64', name='lat', length=600))
    • lon
      PandasIndex
      PandasIndex(Index([  0.125,   0.375,   0.625,   0.875,   1.125,   1.375,   1.625,   1.875,
               2.125,   2.375,
             ...
             357.625, 357.875, 358.125, 358.375, 358.625, 358.875, 359.125, 359.375,
             359.625, 359.875],
            dtype='float64', name='lon', length=1440))
    • time
      PandasIndex
      PandasIndex(DatetimeIndex(['1950-01-01 12:00:00', '1950-01-11 12:00:00',
                     '1950-01-21 12:00:00', '1950-01-31 12:00:00',
                     '1950-02-10 12:00:00', '1950-02-20 12:00:00',
                     '1950-03-02 12:00:00', '1950-03-12 12:00:00',
                     '1950-03-22 12:00:00', '1950-04-01 12:00:00',
                     '1950-04-11 12:00:00', '1950-04-21 12:00:00',
                     '1950-05-01 12:00:00', '1950-05-11 12:00:00',
                     '1950-05-21 12:00:00', '1950-05-31 12:00:00',
                     '1950-06-10 12:00:00', '1950-06-20 12:00:00',
                     '1950-06-30 12:00:00', '1950-07-10 12:00:00',
                     '1950-07-20 12:00:00', '1950-07-30 12:00:00',
                     '1950-08-09 12:00:00', '1950-08-19 12:00:00',
                     '1950-08-29 12:00:00', '1950-09-08 12:00:00',
                     '1950-09-18 12:00:00', '1950-09-28 12:00:00',
                     '1950-10-08 12:00:00', '1950-10-18 12:00:00',
                     '1950-10-28 12:00:00', '1950-11-07 12:00:00',
                     '1950-11-17 12:00:00', '1950-11-27 12:00:00',
                     '1950-12-07 12:00:00', '1950-12-17 12:00:00',
                     '1950-12-27 12:00:00', '1951-01-06 12:00:00',
                     '1951-01-16 12:00:00', '1951-01-26 12:00:00',
                     '1951-02-05 12:00:00', '1951-02-15 12:00:00',
                     '1951-02-25 12:00:00', '1951-03-07 12:00:00',
                     '1951-03-17 12:00:00', '1951-03-27 12:00:00',
                     '1951-04-06 12:00:00', '1951-04-16 12:00:00',
                     '1951-04-26 12:00:00', '1951-05-06 12:00:00',
                     '1951-05-16 12:00:00', '1951-05-26 12:00:00',
                     '1951-06-05 12:00:00', '1951-06-15 12:00:00',
                     '1951-06-25 12:00:00', '1951-07-05 12:00:00',
                     '1951-07-15 12:00:00', '1951-07-25 12:00:00',
                     '1951-08-04 12:00:00', '1951-08-14 12:00:00',
                     '1951-08-24 12:00:00', '1951-09-03 12:00:00',
                     '1951-09-13 12:00:00', '1951-09-23 12:00:00',
                     '1951-10-03 12:00:00', '1951-10-13 12:00:00',
                     '1951-10-23 12:00:00', '1951-11-02 12:00:00',
                     '1951-11-12 12:00:00', '1951-11-22 12:00:00',
                     '1951-12-02 12:00:00', '1951-12-12 12:00:00',
                     '1951-12-22 12:00:00'],
                    dtype='datetime64[ns]', name='time', freq=None))
  • Conventions :
    CF-1.7
    activity :
    NEX-GDDP-CMIP6
    cmip6_institution_id :
    CSIRO-ARCCSS
    cmip6_license :
    CC-BY-SA 4.0
    cmip6_source_id :
    ACCESS-CM2
    contact :
    Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget Thrasher: bridget@climateanalyticsgroup.org
    disclaimer :
    This data is considered provisional and subject to change. This data is provided as is without any warranty of any kind, either express or implied, arising by law or otherwise, including but not limited to warranties of completeness, non-infringement, accuracy, merchantability, or fitness for a particular purpose. The user assumes all risk associated with the use of, or inability to use, this data.
    external_variables :
    areacella
    frequency :
    day
    institution :
    NASA Earth Exchange, NASA Ames Research Center, Moffett Field, CA 94035
    product :
    output
    realm :
    atmos
    references :
    BCSD method: Thrasher et al., 2012, Hydrol. Earth Syst. Sci.,16, 3309-3314. Ref period obs: latest version of the Princeton Global Meteorological Forcings (http://hydrology.princeton.edu/data.php), based on Sheffield et al., 2006, J. Climate, 19 (13), 3088-3111.
    resolution_id :
    0.25 degree
    scenario :
    historical
    source :
    BCSD
    title :
    ACCESS-CM2, r1i1p1f1, historical, global downscaled CMIP6 climate projection data
    variant_label :
    r1i1p1f1
    version :
    1.0
In [3]:
ds.tasmax.isel(time=range(3)).plot(col='time', robust=True);
No description has been provided for this image
In [4]:
def make_pyramids(*, ds, levels:int=3, projection:str) -> str:
    projections = {'mercator': 'web-mercator', 'equirectangular': 'equidistant-cylindrical'}
    path = f"s3://carbonplan-data-viewer/demo/leap-10-2023/{projection}_{levels}_pyramid_tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn.zarr"
    if not upath.UPath(path).exists():
        dt = ndpyramid.pyramid_regrid(ds=ds, levels=levels, projection=projections[projection])
        dt.to_zarr(path, consolidated=True, mode='w')
        time = xr.conventions.encode_cf_variable(dt['0'].time).data.tolist()
    else:
        dt = datatree.open_datatree(path, engine='zarr', chunks={}, decode_times=False)
        time = dt['0'].time.data.tolist()
    print(path)
    source = f"https://carbonplan-data-viewer.s3.amazonaws.com/demo/leap-10-2023/{projection}_{levels}_pyramid_tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn.zarr"
    dims = ds.tasmax.dims
    variable = "tasmax"
    return {"source" : source, "dimensions" : dims, "variable" : variable, 'time': time, 'projection': projection}
In [5]:
%%time

parameters = make_pyramids(ds=ds, levels=3, projection='mercator')
s3://carbonplan-data-viewer/demo/leap-10-2023/mercator_3_pyramid_tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn.zarr
CPU times: user 482 ms, sys: 62.5 ms, total: 544 ms
Wall time: 7.09 s
In [6]:
import carbonplan_maps
In [7]:
map = carbonplan_maps.Widget(
    source=parameters['source'],
    variable=parameters['variable'],
    clim=(200, 300),
    selector={'time': parameters['time'][0]},
    projection=parameters['projection']
)
map
Out[7]:
In [8]:
from ipywidgets import Label

def dashboard(*, map_widget, min_value, max_value, step):

    clim = ipywidgets.FloatRangeSlider(min=200, max=350, description="Clim:")
    opacity = ipywidgets.FloatSlider(min=0, max=1, step=0.001, description="Opacity:")
    region = ipywidgets.Checkbox(description="Region")
    time_widget = ipywidgets.IntSlider(min=min_value, max=max_value, step=step, description="Time:")
     # Linking selector and time_widget

    def update_selector(change):
        map_widget.selector = {'time': change['new']}

    def update_time_widget(change):
        time_widget.value = change['new']['time']

    time_widget.observe(update_selector, names='value')
    map_widget.observe(update_time_widget, names='selector')
    
    
    # Linking
    ipywidgets.jslink((map_widget, "clim"), (clim, "value"))
    ipywidgets.jslink((map_widget, "opacity"), (opacity, "value"))
    ipywidgets.jslink((map_widget, "region"), (region, "value"))

    # Reset Button
    reset_button = ipywidgets.Button(description="Reset")

    def on_reset_button_clicked(_):
        # Reset logic here
        pass

    reset_button.on_click(on_reset_button_clicked)
    
    # Layout
    visual_controls = ipywidgets.HBox([opacity, clim])
    time_and_region = ipywidgets.HBox([time_widget, region, reset_button])

    dash = ipywidgets.VBox([
        Label("Visual Controls"),
        visual_controls,
        Label("Time & Region"),
        time_and_region,
        Label("Map"),
        map_widget,
    ])
    
    return dash

time = parameters['time']
selector = {'time': parameters['time'][0]}
dashboard(map_widget=map, min_value=min(time), max_value=max(time), step=time[1] - time[0])
Out[8]:
In [ ]: