From a1217d56e9b9a2d3949ac49e6c4992b6932045ba Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Tue, 18 Aug 2020 16:46:57 +1200 Subject: [PATCH 01/12] :sparkles: ETL convenience function for converting array to dataframe Adding a new module to deepicedrain for Extract, Transform and Load (ETL) workflows! Putting slices of a 2D array into several columns inside a dataframe is now easier with the array_to_dataframe function. Inspired by https://github.com/dask/dask/issues/5021. The function is generalized so that dask arrays convert to a dask DataFrame, and numpy arrays convert to a pandas DataFrame. --- deepicedrain/README.md | 3 ++ deepicedrain/__init__.py | 1 + deepicedrain/extraload.py | 43 +++++++++++++++++++ deepicedrain/tests/test_array_to_dataframe.py | 37 ++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 deepicedrain/extraload.py create mode 100644 deepicedrain/tests/test_array_to_dataframe.py diff --git a/deepicedrain/README.md b/deepicedrain/README.md index 1dd4c28..6fd52a6 100644 --- a/deepicedrain/README.md +++ b/deepicedrain/README.md @@ -16,3 +16,6 @@ Contents: - Region - Bounding box data class structure that has xarray subsetting capabilities and more! - deltatime_to_utctime - Converts GPS time from an epoch (default is 2018 Jan 1st) to UTC time - lonlat_to_xy - Reprojects longitude/latitude EPSG:4326 coordinates to x/y EPSG:3031 coordinates + +- :card_file_box: extraload.py - Convenience functions for extracting, transforming and loading data + - array_to_dataframe - Turns a 1D/2D numpy/dask array into a tidy pandas/dask dataframe table diff --git a/deepicedrain/__init__.py b/deepicedrain/__init__.py index 1dd8afd..60c15a4 100644 --- a/deepicedrain/__init__.py +++ b/deepicedrain/__init__.py @@ -5,6 +5,7 @@ import deepicedrain from deepicedrain.deltamath import calculate_delta, nanptp, nan_linregress +from deepicedrain.extraload import array_to_dataframe from deepicedrain.spatiotemporal import Region, deltatime_to_utctime, lonlat_to_xy __version__: str = "0.2.1" diff --git a/deepicedrain/extraload.py b/deepicedrain/extraload.py new file mode 100644 index 0000000..4e0e9e3 --- /dev/null +++ b/deepicedrain/extraload.py @@ -0,0 +1,43 @@ +""" +Extract, Tranform and Load (ETL) functions for handling ICESat-2 point clouds. +Copies data seamlessly between different array structures and file formats! +""" +import pandas as pd + + +def array_to_dataframe(array, colname: str = None, startcol: int = 0): + """ + Converts a 1D or 2D data array into a tidy dataframe structure. + An array of shape (m, n) will turn into a table with m rows and n columns. + + These are the possible conversions: + - numpy array -> pandas DataFrame + - dask Array -> dask DataFrame + + Pass in a colname to set the column name. By default, it will automatically + use the array.name attribute in dask Arrays, but this can be overriden. + For 2D arrays, columns will be formatted as 'col_0', 'col_1', 'col_2' and + so on. The startcol argument allows adjustment of the starting column + number, helpful if you prefer starting from 1, e.g. 'col_1', 'col_2', etc. + + See also https://github.com/dask/dask/issues/5021 + """ + if not colname: + colname = array.name if hasattr(array, "name") else "" + + if array.ndim == 1: # 1-dimensional arrays + columns = colname + elif array.ndim == 2: # 2-dimensional arrays + colname += "_" if colname != "" else "" # add underscore to name + columns = [f"{colname}{i+startcol}" for i in range(array.shape[1])] + + try: + # Attempt dask Array to dask DataFrame conversion + dataframe: dask.dataframe.core.DataFrame = array.to_dask_dataframe( + columns=columns + ) + except AttributeError: + # Fallback to converting to pandas.DataFrame + dataframe: pd.DataFrame = pd.DataFrame.from_records(data=array, columns=columns) + + return dataframe diff --git a/deepicedrain/tests/test_array_to_dataframe.py b/deepicedrain/tests/test_array_to_dataframe.py new file mode 100644 index 0000000..344846a --- /dev/null +++ b/deepicedrain/tests/test_array_to_dataframe.py @@ -0,0 +1,37 @@ +""" +Tests the array_to_dataframe function +""" +import dask +import numpy as np +import pandas as pd +import pytest + +from deepicedrain import array_to_dataframe + + +@pytest.mark.parametrize("shape", [(10, 1), (10, 2)]) +def test_numpy_array_to_pandas_dataframe(shape): + """ + Test converting from a numpy.array to a pandas.Dataframe, and ensure that + the colname argument works. + """ + array: np.ndarray = np.ones(shape=shape) + dataframe = array_to_dataframe(array=array) + + assert isinstance(dataframe, pd.DataFrame) + assert len(dataframe.columns) == shape[1] + assert dataframe.columns.to_list() == [str(i) for i in range(shape[1])] + + +@pytest.mark.parametrize("shape", [(10, 1), (10, 2)]) +def test_dask_array_to_dask_dataframe(shape): + """ + Test converting from a dask.array to a dask.dataframe, and ensure that the + startcol argument works. + """ + array: dask.array.core.Array = dask.array.ones(shape=shape, name="varname") + dataframe = array_to_dataframe(array=array, startcol=1) + + assert isinstance(dataframe, dask.dataframe.core.DataFrame) + assert len(dataframe.columns) == shape[1] + assert dataframe.columns.to_list() == [f"varname_{i+1}" for i in range(shape[1])] From b7e576137e69a2b761ba1bd6f49be68e605a14cb Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 19 Aug 2020 15:07:54 +1200 Subject: [PATCH 02/12] :triangular_flag_on_post: Generalize region subset method to DataFrames Make bounding box subsetting work on DataFrames too! This includes pandas, dask and cudf DataFrames. Included a parametrized test for pandas and dask, the cudf one should work too since the APIs are similar. The original xarray.DataArray subsetter code will still work. --- atl11_play.ipynb | 2 +- atl11_play.py | 2 +- atlxi_dhdt.ipynb | 4 ++-- atlxi_dhdt.py | 4 ++-- deepicedrain/spatiotemporal.py | 20 ++++++++++++++------ deepicedrain/tests/test_region.py | 28 +++++++++++++++++++++++++--- 6 files changed, 45 insertions(+), 15 deletions(-) diff --git a/atl11_play.ipynb b/atl11_play.ipynb index 06dc65c..9d46d8c 100644 --- a/atl11_play.ipynb +++ b/atl11_play.ipynb @@ -303,7 +303,7 @@ "# Do the actual computation to find data points within region of interest\n", "placename: str = \"kamb\" # Select Kamb Ice Stream region\n", "region: deepicedrain.Region = regions[placename]\n", - "ds_subset: xr.Dataset = region.subset(ds=ds)\n", + "ds_subset: xr.Dataset = region.subset(data=ds)\n", "ds_subset = ds_subset.unify_chunks()\n", "ds_subset = ds_subset.compute()" ] diff --git a/atl11_play.py b/atl11_play.py index b2c6696..f2a788f 100644 --- a/atl11_play.py +++ b/atl11_play.py @@ -181,7 +181,7 @@ # Do the actual computation to find data points within region of interest placename: str = "kamb" # Select Kamb Ice Stream region region: deepicedrain.Region = regions[placename] -ds_subset: xr.Dataset = region.subset(ds=ds) +ds_subset: xr.Dataset = region.subset(data=ds) ds_subset = ds_subset.unify_chunks() ds_subset = ds_subset.compute() diff --git a/atlxi_dhdt.ipynb b/atlxi_dhdt.ipynb index 804ea66..d05e0c1 100644 --- a/atlxi_dhdt.ipynb +++ b/atlxi_dhdt.ipynb @@ -218,7 +218,7 @@ "# Subset dataset to geographic region of interest\n", "placename: str = \"antarctica\"\n", "region: deepicedrain.Region = regions[placename]\n", - "# ds = region.subset(ds=ds)" + "# ds = region.subset(data=ds)" ] }, { @@ -901,7 +901,7 @@ "region: deepicedrain.Region = regions[placename]\n", "if not os.path.exists(f\"ATLXI/df_dhdt_{placename}.parquet\"):\n", " # Subset dataset to geographic region of interest\n", - " ds_subset: xr.Dataset = region.subset(ds=ds_dhdt)\n", + " ds_subset: xr.Dataset = region.subset(data=ds_dhdt)\n", " # Add a UTC_time column to the dataframe\n", " ds_subset[\"utc_time\"] = deepicedrain.deltatime_to_utctime(\n", " dataarray=ds_subset.delta_ds_subsettime\n", diff --git a/atlxi_dhdt.py b/atlxi_dhdt.py index 654f980..0de4d0a 100644 --- a/atlxi_dhdt.py +++ b/atlxi_dhdt.py @@ -140,7 +140,7 @@ # Subset dataset to geographic region of interest placename: str = "antarctica" region: deepicedrain.Region = regions[placename] -# ds = region.subset(ds=ds) +# ds = region.subset(data=ds) # %% # We need at least 2 points to draw a trend line or compute differences @@ -398,7 +398,7 @@ region: deepicedrain.Region = regions[placename] if not os.path.exists(f"ATLXI/df_dhdt_{placename}.parquet"): # Subset dataset to geographic region of interest - ds_subset: xr.Dataset = region.subset(ds=ds_dhdt) + ds_subset: xr.Dataset = region.subset(data=ds_dhdt) # Add a UTC_time column to the dataframe ds_subset["utc_time"] = deepicedrain.deltatime_to_utctime( dataarray=ds_subset.delta_ds_subsettime diff --git a/deepicedrain/spatiotemporal.py b/deepicedrain/spatiotemporal.py index 7cf2ef2..fe9d4a8 100644 --- a/deepicedrain/spatiotemporal.py +++ b/deepicedrain/spatiotemporal.py @@ -74,18 +74,26 @@ def datashade( ) def subset( - self, ds: xr.Dataset, x_dim: str = "x", y_dim: str = "y", drop: bool = True + self, data: xr.Dataset, x_dim: str = "x", y_dim: str = "y", drop: bool = True ) -> xr.Dataset: """ - Convenience function to find datapoints in an xarray.Dataset - that fit within the bounding boxes of this region + Convenience function to find datapoints in an xarray.Dataset or + pandas.DataFrame that fit within the bounding boxes of this region. + Note that the 'drop' boolean flag is only valid for xarray.Dataset. """ cond = np.logical_and( - np.logical_and(ds[x_dim] > self.xmin, ds[x_dim] < self.xmax), - np.logical_and(ds[y_dim] > self.ymin, ds[y_dim] < self.ymax), + np.logical_and(data[x_dim] > self.xmin, data[x_dim] < self.xmax), + np.logical_and(data[y_dim] > self.ymin, data[y_dim] < self.ymax), ) - return ds.where(cond=cond, drop=drop) + try: + # xarray.DataArray subset method + data_subset = data.where(cond=cond, drop=drop) + except TypeError: + # pandas.DataFrame subset method + data_subset = data.loc[cond] + + return data_subset def deltatime_to_utctime( diff --git a/deepicedrain/tests/test_region.py b/deepicedrain/tests/test_region.py index 9f44e28..867fba0 100644 --- a/deepicedrain/tests/test_region.py +++ b/deepicedrain/tests/test_region.py @@ -7,6 +7,7 @@ import pytest import xarray as xr +import dask.dataframe from deepicedrain import Region, catalog, lonlat_to_xy @@ -51,7 +52,7 @@ def test_region_datashade(): atl11_dataset: xr.Dataset = catalog.test_data.atl11_test_case.to_dask() atl11_dataset["x"], atl11_dataset["y"] = lonlat_to_xy( - longitude=atl11_dataset.longitude, latitude=atl11_dataset.latitude, epsg=3995, + longitude=atl11_dataset.longitude, latitude=atl11_dataset.latitude, epsg=3995 ) atl11_dataset = atl11_dataset.set_coords(["x", "y"]) df: pd.DataFrame = atl11_dataset.h_corr.to_dataframe() @@ -64,7 +65,7 @@ def test_region_datashade(): npt.assert_allclose(agg_grid.max(), 1798.066285) -def test_region_subset(): +def test_region_subset_xarray_dataset(): """ Test that we can subset an xarray.Dataset based on the region's bounds """ @@ -76,6 +77,27 @@ def test_region_subset(): "y": np.linspace(start=-160, stop=160, num=50), }, ) - ds_subset = region.subset(ds=dataset) + ds_subset = region.subset(data=dataset) assert isinstance(ds_subset, xr.Dataset) assert ds_subset.h_corr.shape == (24, 30) + + +@pytest.mark.parametrize("dataframe_type", [pd.DataFrame, dask.dataframe.DataFrame]) +def test_region_subset_dataframe(dataframe_type): + """ + Test that we can subset a pandas or dask DataFrame based on the region's + bounds + """ + region = Region("South Pole", -100, 100, -100, 100) + dataframe = pd.DataFrame( + data={ + "x": np.linspace(start=-200, stop=200, num=50), + "y": np.linspace(start=-160, stop=160, num=50), + "dhdt": np.random.rand(50), + } + ) + if dataframe_type == dask.dataframe.core.DataFrame: + dataframe = dask.dataframe.from_pandas(data=dataframe, npartitions=2) + df_subset = region.subset(data=dataframe) + assert isinstance(df_subset, dataframe_type) + assert len(df_subset.dhdt) == 24 From d3c5eb3fbc77cc1d520dcdeeefac3ec819cce54a Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 19 Aug 2020 17:31:43 +1200 Subject: [PATCH 03/12] :heavy_plus_sign: Add cuspatial again CUDA-accelerated GIS and spatiotemporal algorithms! A repeat of 58fdcdffba5be03a12f29a6a7e1fd3f16a893586, but with a newer version at v0.15.0! Also patch 852a6434a55f5619ee26007199f422f80966296f by bumping up cuml version and switching the scikit-learn order. --- environment.yml | 3 ++- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/environment.yml b/environment.yml index 843ad1a..9deb56a 100644 --- a/environment.yml +++ b/environment.yml @@ -3,7 +3,8 @@ channels: - conda-forge - rapidsai-nightly dependencies: - - rapidsai-nightly::cuml=0.15.0a200817[md5=8e6ede59b1bc5742e21e1e5327173a8b] + - rapidsai-nightly::cuml=0.15.0a200819[md5=7febc89661b8dbc905dc3c52218f9954] + - rapidsai-nightly::cuspatial=0.15.0a200819[md5=c583588629008217835cbd84e50b8543] - conda-forge::geos=3.8.1[md5=7282fbe0c036bacad959c03c05a7eb9a] - conda-forge/label/dev::gmt=6.2.0.dev0+a1a189c[md5=915cb603613887002b58e09859aaac82] - conda-forge::gxx_linux-64=7.5.0[md5=c6afca6a2bfe1ea0ff5566d300460501] diff --git a/pyproject.toml b/pyproject.toml index e5cb9dd..8c1d0d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,11 +28,11 @@ pyepsg = "^0.4.0" pygmt = {git = "https://github.com/GenericMappingTools/pygmt.git", rev = "v0.1.2-36-g4939ee2a"} python = "^3.8" pyproj = "^2.6.0" +scikit-learn = "^0.23.2" toolz = "^0.10.0" tqdm = "^4.48.2" xarray = {git = "https://github.com/weiji14/xarray.git", rev = "v0.16.0-131-gda42dab0"} xrviz = "^0.1.4" -scikit-learn = "^0.23.2" [tool.poetry.dev-dependencies] black = "^19.10b0" From cf993d4ab28d128d3e1b4716f54481d82f7e4124 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Thu, 20 Aug 2020 12:58:23 +1200 Subject: [PATCH 04/12] :sparkles: GPU accelerated point in polygon using cuspatial A very fast way to find points inside polygons! This is really just a convenience function that wraps around `cuspatial.point_in_polygon`, hiding all sorts of boilerplate. Specifically, this handles: 1. Converting a geopandas geodataframe into a cuspatial friendly format, see https://github.com/rapidsai/cuspatial/issues/165 2. Hacky workaround the 31 polygon limit using a for-loop, based on https://github.com/rapidsai/cuspatial/blob/branch-0.15/notebooks/nyc_taxi_years_correlation.ipynb 3. Outputting actual string labels from the geodataframe, instead of non human readable index numbers Also added tests for this in test_spatiotemporal_gpu.py, though it won't work on the CI, only locally where a GPU is available. --- deepicedrain/__init__.py | 12 ++- deepicedrain/spatiotemporal.py | 90 ++++++++++++++++++- .../tests/test_spatiotemporal_conversions.py | 2 +- deepicedrain/tests/test_spatiotemporal_gpu.py | 36 ++++++++ 4 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 deepicedrain/tests/test_spatiotemporal_gpu.py diff --git a/deepicedrain/__init__.py b/deepicedrain/__init__.py index 60c15a4..c710f41 100644 --- a/deepicedrain/__init__.py +++ b/deepicedrain/__init__.py @@ -1,12 +1,16 @@ import importlib.resources import logging -import intake - import deepicedrain -from deepicedrain.deltamath import calculate_delta, nanptp, nan_linregress +import intake +from deepicedrain.deltamath import calculate_delta, nan_linregress, nanptp from deepicedrain.extraload import array_to_dataframe -from deepicedrain.spatiotemporal import Region, deltatime_to_utctime, lonlat_to_xy +from deepicedrain.spatiotemporal import ( + Region, + deltatime_to_utctime, + lonlat_to_xy, + point_in_polygon_gpu, +) __version__: str = "0.2.1" diff --git a/deepicedrain/spatiotemporal.py b/deepicedrain/spatiotemporal.py index fe9d4a8..c1bd3a7 100644 --- a/deepicedrain/spatiotemporal.py +++ b/deepicedrain/spatiotemporal.py @@ -4,13 +4,17 @@ """ import dataclasses import datetime +import os +import tempfile -import datashader +import geopandas as gpd import numpy as np import pandas as pd import pyproj import xarray as xr +import datashader + @dataclasses.dataclass(frozen=True) class Region: @@ -140,3 +144,87 @@ def lonlat_to_xy( ) else: return x, y + + +def point_in_polygon_gpu( + points_df, # cudf.DataFrame with x and y columns of point coordinates + poly_df: gpd.GeoDataFrame, # geopandas.GeoDataFrame with polygon shapes + points_x_col: str = "x", + points_y_col: str = "y", + poly_label_col: str = None, +): + """ + Find polygon labels for each of the input points. + This is a GPU accelerated version that requires cuspatial! + + Parameters + ---------- + points_df : cudf.DataFrame + A dataframe in GPU memory containing the x and y coordinates. + points_x_col : str + Name of the x coordinate column in points_df. Default is "x". + points_y_col : str + Name of the y coordinate column in points_df. Default is "y". + + poly_df : geopandas.GeoDataFrame + A geodataframe in CPU memory containing polygons geometries in each + row. + poly_label_col : str + Name of the column in poly_df that will be used to label the points, + e.g. "placename". Default is to automatically use the first column + unless otherwise specified. + + Returns + ------- + point_labels : cudf.Series + A column of labels that indicates which polygon the points fall into. + + """ + import cudf + import cuspatial + + poly_df_: gpd.GeoDataFrame = poly_df.reset_index() + if poly_label_col is None: + # Simply use first column of geodataframe as label if not provided + poly_label_col: str = poly_df.columns[0] + point_labels: cudf.Series = cudf.Series(index=points_df.index).astype( + poly_df[poly_label_col].dtype + ) + + # Load CPU-based GeoDataFrame into a GPU-based cuspatial friendly format + # This is a workaround until the related feature request at + # https://github.com/rapidsai/cuspatial/issues/165 is implemented + with tempfile.TemporaryDirectory() as tmpdir: + # Save geodataframe to a temporary shapefile, + # so that we can load it into GPU memory using cuspatial + tmpshpfile = os.path.join(tmpdir, "poly_df.shp") + poly_df_.to_file(filename=tmpshpfile, driver="ESRI Shapefile") + + # Load polygon_offsets, ring_offsets and polygon xy points + # from temporary shapefile into GPU memory + poly_offsets, poly_ring_offsets, poly_points = cuspatial.read_polygon_shapefile( + filename=tmpshpfile + ) + + # Run the actual point in polygon algorithm! + # Note that cuspatial's point_in_polygon function has a 31 polygon limit, + # hence the for-loop code below. See also + # https://github.com/rapidsai/cuspatial/blob/branch-0.15/notebooks/nyc_taxi_years_correlation.ipynb + num_poly: int = len(poly_df_) + point_in_poly_iter: list = list(np.arange(0, num_poly, 31)) + [num_poly] + for i in range(len(point_in_poly_iter) - 1): + start, end = point_in_poly_iter[i], point_in_poly_iter[i + 1] + poly_labels: cudf.DataFrame = cuspatial.point_in_polygon( + test_points_x=points_df[points_x_col], + test_points_y=points_df[points_y_col], + poly_offsets=poly_offsets[start:end], + poly_ring_offsets=poly_ring_offsets, + poly_points_x=poly_points.x, + poly_points_y=poly_points.y, + ) + + # Label each point with polygon they fall in + for label in poly_labels.columns: + point_labels.loc[poly_labels[label]] = poly_df_.loc[label][poly_label_col] + + return point_labels diff --git a/deepicedrain/tests/test_spatiotemporal_conversions.py b/deepicedrain/tests/test_spatiotemporal_conversions.py index 7d1cace..c844f01 100644 --- a/deepicedrain/tests/test_spatiotemporal_conversions.py +++ b/deepicedrain/tests/test_spatiotemporal_conversions.py @@ -3,12 +3,12 @@ """ import datetime -import dask import numpy as np import numpy.testing as npt import pandas as pd import xarray as xr +import dask from deepicedrain import catalog, deltatime_to_utctime, lonlat_to_xy diff --git a/deepicedrain/tests/test_spatiotemporal_gpu.py b/deepicedrain/tests/test_spatiotemporal_gpu.py new file mode 100644 index 0000000..696d8c6 --- /dev/null +++ b/deepicedrain/tests/test_spatiotemporal_gpu.py @@ -0,0 +1,36 @@ +""" +Tests GPU accelerated spatial algorithms +""" + +import geopandas as gpd +import numpy as np +import pytest +import shapely.geometry + +from deepicedrain import point_in_polygon_gpu + +cudf = pytest.importorskip(modname="cudf") + + +def test_point_in_polygon_gpu(): + """ + Tests that the Point in Polygon GPU algorithm works + """ + points_df: cudf.DataFrame = cudf.DataFrame( + data={ + "x": np.linspace(start=-200, stop=200, num=50), + "y": np.linspace(start=-160, stop=160, num=50), + } + ) + polygon = { + "placename": ["South Pole"], + "geometry": shapely.geometry.box(minx=-5, maxx=5, miny=-5, maxy=5).buffer(100), + } + poly_df: gpd.GeoDataFrame = gpd.GeoDataFrame(polygon) + + point_labels = point_in_polygon_gpu( + points_df=points_df, poly_df=poly_df, poly_label_col="placename" + ) + assert isinstance(point_labels, cudf.Series) + assert point_labels.count() == 20 # Count non-NaN labels + assert list(point_labels.unique().to_pandas()) == [None, "South Pole"] From 01d18d4fcc0f174bcca9f4a5a697da0ba1113e74 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Fri, 21 Aug 2020 09:16:41 +1200 Subject: [PATCH 05/12] :sparkles: Ndarray to Parquet convenience function Building on top of eb61ff609a7fa12a419cebb35daf45f072fb8ad3, but for n-dimensional arrays, and writing the dataframe to Parquet too! This function might be a little too convenient (read: contains hardcoding), but it smooths out some of the rough edges in terms of PyData file format interoperability. Should contribute this somewhere upstream when I get the time. --- atlxi_dhdt.ipynb | 2 +- atlxi_dhdt.py | 2 +- deepicedrain/__init__.py | 2 +- deepicedrain/extraload.py | 78 ++++++++++- deepicedrain/tests/test_array_to_dataframe.py | 37 ------ .../tests/test_ndarray_to_dataframe.py | 121 ++++++++++++++++++ 6 files changed, 201 insertions(+), 41 deletions(-) delete mode 100644 deepicedrain/tests/test_array_to_dataframe.py create mode 100644 deepicedrain/tests/test_ndarray_to_dataframe.py diff --git a/atlxi_dhdt.ipynb b/atlxi_dhdt.ipynb index d05e0c1..6cceb72 100644 --- a/atlxi_dhdt.ipynb +++ b/atlxi_dhdt.ipynb @@ -904,7 +904,7 @@ " ds_subset: xr.Dataset = region.subset(data=ds_dhdt)\n", " # Add a UTC_time column to the dataframe\n", " ds_subset[\"utc_time\"] = deepicedrain.deltatime_to_utctime(\n", - " dataarray=ds_subset.delta_ds_subsettime\n", + " dataarray=ds_subset.delta_time\n", " )\n", " # Convert xarray.Dataset to pandas.DataFrame for easier analysis\n", " df_many: pd.DataFrame = ds_subset.to_dataframe().dropna()\n", diff --git a/atlxi_dhdt.py b/atlxi_dhdt.py index 0de4d0a..cdbea52 100644 --- a/atlxi_dhdt.py +++ b/atlxi_dhdt.py @@ -401,7 +401,7 @@ ds_subset: xr.Dataset = region.subset(data=ds_dhdt) # Add a UTC_time column to the dataframe ds_subset["utc_time"] = deepicedrain.deltatime_to_utctime( - dataarray=ds_subset.delta_ds_subsettime + dataarray=ds_subset.delta_time ) # Convert xarray.Dataset to pandas.DataFrame for easier analysis df_many: pd.DataFrame = ds_subset.to_dataframe().dropna() diff --git a/deepicedrain/__init__.py b/deepicedrain/__init__.py index c710f41..7284559 100644 --- a/deepicedrain/__init__.py +++ b/deepicedrain/__init__.py @@ -4,7 +4,7 @@ import deepicedrain import intake from deepicedrain.deltamath import calculate_delta, nan_linregress, nanptp -from deepicedrain.extraload import array_to_dataframe +from deepicedrain.extraload import array_to_dataframe, ndarray_to_parquet from deepicedrain.spatiotemporal import ( Region, deltatime_to_utctime, diff --git a/deepicedrain/extraload.py b/deepicedrain/extraload.py index 4e0e9e3..012621b 100644 --- a/deepicedrain/extraload.py +++ b/deepicedrain/extraload.py @@ -4,8 +4,13 @@ """ import pandas as pd +import dask +import zarr -def array_to_dataframe(array, colname: str = None, startcol: int = 0): + +def array_to_dataframe( + array: dask.array.core.Array, colname: str = None, startcol: int = 0 +): """ Converts a 1D or 2D data array into a tidy dataframe structure. An array of shape (m, n) will turn into a table with m rows and n columns. @@ -41,3 +46,74 @@ def array_to_dataframe(array, colname: str = None, startcol: int = 0): dataframe: pd.DataFrame = pd.DataFrame.from_records(data=array, columns=columns) return dataframe + + +def ndarray_to_parquet( + ndarray, + parquetpath: str, + variables: list = None, + dropnacols: list = None, + engine: str = "pyarrow", + **kwargs, +) -> pd.DataFrame: + """ + Converts an n-dimensional xarray Dataset or Zarr Array into an Apache + Parquet columnar file via an intermediate Dask/Pandas DataFrame format. + This is a convenience function that wraps around array_to_dataframe, + intended to make converting n number of arrays easier. + + Parameters + ---------- + ndarray : xarray.Dataset or zarr.hierarchy.Group + An n-dimensional array in xarray containing several coordinate and data + variables, or a Zarr array containing several variables. + parquetpath : str + Filepath to where the resulting parquet file will be stored. + variables : list + Name(s) of the variables/columns that will be stored to the parquet + file. If not provided, all the variables in the zarr group will be + stored. + dropnacols : list + Drop rows containing NaN values in these fields before saving to the + Parquet file. + engine : str + Parquet library to use. Choose from 'auto', 'fastparquet', 'pyarrow'. + Default is "pyarrow". + **kwargs : dict + Extra options to be passed on to pandas.DataFrame.to_parquet. + + Returns + ------- + point_labels : cudf.Series + A column of labels that indicates which polygon the points fall into. + + """ + if variables is None: + try: + variables = [varname for varname, _ in ndarray.arrays()] + except AttributeError: + variables = [c for c in ndarray.coords] + [d for d in ndarray.data_vars] + + if isinstance(ndarray, zarr.hierarchy.Group): + array_func = lambda varname: dask.array.from_zarr(ndarray[varname]) + else: + array_func = lambda varname: ndarray[varname].data + + dataframes: list = [ + array_to_dataframe(array=array_func(varname), colname=varname, startcol=1) + for varname in variables + ] + dataframe: dask.dataframe.core.DataFrame = dask.dataframe.concat( + dfs=dataframes, axis="columns" + ) + if dropnacols: + dataframe = dataframe.dropna(subset=dropnacols) + + # Convert to pandas DataFrame first before saving to a single binary + # parquet file, rather than going directly from a Dask DataFrame to a + # series of parquet files in a parquet folder. This ensures that cudf can + # read it later, see https://github.com/rapidsai/cudf/issues/1688 + df: pd.DataFrame = dataframe.compute() + df.to_parquet(path=parquetpath, engine=engine, **kwargs) + + return df diff --git a/deepicedrain/tests/test_array_to_dataframe.py b/deepicedrain/tests/test_array_to_dataframe.py deleted file mode 100644 index 344846a..0000000 --- a/deepicedrain/tests/test_array_to_dataframe.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -Tests the array_to_dataframe function -""" -import dask -import numpy as np -import pandas as pd -import pytest - -from deepicedrain import array_to_dataframe - - -@pytest.mark.parametrize("shape", [(10, 1), (10, 2)]) -def test_numpy_array_to_pandas_dataframe(shape): - """ - Test converting from a numpy.array to a pandas.Dataframe, and ensure that - the colname argument works. - """ - array: np.ndarray = np.ones(shape=shape) - dataframe = array_to_dataframe(array=array) - - assert isinstance(dataframe, pd.DataFrame) - assert len(dataframe.columns) == shape[1] - assert dataframe.columns.to_list() == [str(i) for i in range(shape[1])] - - -@pytest.mark.parametrize("shape", [(10, 1), (10, 2)]) -def test_dask_array_to_dask_dataframe(shape): - """ - Test converting from a dask.array to a dask.dataframe, and ensure that the - startcol argument works. - """ - array: dask.array.core.Array = dask.array.ones(shape=shape, name="varname") - dataframe = array_to_dataframe(array=array, startcol=1) - - assert isinstance(dataframe, dask.dataframe.core.DataFrame) - assert len(dataframe.columns) == shape[1] - assert dataframe.columns.to_list() == [f"varname_{i+1}" for i in range(shape[1])] diff --git a/deepicedrain/tests/test_ndarray_to_dataframe.py b/deepicedrain/tests/test_ndarray_to_dataframe.py new file mode 100644 index 0000000..308a4f3 --- /dev/null +++ b/deepicedrain/tests/test_ndarray_to_dataframe.py @@ -0,0 +1,121 @@ +""" +Tests various conversions from n-dimensional arrays to columnar dataframe table +structures. +""" +import os +import tempfile + +import numpy as np +import pandas as pd +import pytest +import xarray as xr + +import dask +import zarr +from deepicedrain import array_to_dataframe, catalog, ndarray_to_parquet + + +@pytest.fixture(scope="module", name="dataset") +def fixture_dataset(): + """ + Load the sample ICESat-2 ATL11 data into an xarray, and clean it up to + allow saving to other formats like Zarr + """ + dataset: xr.Dataset = catalog.test_data.atl11_test_case.to_dask() + for key, variable in dataset.variables.items(): + assert isinstance(dataset[key].DIMENSION_LABELS, np.ndarray) + dataset[key].attrs["DIMENSION_LABELS"] = ( + dataset[key].attrs["DIMENSION_LABELS"].astype(str) + ) + + return dataset + + +@pytest.mark.parametrize("shape", [(10, 1), (10, 2)]) +def test_numpy_array_to_pandas_dataframe(shape): + """ + Test converting from a numpy.array to a pandas.Dataframe, and ensure that + the colname argument works. + """ + array: np.ndarray = np.ones(shape=shape) + dataframe = array_to_dataframe(array=array) + + assert isinstance(dataframe, pd.DataFrame) + assert len(dataframe.columns) == shape[1] + assert dataframe.columns.to_list() == [str(i) for i in range(shape[1])] + + +@pytest.mark.parametrize("shape", [(10, 1), (10, 2)]) +def test_dask_array_to_dask_dataframe(shape): + """ + Test converting from a dask.array to a dask.dataframe, and ensure that the + startcol argument works. + """ + array: dask.array.core.Array = dask.array.ones(shape=shape, name="varname") + dataframe = array_to_dataframe(array=array, startcol=1) + + assert isinstance(dataframe, dask.dataframe.core.DataFrame) + assert len(dataframe.columns) == shape[1] + assert dataframe.columns.to_list() == [f"varname_{i+1}" for i in range(shape[1])] + + +def test_xarray_dataset_to_parquet_table(dataset): + """ + Test converting from an xarray Dataset to a parquet table, specifying a + list of variables to store and setting 'snappy' compression. + """ + with tempfile.TemporaryDirectory() as tmpdir: + parquetpath: str = os.path.join(tmpdir, "temp.parquet") + ndarray_to_parquet( + ndarray=dataset, + parquetpath=parquetpath, + variables=["longitude", "latitude", "h_corr", "h_corr_sigma"], + compression="snappy", + ) + + df: dask.dataframe.core.DataFrame = dask.dataframe.read_parquet( + path=parquetpath + ) + assert len(df) == 1404 + assert list(df.columns) == [ + "longitude", + "latitude", + "h_corr_1", + "h_corr_2", + "h_corr_sigma_1", + "h_corr_sigma_2", + ] + assert all(np.issubdtype(dtype, np.float64) for dtype in df.dtypes) + + +def test_zarr_array_to_parquet_table(dataset): + """ + Test converting from a zarr array to a parquet table, specifying a list of + variables to store and setting 'snappy' compression. + """ + with tempfile.TemporaryDirectory() as tmpdir: + zarrstore: str = os.path.join(tmpdir, "temp.zarr") + dataset.to_zarr(store=zarrstore, consolidated=True) + zarrarray: zarr.hierarchy.Group = zarr.open_consolidated(store=zarrstore) + + parquetpath: str = os.path.join(tmpdir, "temp.parquet") + ndarray_to_parquet( + ndarray=zarrarray, + parquetpath=parquetpath, + variables=["longitude", "latitude", "h_corr", "delta_time"], + compression="snappy", + ) + + df: dask.dataframe.core.DataFrame = dask.dataframe.read_parquet( + path=parquetpath + ) + assert len(df) == 1404 + assert list(df.columns) == [ + "longitude", + "latitude", + "h_corr_1", + "h_corr_2", + "delta_time_1", + "delta_time_2", + ] + assert all(np.issubdtype(dtype, np.float64) for dtype in df.dtypes) From a6c89ea496854d298981ffabf7c40e7cbd349ce6 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sat, 22 Aug 2020 11:59:50 +1200 Subject: [PATCH 06/12] :dizzy: From dhdt_plot to IceSat2Explorer, an improved dashboard Improve our HvPlot/Panel dashboard with some new bells and whistles! Like a proper GIS desktop tool, the xy_dhdt dashboard plot can now keep the zoom level when changing between variables (thanks to https://discourse.holoviz.org/t/keep-zoom-level-when-changing-between-variables-in-a-scatter-plot)! Supersedes e4874b0d3552b409e7cfced28bdac004e02c0dd2. This is a major refresh of my old IceSatExplorer code at https://github.com/weiji14/cryospheric-data-lakes/blob/master/code/scripts/h5_to_np_icesat.ipynb, which uses ICESat-1 instead of ICESat-2. The dashboard also takes a lot of cues from the example at https://examples.pyviz.org/datashader_dashboard/dashboard.html, implemented in https://github.com/holoviz/datashader/pull/676. Other significant improvements include a categorical colourmap for the 'referencegroundtrack' variable, and being able to see the height and time of an ICESat-2 measurement at a particular cycle on hover over the points! Oh, and did I mention that the rendering now happens on the GPU?!! Data transformed to and from Parquet is fast! Note that this is a work in progress, and that there are more sweeping improvements to come. I've also split out the crossover analysis code into a separate atlxi_lake.ipynb file since atlxi_dhdt.ipynb was getting too long. --- README.md | 2 +- atlxi_dhdt.ipynb | 530 ++++++++++++----------------------------------- atlxi_dhdt.py | 402 +++++++++++------------------------ atlxi_lake.ipynb | 330 +++++++++++++++++++++++++++++ atlxi_lake.py | 240 +++++++++++++++++++++ 5 files changed, 815 insertions(+), 689 deletions(-) create mode 100644 atlxi_lake.ipynb create mode 100644 atlxi_lake.py diff --git a/README.md b/README.md index 29ade9e..b045e0f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ in Antarctica using remote sensing and machine learning. ![ICESat-2 ATL11 rate of height change over time in Antarctica 2018-10-14 to 2020-05-13](https://user-images.githubusercontent.com/23487320/90118294-2601ff80-ddac-11ea-8b93-7bc9b15f2be0.png) -![DeepIceDrain Pipeline](https://yuml.me/diagram/scruffy;dir:LR/class/[Land-Ice-Elevation|atl06_play.ipynb]->[Convert|atl06_to_atl11.ipynb],[Convert]->[Ice-Sheet-H(t)-Series|atl11_play.ipynb],[Ice-Sheet-H(t)-Series]->[Height-Change-over-Time-(dhdt)|atlxi_dhdt.ipynb]) +![DeepIceDrain Pipeline](https://yuml.me/diagram/scruffy;dir:LR/class/[Land-Ice-Elevation|atl06_play.ipynb]->[Convert|atl06_to_atl11.ipynb],[Convert]->[Ice-Sheet-H(t)-Series|atl11_play.ipynb],[Ice-Sheet-H(t)-Series]->[Height-Change-over-Time-(dhdt)|atlxi_dhdt.ipynb],[Height-Change-over-Time-(dhdt)]->[Subglacial-Lake-Finder|atlxi_lake.ipynb]) # Getting started diff --git a/atlxi_dhdt.ipynb b/atlxi_dhdt.ipynb index 6cceb72..b215faf 100644 --- a/atlxi_dhdt.ipynb +++ b/atlxi_dhdt.ipynb @@ -34,18 +34,22 @@ "source": [ "import itertools\n", "import os\n", + "import warnings\n", "\n", "import numpy as np\n", "import pandas as pd\n", "import xarray as xr\n", "\n", + "import cudf # comment out if no GPU\n", "import dask\n", "import datashader\n", "import deepicedrain\n", "import holoviews as hv\n", + "import hvplot.cudf # comment out if no GPU\n", "import hvplot.pandas\n", "import intake\n", "import panel as pn\n", + "import param\n", "import pygmt\n", "import scipy.stats\n", "import tqdm" @@ -902,63 +906,129 @@ "if not os.path.exists(f\"ATLXI/df_dhdt_{placename}.parquet\"):\n", " # Subset dataset to geographic region of interest\n", " ds_subset: xr.Dataset = region.subset(data=ds_dhdt)\n", - " # Add a UTC_time column to the dataframe\n", + " # Add a UTC_time column to the dataset\n", " ds_subset[\"utc_time\"] = deepicedrain.deltatime_to_utctime(\n", " dataarray=ds_subset.delta_time\n", " )\n", - " # Convert xarray.Dataset to pandas.DataFrame for easier analysis\n", - " df_many: pd.DataFrame = ds_subset.to_dataframe().dropna()\n", - " # Drop delta_time column since timedelta64 dtype cannot be saved to parquet\n", - " # https://github.com/pandas-dev/pandas/issues/31909\n", - " df_many: pd.DataFrame = df_many.drop(columns=\"delta_time\")\n", - " # Need to use_deprecated_int96_timestamps in order to save utc_time column\n", - " # https://issues.apache.org/jira/browse/ARROW-1957\n", - " df_many.to_parquet(\n", - " f\"ATLXI/df_dhdt_{placename}.parquet\", use_deprecated_int96_timestamps=True\n", + " # Save to parquet format\n", + " deepicedrain.ndarray_to_parquet(\n", + " ndarray=ds_subset,\n", + " parquetpath=f\"ATLXI/df_dhdt_{placename}.parquet\",\n", + " variables=[\n", + " \"x\",\n", + " \"y\",\n", + " \"dhdt_slope\",\n", + " \"referencegroundtrack\",\n", + " \"h_corr\",\n", + " \"utc_time\",\n", + " ],\n", + " dropnacols=[\"dhdt_slope\"],\n", + " use_deprecated_int96_timestamps=True,\n", " )\n", - "df_many = pd.read_parquet(f\"ATLXI/df_dhdt_{placename}.parquet\")" + "# df_many = pd.read_parquet(f\"ATLXI/df_dhdt_{placename}.parquet\")\n", + "df_dhdt = cudf.read_parquet(f\"ATLXI/df_dhdt_{placename}.parquet\")" ] }, { "cell_type": "code", "execution_count": 32, - "metadata": {}, + "metadata": { + "lines_to_next_cell": 1 + }, + "outputs": [], + "source": [ + "warnings.filterwarnings(\n", + " action=\"ignore\",\n", + " message=\"The global colormaps dictionary is no longer considered public API.\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "lines_to_next_cell": 1 + }, "outputs": [], "source": [ - "def dhdt_plot(\n", - " cycle: int = 7,\n", - " dhdt_variable: str = \"dhdt_slope\",\n", - " dhdt_range: tuple = (1, 10),\n", - " rasterize: bool = False,\n", - " datashade: bool = False,\n", - ") -> hv.element.chart.Scatter:\n", + "class IceSat2Explorer(param.Parameterized):\n", " \"\"\"\n", - " ICESat-2 rate of height change over time (dhdt) interactive scatter plot.\n", - " Uses HvPlot, and intended to be used inside a Panel dashboard.\n", + " ICESat-2 rate of height change over time (dhdt) interactive dashboard.\n", + " Built using HvPlot and Panel.\n", + "\n", + " Adapted from the \"Panel-based Datashader dashboard\" at\n", + " https://examples.pyviz.org/datashader_dashboard/dashboard.html.\n", + " See also https://github.com/holoviz/datashader/pull/676.\n", " \"\"\"\n", - " df_ = df_many.query(\n", - " expr=\"cycle_number == @cycle & \"\n", - " \"@dhdt_range[0] < abs(dhdt_slope) & abs(dhdt_slope) < @dhdt_range[1]\"\n", - " )\n", - " return df_.hvplot.scatter(\n", - " title=f\"ICESat-2 Cycle {cycle} {dhdt_variable}\",\n", - " x=\"x\",\n", - " y=\"y\",\n", - " c=dhdt_variable,\n", - " cmap=\"gist_earth\" if dhdt_variable == \"h_corr\" else \"BrBG\",\n", - " clim=None,\n", - " # by=\"cycle_number\",\n", - " rasterize=rasterize,\n", - " datashade=datashade,\n", - " dynspread=datashade,\n", - " hover=True,\n", - " hover_cols=[\"referencegroundtrack\", \"dhdt_slope\", \"h_corr\"],\n", - " colorbar=True,\n", - " grid=True,\n", - " frame_width=1000,\n", - " frame_height=600,\n", - " data_aspect=1,\n", - " )" + "\n", + " variable_cmap: dict = {\n", + " \"referencegroundtrack\": \"glasbey\",\n", + " \"dhdt_slope\": \"BrBG\",\n", + " \"h_corr\": \"gist_earth\",\n", + " }\n", + " dhdt_variable = param.Selector(default=\"dhdt_slope\", objects=variable_cmap.keys())\n", + " cycle = param.Integer(default=7, bounds=(2, 7))\n", + " dhdt_range = param.Range(default=(1.0, 10.0), bounds=(0.0, 20.0))\n", + " rasterize = param.Boolean(default=False)\n", + " datashade = param.Boolean(default=False)\n", + "\n", + " df_ = df_dhdt\n", + " plot = df_.hvplot.points(x=\"x\", y=\"y\", c=\"dhdt_slope\", cmap=\"BrBG\")\n", + " startX, endX = plot.range(\"x\")\n", + " startY, endY = plot.range(\"y\")\n", + "\n", + " def keep_zoom(self, x_range, y_range):\n", + " self.startX, self.endX = x_range\n", + " self.startY, self.endY = y_range\n", + "\n", + " @param.depends(\"cycle\", \"dhdt_variable\", \"dhdt_range\", \"rasterize\", \"datashade\")\n", + " def view(self):\n", + " cond = np.logical_and(\n", + " float(self.dhdt_range[0]) < abs(self.df_.dhdt_slope),\n", + " abs(self.df_.dhdt_slope) < float(self.dhdt_range[1]),\n", + " )\n", + " column: str = (\n", + " self.dhdt_variable\n", + " if self.dhdt_variable != \"h_corr\"\n", + " else f\"h_corr_{self.cycle}\"\n", + " )\n", + " if self.dhdt_variable == \"h_corr\":\n", + " df_subset = self.df_.loc[cond].dropna(subset=f\"h_corr_{self.cycle}\")\n", + " else:\n", + " df_subset = self.df_.loc[cond]\n", + " self.plot = df_subset.hvplot.points(\n", + " title=f\"ICESat-2 Cycle {self.cycle} {self.dhdt_variable}\",\n", + " x=\"x\",\n", + " y=\"y\",\n", + " c=column,\n", + " cmap=self.variable_cmap[self.dhdt_variable],\n", + " rasterize=self.rasterize,\n", + " datashade=self.datashade,\n", + " dynspread=self.datashade,\n", + " hover=True,\n", + " hover_cols=[\n", + " \"referencegroundtrack\",\n", + " \"dhdt_slope\",\n", + " f\"h_corr_{self.cycle}\",\n", + " f\"utc_time_{self.cycle}\",\n", + " ],\n", + " colorbar=True,\n", + " grid=True,\n", + " frame_width=1000,\n", + " frame_height=600,\n", + " data_aspect=1,\n", + " )\n", + " self.plot = self.plot.redim.range(\n", + " x=(self.startX, self.endX), y=(self.startY, self.endY)\n", + " )\n", + " self.plot = self.plot.opts(active_tools=[\"pan\", \"wheel_zoom\"])\n", + " rangexy = hv.streams.RangeXY(\n", + " source=self.plot,\n", + " x_range=(self.startX, self.endX),\n", + " y_range=(self.startY, self.endY),\n", + " )\n", + " rangexy.add_subscriber(self.keep_zoom)\n", + " return self.plot" ] }, { @@ -969,27 +1039,24 @@ "source": [ "# Interactive holoviews scatter plot to find referencegroundtrack needed\n", "# Tip: Hover over the points, and find those with high 'dhdt_slope' values\n", - "layout: pn.layout.Column = pn.interact(\n", - " dhdt_plot,\n", - " cycle=pn.widgets.IntSlider(name=\"Cycle Number\", start=2, end=7, step=1, value=7),\n", - " dhdt_variable=pn.widgets.RadioButtonGroup(\n", - " name=\"dhdt_variables\",\n", - " value=\"dhdt_slope\",\n", - " options=[\"referencegroundtrack\", \"dhdt_slope\", \"h_corr\"],\n", - " ),\n", - " dhdt_range=pn.widgets.RangeSlider(\n", - " name=\"dhdt range ±\", start=0, end=20, value=(1, 10), step=0.25\n", - " ),\n", - " rasterize=pn.widgets.Checkbox(name=\"Rasterize\"),\n", - " datashade=pn.widgets.Checkbox(name=\"Datashade\"),\n", + "viewer = IceSat2Explorer()\n", + "widgets: pn.param.Param = pn.Param(\n", + " viewer.param,\n", + " widgets={\n", + " \"dhdt_variable\": pn.widgets.RadioButtonGroup,\n", + " \"cycle\": pn.widgets.IntSlider,\n", + " \"dhdt_range\": pn.widgets.RangeSlider,\n", + " \"rasterize\": pn.widgets.Checkbox,\n", + " \"datashade\": pn.widgets.Checkbox,\n", + " },\n", ")\n", "dashboard: pn.layout.Column = pn.Column(\n", " pn.Row(\n", - " pn.Column(layout[0][1], align=\"center\"),\n", - " pn.Column(layout[0][0], layout[0][2], align=\"center\"),\n", - " pn.Column(layout[0][3], layout[0][4], align=\"center\"),\n", + " pn.Column(widgets[0], widgets[1], align=\"center\"),\n", + " pn.Column(widgets[2], widgets[3], align=\"center\"),\n", + " pn.Column(widgets[4], widgets[5], align=\"center\"),\n", " ),\n", - " layout[1],\n", + " viewer.view,\n", ")\n", "# dashboard" ] @@ -1116,347 +1183,6 @@ "fig.show()" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "# Crossover Track Analysis\n", - "\n", - "To increase the temporal resolution of\n", - "our ice elevation change analysis\n", - "(i.e. at time periods less than\n", - "the 91 day repeat cycle of ICESat-2),\n", - "we can look at the locations where the\n", - "ICESat-2 tracks intersect and get the\n", - "height values there!\n", - "Uses [x2sys_cross](https://docs.generic-mapping-tools.org/6.1/supplements/x2sys/x2sys_cross).\n", - "\n", - "References:\n", - "- Wessel, P. (2010). Tools for analyzing intersecting tracks: The x2sys package.\n", - "Computers & Geosciences, 36(3), 348–354. https://doi.org/10.1016/j.cageo.2009.05.009" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "# Initialize X2SYS database in the X2SYS/ICESAT2 folder\n", - "tag = \"X2SYS\"\n", - "os.environ[\"X2SYS_HOME\"] = os.path.abspath(tag)\n", - "os.getcwd()\n", - "pygmt.x2sys_init(\n", - " tag=\"ICESAT2\",\n", - " fmtfile=f\"{tag}/ICESAT2/xyht\",\n", - " suffix=\"tsv\",\n", - " units=[\"de\", \"se\"], # distance in metres, speed in metres per second\n", - " gap=\"d250e\", # distance gap up to 250 metres allowed\n", - " force=True,\n", - " verbose=\"q\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "# Run crossover analysis on all tracks\n", - "rgts: list = [135, 327, 388, 577, 1080, 1272] # Whillans upstream\n", - "# rgts: list = [236, 501, 562, 1181] # Whillans_downstream\n", - "tracks = [f\"{tag}/track_{i}.tsv\" for i in rgts]\n", - "assert all(os.path.exists(k) for k in tracks)\n", - "\n", - "# Parallelized paired crossover analysis\n", - "futures: list = []\n", - "for track1, track2 in itertools.combinations(rgts, r=2):\n", - " future = client.submit(\n", - " key=f\"{track1}_{track2}\",\n", - " func=pygmt.x2sys_cross,\n", - " tracks=[f\"{tag}/track_{track1}.tsv\", f\"{tag}/track_{track2}.tsv\"],\n", - " tag=\"ICESAT2\",\n", - " region=[-460000, -400000, -560000, -500000],\n", - " interpolation=\"l\", # linear interpolation\n", - " coe=\"e\", # external crossovers\n", - " trackvalues=True, # Get track 1 height (h_1) and track 2 height (h_2)\n", - " # trackvalues=False, # Get crossover error (h_X) and mean height value (h_M)\n", - " # outfile=\"xover_236_562.tsv\"\n", - " )\n", - " futures.append(future)" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 15/15 [00:00<00:00, 244.92it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "26 crossover intersection point locations found with 332 crossover height-time pairs over 6 tracks\n" - ] - } - ], - "source": [ - "crossovers: dict = {}\n", - "for f in tqdm.tqdm(\n", - " iterable=dask.distributed.as_completed(futures=futures), total=len(futures)\n", - "):\n", - " if f.status != \"error\": # skip those track pairs which don't intersect\n", - " crossovers[f.key] = f.result().dropna().reset_index(drop=True)\n", - "\n", - "df_cross: pd.DataFrame = pd.concat(objs=crossovers, names=[\"track1_track2\", \"id\"])\n", - "df: pd.DataFrame = df_cross.reset_index(level=\"track1_track2\").reset_index(drop=True)\n", - "# Report on how many unique crossover intersections there were\n", - "# df.plot.scatter(x=\"x\", y=\"y\") # quick plot of our crossover points\n", - "print(\n", - " f\"{len(df.groupby(by=['x', 'y']))} crossover intersection point locations found \"\n", - " f\"with {len(df)} crossover height-time pairs \"\n", - " f\"over {len(tracks)} tracks\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [], - "source": [ - "# Calculate crossover error\n", - "df[\"h_X\"]: pd.Series = df.h_2 - df.h_1 # crossover error (i.e. height difference)\n", - "df[\"t_D\"]: pd.Series = df.t_2 - df.t_1 # elapsed time in ns (i.e. time difference)\n", - "ns_in_yr: int = (365.25 * 24 * 60 * 60 * 1_000_000_000) # nanoseconds in a year\n", - "df[\"dhdt\"]: pd.Series = df.h_X / (df.t_D.astype(np.int64) / ns_in_yr)" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "# Get some summary statistics of our crossover errors\n", - "sumstats: pd.DataFrame = df[[\"h_X\", \"t_D\", \"dhdt\"]].describe()\n", - "# Find location with highest absolute crossover error, and most sudden height change\n", - "max_h_X: pd.Series = df.iloc[np.nanargmax(df.h_X.abs())] # highest crossover error\n", - "max_dhdt: pd.Series = df.iloc[df.dhdt.argmax()] # most sudden change in height" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 2D Map view of crossover points\n", - "\n", - "Bird's eye view of the crossover points\n", - "overlaid on top of the ICESat-2 tracks." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "# 2D plot of crossover locations\n", - "var: str = \"h_X\"\n", - "fig = pygmt.Figure()\n", - "# Setup basemap\n", - "region = np.array([df.x.min(), df.x.max(), df.y.min(), df.y.max()])\n", - "buffer = np.array([-2000, +2000, -2000, +2000])\n", - "pygmt.makecpt(cmap=\"batlow\", series=[sumstats[var][\"25%\"], sumstats[var][\"75%\"]])\n", - "# Map frame in metre units\n", - "fig.basemap(frame=\"f\", region=region + buffer, projection=\"X8c\")\n", - "# Plot actual track points\n", - "for track in tracks:\n", - " fig.plot(data=track, color=\"green\", style=\"c0.01c\")\n", - "# Plot crossover point locations\n", - "fig.plot(x=df.x, y=df.y, color=df.h_X, cmap=True, style=\"c0.1c\", pen=\"thinnest\")\n", - "# Map frame in kilometre units\n", - "fig.basemap(\n", - " frame=[\n", - " \"WSne\",\n", - " 'xaf+l\"Polar Stereographic X (km)\"',\n", - " 'yaf+l\"Polar Stereographic Y (km)\"',\n", - " ],\n", - " region=(region + buffer) / 1000,\n", - " projection=\"X8c\",\n", - ")\n", - "fig.colorbar(position=\"JMR\", frame=['x+l\"Crossover Error\"', \"y+lm\"])\n", - "fig.savefig(\"figures/crossover_area.png\")\n", - "fig.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 1D plots of height changing over time\n", - "\n", - "Plot height change over time at:\n", - "\n", - "1. One single crossover point location\n", - "2. Many crossover locations over an area" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "# Tidy up dataframe first using pd.wide_to_long\n", - "# I.e. convert 't_1', 't_2', 'h_1', 'h_2' columns into just 't' and 'h'.\n", - "df[\"id\"] = df.index\n", - "df_th: pd.DataFrame = pd.wide_to_long(\n", - " df=df[[\"id\", \"track1_track2\", \"x\", \"y\", \"t_1\", \"t_2\", \"h_1\", \"h_2\"]],\n", - " stubnames=[\"t\", \"h\"],\n", - " i=\"id\",\n", - " j=\"track\",\n", - " sep=\"_\",\n", - ")\n", - "df_th = df_th.reset_index(level=\"track\").drop_duplicates(ignore_index=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "6.02 metres height change at -445283.940476, -538147.2479729999\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "execution_count": 49, - "metadata": { - "image/png": { - "width": 500 - } - }, - "output_type": "execute_result" - } - ], - "source": [ - "# 1D Plot at location with **maximum** absolute crossover height error (max_h_X)\n", - "df_max = df_th.query(expr=\"x == @max_h_X.x & y == @max_h_X.y\").sort_values(by=\"t\")\n", - "track1, track2 = df_max.track1_track2.iloc[0].split(\"_\")\n", - "print(f\"{round(max_h_X.h_X, 2)} metres height change at {max_h_X.x}, {max_h_X.y}\")\n", - "t_min = (df_max.t.min() - pd.Timedelta(2, unit=\"W\")).isoformat()\n", - "t_max = (df_max.t.max() + pd.Timedelta(2, unit=\"W\")).isoformat()\n", - "h_min = df_max.h.min() - 0.2\n", - "h_max = df_max.h.max() + 0.4\n", - "\n", - "fig = pygmt.Figure()\n", - "with pygmt.config(\n", - " FONT_ANNOT_PRIMARY=\"9p\", FORMAT_TIME_PRIMARY_MAP=\"abbreviated\", FORMAT_DATE_MAP=\"o\"\n", - "):\n", - " fig.basemap(\n", - " projection=\"X12c/8c\",\n", - " region=[t_min, t_max, h_min, h_max],\n", - " frame=[\n", - " \"WSne\",\n", - " \"pxa1Of1o+lDate\", # primary time axis, 1 mOnth annotation and minor axis\n", - " \"sx1Y\", # secondary time axis, 1 Year intervals\n", - " 'yaf+l\"Elevation at crossover (m)\"',\n", - " ],\n", - " )\n", - "fig.text(\n", - " text=f\"Track {track1} and {track2} crossover\",\n", - " position=\"TC\",\n", - " offset=\"jTC0c/0.2c\",\n", - " V=\"q\",\n", - ")\n", - "# Plot data points\n", - "fig.plot(x=df_max.t, y=df_max.h, style=\"c0.15c\", color=\"darkblue\", pen=\"thin\")\n", - "# Plot dashed line connecting points\n", - "fig.plot(x=df_max.t, y=df_max.h, pen=f\"faint,blue,-\")\n", - "fig.savefig(f\"figures/crossover_{track1}_{track2}_{min_date}_{max_date}.png\")\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 1D plots of a crossover area, all the height points over time\n", - "t_min = (df_th.t.min() - pd.Timedelta(1, unit=\"W\")).isoformat()\n", - "t_max = (df_th.t.max() + pd.Timedelta(1, unit=\"W\")).isoformat()\n", - "h_min = df_th.h.min() - 0.2\n", - "h_max = df_th.h.max() + 0.2\n", - "\n", - "fig = pygmt.Figure()\n", - "with pygmt.config(\n", - " FONT_ANNOT_PRIMARY=\"9p\", FORMAT_TIME_PRIMARY_MAP=\"abbreviated\", FORMAT_DATE_MAP=\"o\"\n", - "):\n", - " fig.basemap(\n", - " projection=\"X12c/12c\",\n", - " region=[t_min, t_max, h_min, h_max],\n", - " frame=[\n", - " \"WSne\",\n", - " \"pxa1Of1o+lDate\", # primary time axis, 1 mOnth annotation and minor axis\n", - " \"sx1Y\", # secondary time axis, 1 Year intervals\n", - " 'yaf+l\"Elevation at crossover (m)\"',\n", - " ],\n", - " )\n", - "\n", - "crossovers = df_th.groupby(by=[\"x\", \"y\"])\n", - "pygmt.makecpt(cmap=\"categorical\", series=[1, len(crossovers) + 1, 1])\n", - "for i, ((x_coord, y_coord), indexes) in enumerate(crossovers.indices.items()):\n", - " df_ = df_th.loc[indexes].sort_values(by=\"t\")\n", - " # if df_.h.max() - df_.h.min() > 1.0: # plot only > 1 metre height change\n", - " track1, track2 = df_.track1_track2.iloc[0].split(\"_\")\n", - " label = f'\"Track {track1} {track2}\"'\n", - " fig.plot(x=df_.t, y=df_.h, Z=i, style=\"c0.1c\", cmap=True, pen=\"thin+z\", label=label)\n", - " # Plot line connecting points\n", - " fig.plot(\n", - " x=df_.t, y=df_.h, Z=i, pen=f\"faint,+z,-\", cmap=True\n", - " ) # , label=f'\"+g-1l+s0.15c\"')\n", - "fig.legend(position=\"JMR+JMR+o0.2c\", box=\"+gwhite+p1p\")\n", - "fig.savefig(f\"figures/crossover_many_{min_date}_{max_date}.png\")\n", - "fig.show()" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/atlxi_dhdt.py b/atlxi_dhdt.py index cdbea52..9b1becc 100644 --- a/atlxi_dhdt.py +++ b/atlxi_dhdt.py @@ -37,18 +37,22 @@ # %% import itertools import os +import warnings import numpy as np import pandas as pd import xarray as xr +import cudf # comment out if no GPU import dask import datashader import deepicedrain import holoviews as hv +import hvplot.cudf # comment out if no GPU import hvplot.pandas import intake import panel as pn +import param import pygmt import scipy.stats import tqdm @@ -399,84 +403,136 @@ if not os.path.exists(f"ATLXI/df_dhdt_{placename}.parquet"): # Subset dataset to geographic region of interest ds_subset: xr.Dataset = region.subset(data=ds_dhdt) - # Add a UTC_time column to the dataframe + # Add a UTC_time column to the dataset ds_subset["utc_time"] = deepicedrain.deltatime_to_utctime( dataarray=ds_subset.delta_time ) - # Convert xarray.Dataset to pandas.DataFrame for easier analysis - df_many: pd.DataFrame = ds_subset.to_dataframe().dropna() - # Drop delta_time column since timedelta64 dtype cannot be saved to parquet - # https://github.com/pandas-dev/pandas/issues/31909 - df_many: pd.DataFrame = df_many.drop(columns="delta_time") - # Need to use_deprecated_int96_timestamps in order to save utc_time column - # https://issues.apache.org/jira/browse/ARROW-1957 - df_many.to_parquet( - f"ATLXI/df_dhdt_{placename}.parquet", use_deprecated_int96_timestamps=True + # Save to parquet format + deepicedrain.ndarray_to_parquet( + ndarray=ds_subset, + parquetpath=f"ATLXI/df_dhdt_{placename}.parquet", + variables=[ + "x", + "y", + "dhdt_slope", + "referencegroundtrack", + "h_corr", + "utc_time", + ], + dropnacols=["dhdt_slope"], + use_deprecated_int96_timestamps=True, ) -df_many = pd.read_parquet(f"ATLXI/df_dhdt_{placename}.parquet") +# df_many = pd.read_parquet(f"ATLXI/df_dhdt_{placename}.parquet") +df_dhdt = cudf.read_parquet(f"ATLXI/df_dhdt_{placename}.parquet") +# %% +warnings.filterwarnings( + action="ignore", + message="The global colormaps dictionary is no longer considered public API.", +) # %% -def dhdt_plot( - cycle: int = 7, - dhdt_variable: str = "dhdt_slope", - dhdt_range: tuple = (1, 10), - rasterize: bool = False, - datashade: bool = False, -) -> hv.element.chart.Scatter: +class IceSat2Explorer(param.Parameterized): """ - ICESat-2 rate of height change over time (dhdt) interactive scatter plot. - Uses HvPlot, and intended to be used inside a Panel dashboard. + ICESat-2 rate of height change over time (dhdt) interactive dashboard. + Built using HvPlot and Panel. + + Adapted from the "Panel-based Datashader dashboard" at + https://examples.pyviz.org/datashader_dashboard/dashboard.html. + See also https://github.com/holoviz/datashader/pull/676. """ - df_ = df_many.query( - expr="cycle_number == @cycle & " - "@dhdt_range[0] < abs(dhdt_slope) & abs(dhdt_slope) < @dhdt_range[1]" - ) - return df_.hvplot.scatter( - title=f"ICESat-2 Cycle {cycle} {dhdt_variable}", - x="x", - y="y", - c=dhdt_variable, - cmap="gist_earth" if dhdt_variable == "h_corr" else "BrBG", - clim=None, - # by="cycle_number", - rasterize=rasterize, - datashade=datashade, - dynspread=datashade, - hover=True, - hover_cols=["referencegroundtrack", "dhdt_slope", "h_corr"], - colorbar=True, - grid=True, - frame_width=1000, - frame_height=600, - data_aspect=1, - ) + + variable_cmap: dict = { + "referencegroundtrack": "glasbey", + "dhdt_slope": "BrBG", + "h_corr": "gist_earth", + } + dhdt_variable = param.Selector(default="dhdt_slope", objects=variable_cmap.keys()) + cycle = param.Integer(default=7, bounds=(2, 7)) + dhdt_range = param.Range(default=(1.0, 10.0), bounds=(0.0, 20.0)) + rasterize = param.Boolean(default=False) + datashade = param.Boolean(default=False) + + df_ = df_dhdt + plot = df_.hvplot.points(x="x", y="y", c="dhdt_slope", cmap="BrBG") + startX, endX = plot.range("x") + startY, endY = plot.range("y") + + def keep_zoom(self, x_range, y_range): + self.startX, self.endX = x_range + self.startY, self.endY = y_range + + @param.depends("cycle", "dhdt_variable", "dhdt_range", "rasterize", "datashade") + def view(self): + cond = np.logical_and( + float(self.dhdt_range[0]) < abs(self.df_.dhdt_slope), + abs(self.df_.dhdt_slope) < float(self.dhdt_range[1]), + ) + column: str = ( + self.dhdt_variable + if self.dhdt_variable != "h_corr" + else f"h_corr_{self.cycle}" + ) + if self.dhdt_variable == "h_corr": + df_subset = self.df_.loc[cond].dropna(subset=f"h_corr_{self.cycle}") + else: + df_subset = self.df_.loc[cond] + self.plot = df_subset.hvplot.points( + title=f"ICESat-2 Cycle {self.cycle} {self.dhdt_variable}", + x="x", + y="y", + c=column, + cmap=self.variable_cmap[self.dhdt_variable], + rasterize=self.rasterize, + datashade=self.datashade, + dynspread=self.datashade, + hover=True, + hover_cols=[ + "referencegroundtrack", + "dhdt_slope", + f"h_corr_{self.cycle}", + f"utc_time_{self.cycle}", + ], + colorbar=True, + grid=True, + frame_width=1000, + frame_height=600, + data_aspect=1, + ) + self.plot = self.plot.redim.range( + x=(self.startX, self.endX), y=(self.startY, self.endY) + ) + self.plot = self.plot.opts(active_tools=["pan", "wheel_zoom"]) + rangexy = hv.streams.RangeXY( + source=self.plot, + x_range=(self.startX, self.endX), + y_range=(self.startY, self.endY), + ) + rangexy.add_subscriber(self.keep_zoom) + return self.plot # %% # Interactive holoviews scatter plot to find referencegroundtrack needed # Tip: Hover over the points, and find those with high 'dhdt_slope' values -layout: pn.layout.Column = pn.interact( - dhdt_plot, - cycle=pn.widgets.IntSlider(name="Cycle Number", start=2, end=7, step=1, value=7), - dhdt_variable=pn.widgets.RadioButtonGroup( - name="dhdt_variables", - value="dhdt_slope", - options=["referencegroundtrack", "dhdt_slope", "h_corr"], - ), - dhdt_range=pn.widgets.RangeSlider( - name="dhdt range ±", start=0, end=20, value=(1, 10), step=0.25 - ), - rasterize=pn.widgets.Checkbox(name="Rasterize"), - datashade=pn.widgets.Checkbox(name="Datashade"), +viewer = IceSat2Explorer() +widgets: pn.param.Param = pn.Param( + viewer.param, + widgets={ + "dhdt_variable": pn.widgets.RadioButtonGroup, + "cycle": pn.widgets.IntSlider, + "dhdt_range": pn.widgets.RangeSlider, + "rasterize": pn.widgets.Checkbox, + "datashade": pn.widgets.Checkbox, + }, ) dashboard: pn.layout.Column = pn.Column( pn.Row( - pn.Column(layout[0][1], align="center"), - pn.Column(layout[0][0], layout[0][2], align="center"), - pn.Column(layout[0][3], layout[0][4], align="center"), + pn.Column(widgets[0], widgets[1], align="center"), + pn.Column(widgets[2], widgets[3], align="center"), + pn.Column(widgets[4], widgets[5], align="center"), ), - layout[1], + viewer.view, ) # dashboard @@ -559,229 +615,3 @@ def dhdt_plot( fig.show() # %% - - -# %% [markdown] -# # Crossover Track Analysis -# -# To increase the temporal resolution of -# our ice elevation change analysis -# (i.e. at time periods less than -# the 91 day repeat cycle of ICESat-2), -# we can look at the locations where the -# ICESat-2 tracks intersect and get the -# height values there! -# Uses [x2sys_cross](https://docs.generic-mapping-tools.org/6.1/supplements/x2sys/x2sys_cross). -# -# References: -# - Wessel, P. (2010). Tools for analyzing intersecting tracks: The x2sys package. -# Computers & Geosciences, 36(3), 348–354. https://doi.org/10.1016/j.cageo.2009.05.009 - - -# %% -# Initialize X2SYS database in the X2SYS/ICESAT2 folder -tag = "X2SYS" -os.environ["X2SYS_HOME"] = os.path.abspath(tag) -os.getcwd() -pygmt.x2sys_init( - tag="ICESAT2", - fmtfile=f"{tag}/ICESAT2/xyht", - suffix="tsv", - units=["de", "se"], # distance in metres, speed in metres per second - gap="d250e", # distance gap up to 250 metres allowed - force=True, - verbose="q", -) - -# %% -# Run crossover analysis on all tracks -rgts: list = [135, 327, 388, 577, 1080, 1272] # Whillans upstream -# rgts: list = [236, 501, 562, 1181] # Whillans_downstream -tracks = [f"{tag}/track_{i}.tsv" for i in rgts] -assert all(os.path.exists(k) for k in tracks) - -# Parallelized paired crossover analysis -futures: list = [] -for track1, track2 in itertools.combinations(rgts, r=2): - future = client.submit( - key=f"{track1}_{track2}", - func=pygmt.x2sys_cross, - tracks=[f"{tag}/track_{track1}.tsv", f"{tag}/track_{track2}.tsv"], - tag="ICESAT2", - region=[-460000, -400000, -560000, -500000], - interpolation="l", # linear interpolation - coe="e", # external crossovers - trackvalues=True, # Get track 1 height (h_1) and track 2 height (h_2) - # trackvalues=False, # Get crossover error (h_X) and mean height value (h_M) - # outfile="xover_236_562.tsv" - ) - futures.append(future) - - -# %% -crossovers: dict = {} -for f in tqdm.tqdm( - iterable=dask.distributed.as_completed(futures=futures), total=len(futures) -): - if f.status != "error": # skip those track pairs which don't intersect - crossovers[f.key] = f.result().dropna().reset_index(drop=True) - -df_cross: pd.DataFrame = pd.concat(objs=crossovers, names=["track1_track2", "id"]) -df: pd.DataFrame = df_cross.reset_index(level="track1_track2").reset_index(drop=True) -# Report on how many unique crossover intersections there were -# df.plot.scatter(x="x", y="y") # quick plot of our crossover points -print( - f"{len(df.groupby(by=['x', 'y']))} crossover intersection point locations found " - f"with {len(df)} crossover height-time pairs " - f"over {len(tracks)} tracks" -) - - -# %% -# Calculate crossover error -df["h_X"]: pd.Series = df.h_2 - df.h_1 # crossover error (i.e. height difference) -df["t_D"]: pd.Series = df.t_2 - df.t_1 # elapsed time in ns (i.e. time difference) -ns_in_yr: int = (365.25 * 24 * 60 * 60 * 1_000_000_000) # nanoseconds in a year -df["dhdt"]: pd.Series = df.h_X / (df.t_D.astype(np.int64) / ns_in_yr) - -# %% -# Get some summary statistics of our crossover errors -sumstats: pd.DataFrame = df[["h_X", "t_D", "dhdt"]].describe() -# Find location with highest absolute crossover error, and most sudden height change -max_h_X: pd.Series = df.iloc[np.nanargmax(df.h_X.abs())] # highest crossover error -max_dhdt: pd.Series = df.iloc[df.dhdt.argmax()] # most sudden change in height - - -# %% [markdown] -# ### 2D Map view of crossover points -# -# Bird's eye view of the crossover points -# overlaid on top of the ICESat-2 tracks. - -# %% -# 2D plot of crossover locations -var: str = "h_X" -fig = pygmt.Figure() -# Setup basemap -region = np.array([df.x.min(), df.x.max(), df.y.min(), df.y.max()]) -buffer = np.array([-2000, +2000, -2000, +2000]) -pygmt.makecpt(cmap="batlow", series=[sumstats[var]["25%"], sumstats[var]["75%"]]) -# Map frame in metre units -fig.basemap(frame="f", region=region + buffer, projection="X8c") -# Plot actual track points -for track in tracks: - fig.plot(data=track, color="green", style="c0.01c") -# Plot crossover point locations -fig.plot(x=df.x, y=df.y, color=df.h_X, cmap=True, style="c0.1c", pen="thinnest") -# Map frame in kilometre units -fig.basemap( - frame=[ - "WSne", - 'xaf+l"Polar Stereographic X (km)"', - 'yaf+l"Polar Stereographic Y (km)"', - ], - region=(region + buffer) / 1000, - projection="X8c", -) -fig.colorbar(position="JMR", frame=['x+l"Crossover Error"', "y+lm"]) -fig.savefig("figures/crossover_area.png") -fig.show() - - -# %% [markdown] -# ### 1D plots of height changing over time -# -# Plot height change over time at: -# -# 1. One single crossover point location -# 2. Many crossover locations over an area - -# %% -# Tidy up dataframe first using pd.wide_to_long -# I.e. convert 't_1', 't_2', 'h_1', 'h_2' columns into just 't' and 'h'. -df["id"] = df.index -df_th: pd.DataFrame = pd.wide_to_long( - df=df[["id", "track1_track2", "x", "y", "t_1", "t_2", "h_1", "h_2"]], - stubnames=["t", "h"], - i="id", - j="track", - sep="_", -) -df_th = df_th.reset_index(level="track").drop_duplicates(ignore_index=True) - -# %% -# 1D Plot at location with **maximum** absolute crossover height error (max_h_X) -df_max = df_th.query(expr="x == @max_h_X.x & y == @max_h_X.y").sort_values(by="t") -track1, track2 = df_max.track1_track2.iloc[0].split("_") -print(f"{round(max_h_X.h_X, 2)} metres height change at {max_h_X.x}, {max_h_X.y}") -t_min = (df_max.t.min() - pd.Timedelta(2, unit="W")).isoformat() -t_max = (df_max.t.max() + pd.Timedelta(2, unit="W")).isoformat() -h_min = df_max.h.min() - 0.2 -h_max = df_max.h.max() + 0.4 - -fig = pygmt.Figure() -with pygmt.config( - FONT_ANNOT_PRIMARY="9p", FORMAT_TIME_PRIMARY_MAP="abbreviated", FORMAT_DATE_MAP="o" -): - fig.basemap( - projection="X12c/8c", - region=[t_min, t_max, h_min, h_max], - frame=[ - "WSne", - "pxa1Of1o+lDate", # primary time axis, 1 mOnth annotation and minor axis - "sx1Y", # secondary time axis, 1 Year intervals - 'yaf+l"Elevation at crossover (m)"', - ], - ) -fig.text( - text=f"Track {track1} and {track2} crossover", - position="TC", - offset="jTC0c/0.2c", - V="q", -) -# Plot data points -fig.plot(x=df_max.t, y=df_max.h, style="c0.15c", color="darkblue", pen="thin") -# Plot dashed line connecting points -fig.plot(x=df_max.t, y=df_max.h, pen=f"faint,blue,-") -fig.savefig(f"figures/crossover_{track1}_{track2}_{min_date}_{max_date}.png") -fig.show() - -# %% -# 1D plots of a crossover area, all the height points over time -t_min = (df_th.t.min() - pd.Timedelta(1, unit="W")).isoformat() -t_max = (df_th.t.max() + pd.Timedelta(1, unit="W")).isoformat() -h_min = df_th.h.min() - 0.2 -h_max = df_th.h.max() + 0.2 - -fig = pygmt.Figure() -with pygmt.config( - FONT_ANNOT_PRIMARY="9p", FORMAT_TIME_PRIMARY_MAP="abbreviated", FORMAT_DATE_MAP="o" -): - fig.basemap( - projection="X12c/12c", - region=[t_min, t_max, h_min, h_max], - frame=[ - "WSne", - "pxa1Of1o+lDate", # primary time axis, 1 mOnth annotation and minor axis - "sx1Y", # secondary time axis, 1 Year intervals - 'yaf+l"Elevation at crossover (m)"', - ], - ) - -crossovers = df_th.groupby(by=["x", "y"]) -pygmt.makecpt(cmap="categorical", series=[1, len(crossovers) + 1, 1]) -for i, ((x_coord, y_coord), indexes) in enumerate(crossovers.indices.items()): - df_ = df_th.loc[indexes].sort_values(by="t") - # if df_.h.max() - df_.h.min() > 1.0: # plot only > 1 metre height change - track1, track2 = df_.track1_track2.iloc[0].split("_") - label = f'"Track {track1} {track2}"' - fig.plot(x=df_.t, y=df_.h, Z=i, style="c0.1c", cmap=True, pen="thin+z", label=label) - # Plot line connecting points - fig.plot( - x=df_.t, y=df_.h, Z=i, pen=f"faint,+z,-", cmap=True - ) # , label=f'"+g-1l+s0.15c"') -fig.legend(position="JMR+JMR+o0.2c", box="+gwhite+p1p") -fig.savefig(f"figures/crossover_many_{min_date}_{max_date}.png") -fig.show() - -# %% diff --git a/atlxi_lake.ipynb b/atlxi_lake.ipynb new file mode 100644 index 0000000..78c663b --- /dev/null +++ b/atlxi_lake.ipynb @@ -0,0 +1,330 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "lines_to_next_cell": 2 + }, + "source": [ + "# Crossover Track Analysis\n", + "\n", + "To increase the temporal resolution of\n", + "our ice elevation change analysis\n", + "(i.e. at time periods less than\n", + "the 91 day repeat cycle of ICESat-2),\n", + "we can look at the locations where the\n", + "ICESat-2 tracks intersect and get the\n", + "height values there!\n", + "Uses [x2sys_cross](https://docs.generic-mapping-tools.org/6.1/supplements/x2sys/x2sys_cross).\n", + "\n", + "References:\n", + "- Wessel, P. (2010). Tools for analyzing intersecting tracks: The x2sys package.\n", + "Computers & Geosciences, 36(3), 348–354. https://doi.org/10.1016/j.cageo.2009.05.009" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize X2SYS database in the X2SYS/ICESAT2 folder\n", + "tag = \"X2SYS\"\n", + "os.environ[\"X2SYS_HOME\"] = os.path.abspath(tag)\n", + "os.getcwd()\n", + "pygmt.x2sys_init(\n", + " tag=\"ICESAT2\",\n", + " fmtfile=f\"{tag}/ICESAT2/xyht\",\n", + " suffix=\"tsv\",\n", + " units=[\"de\", \"se\"], # distance in metres, speed in metres per second\n", + " gap=\"d250e\", # distance gap up to 250 metres allowed\n", + " force=True,\n", + " verbose=\"q\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "# Run crossover analysis on all tracks\n", + "rgts: list = [135, 327, 388, 577, 1080, 1272] # Whillans upstream\n", + "# rgts: list = [236, 501, 562, 1181] # Whillans_downstream\n", + "tracks = [f\"{tag}/track_{i}.tsv\" for i in rgts]\n", + "assert all(os.path.exists(k) for k in tracks)\n", + "\n", + "# Parallelized paired crossover analysis\n", + "futures: list = []\n", + "for track1, track2 in itertools.combinations(rgts, r=2):\n", + " future = client.submit(\n", + " key=f\"{track1}_{track2}\",\n", + " func=pygmt.x2sys_cross,\n", + " tracks=[f\"{tag}/track_{track1}.tsv\", f\"{tag}/track_{track2}.tsv\"],\n", + " tag=\"ICESAT2\",\n", + " region=[-460000, -400000, -560000, -500000],\n", + " interpolation=\"l\", # linear interpolation\n", + " coe=\"e\", # external crossovers\n", + " trackvalues=True, # Get track 1 height (h_1) and track 2 height (h_2)\n", + " # trackvalues=False, # Get crossover error (h_X) and mean height value (h_M)\n", + " # outfile=\"xover_236_562.tsv\"\n", + " )\n", + " futures.append(future)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "crossovers: dict = {}\n", + "for f in tqdm.tqdm(\n", + " iterable=dask.distributed.as_completed(futures=futures), total=len(futures)\n", + "):\n", + " if f.status != \"error\": # skip those track pairs which don't intersect\n", + " crossovers[f.key] = f.result().dropna().reset_index(drop=True)\n", + "\n", + "df_cross: pd.DataFrame = pd.concat(objs=crossovers, names=[\"track1_track2\", \"id\"])\n", + "df: pd.DataFrame = df_cross.reset_index(level=\"track1_track2\").reset_index(drop=True)\n", + "# Report on how many unique crossover intersections there were\n", + "# df.plot.scatter(x=\"x\", y=\"y\") # quick plot of our crossover points\n", + "print(\n", + " f\"{len(df.groupby(by=['x', 'y']))} crossover intersection point locations found \"\n", + " f\"with {len(df)} crossover height-time pairs \"\n", + " f\"over {len(tracks)} tracks\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate crossover error\n", + "df[\"h_X\"]: pd.Series = df.h_2 - df.h_1 # crossover error (i.e. height difference)\n", + "df[\"t_D\"]: pd.Series = df.t_2 - df.t_1 # elapsed time in ns (i.e. time difference)\n", + "ns_in_yr: int = (365.25 * 24 * 60 * 60 * 1_000_000_000) # nanoseconds in a year\n", + "df[\"dhdt\"]: pd.Series = df.h_X / (df.t_D.astype(np.int64) / ns_in_yr)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "# Get some summary statistics of our crossover errors\n", + "sumstats: pd.DataFrame = df[[\"h_X\", \"t_D\", \"dhdt\"]].describe()\n", + "# Find location with highest absolute crossover error, and most sudden height change\n", + "max_h_X: pd.Series = df.iloc[np.nanargmax(df.h_X.abs())] # highest crossover error\n", + "max_dhdt: pd.Series = df.iloc[df.dhdt.argmax()] # most sudden change in height" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2D Map view of crossover points\n", + "\n", + "Bird's eye view of the crossover points\n", + "overlaid on top of the ICESat-2 tracks." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "# 2D plot of crossover locations\n", + "var: str = \"h_X\"\n", + "fig = pygmt.Figure()\n", + "# Setup basemap\n", + "region = np.array([df.x.min(), df.x.max(), df.y.min(), df.y.max()])\n", + "buffer = np.array([-2000, +2000, -2000, +2000])\n", + "pygmt.makecpt(cmap=\"batlow\", series=[sumstats[var][\"25%\"], sumstats[var][\"75%\"]])\n", + "# Map frame in metre units\n", + "fig.basemap(frame=\"f\", region=region + buffer, projection=\"X8c\")\n", + "# Plot actual track points\n", + "for track in tracks:\n", + " fig.plot(data=track, color=\"green\", style=\"c0.01c\")\n", + "# Plot crossover point locations\n", + "fig.plot(x=df.x, y=df.y, color=df.h_X, cmap=True, style=\"c0.1c\", pen=\"thinnest\")\n", + "# Map frame in kilometre units\n", + "fig.basemap(\n", + " frame=[\n", + " \"WSne\",\n", + " 'xaf+l\"Polar Stereographic X (km)\"',\n", + " 'yaf+l\"Polar Stereographic Y (km)\"',\n", + " ],\n", + " region=(region + buffer) / 1000,\n", + " projection=\"X8c\",\n", + ")\n", + "fig.colorbar(position=\"JMR\", frame=['x+l\"Crossover Error\"', \"y+lm\"])\n", + "fig.savefig(\"figures/crossover_area.png\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1D plots of height changing over time\n", + "\n", + "Plot height change over time at:\n", + "\n", + "1. One single crossover point location\n", + "2. Many crossover locations over an area" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Tidy up dataframe first using pd.wide_to_long\n", + "# I.e. convert 't_1', 't_2', 'h_1', 'h_2' columns into just 't' and 'h'.\n", + "df[\"id\"] = df.index\n", + "df_th: pd.DataFrame = pd.wide_to_long(\n", + " df=df[[\"id\", \"track1_track2\", \"x\", \"y\", \"t_1\", \"t_2\", \"h_1\", \"h_2\"]],\n", + " stubnames=[\"t\", \"h\"],\n", + " i=\"id\",\n", + " j=\"track\",\n", + " sep=\"_\",\n", + ")\n", + "df_th = df_th.reset_index(level=\"track\").drop_duplicates(ignore_index=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 1D Plot at location with **maximum** absolute crossover height error (max_h_X)\n", + "df_max = df_th.query(expr=\"x == @max_h_X.x & y == @max_h_X.y\").sort_values(by=\"t\")\n", + "track1, track2 = df_max.track1_track2.iloc[0].split(\"_\")\n", + "print(f\"{round(max_h_X.h_X, 2)} metres height change at {max_h_X.x}, {max_h_X.y}\")\n", + "t_min = (df_max.t.min() - pd.Timedelta(2, unit=\"W\")).isoformat()\n", + "t_max = (df_max.t.max() + pd.Timedelta(2, unit=\"W\")).isoformat()\n", + "h_min = df_max.h.min() - 0.2\n", + "h_max = df_max.h.max() + 0.4\n", + "\n", + "fig = pygmt.Figure()\n", + "with pygmt.config(\n", + " FONT_ANNOT_PRIMARY=\"9p\", FORMAT_TIME_PRIMARY_MAP=\"abbreviated\", FORMAT_DATE_MAP=\"o\"\n", + "):\n", + " fig.basemap(\n", + " projection=\"X12c/8c\",\n", + " region=[t_min, t_max, h_min, h_max],\n", + " frame=[\n", + " \"WSne\",\n", + " \"pxa1Of1o+lDate\", # primary time axis, 1 mOnth annotation and minor axis\n", + " \"sx1Y\", # secondary time axis, 1 Year intervals\n", + " 'yaf+l\"Elevation at crossover (m)\"',\n", + " ],\n", + " )\n", + "fig.text(\n", + " text=f\"Track {track1} and {track2} crossover\",\n", + " position=\"TC\",\n", + " offset=\"jTC0c/0.2c\",\n", + " V=\"q\",\n", + ")\n", + "# Plot data points\n", + "fig.plot(x=df_max.t, y=df_max.h, style=\"c0.15c\", color=\"darkblue\", pen=\"thin\")\n", + "# Plot dashed line connecting points\n", + "fig.plot(x=df_max.t, y=df_max.h, pen=f\"faint,blue,-\")\n", + "fig.savefig(f\"figures/crossover_{track1}_{track2}_{min_date}_{max_date}.png\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 1D plots of a crossover area, all the height points over time\n", + "t_min = (df_th.t.min() - pd.Timedelta(1, unit=\"W\")).isoformat()\n", + "t_max = (df_th.t.max() + pd.Timedelta(1, unit=\"W\")).isoformat()\n", + "h_min = df_th.h.min() - 0.2\n", + "h_max = df_th.h.max() + 0.2\n", + "\n", + "fig = pygmt.Figure()\n", + "with pygmt.config(\n", + " FONT_ANNOT_PRIMARY=\"9p\", FORMAT_TIME_PRIMARY_MAP=\"abbreviated\", FORMAT_DATE_MAP=\"o\"\n", + "):\n", + " fig.basemap(\n", + " projection=\"X12c/12c\",\n", + " region=[t_min, t_max, h_min, h_max],\n", + " frame=[\n", + " \"WSne\",\n", + " \"pxa1Of1o+lDate\", # primary time axis, 1 mOnth annotation and minor axis\n", + " \"sx1Y\", # secondary time axis, 1 Year intervals\n", + " 'yaf+l\"Elevation at crossover (m)\"',\n", + " ],\n", + " )\n", + "\n", + "crossovers = df_th.groupby(by=[\"x\", \"y\"])\n", + "pygmt.makecpt(cmap=\"categorical\", series=[1, len(crossovers) + 1, 1])\n", + "for i, ((x_coord, y_coord), indexes) in enumerate(crossovers.indices.items()):\n", + " df_ = df_th.loc[indexes].sort_values(by=\"t\")\n", + " # if df_.h.max() - df_.h.min() > 1.0: # plot only > 1 metre height change\n", + " track1, track2 = df_.track1_track2.iloc[0].split(\"_\")\n", + " label = f'\"Track {track1} {track2}\"'\n", + " fig.plot(x=df_.t, y=df_.h, Z=i, style=\"c0.1c\", cmap=True, pen=\"thin+z\", label=label)\n", + " # Plot line connecting points\n", + " fig.plot(\n", + " x=df_.t, y=df_.h, Z=i, pen=f\"faint,+z,-\", cmap=True\n", + " ) # , label=f'\"+g-1l+s0.15c\"')\n", + "fig.legend(position=\"JMR+JMR+o0.2c\", box=\"+gwhite+p1p\")\n", + "fig.savefig(f\"figures/crossover_many_{min_date}_{max_date}.png\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "jupytext": { + "encoding": "# -*- coding: utf-8 -*-", + "formats": "ipynb,py:hydrogen" + }, + "kernelspec": { + "display_name": "deepicedrain", + "language": "python", + "name": "deepicedrain" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/atlxi_lake.py b/atlxi_lake.py new file mode 100644 index 0000000..18b4e79 --- /dev/null +++ b/atlxi_lake.py @@ -0,0 +1,240 @@ +# -*- coding: utf-8 -*- +# --- +# jupyter: +# jupytext: +# formats: ipynb,py:hydrogen +# text_representation: +# extension: .py +# format_name: hydrogen +# format_version: '1.3' +# jupytext_version: 1.5.2 +# kernelspec: +# display_name: deepicedrain +# language: python +# name: deepicedrain +# --- + +# %% [markdown] +# # Crossover Track Analysis +# +# To increase the temporal resolution of +# our ice elevation change analysis +# (i.e. at time periods less than +# the 91 day repeat cycle of ICESat-2), +# we can look at the locations where the +# ICESat-2 tracks intersect and get the +# height values there! +# Uses [x2sys_cross](https://docs.generic-mapping-tools.org/6.1/supplements/x2sys/x2sys_cross). +# +# References: +# - Wessel, P. (2010). Tools for analyzing intersecting tracks: The x2sys package. +# Computers & Geosciences, 36(3), 348–354. https://doi.org/10.1016/j.cageo.2009.05.009 + + +# %% +# Initialize X2SYS database in the X2SYS/ICESAT2 folder +tag = "X2SYS" +os.environ["X2SYS_HOME"] = os.path.abspath(tag) +os.getcwd() +pygmt.x2sys_init( + tag="ICESAT2", + fmtfile=f"{tag}/ICESAT2/xyht", + suffix="tsv", + units=["de", "se"], # distance in metres, speed in metres per second + gap="d250e", # distance gap up to 250 metres allowed + force=True, + verbose="q", +) + +# %% +# Run crossover analysis on all tracks +rgts: list = [135, 327, 388, 577, 1080, 1272] # Whillans upstream +# rgts: list = [236, 501, 562, 1181] # Whillans_downstream +tracks = [f"{tag}/track_{i}.tsv" for i in rgts] +assert all(os.path.exists(k) for k in tracks) + +# Parallelized paired crossover analysis +futures: list = [] +for track1, track2 in itertools.combinations(rgts, r=2): + future = client.submit( + key=f"{track1}_{track2}", + func=pygmt.x2sys_cross, + tracks=[f"{tag}/track_{track1}.tsv", f"{tag}/track_{track2}.tsv"], + tag="ICESAT2", + region=[-460000, -400000, -560000, -500000], + interpolation="l", # linear interpolation + coe="e", # external crossovers + trackvalues=True, # Get track 1 height (h_1) and track 2 height (h_2) + # trackvalues=False, # Get crossover error (h_X) and mean height value (h_M) + # outfile="xover_236_562.tsv" + ) + futures.append(future) + + +# %% +crossovers: dict = {} +for f in tqdm.tqdm( + iterable=dask.distributed.as_completed(futures=futures), total=len(futures) +): + if f.status != "error": # skip those track pairs which don't intersect + crossovers[f.key] = f.result().dropna().reset_index(drop=True) + +df_cross: pd.DataFrame = pd.concat(objs=crossovers, names=["track1_track2", "id"]) +df: pd.DataFrame = df_cross.reset_index(level="track1_track2").reset_index(drop=True) +# Report on how many unique crossover intersections there were +# df.plot.scatter(x="x", y="y") # quick plot of our crossover points +print( + f"{len(df.groupby(by=['x', 'y']))} crossover intersection point locations found " + f"with {len(df)} crossover height-time pairs " + f"over {len(tracks)} tracks" +) + + +# %% +# Calculate crossover error +df["h_X"]: pd.Series = df.h_2 - df.h_1 # crossover error (i.e. height difference) +df["t_D"]: pd.Series = df.t_2 - df.t_1 # elapsed time in ns (i.e. time difference) +ns_in_yr: int = (365.25 * 24 * 60 * 60 * 1_000_000_000) # nanoseconds in a year +df["dhdt"]: pd.Series = df.h_X / (df.t_D.astype(np.int64) / ns_in_yr) + +# %% +# Get some summary statistics of our crossover errors +sumstats: pd.DataFrame = df[["h_X", "t_D", "dhdt"]].describe() +# Find location with highest absolute crossover error, and most sudden height change +max_h_X: pd.Series = df.iloc[np.nanargmax(df.h_X.abs())] # highest crossover error +max_dhdt: pd.Series = df.iloc[df.dhdt.argmax()] # most sudden change in height + + +# %% [markdown] +# ### 2D Map view of crossover points +# +# Bird's eye view of the crossover points +# overlaid on top of the ICESat-2 tracks. + +# %% +# 2D plot of crossover locations +var: str = "h_X" +fig = pygmt.Figure() +# Setup basemap +region = np.array([df.x.min(), df.x.max(), df.y.min(), df.y.max()]) +buffer = np.array([-2000, +2000, -2000, +2000]) +pygmt.makecpt(cmap="batlow", series=[sumstats[var]["25%"], sumstats[var]["75%"]]) +# Map frame in metre units +fig.basemap(frame="f", region=region + buffer, projection="X8c") +# Plot actual track points +for track in tracks: + fig.plot(data=track, color="green", style="c0.01c") +# Plot crossover point locations +fig.plot(x=df.x, y=df.y, color=df.h_X, cmap=True, style="c0.1c", pen="thinnest") +# Map frame in kilometre units +fig.basemap( + frame=[ + "WSne", + 'xaf+l"Polar Stereographic X (km)"', + 'yaf+l"Polar Stereographic Y (km)"', + ], + region=(region + buffer) / 1000, + projection="X8c", +) +fig.colorbar(position="JMR", frame=['x+l"Crossover Error"', "y+lm"]) +fig.savefig("figures/crossover_area.png") +fig.show() + + +# %% [markdown] +# ### 1D plots of height changing over time +# +# Plot height change over time at: +# +# 1. One single crossover point location +# 2. Many crossover locations over an area + +# %% +# Tidy up dataframe first using pd.wide_to_long +# I.e. convert 't_1', 't_2', 'h_1', 'h_2' columns into just 't' and 'h'. +df["id"] = df.index +df_th: pd.DataFrame = pd.wide_to_long( + df=df[["id", "track1_track2", "x", "y", "t_1", "t_2", "h_1", "h_2"]], + stubnames=["t", "h"], + i="id", + j="track", + sep="_", +) +df_th = df_th.reset_index(level="track").drop_duplicates(ignore_index=True) + +# %% +# 1D Plot at location with **maximum** absolute crossover height error (max_h_X) +df_max = df_th.query(expr="x == @max_h_X.x & y == @max_h_X.y").sort_values(by="t") +track1, track2 = df_max.track1_track2.iloc[0].split("_") +print(f"{round(max_h_X.h_X, 2)} metres height change at {max_h_X.x}, {max_h_X.y}") +t_min = (df_max.t.min() - pd.Timedelta(2, unit="W")).isoformat() +t_max = (df_max.t.max() + pd.Timedelta(2, unit="W")).isoformat() +h_min = df_max.h.min() - 0.2 +h_max = df_max.h.max() + 0.4 + +fig = pygmt.Figure() +with pygmt.config( + FONT_ANNOT_PRIMARY="9p", FORMAT_TIME_PRIMARY_MAP="abbreviated", FORMAT_DATE_MAP="o" +): + fig.basemap( + projection="X12c/8c", + region=[t_min, t_max, h_min, h_max], + frame=[ + "WSne", + "pxa1Of1o+lDate", # primary time axis, 1 mOnth annotation and minor axis + "sx1Y", # secondary time axis, 1 Year intervals + 'yaf+l"Elevation at crossover (m)"', + ], + ) +fig.text( + text=f"Track {track1} and {track2} crossover", + position="TC", + offset="jTC0c/0.2c", + V="q", +) +# Plot data points +fig.plot(x=df_max.t, y=df_max.h, style="c0.15c", color="darkblue", pen="thin") +# Plot dashed line connecting points +fig.plot(x=df_max.t, y=df_max.h, pen=f"faint,blue,-") +fig.savefig(f"figures/crossover_{track1}_{track2}_{min_date}_{max_date}.png") +fig.show() + +# %% +# 1D plots of a crossover area, all the height points over time +t_min = (df_th.t.min() - pd.Timedelta(1, unit="W")).isoformat() +t_max = (df_th.t.max() + pd.Timedelta(1, unit="W")).isoformat() +h_min = df_th.h.min() - 0.2 +h_max = df_th.h.max() + 0.2 + +fig = pygmt.Figure() +with pygmt.config( + FONT_ANNOT_PRIMARY="9p", FORMAT_TIME_PRIMARY_MAP="abbreviated", FORMAT_DATE_MAP="o" +): + fig.basemap( + projection="X12c/12c", + region=[t_min, t_max, h_min, h_max], + frame=[ + "WSne", + "pxa1Of1o+lDate", # primary time axis, 1 mOnth annotation and minor axis + "sx1Y", # secondary time axis, 1 Year intervals + 'yaf+l"Elevation at crossover (m)"', + ], + ) + +crossovers = df_th.groupby(by=["x", "y"]) +pygmt.makecpt(cmap="categorical", series=[1, len(crossovers) + 1, 1]) +for i, ((x_coord, y_coord), indexes) in enumerate(crossovers.indices.items()): + df_ = df_th.loc[indexes].sort_values(by="t") + # if df_.h.max() - df_.h.min() > 1.0: # plot only > 1 metre height change + track1, track2 = df_.track1_track2.iloc[0].split("_") + label = f'"Track {track1} {track2}"' + fig.plot(x=df_.t, y=df_.h, Z=i, style="c0.1c", cmap=True, pen="thin+z", label=label) + # Plot line connecting points + fig.plot( + x=df_.t, y=df_.h, Z=i, pen=f"faint,+z,-", cmap=True + ) # , label=f'"+g-1l+s0.15c"') +fig.legend(position="JMR+JMR+o0.2c", box="+gwhite+p1p") +fig.savefig(f"figures/crossover_many_{min_date}_{max_date}.png") +fig.show() + +# %% From 55cf34f58cfd5758bebaf7560903d1a8470b97c6 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Mon, 24 Aug 2020 13:54:29 +1200 Subject: [PATCH 07/12] :heavy_plus_sign: Add intake-parquet Parquet plugin for intake! Also edit Github Actions workflow to test on Pull Requests targeting any branch. --- .github/workflows/python-app.yml | 6 ++- poetry.lock | 67 +++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index c5f49b1..def7cd9 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -2,9 +2,11 @@ name: Test DeepIceDrain on: push: - branches: [ master ] + branches: + - master pull_request: - branches: [ master ] + branches: + - "**" jobs: test: diff --git a/poetry.lock b/poetry.lock index de3c871..cc40dfb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -504,6 +504,30 @@ version = "0.15" monotonic = ">=0.1" six = "*" +[[package]] +category = "main" +description = "Python support for Parquet file format" +name = "fastparquet" +optional = false +python-versions = ">=3.6," +version = "0.4.1" + +[package.dependencies] +numba = ">=0.28" +numpy = ">=1.11" +packaging = "*" +pandas = ">=0.19" +six = "*" +thrift = ">=0.11.0" + +[package.extras] +brotli = ["brotli"] +lz4 = ["lz4 (>=0.19.1)"] +lzo = ["python-lzo"] +snappy = ["python-snappy"] +zstandard = ["zstandard"] +zstd = ["zstd"] + [[package]] category = "main" description = "Fast, re-entrant optimistic lock implemented in Cython" @@ -726,6 +750,21 @@ dataframe = ["dask", "msgpack-numpy", "pyarrow"] plot = ["hvplot", "panel (>=0.7.0)", "bokeh (<2.0)"] server = ["tornado", "python-snappy"] +[[package]] +category = "main" +description = "Intake parquet plugin" +name = "intake-parquet" +optional = false +python-versions = "*" +version = "0.2.3" + +[package.dependencies] +dask = "*" +fastparquet = "*" +intake = "*" +pandas = "*" +pyarrow = "*" + [[package]] category = "main" description = "xarray plugins for Intake" @@ -1827,6 +1866,22 @@ optional = false python-versions = ">=3.5" version = "2.1.0" +[[package]] +category = "main" +description = "Python bindings for the Apache Thrift RPC system" +name = "thrift" +optional = false +python-versions = "*" +version = "0.13.0" + +[package.dependencies] +six = ">=1.7.2" + +[package.extras] +all = ["tornado (>=4.0)", "twisted"] +tornado = ["tornado (>=4.0)"] +twisted = ["twisted"] + [[package]] category = "dev" description = "Python Library for Tom's Obvious, Minimal Language" @@ -2013,7 +2068,7 @@ heapdict = "*" cuda = ["cupy-cuda102", "dask-cuda"] [metadata] -content-hash = "73db6f447e88e5c89b3ac6ade0a664ab2b03465b9684e3c0d0e15d890a45d65e" +content-hash = "2b3f82ed5a8f252d769be1e554a8f9ef03ac2f416aec9e6eb9992878c9d2dbfa" python-versions = "^3.8" [metadata.files] @@ -2265,6 +2320,10 @@ fasteners = [ {file = "fasteners-0.15-py2.py3-none-any.whl", hash = "sha256:007e4d2b2d4a10093f67e932e5166722d2eab83b77724156e92ad013c6226574"}, {file = "fasteners-0.15.tar.gz", hash = "sha256:3a176da6b70df9bb88498e1a18a9e4a8579ed5b9141207762368a1017bf8f5ef"}, ] +fastparquet = [ + {file = "fastparquet-0.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c231110272c28d681a3cadbbe81a20f74c0a382c4b983107b3e7b625e8fd7251"}, + {file = "fastparquet-0.4.1.tar.gz", hash = "sha256:a6501352c875416471c875ace0ff702a7889e14eeda4c12eaaed8307fd485f6a"}, +] fastrlock = [ {file = "fastrlock-0.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:d92b85a8f609eea194a54978ccd97a385c6e78fb1ee86ae9dc07f1f50b225d9b"}, {file = "fastrlock-0.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:0111bd05a6f7c2a4005b4a847351454b66536d8320295c5505d41c360862b332"}, @@ -2375,6 +2434,9 @@ intake = [ {file = "intake-0.6.0-py3-none-any.whl", hash = "sha256:32a6d77965f3b89a12894c29e8c7516391a0be4c42902af52f12747186920bcc"}, {file = "intake-0.6.0.tar.gz", hash = "sha256:0c284abeb74927a7366dcab6cefc010c4d050365b8af61c37326a2473a490a4e"}, ] +intake-parquet = [ + {file = "intake-parquet-0.2.3.tar.gz", hash = "sha256:353078f8b3358abdf0b1fa959fb8bd7f40466c8d04c474f51015b13822b9b8e3"}, +] intake-xarray = [] ipykernel = [ {file = "ipykernel-5.3.4-py3-none-any.whl", hash = "sha256:d6fbba26dba3cebd411382bc484f7bc2caa98427ae0ddb4ab37fe8bfeb5c7dd3"}, @@ -3115,6 +3177,9 @@ threadpoolctl = [ {file = "threadpoolctl-2.1.0-py3-none-any.whl", hash = "sha256:38b74ca20ff3bb42caca8b00055111d74159ee95c4370882bbff2b93d24da725"}, {file = "threadpoolctl-2.1.0.tar.gz", hash = "sha256:ddc57c96a38beb63db45d6c159b5ab07b6bced12c45a1f07b2b92f272aebfa6b"}, ] +thrift = [ + {file = "thrift-0.13.0.tar.gz", hash = "sha256:9af1c86bf73433afc6010ed376a6c6aca2b54099cc0d61895f640870a9ae7d89"}, +] toml = [ {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, diff --git a/pyproject.toml b/pyproject.toml index 8c1d0d0..9430c4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ geopandas = "^0.8.1" geoviews = "^1.8.1" h5netcdf = "^0.8.1" intake = {extras = ["dataframe", "server"], version = "^0.6.0"} +intake-parquet = "^0.2.3" intake-xarray = { git = "https://github.com/intake/intake-xarray.git", rev = "bf98a3c69eea81be716b310e33aeefbf1a89b1d0" } jupyterlab = "^2.2.4" lxml = "^4.5.2" From 77b7fc888e2750e6b06083693990dd21cc7c41d8 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Mon, 24 Aug 2020 16:42:27 +1200 Subject: [PATCH 08/12] :art: Move IceSat2Explorer dashboard into vizplots.py Improving the dashboard while making the code more maintainable by moving the pure hvplot scatterplot stuff into the intake atlas_catalog.yaml file, and placing the dashboard/widgets under vizplots.py. This is yet another attempt at tidying up the code in the jupyter notebook, moving them into the deepicedrain package instead! Also updated the alongtrack plot code to work with the new df_dhdt columnar data structure. Will need to put the df_dhdt_{placename}.parquet data somewhere in the cloud (when I have time) so that the dashboard app can be used by more people, and also to enable unit testing of the visualization generators (always a tricky thing to test)! The dashboard is also currently hardcoded to plot the "whillans_upstream" area, will need to see the placename can be used as an argument into the IceSat2Explorer class. --- atlxi_dhdt.ipynb | 141 ++++---------------------------- atlxi_dhdt.py | 128 ++++------------------------- deepicedrain/README.md | 4 + deepicedrain/__init__.py | 1 + deepicedrain/atlas_catalog.yaml | 55 +++++++++++++ deepicedrain/vizplots.py | 124 ++++++++++++++++++++++++++++ 6 files changed, 220 insertions(+), 233 deletions(-) create mode 100644 deepicedrain/vizplots.py diff --git a/atlxi_dhdt.ipynb b/atlxi_dhdt.ipynb index b215faf..aea20db 100644 --- a/atlxi_dhdt.ipynb +++ b/atlxi_dhdt.ipynb @@ -910,13 +910,16 @@ " ds_subset[\"utc_time\"] = deepicedrain.deltatime_to_utctime(\n", " dataarray=ds_subset.delta_time\n", " )\n", - " # Save to parquet format\n", + " # Save to parquet format. If the dask workers get killed, reduce the number\n", + " # of workers (e.g. 72 to 32) so that each worker will have more memory\n", " deepicedrain.ndarray_to_parquet(\n", " ndarray=ds_subset,\n", " parquetpath=f\"ATLXI/df_dhdt_{placename}.parquet\",\n", " variables=[\n", " \"x\",\n", + " \"x_atc\",\n", " \"y\",\n", + " \"y_atc\",\n", " \"dhdt_slope\",\n", " \"referencegroundtrack\",\n", " \"h_corr\",\n", @@ -925,22 +928,8 @@ " dropnacols=[\"dhdt_slope\"],\n", " use_deprecated_int96_timestamps=True,\n", " )\n", - "# df_many = pd.read_parquet(f\"ATLXI/df_dhdt_{placename}.parquet\")\n", - "df_dhdt = cudf.read_parquet(f\"ATLXI/df_dhdt_{placename}.parquet\")" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": { - "lines_to_next_cell": 1 - }, - "outputs": [], - "source": [ - "warnings.filterwarnings(\n", - " action=\"ignore\",\n", - " message=\"The global colormaps dictionary is no longer considered public API.\",\n", - ")" + "# df_dhdt = pd.read_parquet(f\"ATLXI/df_dhdt_{placename}.parquet\")\n", + "df_dhdt: cudf.DataFrame = cudf.read_parquet(f\"ATLXI/df_dhdt_{placename}.parquet\")" ] }, { @@ -950,114 +939,11 @@ "lines_to_next_cell": 1 }, "outputs": [], - "source": [ - "class IceSat2Explorer(param.Parameterized):\n", - " \"\"\"\n", - " ICESat-2 rate of height change over time (dhdt) interactive dashboard.\n", - " Built using HvPlot and Panel.\n", - "\n", - " Adapted from the \"Panel-based Datashader dashboard\" at\n", - " https://examples.pyviz.org/datashader_dashboard/dashboard.html.\n", - " See also https://github.com/holoviz/datashader/pull/676.\n", - " \"\"\"\n", - "\n", - " variable_cmap: dict = {\n", - " \"referencegroundtrack\": \"glasbey\",\n", - " \"dhdt_slope\": \"BrBG\",\n", - " \"h_corr\": \"gist_earth\",\n", - " }\n", - " dhdt_variable = param.Selector(default=\"dhdt_slope\", objects=variable_cmap.keys())\n", - " cycle = param.Integer(default=7, bounds=(2, 7))\n", - " dhdt_range = param.Range(default=(1.0, 10.0), bounds=(0.0, 20.0))\n", - " rasterize = param.Boolean(default=False)\n", - " datashade = param.Boolean(default=False)\n", - "\n", - " df_ = df_dhdt\n", - " plot = df_.hvplot.points(x=\"x\", y=\"y\", c=\"dhdt_slope\", cmap=\"BrBG\")\n", - " startX, endX = plot.range(\"x\")\n", - " startY, endY = plot.range(\"y\")\n", - "\n", - " def keep_zoom(self, x_range, y_range):\n", - " self.startX, self.endX = x_range\n", - " self.startY, self.endY = y_range\n", - "\n", - " @param.depends(\"cycle\", \"dhdt_variable\", \"dhdt_range\", \"rasterize\", \"datashade\")\n", - " def view(self):\n", - " cond = np.logical_and(\n", - " float(self.dhdt_range[0]) < abs(self.df_.dhdt_slope),\n", - " abs(self.df_.dhdt_slope) < float(self.dhdt_range[1]),\n", - " )\n", - " column: str = (\n", - " self.dhdt_variable\n", - " if self.dhdt_variable != \"h_corr\"\n", - " else f\"h_corr_{self.cycle}\"\n", - " )\n", - " if self.dhdt_variable == \"h_corr\":\n", - " df_subset = self.df_.loc[cond].dropna(subset=f\"h_corr_{self.cycle}\")\n", - " else:\n", - " df_subset = self.df_.loc[cond]\n", - " self.plot = df_subset.hvplot.points(\n", - " title=f\"ICESat-2 Cycle {self.cycle} {self.dhdt_variable}\",\n", - " x=\"x\",\n", - " y=\"y\",\n", - " c=column,\n", - " cmap=self.variable_cmap[self.dhdt_variable],\n", - " rasterize=self.rasterize,\n", - " datashade=self.datashade,\n", - " dynspread=self.datashade,\n", - " hover=True,\n", - " hover_cols=[\n", - " \"referencegroundtrack\",\n", - " \"dhdt_slope\",\n", - " f\"h_corr_{self.cycle}\",\n", - " f\"utc_time_{self.cycle}\",\n", - " ],\n", - " colorbar=True,\n", - " grid=True,\n", - " frame_width=1000,\n", - " frame_height=600,\n", - " data_aspect=1,\n", - " )\n", - " self.plot = self.plot.redim.range(\n", - " x=(self.startX, self.endX), y=(self.startY, self.endY)\n", - " )\n", - " self.plot = self.plot.opts(active_tools=[\"pan\", \"wheel_zoom\"])\n", - " rangexy = hv.streams.RangeXY(\n", - " source=self.plot,\n", - " x_range=(self.startX, self.endX),\n", - " y_range=(self.startY, self.endY),\n", - " )\n", - " rangexy.add_subscriber(self.keep_zoom)\n", - " return self.plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], "source": [ "# Interactive holoviews scatter plot to find referencegroundtrack needed\n", "# Tip: Hover over the points, and find those with high 'dhdt_slope' values\n", - "viewer = IceSat2Explorer()\n", - "widgets: pn.param.Param = pn.Param(\n", - " viewer.param,\n", - " widgets={\n", - " \"dhdt_variable\": pn.widgets.RadioButtonGroup,\n", - " \"cycle\": pn.widgets.IntSlider,\n", - " \"dhdt_range\": pn.widgets.RangeSlider,\n", - " \"rasterize\": pn.widgets.Checkbox,\n", - " \"datashade\": pn.widgets.Checkbox,\n", - " },\n", - ")\n", - "dashboard: pn.layout.Column = pn.Column(\n", - " pn.Row(\n", - " pn.Column(widgets[0], widgets[1], align=\"center\"),\n", - " pn.Column(widgets[2], widgets[3], align=\"center\"),\n", - " pn.Column(widgets[4], widgets[5], align=\"center\"),\n", - " ),\n", - " viewer.view,\n", - ")\n", + "viewer = deepicedrain.IceSat2Explorer(name=\"ICESat-2 Explorer\")\n", + "dashboard: pn.layout.Column = pn.Column(viewer.widgets, viewer.view)\n", "# dashboard" ] }, @@ -1089,7 +975,16 @@ "rgts: list = [135, 327, 388, 577, 1080, 1272] # Whillans upstream\n", "# rgts: list = [236, 501 , 562, 1181] # whillans_downstream\n", "for rgt in rgts:\n", - " df_rgt: pd.DataFrame = df_many.query(expr=\"referencegroundtrack == @rgt\")\n", + " df_rgt: pd.DataFrame = df_dhdt.query(expr=\"referencegroundtrack == @rgt\")\n", + " df_rgt[\"id\"] = df_rgt.index\n", + " df_rgt = pd.wide_to_long(\n", + " df=df_rgt.to_pandas(),\n", + " stubnames=[\"h_corr\", \"utc_time\"],\n", + " i=\"id\",\n", + " j=\"cycle_number\",\n", + " sep=\"_\",\n", + " )\n", + " df_rgt = df_rgt.dropna()\n", "\n", " # Save track data to CSV for crossover analysis later\n", " df_rgt[[\"x\", \"y\", \"h_corr\", \"utc_time\"]].to_csv(\n", diff --git a/atlxi_dhdt.py b/atlxi_dhdt.py index 9b1becc..0cac79d 100644 --- a/atlxi_dhdt.py +++ b/atlxi_dhdt.py @@ -407,13 +407,16 @@ ds_subset["utc_time"] = deepicedrain.deltatime_to_utctime( dataarray=ds_subset.delta_time ) - # Save to parquet format + # Save to parquet format. If the dask workers get killed, reduce the number + # of workers (e.g. 72 to 32) so that each worker will have more memory deepicedrain.ndarray_to_parquet( ndarray=ds_subset, parquetpath=f"ATLXI/df_dhdt_{placename}.parquet", variables=[ "x", + "x_atc", "y", + "y_atc", "dhdt_slope", "referencegroundtrack", "h_corr", @@ -422,118 +425,14 @@ dropnacols=["dhdt_slope"], use_deprecated_int96_timestamps=True, ) -# df_many = pd.read_parquet(f"ATLXI/df_dhdt_{placename}.parquet") -df_dhdt = cudf.read_parquet(f"ATLXI/df_dhdt_{placename}.parquet") - -# %% -warnings.filterwarnings( - action="ignore", - message="The global colormaps dictionary is no longer considered public API.", -) - -# %% -class IceSat2Explorer(param.Parameterized): - """ - ICESat-2 rate of height change over time (dhdt) interactive dashboard. - Built using HvPlot and Panel. - - Adapted from the "Panel-based Datashader dashboard" at - https://examples.pyviz.org/datashader_dashboard/dashboard.html. - See also https://github.com/holoviz/datashader/pull/676. - """ - - variable_cmap: dict = { - "referencegroundtrack": "glasbey", - "dhdt_slope": "BrBG", - "h_corr": "gist_earth", - } - dhdt_variable = param.Selector(default="dhdt_slope", objects=variable_cmap.keys()) - cycle = param.Integer(default=7, bounds=(2, 7)) - dhdt_range = param.Range(default=(1.0, 10.0), bounds=(0.0, 20.0)) - rasterize = param.Boolean(default=False) - datashade = param.Boolean(default=False) - - df_ = df_dhdt - plot = df_.hvplot.points(x="x", y="y", c="dhdt_slope", cmap="BrBG") - startX, endX = plot.range("x") - startY, endY = plot.range("y") - - def keep_zoom(self, x_range, y_range): - self.startX, self.endX = x_range - self.startY, self.endY = y_range - - @param.depends("cycle", "dhdt_variable", "dhdt_range", "rasterize", "datashade") - def view(self): - cond = np.logical_and( - float(self.dhdt_range[0]) < abs(self.df_.dhdt_slope), - abs(self.df_.dhdt_slope) < float(self.dhdt_range[1]), - ) - column: str = ( - self.dhdt_variable - if self.dhdt_variable != "h_corr" - else f"h_corr_{self.cycle}" - ) - if self.dhdt_variable == "h_corr": - df_subset = self.df_.loc[cond].dropna(subset=f"h_corr_{self.cycle}") - else: - df_subset = self.df_.loc[cond] - self.plot = df_subset.hvplot.points( - title=f"ICESat-2 Cycle {self.cycle} {self.dhdt_variable}", - x="x", - y="y", - c=column, - cmap=self.variable_cmap[self.dhdt_variable], - rasterize=self.rasterize, - datashade=self.datashade, - dynspread=self.datashade, - hover=True, - hover_cols=[ - "referencegroundtrack", - "dhdt_slope", - f"h_corr_{self.cycle}", - f"utc_time_{self.cycle}", - ], - colorbar=True, - grid=True, - frame_width=1000, - frame_height=600, - data_aspect=1, - ) - self.plot = self.plot.redim.range( - x=(self.startX, self.endX), y=(self.startY, self.endY) - ) - self.plot = self.plot.opts(active_tools=["pan", "wheel_zoom"]) - rangexy = hv.streams.RangeXY( - source=self.plot, - x_range=(self.startX, self.endX), - y_range=(self.startY, self.endY), - ) - rangexy.add_subscriber(self.keep_zoom) - return self.plot - +# df_dhdt = pd.read_parquet(f"ATLXI/df_dhdt_{placename}.parquet") +df_dhdt: cudf.DataFrame = cudf.read_parquet(f"ATLXI/df_dhdt_{placename}.parquet") # %% # Interactive holoviews scatter plot to find referencegroundtrack needed # Tip: Hover over the points, and find those with high 'dhdt_slope' values -viewer = IceSat2Explorer() -widgets: pn.param.Param = pn.Param( - viewer.param, - widgets={ - "dhdt_variable": pn.widgets.RadioButtonGroup, - "cycle": pn.widgets.IntSlider, - "dhdt_range": pn.widgets.RangeSlider, - "rasterize": pn.widgets.Checkbox, - "datashade": pn.widgets.Checkbox, - }, -) -dashboard: pn.layout.Column = pn.Column( - pn.Row( - pn.Column(widgets[0], widgets[1], align="center"), - pn.Column(widgets[2], widgets[3], align="center"), - pn.Column(widgets[4], widgets[5], align="center"), - ), - viewer.view, -) +viewer = deepicedrain.IceSat2Explorer(name="ICESat-2 Explorer") +dashboard: pn.layout.Column = pn.Column(viewer.widgets, viewer.view) # dashboard # %% @@ -545,7 +444,16 @@ def view(self): rgts: list = [135, 327, 388, 577, 1080, 1272] # Whillans upstream # rgts: list = [236, 501 , 562, 1181] # whillans_downstream for rgt in rgts: - df_rgt: pd.DataFrame = df_many.query(expr="referencegroundtrack == @rgt") + df_rgt: pd.DataFrame = df_dhdt.query(expr="referencegroundtrack == @rgt") + df_rgt["id"] = df_rgt.index + df_rgt = pd.wide_to_long( + df=df_rgt.to_pandas(), + stubnames=["h_corr", "utc_time"], + i="id", + j="cycle_number", + sep="_", + ) + df_rgt = df_rgt.dropna() # Save track data to CSV for crossover analysis later df_rgt[["x", "y", "h_corr", "utc_time"]].to_csv( diff --git a/deepicedrain/README.md b/deepicedrain/README.md index 6fd52a6..e36e3ff 100644 --- a/deepicedrain/README.md +++ b/deepicedrain/README.md @@ -19,3 +19,7 @@ Contents: - :card_file_box: extraload.py - Convenience functions for extracting, transforming and loading data - array_to_dataframe - Turns a 1D/2D numpy/dask array into a tidy pandas/dask dataframe table + - ndarray_to_parquet - Turns an n-dimensional xarray/zarr array into an a parquet columnar format + +- :world_map: vizplots.py - Makes interactive dashboard plots and publication quality figures + - IceSat2Explorer - Dashboard for interacting with ICESat-2 point clouds on a 2D map diff --git a/deepicedrain/__init__.py b/deepicedrain/__init__.py index 7284559..97b12a2 100644 --- a/deepicedrain/__init__.py +++ b/deepicedrain/__init__.py @@ -11,6 +11,7 @@ lonlat_to_xy, point_in_polygon_gpu, ) +from deepicedrain.vizplots import IceSat2Explorer __version__: str = "0.2.1" diff --git a/deepicedrain/atlas_catalog.yaml b/deepicedrain/atlas_catalog.yaml index cd9bda1..3bf2035 100644 --- a/deepicedrain/atlas_catalog.yaml +++ b/deepicedrain/atlas_catalog.yaml @@ -104,6 +104,61 @@ sources: height: 500 geo: True coastline: True + icesat2dhdt: + description: 'Preprocessed ICESat-2 height change over time (dhdt) data in columnar Parquet format' + args: + urlpath: ATLXI/df_dhdt_{{placename}}.parquet + engine: pyarrow + parameters: + placename: + description: Name of a geographic locality in Antarctica + type: str + default: siple_coast + allowed: + - siple_coast + - whillans_upstream + - whillans_downstream + cycle: + description: ICESat-2 Cycle number + type: int + default: 7 + allowed: [1, 2, 3, 4, 5, 6, 7] + driver: intake_parquet.source.ParquetSource + metadata: + fields: + x: + label: Polar Stereographic X (m) + y: + label: Polar Stereographic Y (m) + plot: + kind: points + x: x + y: y + persist: True + rasterize: True + datashade: False + dynspread: False + hover: True + hover_cols: + - referencegroundtrack + - dhdt_slope + - h_corr_{{cycle}} + - utc_time_{{cycle}} + colorbar: True + grid: True + frame_width: 1000 + frame_height: 600 + data_aspect: 1 + plots: + dhdt_slope: + c: dhdt_slope + cmap: BrBG + referencegroundtrack: + c: referencegroundtrack + cmap: glasbey + h_corr: + c: h_corr_{{cycle}} + cmap: gist_earth test_data: description: 'Sample ICESat-2 datasets for testing purposes' args: diff --git a/deepicedrain/vizplots.py b/deepicedrain/vizplots.py new file mode 100644 index 0000000..7221c43 --- /dev/null +++ b/deepicedrain/vizplots.py @@ -0,0 +1,124 @@ +""" +Creates interactive visualizations for Exploratory Data Analysis using PyViz +and produce publication quality figures using PyGMT! +""" + +import os +import warnings + +import numpy as np + +import holoviews as hv +import intake +import panel as pn +import param + +warnings.filterwarnings( + action="ignore", + message="The global colormaps dictionary is no longer considered public API.", +) + + +class IceSat2Explorer(param.Parameterized): + """ + ICESat-2 rate of height change over time (dhdt) interactive dashboard. + Built using HvPlot and Panel. + + Adapted from the "Panel-based Datashader dashboard" at + https://examples.pyviz.org/datashader_dashboard/dashboard.html. + See also https://github.com/holoviz/datashader/pull/676. + """ + + plot_variable = param.Selector( + default="dhdt_slope", objects=["referencegroundtrack", "dhdt_slope", "h_corr"] + ) + cycle_number = param.Integer(default=7, bounds=(2, 7)) + dhdt_range = param.Range(default=(1.0, 10.0), bounds=(0.0, 20.0)) + rasterize = param.Boolean(default=True) + datashade = param.Boolean(default=False) + + # Load from intake data source + # catalog = intake.cat.atlas_cat + catalog: intake.catalog.local.YAMLFileCatalog = intake.open_catalog( + os.path.join(os.path.dirname(__file__), "atlas_catalog.yaml") + ) + source = catalog.icesat2dhdt(placename="whillans_upstream") + try: + import cudf + import hvplot.cudf + + df_ = cudf.read_parquet(source._urlpath) + except: + df_ = source.to_dask() + plot: hv.core.spaces.DynamicMap = source.plot.dhdt_slope() # default plot + startX, endX = plot.range("x") + startY, endY = plot.range("y") + + def keep_zoom(self, x_range, y_range): + self.startX, self.endX = x_range + self.startY, self.endY = y_range + + @param.depends( + "cycle_number", "plot_variable", "dhdt_range", "rasterize", "datashade" + ) + def view(self) -> hv.core.spaces.DynamicMap: + # Filter/Subset data to what's needed. Wait for + # https://github.com/holoviz/hvplot/issues/72 to do it properly + cond = np.logical_and( + float(self.dhdt_range[0]) < abs(self.df_.dhdt_slope), + abs(self.df_.dhdt_slope) < float(self.dhdt_range[1]), + ) + if self.plot_variable == "h_corr": + df_subset = self.df_.loc[cond].dropna( + subset=[f"h_corr_{self.cycle_number}"] + ) + else: + df_subset = self.df_.loc[cond] + + # Create the plot! Uses plot_kwargs from catalog metdata + # self.plot = getattr(source.plot, self.plot_variable)() + self.source = self.catalog.icesat2dhdt(cycle=self.cycle_number) + plot_kwargs = { + "xlabel": self.source.metadata["fields"]["x"]["label"], + "ylabel": self.source.metadata["fields"]["y"]["label"], + **self.source.metadata["plot"], + **self.source.metadata["plots"][self.plot_variable], + } + plot_kwargs.update( + rasterize=self.rasterize, datashade=self.datashade, dynspread=self.datashade + ) + self.plot = df_subset.hvplot( + title=f"ICESat-2 Cycle {self.cycle_number} {self.plot_variable}", + **plot_kwargs, + ) + + # Keep zoom level intact when changing the plot_variable + self.plot = self.plot.redim.range( + x=(self.startX, self.endX), y=(self.startY, self.endY) + ) + self.plot = self.plot.opts(active_tools=["pan", "wheel_zoom"]) + rangexy = hv.streams.RangeXY( + source=self.plot, + x_range=(self.startX, self.endX), + y_range=(self.startY, self.endY), + ) + rangexy.add_subscriber(self.keep_zoom) + + return self.plot + + def widgets(self): + _widgets = pn.Param( + self.param, + widgets={ + "plot_variable": pn.widgets.RadioButtonGroup, + "cycle_number": pn.widgets.IntSlider, + "dhdt_range": {"type": pn.widgets.RangeSlider, "name": "dhdt_range_±"}, + "rasterize": pn.widgets.Checkbox, + "datashade": pn.widgets.Checkbox, + }, + ) + return pn.Row( + pn.Column(_widgets[0], _widgets[1], align="center"), + pn.Column(_widgets[2], _widgets[3], align="center"), + pn.Column(_widgets[4], _widgets[5], align="center"), + ) From a889417fc97503920922c2392a8ce0c4a9a35ab9 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Tue, 25 Aug 2020 00:29:11 +1200 Subject: [PATCH 09/12] :ambulance: Temporarily workaround not having parquet dataset Fix Continuous Integration tests failing due to the IceSat2Explorer class not being able to load df_dhdt_whillans_upstream.parquet. Really need to put the file up somewhere, but until I find a good data repository (ideally with versioning), this hacky workaround will be a necessary evil. --- deepicedrain/atlas_catalog.yaml | 8 ++++---- deepicedrain/tests/test_deepicedrain.py | 7 ++++++- deepicedrain/vizplots.py | 24 +++++++++++++----------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/deepicedrain/atlas_catalog.yaml b/deepicedrain/atlas_catalog.yaml index 3bf2035..01b4767 100644 --- a/deepicedrain/atlas_catalog.yaml +++ b/deepicedrain/atlas_catalog.yaml @@ -140,10 +140,10 @@ sources: dynspread: False hover: True hover_cols: - - referencegroundtrack - - dhdt_slope - - h_corr_{{cycle}} - - utc_time_{{cycle}} + - referencegroundtrack + - dhdt_slope + - h_corr_{{cycle}} + - utc_time_{{cycle}} colorbar: True grid: True frame_width: 1000 diff --git a/deepicedrain/tests/test_deepicedrain.py b/deepicedrain/tests/test_deepicedrain.py index 4889545..555cc4c 100644 --- a/deepicedrain/tests/test_deepicedrain.py +++ b/deepicedrain/tests/test_deepicedrain.py @@ -12,6 +12,11 @@ def test_deepicedrain_catalog(): Test that the intake ATLAS data catalog can be loaded via both `deepicedrain.catalog` and `intake.cat.atlas_cat` """ - catalog_entries = ["icesat2atlasdownloader", "icesat2atl06", "test_data"] + catalog_entries = [ + "icesat2atlasdownloader", + "icesat2atl06", + "icesat2dhdt", + "test_data", + ] assert list(catalog) == catalog_entries assert list(intake.cat.atlas_cat) == catalog_entries diff --git a/deepicedrain/vizplots.py b/deepicedrain/vizplots.py index 7221c43..0c86ca2 100644 --- a/deepicedrain/vizplots.py +++ b/deepicedrain/vizplots.py @@ -42,17 +42,19 @@ class IceSat2Explorer(param.Parameterized): catalog: intake.catalog.local.YAMLFileCatalog = intake.open_catalog( os.path.join(os.path.dirname(__file__), "atlas_catalog.yaml") ) - source = catalog.icesat2dhdt(placename="whillans_upstream") - try: - import cudf - import hvplot.cudf - - df_ = cudf.read_parquet(source._urlpath) - except: - df_ = source.to_dask() - plot: hv.core.spaces.DynamicMap = source.plot.dhdt_slope() # default plot - startX, endX = plot.range("x") - startY, endY = plot.range("y") + placename: str = "whillans_upstream" + source = catalog.icesat2dhdt(placename=placename) + if os.path.exists(f"ATLXI/df_dhdt_{placename}.parquet"): + try: + import cudf + import hvplot.cudf + + df_ = cudf.read_parquet(source._urlpath) + except ImportError: + df_ = source.to_dask() + plot: hv.core.spaces.DynamicMap = source.plot.dhdt_slope() # default plot + startX, endX = plot.range("x") + startY, endY = plot.range("y") def keep_zoom(self, x_range, y_range): self.startX, self.endX = x_range From 442209368fc24d3cb9c1e9fa2eb80c7812acd753 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Fri, 28 Aug 2020 15:50:45 +1200 Subject: [PATCH 10/12] :pushpin: Pin cuml and cuspatial from 0.15.0a200819 to 0.15.0 Pinning the RAPIDS AI libraries from the alpha/development versions to the stable release version. Also generating a environment-linux-64.lock for full reproducibility! Bumps [cuml](https://github.com/rapidsai/cuml) from 0.15.0a200819 to 0.15.0. - [Release notes](https://github.com/rapidsai/cuml/releases) - [Changelog](https://github.com/rapidsai/cuml/blob/branch-0.15/CHANGELOG.md) - [Commits](https://github.com/rapidsai/cuml/compare/v0.15.0a...v0.15.0) Bumps [cuspatial](https://github.com/rapidsai/cuspatial) from 0.15.0a200819 to 0.15.0 - [Release notes](https://github.com/rapidsai/cuspatial/releases) - [Changelog](https://github.com/rapidsai/cuspatial/blob/branch-0.15/CHANGELOG.md) - [Commits](https://github.com/rapidsai/cuspatial/compare/v0.15.0a...v0.15.0) --- README.md | 31 ++++- environment-linux-64.lock | 257 ++++++++++++++++++++++++++++++++++++++ environment.yml | 8 +- 3 files changed, 286 insertions(+), 10 deletions(-) create mode 100644 environment-linux-64.lock diff --git a/README.md b/README.md index b045e0f..24c299d 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ To just try out the scripts, download the `environment.yml` file from the reposi conda env create --name deepicedrain --file environment.yml pip install git+https://github.com/weiji14/deepicedrain.git -### Advanced +### Intermediate To help out with development, start by cloning this [repo-url](/../../) @@ -77,11 +77,6 @@ Then install the python libraries listed in the `pyproject.toml`/`poetry.lock` f poetry install -If you have a [CUDA](https://en.wikipedia.org/wiki/CUDA)-capable GPU, -you can also install the optional "cuda" packages to accelerate some calculations. - - poetry install --extras cuda - Finally, double-check that the libraries have been installed. poetry show @@ -93,6 +88,30 @@ Finally, double-check that the libraries have been installed. jupyter labextension list # ensure that extensions are installed + +### Advanced + +This is for those who want full reproducibility of the conda environment, +and more computing power by using Graphical Processing Units (GPU). + +Making an explicit conda-lock file +(only needed if creating a new conda environment/refreshing an existing one). + + conda env create -f environment.yml + conda list --explicit > environment-linux-64.lock + +Creating/Installing a virtual environment from a conda lock file. +See also https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#building-identical-conda-environments. + + conda create --name deepicedrain --file environment-linux-64.lock + conda install --name deepicedrain --file environment-linux-64.lock + +If you have a [CUDA](https://en.wikipedia.org/wiki/CUDA)-capable GPU, +you can also install the optional "cuda" packages to accelerate some calculations. + + poetry install --extras cuda + + ## Running jupyter lab conda activate deepicedrain diff --git a/environment-linux-64.lock b/environment-linux-64.lock new file mode 100644 index 0000000..e7786bf --- /dev/null +++ b/environment-linux-64.lock @@ -0,0 +1,257 @@ +# This file may be used to create an environment using: +# $ conda create --name --file +# platform: linux-64 +@EXPLICIT +https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/arrow-cpp-proc-1.0.1-cuda.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2020.6.20-hecda079_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/dcw-gmt-1.1.4-1001.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/faiss-proc-1.0.0-cuda.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gshhg-gmt-2.3.7-1002.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.34-h53a641e_7.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-7.5.0-hdf63c60_16.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-9.3.0-hdf63c60_16.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.9-1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.34-h53a641e_7.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-9.3.0-h24d8f2e_16.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-1_gnu.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.34-hc952b39_20.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-9.3.0-h24d8f2e_16.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/abseil-cpp-20200225.2-he1b5a44_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.0.7-he1b5a44_1004.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h516909a_3.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.16.1-h516909a_3.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/chrpath-0.16-h14c3975_1001.tar.bz2 +https://conda.anaconda.org/nvidia/linux-64/cudatoolkit-10.2.89-h6bb024c_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/dlpack-0.3-he1b5a44_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.1.5-he1b5a44_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/expat-2.2.9-he1b5a44_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.8-nompi_h7f3a6c3_1111.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/freexl-1.0.5-h516909a_1002.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.10-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-7.5.0-hd420e75_6.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/geos-3.8.1-he1b5a44_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-he1b5a44_1004.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/ghostscript-9.22-hf484d3e_1001.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.1-h516909a_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gmp-6.2.0-he1b5a44_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-he1b5a44_1001.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/icu-64.2-he1b5a44_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/jpeg-9d-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/json-c-0.13.1-hbfbb72e_1002.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h14c3975_1001.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.2.1-he1b5a44_1007.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.16-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.10-pthreads_hb3c22a3_4.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.32.1-h14c3975_1000.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libuv-1.34.0-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-0.10.0-he1b5a44_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.2-he1b5a44_3.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.2-he1b5a44_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/nettle-3.4.1-h1bed415_1002.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/nspr-4.28-he1b5a44_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1g-h516909a_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pcre-8.44-he1b5a44_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/perl-5.26.2-h516909a_1006.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pixman-0.38.0-h516909a_1003.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h14c3975_1001.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/re2-2020.07.06-he1b5a44_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/snappy-1.1.8-he1b5a44_3.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.7.0-hc9558a2_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/tbb-2020.1-hc9558a2_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/tzcode-2020a-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/x264-1!152.20180806-h14c3975_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h14c3975_1002.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.0.10-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.9-h14c3975_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.3-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-renderproto-0.11.1-h14c3975_1002.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-xextproto-7.3.0-h14c3975_1002.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h14c3975_1007.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.5-h516909a_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.11-h516909a_1007.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/boost-cpp-1.72.0-h8e57a91_0.tar.bz2 +https://repo.anaconda.com/pkgs/main/linux-64/cudnn-7.6.5-cuda10.2_0.conda +https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-7.5.0-h09487f9_20.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-hc5be6a0_1002.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/glog-0.4.0-h49b9bf7_3.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gnutls-3.6.13-h79a8f9a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-7.5.0-hdf63c60_6.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.13-hf30be14_1003.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.10.5-nompi_h3c11f04_1104.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.8.0-17_openblas.tar.bz2 +https://conda.anaconda.org/nvidia/linux-64/libcumlprims-0.15.0-cuda10.2_gdbd0d39_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.10-hcdb4288_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libllvm10-10.0.1-he513fc3_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libllvm9-9.0.1-he513fc3_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.41.0-h8cfc5f6_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.37-hed695b0_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-3.12.4-h8b12597_0.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/librmm-0.15.0-cuda10.2_g8005ca5_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.9.0-hab1572f_5.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.13-h14c3975_1002.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.10-hee79883_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/nccl-2.7.8.1-hc6a2c23_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/nodejs-13.13.0-hf5d1a2b_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/openh264-2.1.1-h8b12597_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/parallel-20200522-0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/readline-8.0-he28a2e2_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.10-hed695b0_0.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/ucx-1.8.1+g6b29558-cuda10.2_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.2-h8412b87_1004.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.3-h84519dc_1000.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/zstd-1.4.5-h6597ccf_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/freetype-2.10.2-he06d7ca_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/grpc-cpp-1.30.2-heedbac9_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-7.5.0-h09487f9_20.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/kealib-1.4.13-hec59c27_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/krb5-1.17.1-hfafb76e_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.8.0-17_openblas.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libclang-9.0.1-default_hde54327_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.1.0-h3c4fd83_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-hb574062_1011.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.8.0-17_openblas.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.1.0-hc3755c2_3.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.33.0-h4cf870e_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/thrift-cpp-0.13.0-h62aa4f2_3.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.6.12-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-4.3.1-h167e202_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.13.1-h86ecdb6_1001.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.11-hbd6801e_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.71.1-hcdd3856_5.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libfaiss-1.6.3-he61ee18_1_cuda.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libpq-12.3-h5513abc_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.0.2-h56121f0_5.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/nss-3.56-he751ad9_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.3.1-h981e76c_3.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/proj-6.3.1-hc80f0dc_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/python-3.8.3-cpython_he5300dc_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.10-h516909a_1002.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxt-1.2.0-h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/attrs-20.1.0-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/cachy-0.3.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/cfitsio-3.470-hce51eda_6.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/click-7.1.2-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-1.6.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/curl-7.71.1-he644dc0_5.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/fsspec-0.8.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.5.1-hcbe54f9_9.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/glib-2.65.0-h6f030ca_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/heapdict-1.0.1-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/idna-2.10-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/jeepney-0.4.3-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libgd-2.2.5-h307a58e_1007.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libspatialite-4.3.0a-heb269f5_1037.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/locket-0.2.0-py_2.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/lockfile-0.12.2-py_1.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/olefile-0.46-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/pastel-0.2.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/pkginfo-1.5.0.1-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/postgresql-12.3-h8573dbc_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.6.0-py_1001.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/pycparser-2.20-pyh9f0ad1d_2.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/pylev-1.3.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/pyparsing-2.4.7-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.8-1_cp38.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/pytz-2020.1-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/shellingham-1.3.2-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/six-1.15.0-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.2.2-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/tblib-1.6.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/toolz-0.10.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-3.7.4.2-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-py_1.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/wheel-0.35.1-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/zipp-3.1.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.7.164-hba45d7a_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-hcf35c78_1003.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/certifi-2020.6.20-py38h32f6830_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/cffi-1.14.1-py38h5bae8af_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/chardet-3.0.4-py38h32f6830_1006.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/clikit-0.4.3-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.10.1-py38h516909a_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-he372182_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/entrypoints-0.3-py38h32f6830_1001.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/fastavro-1.0.0.post1-py38h1e0a361_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/fastrlock-0.5-py38h950e882_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.14.5-h36ae1b5_2.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/html5lib-1.1-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-1.7.0-py38h32f6830_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libdap4-3.20.6-h1d1bd15_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.7.4-nompi_h9f9fd6a_101.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.34.0-py38h4f45e52_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-1.1.1-py38h1e0a361_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.0-py38hbf85e49_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/numpy-1.19.1-py38hbc27379_2.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/packaging-20.4-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/partd-1.1.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pexpect-4.8.0-py38h32f6830_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pillow-7.2.0-py38h9776b28_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/psutil-5.7.2-py38h1e0a361_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pyrsistent-0.14.11-py38h1e0a361_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py38h32f6830_1.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.1-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pyyaml-5.3.1-py38h1e0a361_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/tiledb-1.7.0-h8efa9f0_4.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/tomlkit-0.5.11-py38h32f6830_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.0.4-py38h1e0a361_1.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/zict-2.0.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/arrow-cpp-0.17.1-py38h1234567_11_cuda.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py38h1e0a361_1000.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/cleo-0.7.6-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/cryptography-3.0-py38h766eaa4_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/dask-core-2.24.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gobject-introspection-1.64.1-py38h03d966d_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.14.5-h0935bb2_2.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-2.4.0-h9f30f68_3.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-1.7.0-0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/numcodecs-0.6.4-py38he1b5a44_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pandas-1.1.1-py38h950e882_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/poppler-0.67.0-h14e79db_8.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.5.2-py38h8c5af15_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/setuptools-49.6.0-py38h32f6830_0.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/ucx-py-0.15.0+g6b29558-py38_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.36.0-haf93ef1_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/cupy-7.8.0-py38hb1193b0_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/distributed-2.24.0-py38h32f6830_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.38.2-h3f25603_4.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/jinja2-2.11.2-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/joblib-0.16.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/jsonschema-3.2.0-py38h32f6830_1.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/libcudf-0.15.0-cuda10.2_g71cb8c0e0_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.0.4-h94bbfbd_3.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/numba-0.51.0-py38hc5bc63f_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pango-1.42.4-h7062337_4.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/parquet-cpp-1.5.1-2.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/pip-20.1.1-py_1.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/pyopenssl-19.1.0-py_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.5-hd8c4c69_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/secretstorage-3.1.2-py38h32f6830_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/treelite-0.92-py38h4e709cc_2.tar.bz2 +# no URL for: treelite_runtime-0.92-py3.8.egg-info +https://conda.anaconda.org/conda-forge/linux-64/atk-2.36.0-1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/bokeh-2.2.0-py38h32f6830_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gdal-3.0.4-py38h172510d_3.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/keyring-18.0.1-py38h32f6830_0.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/libcuspatial-0.15.0-cuda10.2_gc5b7527_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-0.17.1-py38h1234567_11_cuda.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/rmm-0.15.0-cuda_10.2_py38_g8005ca5_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/urllib3-1.25.10-py_0.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/cudf-0.15.0-cuda_10.2_py38_g71cb8c0e0_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/dask-2.24.0-py_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.32-h586f36d_1.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/requests-2.24.0-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/cachecontrol-0.12.6-py_0.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/cuspatial-0.15.0-py38_gc5b7527_0.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/dask-cudf-0.15.0-py38_g71cb8c0e0_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/gnuplot-5.2.7-h0fb2448_3.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/libcuml-0.15.0-cuda10.2_ga3002e587_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-0.8.0-py_1.tar.bz2 +https://conda.anaconda.org/rapidsai/linux-64/cuml-0.15.0-cuda10.2_py38_ga3002e587_0.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/graphicsmagick-1.3.35-h0a8fd69_1.tar.bz2 +https://conda.anaconda.org/conda-forge/linux-64/poetry-1.0.9-py38h32f6830_0.tar.bz2 +https://conda.anaconda.org/conda-forge/label/dev/linux-64/gmt-6.2.0.dev0+a1a189c-ha86b383_0.tar.bz2 diff --git a/environment.yml b/environment.yml index 9deb56a..1316f3d 100644 --- a/environment.yml +++ b/environment.yml @@ -1,15 +1,15 @@ name: deepicedrain channels: - conda-forge - - rapidsai-nightly + - nvidia + - rapidsai dependencies: - - rapidsai-nightly::cuml=0.15.0a200819[md5=7febc89661b8dbc905dc3c52218f9954] - - rapidsai-nightly::cuspatial=0.15.0a200819[md5=c583588629008217835cbd84e50b8543] + - rapidsai::cuml=0.15.0[md5=09125e971304f6020788d3da4bf1ef55] + - rapidsai::cuspatial=0.15.0[md5=728b9006370db38fa4fc3d0fe19fc8c3] - conda-forge::geos=3.8.1[md5=7282fbe0c036bacad959c03c05a7eb9a] - conda-forge/label/dev::gmt=6.2.0.dev0+a1a189c[md5=915cb603613887002b58e09859aaac82] - conda-forge::gxx_linux-64=7.5.0[md5=c6afca6a2bfe1ea0ff5566d300460501] - conda-forge::nodejs=13.13.0[md5=cf25f8cee7aad7f417980872d34d8fd6] - - numba::numba=0.51.0[md5=1151f4a9312696ac4694d82089c92a11] - conda-forge::numcodecs=0.6.4[md5=4873a5169bb684b4e120e7bcb287488e] - conda-forge::parallel=20200522[md5=8fa06db0afc1e7b1f136277557a4b65e] - conda-forge::pip=20.1.1[md5=5e42afe5672d5baf3d9d2260239aa923] From 3193f7a3508faa3440acee34a30809a7f46ca252 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Tue, 1 Sep 2020 15:35:22 +1200 Subject: [PATCH 11/12] :sparkles: Cluster active subglacial lakes using DBSCAN Detect active subglacial lakes in Antarctica using Density-based spatial clustering of applications with noise (DBSCAN)! The subglacial lake detector works by finding clusters of high (filling at > 1m/yr) or low (draining at < -1 m/yr) height change over time (dhdt) values, for each drainage basin (that is grounded) in Antarctica. CUDA GPUs are awesome, the point in polygon takes 15 seconds, and the lake clustering takes 12 seconds, and this is working on >13 million points! Each cluster of points is then converted to a convex hull polygon, and we store some basic attribute information with the geometry such as the basin name, maximum absolute dhdt value, and reference ground tracks. The lakes are output to a geojson file using EPSG:3031 projection. This is a long overdue commit as the code has been working since mid-August, but I kept wanting to refactor it (still need to!). The DBSCAN clustering parameters (eps=2500 and min_samples=250) work ok for the Siple Coast and Slessor Glacier, but fails for Pine Island Glacier since there's a lot of downwasting. Algorithm definitely needs more work. The visualizations and crossover analysis code also need to be refreshed (since the schema has changed), but it's sitting locally on my computer, waiting to be tidied up a bit more. --- antarctic_subglacial_lakes.geojson | 164 +++++++++++++++++ atlxi_lake.ipynb | 272 +++++++++++++++++++++++++++++ atlxi_lake.py | 196 +++++++++++++++++++++ 3 files changed, 632 insertions(+) create mode 100644 antarctic_subglacial_lakes.geojson diff --git a/antarctic_subglacial_lakes.geojson b/antarctic_subglacial_lakes.geojson new file mode 100644 index 0000000..3755401 --- /dev/null +++ b/antarctic_subglacial_lakes.geojson @@ -0,0 +1,164 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3031" } }, +"features": [ +{ "type": "Feature", "properties": { "basin_name": "Academy", "maxabsdhdt": 4.8424224853515625, "refgtracks": "177|238|293|299|354|415|680|735|741|796|857|918|1122|1183|1238|1299|1360" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -501698.135690001072362, 339635.674512860365212 ], [ -508327.173474431911018, 340165.848679545917548 ], [ -512517.763470780628268, 342772.400027683761436 ], [ -514945.960376250150148, 345015.849779972224496 ], [ -518920.546293459483422, 350618.092845593870152 ], [ -519782.86454750323901, 351862.0789259520825 ], [ -520663.237997316173278, 353508.984668915451039 ], [ -520610.809603448666167, 354087.42429588426603 ], [ -517879.538462423835881, 359489.577506819623522 ], [ -516332.140296770492569, 360867.491474465059582 ], [ -515319.817832938744687, 361055.582976497942582 ], [ -514981.569780705438461, 360968.528341422381345 ], [ -514530.738113123341464, 360851.822274019592442 ], [ -512727.561755413829815, 360384.486415424442384 ], [ -500443.397647497127764, 343610.275012000929564 ], [ -500293.831345356767997, 343138.300754267780576 ], [ -500622.652955966710579, 341268.393880449177232 ], [ -501698.135690001072362, 339635.674512860365212 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Academy", "maxabsdhdt": 3.9260616302490234, "refgtracks": "177|238|476|619|680|857|918|1122|1183|1360" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -497786.328183327394072, 332088.610320489038713 ], [ -498778.374132854980417, 332293.046518240240403 ], [ -500798.973017002746928, 333521.483229274163023 ], [ -501555.884590470290277, 336244.983258099178784 ], [ -501354.373361423437018, 337007.302233676600736 ], [ -500513.257339937612414, 339429.669991560978815 ], [ -499807.730158526857849, 340062.837583480635658 ], [ -498296.502868321724236, 340495.571356083324645 ], [ -495358.651708461518865, 339347.052389719232451 ], [ -494967.490898888849188, 338768.47857572248904 ], [ -494739.318600281316321, 338430.970807416539174 ], [ -494706.724631432327442, 338382.755228668567725 ], [ -494417.455077170336153, 335506.06834310607519 ], [ -495439.58032243186608, 333603.108452799613588 ], [ -497786.328183327394072, 332088.610320489038713 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Academy", "maxabsdhdt": 4.8680920600891113, "refgtracks": "354|375|415|796|817|857|1259|1299|1320|1360" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -449412.473553334537428, 328480.823905905184802 ], [ -454201.610384901985526, 329144.775962976622395 ], [ -455161.039130253833719, 330740.358251922181807 ], [ -455520.040264820330776, 331338.920639854215551 ], [ -455699.510174617404118, 331638.460151162580587 ], [ -455102.630958177964203, 337019.791928583639674 ], [ -451012.391295453358907, 337552.616254119668156 ], [ -450823.987355798541103, 337258.297466212126892 ], [ -450502.225942899880465, 336705.029063869500533 ], [ -449823.150509979168419, 335332.244156321976334 ], [ -449412.473553334537428, 328480.823905905184802 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": -7.6433358192443848, "refgtracks": "114|175|236|424|485|556|617|678|1059|1120|1369" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -292749.211303453426808, -332505.065131749550346 ], [ -294325.544416625110898, -331606.813703026797157 ], [ -296299.432532135921065, -330332.083804749068804 ], [ -296260.562830049253535, -330162.203276600514073 ], [ -295402.762447570625227, -327284.295428360579535 ], [ -293704.768119366315659, -326769.662556808150839 ], [ -291661.865844920044765, -327030.899087157042231 ], [ -290839.130555192765314, -327469.457657817401923 ], [ -290424.981469823978841, -327872.215172558964696 ], [ -290361.225946046644822, -328623.45074753544759 ], [ -290673.058456150523853, -330955.482573958928697 ], [ -290739.744449078803882, -331297.236473604047205 ], [ -290773.096019653661642, -331468.134047953179106 ], [ -290784.337833385216072, -331525.230836585513316 ], [ -290829.318510701647028, -331753.672236288315617 ], [ -290840.659969657950569, -331810.876300517411437 ], [ -291391.367004793079104, -332361.895470431656577 ], [ -292749.211303453426808, -332505.065131749550346 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": -20.673181533813477, "refgtracks": "180|241|251|302|312|373|622|683|744|754|815|876|1125|1186|1196|1257|1318" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -313444.168575974472333, -318884.036893162876368 ], [ -316710.820750342798419, -318384.304969486314803 ], [ -319202.213296801375691, -317469.577931020292453 ], [ -319675.086528531915974, -316821.39837405126309 ], [ -319758.35216981935082, -315128.057881062035449 ], [ -319626.104740737588145, -314444.682118452561554 ], [ -319523.058162646833807, -314348.151595434930641 ], [ -317243.802956878149416, -312908.218223614443559 ], [ -314728.230724691762589, -312107.097383967193309 ], [ -313344.306823374237865, -312106.685211878560949 ], [ -312848.591683647362515, -312293.072684388840571 ], [ -311730.66200569953071, -313587.294138099008705 ], [ -311360.668841079517733, -315283.847998760058545 ], [ -311802.711883229552768, -317224.960426728357561 ], [ -313444.168575974472333, -318884.036893162876368 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": -9.401362419128418, "refgtracks": "190|251|312|424|485|693|754|866|1135|1196|1308|1369" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -329934.775745178863872, -342316.562403160205577 ], [ -331780.036345734319184, -341731.446078111184761 ], [ -332214.953242240881082, -341137.346461797773372 ], [ -332628.532678401505109, -338018.181614135100972 ], [ -332593.405822028231341, -337801.94069095159648 ], [ -332473.060459673230071, -337519.196138337661978 ], [ -331125.453599908272736, -336628.068245353293605 ], [ -330092.333401250420138, -336289.113589027372655 ], [ -327831.080826256540604, -337162.977314047224354 ], [ -326892.295130595855881, -338781.535132104472723 ], [ -327225.486749879550189, -340013.813091513875406 ], [ -327725.437709275109228, -341565.642936267366167 ], [ -329934.775745178863872, -342316.562403160205577 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": -11.96894645690918, "refgtracks": "241|302|373|434|683|744|876|937|1125|1186|1247|1318|1379" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -309356.862676143471617, -319195.129084427084308 ], [ -309835.084382008470129, -319104.64420539047569 ], [ -310395.586224169877823, -318608.093295004102401 ], [ -311202.838456808007322, -317275.36623468255857 ], [ -311186.814801877830178, -317051.969447444600519 ], [ -310688.213425960275345, -315097.205330548575148 ], [ -310237.113175877835602, -314727.779346302733757 ], [ -308672.105221296544187, -314536.718040412175469 ], [ -306641.415854896709789, -315409.864230352803133 ], [ -306291.887893221050035, -316066.233077244251035 ], [ -306647.152130066882819, -317550.383723092614673 ], [ -306808.785272734297905, -317815.890342721657362 ], [ -307527.801958922646008, -318963.428538189677056 ], [ -307777.156370658543892, -319076.238898655690718 ], [ -309356.862676143471617, -319195.129084427084308 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": 1.4604595899581909, "refgtracks": "114|516|556|577|958|998|1059" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -325366.970910106203519, -458146.424944311322179 ], [ -326323.384379715309478, -458026.242920871940441 ], [ -327673.765355130191892, -457736.476091065793298 ], [ -327118.282612780982163, -454396.272536022588611 ], [ -326496.467138476902619, -452634.500275583355688 ], [ -325880.49173138930928, -453630.190964729466941 ], [ -324856.796881199348718, -456218.666792684176471 ], [ -325366.970910106203519, -458146.424944311322179 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": 8.801426887512207, "refgtracks": "59|120|562|623|861|922|1004|1065|1364" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -291692.393905071367044, -507353.800604419317096 ], [ -297381.923757927143015, -504478.552733939199243 ], [ -297916.769572265679017, -502830.662891381594818 ], [ -297676.847260824521072, -501596.236908340826631 ], [ -296754.700478653016035, -500160.32669001002796 ], [ -294952.26295750256395, -499507.018541558354627 ], [ -293471.151894492038991, -499446.652015662693884 ], [ -291907.385663093184121, -500238.632719319197349 ], [ -289523.101198431046214, -502553.984937567613088 ], [ -287933.601476365001872, -504414.693280325969681 ], [ -288103.955886451760307, -505618.21484694594983 ], [ -288931.826449274434708, -506259.442418308055494 ], [ -289529.839116409420967, -506722.399631503794808 ], [ -291692.393905071367044, -507353.800604419317096 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": 4.815760612487793, "refgtracks": "120|181|541|602|623|983|1044|1065|1126" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -282485.100088778010104, -505207.713102238427382 ], [ -282667.480926431599073, -505154.344956744578667 ], [ -283247.178667731350288, -504925.802971281344071 ], [ -283506.058072371524759, -504777.698694859282114 ], [ -284423.822480598930269, -502766.35518956638407 ], [ -283993.408837764465716, -501328.763789989694487 ], [ -283457.779701328952797, -499781.350461453315802 ], [ -283412.059027951967437, -499745.155227740877308 ], [ -283366.482034251268487, -499709.210048949287739 ], [ -281982.89308582781814, -499669.114975707838312 ], [ -278252.954707714321557, -501015.266810115601402 ], [ -278614.840150392381474, -504673.603130627598148 ], [ -278621.055244974326342, -504730.783147111418657 ], [ -278649.962271517899353, -504786.185008043830749 ], [ -280235.027995111420751, -505179.867023271333892 ], [ -282485.100088778010104, -505207.713102238427382 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": 7.4906573295593262, "refgtracks": "251|312|363|424|693|754|805|866|1135|1196|1308" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -329168.735473590611946, -336626.585354070237372 ], [ -330023.424019784142729, -336067.208767123578582 ], [ -330796.469587278494146, -335513.798757267184556 ], [ -331431.892489744757768, -334871.804176273173653 ], [ -331357.137671215343289, -334344.210989561688621 ], [ -329273.763443119125441, -330259.398040499421768 ], [ -329194.223104703880381, -330104.61392887722468 ], [ -327606.940326273324899, -329742.484380004287232 ], [ -325814.347581607988104, -330257.326488584862091 ], [ -323800.942203451122623, -330940.926148944592569 ], [ -323024.957583415904082, -331764.830743396247271 ], [ -322915.9005069041159, -332026.354100011987612 ], [ -323137.174811025033705, -333728.065310269768815 ], [ -323285.010790428728797, -334225.948345907090697 ], [ -324023.871283055923413, -335761.66993503406411 ], [ -325592.402440168778412, -336093.802525848150253 ], [ -329168.735473590611946, -336626.585354070237372 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": 2.1375339031219482, "refgtracks": "404|486|547|907|989|1349" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -241237.797549881564919, -528537.286047886358574 ], [ -243122.276439365465194, -527802.38310307613574 ], [ -243485.767408678802894, -527306.008510191692039 ], [ -243850.698825195257086, -525991.676893597468734 ], [ -243816.583291933871806, -524245.156299254682381 ], [ -243745.159520355548011, -522618.713343093753792 ], [ -243688.999469233618584, -522387.441871401853859 ], [ -241323.528573542716913, -523039.877189574937802 ], [ -240515.550686559290625, -523871.175812791916542 ], [ -240379.513994938781252, -524410.182960596401244 ], [ -240136.2118094417674, -527500.895751378731802 ], [ -240694.763285421067849, -528113.241281395428814 ], [ -241237.797549881564919, -528537.286047886358574 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": 6.1209502220153809, "refgtracks": "83|586|647|698|759|1089|1140|1201" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -368385.692617217369843, -290762.703054134326521 ], [ -370689.078826190787368, -290178.297535144432914 ], [ -371065.614370502822567, -289735.26246223715134 ], [ -371242.670743667695206, -287875.967940128874034 ], [ -370882.043181387067307, -286888.802963651483878 ], [ -370310.284278376551811, -286782.851916135638021 ], [ -369108.903103500430007, -286562.182854253507685 ], [ -368765.454378446913324, -286499.107196088065393 ], [ -367965.538621533312835, -286352.499753507436253 ], [ -367564.771446541592013, -286279.611160888278391 ], [ -366774.181570074346382, -287096.923442603379954 ], [ -366467.837970336491708, -288670.484707988274749 ], [ -366567.549001554318238, -289440.908940354885999 ], [ -368385.692617217369843, -290762.703054134326521 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": 2.8197650909423828, "refgtracks": "404|547|989|1050|1349" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -240895.195286282309098, -532531.765309803886339 ], [ -243317.401634228706826, -531753.400274397106841 ], [ -244095.157997925765812, -531398.233947408618405 ], [ -244163.173308294062736, -531288.424734040978365 ], [ -244071.169895516708493, -529884.039533463073894 ], [ -244011.456518826220417, -529537.87381840904709 ], [ -240697.503552285255864, -528171.639177831704728 ], [ -240305.38578573934501, -528842.18997804983519 ], [ -240380.247153004980646, -532253.101478321943432 ], [ -240422.725355647242395, -532293.151349872467108 ], [ -240507.591346755158156, -532373.053201935254037 ], [ -240592.188323452690383, -532452.352294669137336 ], [ -240895.195286282309098, -532531.765309803886339 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mercer", "maxabsdhdt": 6.2716660499572754, "refgtracks": "424|485|754|866|927|1196" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -325117.125646039436106, -339439.887367427232675 ], [ -325464.776029683067463, -339411.293076815200038 ], [ -326320.902742142963689, -338606.620277549489401 ], [ -326768.580472727015149, -337853.145728161616717 ], [ -326819.678349480847828, -337342.523943010834046 ], [ -325608.703204121382441, -336149.660062011796981 ], [ -324040.277537328249309, -335817.298702164145652 ], [ -322228.214212258579209, -336286.81834509561304 ], [ -321884.480572077794932, -337222.088893726642709 ], [ -322220.682117223506793, -338294.411077902768739 ], [ -323057.111015455040615, -339133.608119017793797 ], [ -325117.125646039436106, -339439.887367427232675 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Land", "maxabsdhdt": 3.4808290004730225, "refgtracks": "601|669|1043" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -952338.020201735664159, -1238274.552932307589799 ], [ -953404.816353534464724, -1237572.181943414267153 ], [ -954755.53664674051106, -1235977.867037421558052 ], [ -954284.555289524956606, -1235531.434636940481141 ], [ -953042.395854311413132, -1234355.042204215191305 ], [ -952871.02893375151325, -1234192.824808288598433 ], [ -951799.986903922515921, -1233178.999899327987805 ], [ -951157.055394680122845, -1232571.05855758022517 ], [ -950899.680506950826384, -1232328.102168335579336 ], [ -950570.140083165140823, -1232398.826083352789283 ], [ -950270.502715090522543, -1232541.630598639836535 ], [ -948996.203883267822675, -1235110.956994397798553 ], [ -951438.2359183850931, -1237422.822971661342308 ], [ -951481.081847529509105, -1237463.381517929956317 ], [ -951866.69899536378216, -1237828.408772850874811 ], [ -951909.545701791997999, -1237868.967400296125561 ], [ -952038.087248355732299, -1237990.643352950923145 ], [ -952252.32416254049167, -1238193.436283693881705 ], [ -952295.172195749008097, -1238233.994751137914136 ], [ -952338.020201735664159, -1238274.552932307589799 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Land", "maxabsdhdt": 3.3711490631103516, "refgtracks": "601|669|1043" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -945318.977475504041649, -1230395.415514584397897 ], [ -948370.386375282774679, -1229937.577913418179378 ], [ -948669.87078495323658, -1229584.424535370664671 ], [ -948843.408061790163629, -1229242.14541627233848 ], [ -947965.389034711639397, -1227638.316914965165779 ], [ -947398.335063315462321, -1226603.950697673950344 ], [ -946970.446224130108021, -1225829.622059342218563 ], [ -946941.660284754703753, -1225778.143039144808426 ], [ -946585.275584455695935, -1225749.195600738050416 ], [ -944769.502840405213647, -1226534.571385803399608 ], [ -944081.533837453927845, -1228114.129674017196521 ], [ -944924.819574069348164, -1229669.767128653591499 ], [ -945121.682219051988795, -1230032.706920329947025 ], [ -945149.805914588388987, -1230084.555567493662238 ], [ -945318.977475504041649, -1230395.415514584397897 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Land", "maxabsdhdt": 5.9397964477539062, "refgtracks": "601|608|1043|1050" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -921648.312210126430728, -1179747.139506354462355 ], [ -923885.802754628937691, -1178934.062095494242385 ], [ -924269.856791832018644, -1177633.126327406847849 ], [ -924241.463892313186079, -1177581.49779283744283 ], [ -924213.069693977246061, -1177529.868615176528692 ], [ -924071.100232706870884, -1177271.727141749113798 ], [ -923957.523505342076533, -1177065.21503060660325 ], [ -923843.94731571059674, -1176858.705034456215799 ], [ -923503.213711146614514, -1176239.182612132979557 ], [ -923474.819344522547908, -1176187.556928774807602 ], [ -923389.634353971341625, -1176032.679122153436765 ], [ -923332.844577169744298, -1175929.427707565715536 ], [ -922565.734865249367431, -1174535.808795919176191 ], [ -919757.613813483854756, -1175153.294133811956272 ], [ -919405.561962413485162, -1175668.857519983546808 ], [ -919391.223163660615683, -1175713.107825781684369 ], [ -920094.979919061297551, -1177007.145119445631281 ], [ -921559.624594692140818, -1179698.444148269947618 ], [ -921648.312210126430728, -1179747.139506354462355 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -11.970078468322754, "refgtracks": "51|180|554|622|996|1125" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1486993.658089738339186, -618292.053564867586829 ], [ -1489816.231178182177246, -615236.316554313292727 ], [ -1489996.955413347575814, -614712.071379500674084 ], [ -1489256.240286140004173, -606701.010510036139749 ], [ -1489205.609294786583632, -606670.619413928128779 ], [ -1489154.978174269199371, -606640.228676295024343 ], [ -1489003.083531301468611, -606549.059424969716929 ], [ -1483433.249640030553564, -603207.662586740683764 ], [ -1474476.584163443185389, -602261.501943336101249 ], [ -1470230.553845666581765, -602650.068275699741207 ], [ -1469912.307635122444481, -603029.796248003374785 ], [ -1469721.339712606742978, -603275.066961201489903 ], [ -1468296.15701387100853, -609487.82064502837602 ], [ -1468182.390480247559026, -610682.856109375250526 ], [ -1468635.272400384768844, -610960.621291939401999 ], [ -1473015.704998844303191, -613642.051379685173742 ], [ -1473116.664261284051463, -613703.281605611904524 ], [ -1478182.752248332137242, -615816.117830115836114 ], [ -1486993.658089738339186, -618292.053564867586829 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -7.5739521980285645, "refgtracks": "51|493|622" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1494716.534780031070113, -608415.871856927056797 ], [ -1496642.408342961221933, -607248.411546552204527 ], [ -1496854.06038912339136, -606722.361977652646601 ], [ -1496745.520290441811085, -605468.691655074479058 ], [ -1492640.328737932257354, -604218.55510410759598 ], [ -1492433.704102041432634, -604454.277086140937172 ], [ -1492286.713912111707032, -604635.554021165240556 ], [ -1493106.540876480517909, -608037.641915383399464 ], [ -1494371.464837891515344, -608335.103877555811778 ], [ -1494716.534780031070113, -608415.871856927056797 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -4.6550264358520508, "refgtracks": "112|180|554|1125" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1449622.873585817636922, -607565.347604951122776 ], [ -1453605.038091481197625, -605950.678208690718748 ], [ -1453762.4004948255606, -605753.711003085481934 ], [ -1453065.122720842482522, -602401.334987755166367 ], [ -1452897.778078381437808, -602340.126389428856783 ], [ -1452668.548463454004377, -602283.80338410846889 ], [ -1452611.181523670209572, -602269.969526518369094 ], [ -1451578.388158203102648, -602021.792824922129512 ], [ -1450488.14962867111899, -601760.213201739126816 ], [ -1450258.586850254330784, -601705.320320339989848 ], [ -1448534.645840427139774, -601302.96566878980957 ], [ -1446265.018382735783234, -601162.606436122325249 ], [ -1445949.535755338147283, -601385.140372258378193 ], [ -1445174.00221960619092, -603908.280491922982037 ], [ -1445457.664510174654424, -604989.404416057514027 ], [ -1446810.248883335851133, -605831.088307312689722 ], [ -1449622.873585817636922, -607565.347604951122776 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -3.9650192260742188, "refgtracks": "173|241|1118" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1430273.136785346083343, -624198.414385868934914 ], [ -1432306.588181018130854, -622321.840455763158388 ], [ -1432357.214943998027593, -621582.523686840664595 ], [ -1432257.666574353352189, -621519.182967417407781 ], [ -1432108.345066379988566, -621424.17397981020622 ], [ -1432058.571172098396346, -621392.504631743184291 ], [ -1423497.171666633337736, -615947.765053613809869 ], [ -1423447.400606476003304, -615916.11382052837871 ], [ -1423148.773992624599487, -615726.208658738993108 ], [ -1422999.462429530918598, -615631.257364002522081 ], [ -1422949.690285990247503, -615599.606734608183615 ], [ -1422651.06460436899215, -615409.710517266532406 ], [ -1422402.209126546513289, -615251.464251525350846 ], [ -1422352.438273850595579, -615219.815264137461782 ], [ -1419664.569418757222593, -613511.354224456124939 ], [ -1418619.134628732921556, -612847.290924417437054 ], [ -1413722.906693445518613, -610724.241994899231941 ], [ -1413157.107841657474637, -610556.709564436459914 ], [ -1413100.110544614261016, -610541.583962875651196 ], [ -1411557.91542697395198, -610146.049946865532547 ], [ -1408073.807994137518108, -609252.746573477168567 ], [ -1407296.122351538855582, -609826.288588100462221 ], [ -1406500.196580470073968, -612301.05690204014536 ], [ -1406444.910864542704076, -613227.277797772781923 ], [ -1407486.748699286719784, -613896.485930762020871 ], [ -1409372.206617447314784, -615107.216523350216448 ], [ -1419297.419781607575715, -621479.803727964521386 ], [ -1419446.570611618692055, -621575.009204216185026 ], [ -1419546.089390563312918, -621638.343952428782359 ], [ -1429680.720710921334103, -624197.997800730285235 ], [ -1430273.136785346083343, -624198.414385868934914 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -4.6659488677978516, "refgtracks": "173|744|1118" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1444771.013913695467636, -633897.379213590640575 ], [ -1445055.941169311758131, -633607.486380911781453 ], [ -1445227.19857240980491, -633325.989761309116147 ], [ -1440625.721361177274957, -630787.279135885182768 ], [ -1440402.97052221884951, -631088.118191446643323 ], [ -1440037.783509076340124, -631944.803824983770028 ], [ -1444771.013913695467636, -633897.379213590640575 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -32.835506439208984, "refgtracks": "173|241|615|683" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1428864.355001702206209, -615422.614573395228945 ], [ -1429205.761338630924001, -614691.56849135120865 ], [ -1429143.6622219318524, -614344.767138986615464 ], [ -1421448.894453427288681, -610704.661246572970413 ], [ -1420063.694722828455269, -612350.262229960993864 ], [ -1428864.355001702206209, -615422.614573395228945 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -2.5648794174194336, "refgtracks": "173|241|615|683" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1435188.371031696442515, -619629.464129692874849 ], [ -1435386.494991394924, -619569.05509429180529 ], [ -1435726.518527299864218, -616336.653600896126591 ], [ -1435765.364540949463844, -615500.5418354322901 ], [ -1435699.70323569024913, -615382.438237203168683 ], [ -1435649.992397944210097, -615350.770130357705057 ], [ -1430497.890939444769174, -612130.213966091629118 ], [ -1429262.905867124907672, -614706.1988491154043 ], [ -1427815.709430576302111, -617741.853808080311865 ], [ -1429124.221642466494814, -618101.452284276252612 ], [ -1429181.370968053350225, -618116.087346477434039 ], [ -1429409.967617275891826, -618174.627384410239756 ], [ -1429981.463652349542826, -618320.975822559208609 ], [ -1430038.613831349648535, -618335.610294978250749 ], [ -1430095.763565325876698, -618350.243964714580216 ], [ -1430267.215332013554871, -618394.145460429717787 ], [ -1430895.875503193587065, -618555.115977015928365 ], [ -1431638.853698455961421, -618745.344342498807237 ], [ -1432381.844392565311864, -618935.565364917158149 ], [ -1432953.388496597064659, -619081.87692865752615 ], [ -1433067.819784392835572, -619110.661031245137565 ], [ -1435188.371031696442515, -619629.464129692874849 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -5.5391964912414551, "refgtracks": "180|554|996" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1467857.266041105845943, -606577.352202094742097 ], [ -1468097.050233103102073, -606184.008844146388583 ], [ -1468160.254311731783673, -606018.959184947074391 ], [ -1463301.370665608672425, -603278.856382645783015 ], [ -1463022.8472773169633, -603620.911094252602197 ], [ -1462647.727522539673373, -604706.476843561278656 ], [ -1467857.266041105845943, -606577.352202094742097 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -4.9165701866149902, "refgtracks": "112|180|554|1125" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1459948.991730506764725, -609550.06277579208836 ], [ -1461413.560725706629455, -607811.314752071746625 ], [ -1461385.724036112427711, -606523.318414684850723 ], [ -1459426.870146058965474, -605314.570528971380554 ], [ -1458772.895002716686577, -604913.37590829457622 ], [ -1458219.095536309527233, -604574.630065587465651 ], [ -1458168.750827577896416, -604543.836970112286508 ], [ -1458067.802031826227903, -604482.668830554932356 ], [ -1455383.713416528888047, -606379.312868630862795 ], [ -1455322.382002137135714, -606709.079836708377115 ], [ -1455797.8905138829723, -607450.48234876350034 ], [ -1456539.396924940403551, -607934.615368665778078 ], [ -1456589.604287148686126, -607965.639515829388984 ], [ -1456639.815979002509266, -607996.656172531656921 ], [ -1456690.031670141266659, -608027.667565645067953 ], [ -1459948.991730506764725, -609550.06277579208836 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -5.7402434349060059, "refgtracks": "51|493|622" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1492236.076566400472075, -604605.160994280362502 ], [ -1492376.219367721350864, -604440.737583217327483 ], [ -1492589.565004665637389, -604188.387864963617176 ], [ -1491990.791554189752787, -600925.24300979601685 ], [ -1491933.311988152330741, -600911.707944563007914 ], [ -1491760.872953343903646, -600871.10657509509474 ], [ -1491185.975042941281572, -600736.218491241335869 ], [ -1490208.635055769234896, -600507.037826450308785 ], [ -1489978.61752731888555, -600453.359579592593946 ], [ -1487411.497529330663383, -601112.342457728344016 ], [ -1486715.904819085961208, -601294.326676952303387 ], [ -1485247.000079285353422, -602768.975403586751781 ], [ -1492236.076566400472075, -604605.160994280362502 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Smith", "maxabsdhdt": -3.9841656684875488, "refgtracks": "173|744|1118" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1439299.029274416388944, -634332.60834335477557 ], [ -1440575.950459422077984, -630755.588820325676352 ], [ -1438884.006792794680223, -629677.70010718354024 ], [ -1438834.234879845753312, -629646.011829582275823 ], [ -1438684.391299361828715, -629551.782707697129808 ], [ -1438317.869774544378743, -629748.239553973544389 ], [ -1437352.349619982065633, -631251.866281652939506 ], [ -1437561.867294033057988, -633215.604727513971739 ], [ -1438057.928990721469745, -633535.150909859919921 ], [ -1438454.995301297400147, -633790.463738820049912 ], [ -1438504.627927253954113, -633822.377029963652603 ], [ -1439299.029274416388944, -634332.60834335477557 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -3.6175618171691895, "refgtracks": "27|340|469" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1614868.580491245724261, -238497.144019830797333 ], [ -1621550.223863640567288, -237052.402171563677257 ], [ -1622161.592871858039871, -235967.562062790180789 ], [ -1622031.219902534969151, -235283.325149442360271 ], [ -1621394.413388211745769, -233519.015321955987019 ], [ -1620716.984040390234441, -233309.41059015301289 ], [ -1619982.326125941937789, -233084.920018116710708 ], [ -1619586.74338709237054, -232964.048115680605406 ], [ -1619360.697917212499306, -232894.979457100387663 ], [ -1619304.186276230029762, -232877.714737485977821 ], [ -1619191.162121159257367, -232843.185839847137686 ], [ -1612792.562931230291724, -234374.076472739950987 ], [ -1612104.447568906005472, -235383.646842994756298 ], [ -1611999.421115926234052, -236014.972954449855024 ], [ -1612891.743793189525604, -237890.982288793195039 ], [ -1614246.960545129142702, -238307.677661831403384 ], [ -1614755.436651555122808, -238463.09272876495379 ], [ -1614811.936835245927796, -238480.352524684887612 ], [ -1614868.580491245724261, -238497.144019830797333 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -8.8322439193725586, "refgtracks": "27|340|469" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1611987.809511348837987, -237614.759427558630705 ], [ -1612623.058997433399782, -234322.29947643505875 ], [ -1610928.050746988505125, -233804.556724304915406 ], [ -1610871.550936723593622, -233787.300788150605513 ], [ -1609854.563048435375094, -233476.711594633990899 ], [ -1609572.069970555603504, -233390.439341798453825 ], [ -1609176.581120522459969, -233269.664379791822284 ], [ -1609120.082374698249623, -233252.412948198674712 ], [ -1609063.294515141984448, -233236.107828098087339 ], [ -1607791.639254574896768, -235423.79375821899157 ], [ -1607864.272809455404058, -236034.072873083787272 ], [ -1609050.094124210998416, -236717.241195312613854 ], [ -1609613.871987581020221, -236893.608444123790832 ], [ -1610065.823863217141479, -237031.677079839340877 ], [ -1610122.319366150768474, -237048.931501137732994 ], [ -1610630.837744022719562, -237204.044132624170743 ], [ -1610743.861148138763383, -237238.452892008295748 ], [ -1611987.809511348837987, -237614.759427558630705 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -8.3976497650146484, "refgtracks": "27|401|843|972" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1576704.410969766788185, -245789.278236300422577 ], [ -1583961.490573281422257, -244610.017908772308147 ], [ -1584149.492041621357203, -244172.765694072993938 ], [ -1584150.681628639809787, -242794.528009296394885 ], [ -1577524.460360686760396, -239491.903508517192677 ], [ -1571798.047507618553936, -239513.959279283619253 ], [ -1571266.766600047471002, -239516.461088854324771 ], [ -1570914.804727860493585, -240013.565515599795617 ], [ -1570744.399285033345222, -240389.666618026356446 ], [ -1572092.323379256296903, -244319.066866749693872 ], [ -1572148.555424832971767, -244337.027199827251025 ], [ -1572654.647180660627782, -244498.670465353148757 ], [ -1574398.18031411874108, -245054.559948650246952 ], [ -1576366.846834223018959, -245681.943500675959513 ], [ -1576704.410969766788185, -245789.278236300422577 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -4.5534038543701172, "refgtracks": "35|545|980|987" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1510406.668318179203197, -103506.768776127806632 ], [ -1510700.16240395908244, -103480.011206215218408 ], [ -1513165.239015067927539, -103251.52291967299243 ], [ -1513282.624576263595372, -103240.617399142953218 ], [ -1513341.317157571669668, -103235.164195532415761 ], [ -1513517.396284357411787, -103218.800819787982618 ], [ -1514045.633071517571807, -103169.709369013828109 ], [ -1514442.053029050352052, -99793.055505865791929 ], [ -1514527.203525817487389, -98844.382928752806038 ], [ -1514354.837538303574547, -98803.754715591960121 ], [ -1514297.53545711375773, -98790.472749741078587 ], [ -1509646.808661165880039, -97713.209777545489487 ], [ -1509589.366062083048746, -97700.01632606126077 ], [ -1509531.924325951142237, -97686.82327165613242 ], [ -1509300.057197283022106, -97643.192201065307017 ], [ -1505224.994698897702619, -100116.373491979640676 ], [ -1504465.755497677018866, -100719.867504122332321 ], [ -1510406.668318179203197, -103506.768776127806632 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.225644588470459, "refgtracks": "35|606|980" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1578754.582613520557061, -120922.17404809036816 ], [ -1582161.970162883168086, -118308.046411216913839 ], [ -1582156.827623848570511, -118150.050038313522236 ], [ -1580335.296199699863791, -114021.033502007310744 ], [ -1579932.778983093099669, -113927.871804266207619 ], [ -1579875.276594608556479, -113914.563170053457725 ], [ -1579645.26918054651469, -113861.329963515861891 ], [ -1579587.76612609019503, -113848.021731827073381 ], [ -1579357.758431828580797, -113794.789929893478984 ], [ -1578840.245222606696188, -113675.025883073190926 ], [ -1578610.239674866432324, -113621.79842475130863 ], [ -1578552.738627614220604, -113608.491684298947803 ], [ -1578495.238493578974158, -113595.185282172940788 ], [ -1578150.232265165541321, -113515.348396690460504 ], [ -1577690.228333712555468, -113408.906418218481122 ], [ -1577632.727851477684453, -113395.601649100557552 ], [ -1576482.621556298108771, -113130.005262778504402 ], [ -1576425.116705102147534, -113116.726747684646398 ], [ -1575735.008688367204741, -112957.624082927912241 ], [ -1575332.417737670708448, -112864.955403992717038 ], [ -1571593.032215230632573, -115414.168020366050769 ], [ -1571477.381839454406872, -115785.253642628915259 ], [ -1573527.486330680549145, -119688.359949183024582 ], [ -1576054.652209030929953, -120285.544183696227265 ], [ -1578237.382425580639392, -120801.027731007648981 ], [ -1578582.144633457763121, -120881.950113241240615 ], [ -1578754.582613520557061, -120922.17404809036816 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.710410475730896, "refgtracks": "35|164|606|980" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1585832.846937771886587, -121658.179111857200041 ], [ -1585891.741863605333492, -121654.171257014982984 ], [ -1588128.547860681079328, -121485.440175838462892 ], [ -1589482.33824696065858, -121381.793281264675898 ], [ -1589894.366981480969116, -121350.226701202642289 ], [ -1592543.203276213724166, -121147.225571645321907 ], [ -1592602.067691966192797, -121142.70961501878628 ], [ -1592733.389820840442553, -120806.952891715773148 ], [ -1592723.820495467400178, -120640.06450757222774 ], [ -1591277.715146888047457, -117416.718230940794456 ], [ -1590806.904445521533489, -117454.405620438978076 ], [ -1590218.406938172644004, -117501.63088851646171 ], [ -1585451.938367273891345, -117885.358468874648679 ], [ -1585157.737867566524073, -117909.178780243804795 ], [ -1584981.218716516857967, -117923.471105527336476 ], [ -1584517.435271406313404, -118864.479942026679055 ], [ -1585832.846937771886587, -121658.179111857200041 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.7406028509140015, "refgtracks": "35|164|606|980" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1596270.685464693931863, -124196.602255612262525 ], [ -1596506.216407993808389, -124179.037488963615033 ], [ -1597153.828813054133207, -124129.369623125123326 ], [ -1597624.824974320130423, -124093.243571053157211 ], [ -1597978.074622126063332, -124066.144856348822941 ], [ -1598272.450553784845397, -124043.556326099147554 ], [ -1599037.837286166613922, -123984.823972266050987 ], [ -1599391.096169758588076, -123957.713072612430551 ], [ -1599567.726907152449712, -123944.156464241081267 ], [ -1599979.866555897751823, -123912.521146163941012 ], [ -1600450.88743150094524, -123876.359786536559113 ], [ -1600980.791295975446701, -123835.671422701590927 ], [ -1601039.669816304696724, -123831.150279564157245 ], [ -1601275.184177293209359, -123813.064519759704126 ], [ -1601569.579001290723681, -123790.453458233532729 ], [ -1601927.428534975042567, -122982.12699131348927 ], [ -1601726.711178545374423, -120440.212069317916757 ], [ -1600905.425025678938255, -119981.465716206672369 ], [ -1595490.132212258409709, -120417.686780324773281 ], [ -1594169.871468791272491, -121146.665338093458558 ], [ -1596270.685464693931863, -124196.602255612262525 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.4033622741699219, "refgtracks": "35|164|538|980|1109" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1595631.952741797547787, -131431.431523286184529 ], [ -1604221.767569024814293, -130940.045293078030227 ], [ -1624262.226805541198701, -129357.852705487544881 ], [ -1624747.323907302226871, -128383.591047860973049 ], [ -1624079.232073951745406, -126031.95285585768579 ], [ -1623144.367646741680801, -125463.742607054344262 ], [ -1601687.337418720591813, -123781.409090479719453 ], [ -1601628.458197862375528, -123785.931159345622291 ], [ -1590855.916439216583967, -127205.447933366522193 ], [ -1590696.291355712572113, -127959.556875394744566 ], [ -1590681.458078984869644, -128450.912830341534573 ], [ -1595631.952741797547787, -131431.431523286184529 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -3.4117388725280762, "refgtracks": "35|477|545|987" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1530425.126749241957441, -101662.554905733850319 ], [ -1533653.961472841445357, -101357.616013159989961 ], [ -1533769.325042853830382, -101324.523231997329276 ], [ -1534518.053490753984079, -100042.720028904703213 ], [ -1534555.318104636622593, -99803.106515086852596 ], [ -1534046.273449964122847, -97950.490362120486679 ], [ -1531893.578958452446386, -95792.5767698114214 ], [ -1530858.118714353069663, -95559.671837888599839 ], [ -1526130.516748830443248, -98102.55719432180922 ], [ -1530425.126749241957441, -101662.554905733850319 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -3.1918010711669922, "refgtracks": "35|477|545|987" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1535496.18968953890726, -103677.882953987063956 ], [ -1538885.842896695481613, -101047.97741970399511 ], [ -1538936.640184361953288, -100844.513713608350372 ], [ -1536630.922053968068212, -97723.665057449994492 ], [ -1535913.814794139238074, -97556.717518198638572 ], [ -1535678.986924217781052, -97579.221629426232539 ], [ -1535561.597657355014235, -97590.720522154500941 ], [ -1534281.479078097268939, -97931.576267636744888 ], [ -1532393.263598834397271, -102963.905698351329193 ], [ -1532508.034365549916402, -102990.340496826698654 ], [ -1532910.391932748258114, -103082.998686043458292 ], [ -1533599.868029076606035, -103241.699538705099258 ], [ -1534576.865229575196281, -103466.529469811925082 ], [ -1535381.346118249231949, -103651.502134301990736 ], [ -1535496.18968953890726, -103677.882953987063956 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.4127118587493896, "refgtracks": "35|477|606" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1597339.620615863241255, -116929.091256764018908 ], [ -1597398.51391592877917, -116924.80065310226928 ], [ -1598163.692941028159112, -116863.614292495447444 ], [ -1600576.810394956264645, -116667.930334728807793 ], [ -1601224.239957549842075, -116615.274914870358771 ], [ -1601459.670854484429583, -116596.123336329197627 ], [ -1602048.252720789983869, -116548.242021247569937 ], [ -1602107.111088995821774, -116543.450549684130237 ], [ -1603637.335662890924141, -116417.432766551602981 ], [ -1604049.319161067716777, -116383.401934646055452 ], [ -1604150.759473742218688, -116120.979922140395502 ], [ -1603425.804526092018932, -113092.228082881207229 ], [ -1603307.051726272329688, -113089.005631843785523 ], [ -1596833.404089152580127, -113616.368366243390483 ], [ -1596598.972369394265115, -113647.33871553602512 ], [ -1596471.586666128598154, -113828.773945277833263 ], [ -1596327.127864589449018, -114307.940053483675001 ], [ -1597339.620615863241255, -116929.091256764018908 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.5960536003112793, "refgtracks": "35|164|477|1109" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1638268.009687540121377, -127466.112877050923998 ], [ -1644721.437328192172572, -125552.818651620444143 ], [ -1645295.428904845612124, -124483.193027727262233 ], [ -1645239.611679987981915, -123752.462496466949233 ], [ -1638853.468179761897773, -120912.56760149076581 ], [ -1635140.420947477221489, -121185.938927905182936 ], [ -1634691.636648307088763, -124570.803104998209164 ], [ -1634863.019457357237116, -125245.800265994839719 ], [ -1635908.287691959645599, -126916.596142944952589 ], [ -1637922.24087937688455, -127387.56230975696235 ], [ -1638268.009687540121377, -127466.112877050923998 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -9.1220684051513672, "refgtracks": "142|271|713|1087" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1561350.582032784586772, -323530.688979324302636 ], [ -1563265.486238949932158, -320194.143112437566742 ], [ -1563380.457005708245561, -317767.750005907204468 ], [ -1563371.062535487348214, -316678.566451354534365 ], [ -1562872.962039995705709, -316493.659193132945802 ], [ -1559994.308357090922073, -315427.627195326378569 ], [ -1559938.947529351571575, -315407.1377331923577 ], [ -1559606.782612241338938, -315284.205020383058582 ], [ -1559551.421812946908176, -315263.716881259169895 ], [ -1559219.258274062536657, -315140.789084639865905 ], [ -1559163.897354887099937, -315120.302712232456543 ], [ -1559108.534749840386212, -315099.821220566285774 ], [ -1559053.160705987596884, -315079.344576181145385 ], [ -1555073.950119570130482, -317158.404091133794282 ], [ -1554145.230068773496896, -320676.877445647318382 ], [ -1554190.254831176018342, -321130.098393327847589 ], [ -1561350.582032784586772, -323530.688979324302636 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.4220180511474609, "refgtracks": "157|225|667|1102" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1516293.711298561189324, -147409.062035735609243 ], [ -1516587.904525423888117, -147391.030180128407665 ], [ -1517352.755519174970686, -147343.17837019302533 ], [ -1518117.616984542226419, -147295.311892332480056 ], [ -1519941.522522664163262, -147180.4971342922654 ], [ -1522118.526936358073726, -147043.444548950559692 ], [ -1522295.031330385012552, -147032.114198300783755 ], [ -1522412.410004338948056, -147019.782701533957152 ], [ -1522676.059273786842823, -146347.069919267814839 ], [ -1521518.643681603949517, -143569.572308985079871 ], [ -1520200.0578949439805, -142268.166425568517298 ], [ -1520143.108587075723335, -142252.916896339331288 ], [ -1519857.743627755669877, -142179.056820498517482 ], [ -1519572.380038312869146, -142105.199734760564752 ], [ -1519401.162507640663534, -142060.88699150440516 ], [ -1519344.089605035958812, -142046.116169149201596 ], [ -1518887.511383046396077, -141927.952459223743062 ], [ -1518602.151422726223245, -141854.104062387370504 ], [ -1517803.150068186921999, -141647.339032277232036 ], [ -1517746.078760523581877, -141632.572084921004716 ], [ -1517689.007168875075877, -141617.805124895472545 ], [ -1517631.935881530167535, -141603.039119829947595 ], [ -1517175.244982698466629, -141485.3752675812284 ], [ -1514282.451591009274125, -144039.940018402936403 ], [ -1514230.754493675427511, -144158.257528160262154 ], [ -1514214.037028472870588, -144292.406550925748888 ], [ -1515912.226707049878314, -147266.36572162451921 ], [ -1516293.711298561189324, -147409.062035735609243 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -3.0236566066741943, "refgtracks": "157|225|667|1102" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1526500.243560546543449, -150099.069416335987626 ], [ -1526735.690817923285067, -150085.097399709018646 ], [ -1533151.379323238739744, -149693.451879002328496 ], [ -1533406.73231010697782, -149125.059462611650815 ], [ -1533123.590354685904458, -146365.895379438676173 ], [ -1532927.895669480552897, -145567.13518793520052 ], [ -1532813.852169808931649, -145537.071752074727556 ], [ -1530873.17060064827092, -145033.674675812508212 ], [ -1530644.844536853954196, -144974.512813800567528 ], [ -1530587.76262650010176, -144959.723559277917957 ], [ -1530530.680405154358596, -144944.934519765141886 ], [ -1529617.258469671243802, -144708.794509473897051 ], [ -1527162.334346162388101, -144074.910109997348627 ], [ -1527048.058452305849642, -144045.795995293534361 ], [ -1522733.129478453658521, -146361.850249638402602 ], [ -1522530.146226640790701, -147012.972860627080081 ], [ -1526500.243560546543449, -150099.069416335987626 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -3.5004751682281494, "refgtracks": "157|225|599|667|1102|1170" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1552936.907369694905356, -157954.609338118898449 ], [ -1556209.566197860287502, -155272.23873478686437 ], [ -1556298.951017462415621, -155067.867574938049074 ], [ -1554545.691838778555393, -152035.650519836141029 ], [ -1544270.766543664038181, -141331.403662303928286 ], [ -1543299.184976071352139, -141083.540583166497527 ], [ -1543242.027237377827987, -141068.989798927796073 ], [ -1539755.324502934934571, -140182.027750693348935 ], [ -1538783.562729787779972, -139935.250978359399596 ], [ -1535447.943439601222053, -142521.658112837321823 ], [ -1535404.181915748398751, -142645.138470112986397 ], [ -1533210.24006563378498, -149689.825339080038248 ], [ -1536057.231587878195569, -153492.627972451038659 ], [ -1536171.143647213699296, -153523.221483115747105 ], [ -1552594.581948873354122, -157864.567684840323636 ], [ -1552936.907369694905356, -157954.609338118898449 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.7808902263641357, "refgtracks": "157|728|1102|1170" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1563260.691995443310589, -160686.922033041802933 ], [ -1571870.937062216922641, -159525.944620191119611 ], [ -1571954.865534399170429, -157694.572864626534283 ], [ -1565075.011263687163591, -153904.444843049859628 ], [ -1563875.761921640485525, -153592.482226711697876 ], [ -1559818.867305364459753, -152547.47206349359476 ], [ -1556356.048512199893594, -155082.701735526236007 ], [ -1556268.47017729957588, -155268.845741072203964 ], [ -1557898.975581969600171, -159267.53856974260998 ], [ -1559096.642358271637931, -159585.03795669451938 ], [ -1559267.757506900234148, -159630.330575102794683 ], [ -1563260.691995443310589, -160686.922033041802933 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.1264758110046387, "refgtracks": "157|286|728|1102" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1586937.112920681014657, -166963.566494472499471 ], [ -1591158.049437006236985, -164639.350022913422436 ], [ -1591255.791961483191699, -164150.815235192072578 ], [ -1591245.555973158683628, -163752.257865331019275 ], [ -1583317.451164482161403, -162073.973078226612415 ], [ -1582883.427848559338599, -162443.965123482310446 ], [ -1581916.724571803351864, -164713.56789989254321 ], [ -1582429.488588595530018, -165767.043134292121977 ], [ -1582828.797497550258413, -165873.437873755057808 ], [ -1582885.847934908466414, -165888.605906568205683 ], [ -1583741.614744174294174, -166116.100376195157878 ], [ -1585111.041364496340975, -166479.466953934286721 ], [ -1586937.112920681014657, -166963.566494472499471 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -5.079927921295166, "refgtracks": "157|286|1102" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1604124.672009263653308, -170276.119669191422872 ], [ -1604183.713694913778454, -170274.18965856891009 ], [ -1605009.662515323609114, -170234.200670112273656 ], [ -1608844.547634051414207, -170047.233953200222459 ], [ -1610673.167799381539226, -169949.371239436499309 ], [ -1610736.13248906773515, -169839.71651884081075 ], [ -1610623.904417130630463, -169209.487729588727234 ], [ -1609976.978462585015222, -166656.618338815897005 ], [ -1609799.921659373911098, -166663.94910345680546 ], [ -1608502.06287700519897, -166728.562722643750021 ], [ -1604077.818289482034743, -166949.016795576870209 ], [ -1603959.940813022200018, -166956.851074826612603 ], [ -1603481.411475408123806, -167343.662264226033585 ], [ -1603258.078497293172404, -167852.340063638432184 ], [ -1604124.672009263653308, -170276.119669191422872 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.6542291641235352, "refgtracks": "157|599|667" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1527107.654799863463268, -143196.762197573581943 ], [ -1536614.926433885470033, -143093.913063881540438 ], [ -1532553.829950243467465, -138351.034421435877448 ], [ -1529696.516750109149143, -137624.013976636430016 ], [ -1529582.242205801885575, -137594.961712075193645 ], [ -1529067.918018436757848, -137464.21300958894426 ], [ -1528896.477941140532494, -137420.630651109357132 ], [ -1528324.892158503411338, -137275.840612068277551 ], [ -1525219.310240061488003, -139920.178113625821425 ], [ -1525182.532028010580689, -139991.62316904932959 ], [ -1525140.920712753897533, -140120.450399036781164 ], [ -1527107.654799863463268, -143196.762197573581943 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.7694759368896484, "refgtracks": "157|225|599|667|1170" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1565132.120958493091166, -153919.292614103731466 ], [ -1570024.367778070271015, -151753.946792761649704 ], [ -1570427.453710805159062, -151453.238002350990428 ], [ -1570450.398147271480411, -151116.114814702916192 ], [ -1565132.695851005613804, -146666.822930722933961 ], [ -1565021.397102783434093, -146625.678223243507091 ], [ -1564964.58753859391436, -146609.654452173097525 ], [ -1564107.065506026614457, -146390.373586659581633 ], [ -1563135.205440446967259, -146141.944889019185212 ], [ -1560962.437427450204268, -145587.273837883840315 ], [ -1550615.623218489577994, -142947.839610480616102 ], [ -1550558.459480209974572, -142933.276207837508991 ], [ -1550329.802240038989112, -142875.022839827142889 ], [ -1550215.472349428106099, -142845.897403074137401 ], [ -1550158.308816339820623, -142831.335052690410521 ], [ -1550101.143814572598785, -142816.772549647925189 ], [ -1549643.83178336895071, -142700.274594681075541 ], [ -1549472.340487900422886, -142656.589321677049156 ], [ -1549300.850222756154835, -142612.904569532605819 ], [ -1547428.24207951198332, -145490.981216222164221 ], [ -1547392.773150393739343, -145563.031618375767721 ], [ -1548575.765152657637373, -148749.187736482912442 ], [ -1558562.930067359469831, -152219.607769303343957 ], [ -1558619.911359948338941, -152234.922901165817166 ], [ -1558848.205976667813957, -152294.747739616752369 ], [ -1559704.657142156502232, -152517.795431112026563 ], [ -1559761.761654713191092, -152532.633293719030917 ], [ -1565132.120958493091166, -153919.292614103731466 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.1808464527130127, "refgtracks": "157|286|599|728" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1616171.708165521733463, -166346.166585804428905 ], [ -1617175.584291948704049, -166312.850189330143621 ], [ -1623725.160130430478603, -165981.594526997447247 ], [ -1623852.593515417538583, -165784.518413790734485 ], [ -1623213.917621802771464, -162022.302559405507054 ], [ -1622278.694162854924798, -161279.218468992388807 ], [ -1616785.057975107105449, -159871.415189054270741 ], [ -1611293.508593894774094, -159335.458827599417418 ], [ -1610821.627632173476741, -159360.611872712499462 ], [ -1606398.205482940655202, -159600.781263646407751 ], [ -1605830.094654012937099, -160505.603855927358381 ], [ -1604278.269147542538121, -163050.851272725703893 ], [ -1604384.450319586787373, -163600.86553035734687 ], [ -1604663.513924937229604, -164218.03951620333828 ], [ -1606663.568000855157152, -164740.115430523990653 ], [ -1610379.99136871797964, -165702.81561497997609 ], [ -1616171.708165521733463, -166346.166585804428905 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.8574448823928833, "refgtracks": "164|538|606|980" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1573020.828718042932451, -125973.011391821535653 ], [ -1573138.523620340041816, -125964.051539265783504 ], [ -1573197.371337137650698, -125959.571575783353182 ], [ -1573256.219231685157865, -125955.091476416011574 ], [ -1573550.459591493243352, -125932.687411142935161 ], [ -1573727.004325283691287, -125919.243400407882291 ], [ -1574256.643153110519052, -125878.909608613466844 ], [ -1574315.492188586853445, -125874.427945559757063 ], [ -1574609.738145190756768, -125852.016219696233748 ], [ -1575021.684739702148363, -125820.636512962286361 ], [ -1575080.534506103256717, -125816.152843575342558 ], [ -1575139.384421822149307, -125811.668592200760031 ], [ -1576380.839556953171268, -123782.777090900708572 ], [ -1576769.658751874929294, -122351.091331471965532 ], [ -1576776.963398871244863, -121928.340169409144437 ], [ -1568300.918082004645839, -118454.861475017722114 ], [ -1565889.232373567763716, -117883.968543666080222 ], [ -1565429.792183319339529, -117775.588316252615186 ], [ -1563132.515177089488134, -117234.316893185372464 ], [ -1563017.208123886957765, -117209.152663597662468 ], [ -1559497.484522487735376, -119796.447628564492334 ], [ -1559401.078093773918226, -120143.967658408539137 ], [ -1560824.443188082426786, -123564.591313879616791 ], [ -1561146.764859432121739, -123987.753345025455928 ], [ -1563498.755670574260876, -124553.625151247542817 ], [ -1565277.260107872541994, -124981.10519472740998 ], [ -1566367.379908038070425, -125242.966530655598035 ], [ -1567342.775748391868547, -125477.245445586755523 ], [ -1567629.742505583679304, -125545.81751850413275 ], [ -1573020.828718042932451, -125973.011391821535653 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.1623144149780273, "refgtracks": "164|538|980|1109" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1587884.860431843437254, -130425.464191567851231 ], [ -1589032.705368785886094, -128569.228546029320569 ], [ -1588871.229611159069464, -128098.101179281671648 ], [ -1588213.379683342762291, -126580.309920698666247 ], [ -1587639.043513318290934, -126443.954990162572358 ], [ -1587581.598785528214648, -126430.368214121757774 ], [ -1587524.153973073931411, -126416.782039807221736 ], [ -1585685.950503486208618, -125982.05903637582378 ], [ -1585628.507684015901759, -125968.474632473502425 ], [ -1585226.294495339272544, -125873.87233590867254 ], [ -1584246.779736503493041, -126121.872153805772541 ], [ -1582866.750367308501154, -128555.115774796329788 ], [ -1582968.21635273960419, -129005.644537778222002 ], [ -1584499.068074824521318, -129608.816397735950886 ], [ -1587884.860431843437254, -130425.464191567851231 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.9070290327072144, "refgtracks": "164|538|1109" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1575971.672086291247979, -130981.804062624665676 ], [ -1576963.323999090353027, -129437.007143975075451 ], [ -1576980.734988529002294, -129003.256575470266398 ], [ -1576350.696841223165393, -127645.649695423533558 ], [ -1576293.318940711207688, -127631.822800823676516 ], [ -1576006.431533030699939, -127562.700784325454151 ], [ -1573424.382388802245259, -126941.173936648177914 ], [ -1572037.049841622589156, -129379.933155252758297 ], [ -1572136.34878387930803, -129783.575391187929199 ], [ -1573045.613493662793189, -130277.158048555851565 ], [ -1574250.397592968540266, -130567.475261750791105 ], [ -1575971.672086291247979, -130981.804062624665676 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.3876519203186035, "refgtracks": "218|286|728|1163" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1519068.661455299006775, -170247.381565308052814 ], [ -1525929.693024713080376, -168685.796443784725852 ], [ -1526172.440792627632618, -167420.162169196672039 ], [ -1519538.976698611862957, -164215.611988549761008 ], [ -1519009.032380502903834, -164242.784690831962507 ], [ -1517949.152976914076135, -164297.598711130092852 ], [ -1517772.491836225381121, -164306.811222104617627 ], [ -1517124.872756986645982, -164340.590153529832605 ], [ -1516653.865287474356592, -164365.644797378772637 ], [ -1515472.484239936573431, -165796.617212844226742 ], [ -1515825.941880025435239, -167738.759602174803149 ], [ -1515925.429412987548858, -167905.403859200217994 ], [ -1518330.293879630975425, -170041.903808933013352 ], [ -1518386.957170609850436, -170058.185660874034511 ], [ -1518443.754236823646352, -170073.992346270446433 ], [ -1518841.339349368819967, -170184.630268919339869 ], [ -1519068.661455299006775, -170247.381565308052814 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.7830259799957275, "refgtracks": "218|286|728|1163" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1525977.889856782043353, -170754.954851981427055 ], [ -1526036.813032460166141, -170752.631099775375333 ], [ -1526449.136275415075943, -170733.400601621164242 ], [ -1527273.792395493481308, -170694.923886696953559 ], [ -1529806.740214169025421, -170576.573798836237984 ], [ -1530513.614893450867385, -170543.184955156379147 ], [ -1530631.424401474418119, -170537.53685176250292 ], [ -1530808.138790117343888, -170529.064132450323086 ], [ -1531054.160943748662248, -169877.409166955010733 ], [ -1530177.659397165989503, -167232.049682285374729 ], [ -1529487.885225489735603, -167026.881860243738629 ], [ -1529428.995608791010454, -167029.987165220314637 ], [ -1527426.633568459190428, -167136.008826643286739 ], [ -1527014.462384283076972, -167158.049909640307305 ], [ -1526719.991190808359534, -167174.166529088484822 ], [ -1526231.359568189596757, -167417.828718247474171 ], [ -1526100.271353679243475, -168732.693402411299758 ], [ -1525977.889856782043353, -170754.954851981427055 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -3.3064334392547607, "refgtracks": "279|721|850|1292" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1588487.271401347126812, -203540.132595053175464 ], [ -1588723.389598815236241, -203534.317942058230983 ], [ -1588959.495787109481171, -203528.009836260316661 ], [ -1589313.657083046156913, -203518.542982979677618 ], [ -1589608.793461991706863, -203510.651976791006746 ], [ -1589962.959389794850722, -203501.178496652108151 ], [ -1590081.01520377676934, -203498.019775064632995 ], [ -1590140.04321428318508, -203496.440385380556108 ], [ -1590553.241416254080832, -203485.384425030671991 ], [ -1590789.356248868862167, -203479.063738379336428 ], [ -1591025.47215036675334, -203472.742021329235286 ], [ -1591320.618622593581676, -203464.838851649023127 ], [ -1591379.648110596463084, -203463.257543430139776 ], [ -1592147.037907553138211, -203442.696575693029445 ], [ -1592383.160232184454799, -203436.368241661490174 ], [ -1593003.063833754742518, -202359.57788213546155 ], [ -1593102.107098527019843, -201855.006478775845608 ], [ -1591762.534994869492948, -200120.11659172232612 ], [ -1587242.099758712109178, -199748.555235609528609 ], [ -1587183.079557349905372, -199750.390720695984783 ], [ -1587124.059819877147675, -199752.228899840294616 ], [ -1586799.549759508110583, -200058.376887242426164 ], [ -1586595.239342933753505, -200498.894896800833521 ], [ -1588487.271401347126812, -203540.132595053175464 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -3.0069644451141357, "refgtracks": "286|660|728|1102" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1569699.540745229460299, -168643.575873125286307 ], [ -1571055.732933080988005, -168584.710201623325702 ], [ -1571232.582993649644777, -168576.06461059502908 ], [ -1571291.533037395449355, -168573.182634954224341 ], [ -1571645.235394188202918, -168555.886580979829887 ], [ -1571998.939966863486916, -168538.58686264482094 ], [ -1572116.842127181356773, -168532.817043947492493 ], [ -1573413.759295142954215, -168468.863374635810032 ], [ -1574003.263985826633871, -168439.508868232078385 ], [ -1574615.572012493386865, -167139.089921706734458 ], [ -1574724.362778879702091, -165066.43809090720606 ], [ -1574156.477934127207845, -163573.679264900478302 ], [ -1573757.280536203179508, -163467.366024823626503 ], [ -1571361.073284367565066, -162832.723798117571278 ], [ -1569124.462988489773124, -165340.750487067125505 ], [ -1568990.3052465938963, -166110.023803308082279 ], [ -1569699.540745229460299, -168643.575873125286307 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.4043872356414795, "refgtracks": "286|721|1163" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1527272.151476589730009, -176195.218438728799811 ], [ -1527491.471808589994907, -176045.244586456014076 ], [ -1528434.054230039706454, -173967.985933896619827 ], [ -1527474.832370520103723, -172589.244157404784346 ], [ -1527304.42636068421416, -172541.755783945263829 ], [ -1526395.56909787026234, -172288.6191555505211 ], [ -1524975.47956803906709, -171893.22123218694469 ], [ -1524918.676605983171612, -171877.406097889266675 ], [ -1524748.136304159183055, -171830.435029409476556 ], [ -1523426.917077829362825, -174201.227685045014368 ], [ -1523459.073051030980423, -174920.964557380561018 ], [ -1524094.827589958906174, -175295.440642580972053 ], [ -1524151.423135950230062, -175312.003435046965024 ], [ -1524208.155534841120243, -175328.08297412795946 ], [ -1527045.134215070167556, -176131.153610833367566 ], [ -1527158.60242515639402, -176163.245545614452567 ], [ -1527215.374853592831641, -176179.232150789757725 ], [ -1527272.151476589730009, -176195.218438728799811 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -7.403261661529541, "refgtracks": "340|469|782|911" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1602509.819206407526508, -231235.415617244492751 ], [ -1605958.77066475758329, -228772.892161817057058 ], [ -1606040.441916648764163, -228226.773533142724773 ], [ -1606041.783453341107816, -228198.787083671428263 ], [ -1597498.725009114481509, -225663.197027361631626 ], [ -1597042.319325965130702, -226082.27190870672348 ], [ -1595587.058761791791767, -228338.966584977548337 ], [ -1595505.339702316559851, -228873.744054503855295 ], [ -1595559.812744439113885, -229121.13234103663126 ], [ -1595616.299037243705243, -229138.36449670951697 ], [ -1595672.786070721922442, -229155.594605916820001 ], [ -1596350.637887981254607, -229362.353358321910491 ], [ -1596520.104087718063965, -229414.038091171067208 ], [ -1601999.941011181334034, -231084.976697190926643 ], [ -1602509.819206407526508, -231235.415617244492751 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.3844614028930664, "refgtracks": "35|164|477|1109" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1646835.956575770163909, -127710.069507919281023 ], [ -1647131.981632121140137, -127705.731427650724072 ], [ -1647426.779963182052597, -127684.606667829764774 ], [ -1650197.628934220876545, -127481.302424233945203 ], [ -1650315.540673198876902, -127472.649950972598162 ], [ -1650374.497080301633105, -127468.322301064181374 ], [ -1650905.104236668208614, -127429.366481377073796 ], [ -1650964.06081018038094, -127425.03629764962534 ], [ -1651316.535966257331893, -127381.784405141384923 ], [ -1651456.794343705754727, -127123.58559038459498 ], [ -1651840.072340657934546, -124019.906039156019688 ], [ -1651299.591656970325857, -122893.194859760988038 ], [ -1647727.371561063453555, -122074.725661294491147 ], [ -1647612.135201353114098, -122048.367930512715247 ], [ -1647150.052767587359995, -121947.96593434746319 ], [ -1645593.210804373724386, -123724.961758357283543 ], [ -1645531.211729107424617, -124465.894667576882057 ], [ -1646835.956575770163909, -127710.069507919281023 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.8041787147521973, "refgtracks": "164|477|606|919" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1633646.697399511933327, -117976.677518362892442 ], [ -1635708.648868201766163, -117817.045830694507458 ], [ -1635767.561976331751794, -117812.470358808684978 ], [ -1635826.47469647647813, -117807.888292853211169 ], [ -1636356.657355699921027, -117766.204541806509951 ], [ -1637058.926770358812064, -117027.793679894559318 ], [ -1637174.226192627567798, -116251.876876513866591 ], [ -1636373.924772744765505, -113741.627318540005945 ], [ -1635254.813730291323736, -113832.577112181097618 ], [ -1632957.851733016315848, -114020.19815272881533 ], [ -1632840.065724357962608, -114029.864767588034738 ], [ -1632663.386814856668934, -114044.36577705750824 ], [ -1632604.494051416870207, -114049.20016209068126 ], [ -1632103.864662060281262, -115096.594031956905383 ], [ -1632817.97072926373221, -117373.816691473184619 ], [ -1633646.697399511933327, -117976.677518362892442 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.9967057704925537, "refgtracks": "538|606|980" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1550923.982710148906335, -120669.998106197526795 ], [ -1551218.0368390919175, -120646.921243066011812 ], [ -1555099.419393746182323, -120338.578018120024353 ], [ -1558745.753179690334946, -120047.770681366397184 ], [ -1558922.147545439423993, -120033.102449399986654 ], [ -1559038.125375763745978, -119688.1277900885907 ], [ -1557734.288915958255529, -115962.665088706198731 ], [ -1557619.555725504411384, -115935.121829043375328 ], [ -1556987.902784836478531, -115786.260930390038993 ], [ -1556241.410879696486518, -115610.353569478160352 ], [ -1552508.964239950757474, -114731.544926251925062 ], [ -1552451.433054923545569, -114718.50151352789544 ], [ -1545660.190538656199351, -116537.99052327524987 ], [ -1545342.684163208818063, -116786.711331069804146 ], [ -1545307.58218677341938, -117781.113382640702184 ], [ -1550923.982710148906335, -120669.998106197526795 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.7929744720458984, "refgtracks": "538|667|980|1109" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1620976.782761336304247, -136896.851493591879262 ], [ -1628280.371228456497192, -136756.560420020367019 ], [ -1628463.096339213661849, -136387.797405985649675 ], [ -1628554.218994226772338, -136135.803518678527325 ], [ -1628806.054994424106553, -132366.406657493003877 ], [ -1624851.526373970787972, -129315.443794377148151 ], [ -1604472.321546236751601, -130429.96612190817541 ], [ -1604279.161560476059094, -130953.904951986143715 ], [ -1606297.200984949944541, -133996.300841707125073 ], [ -1620976.782761336304247, -136896.851493591879262 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.7057464122772217, "refgtracks": "538|667|980" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1624107.849557250970975, -139175.526413034182042 ], [ -1628943.044351703487337, -133015.374142873683013 ], [ -1628752.837069064145908, -132742.790937450015917 ], [ -1627197.630400010151789, -132387.256905462243594 ], [ -1627081.842976561514661, -132363.399831289192662 ], [ -1611877.966972507303581, -136218.369677041046089 ], [ -1613313.21011492004618, -136565.508799064584309 ], [ -1624107.849557250970975, -139175.526413034182042 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.5687770843505859, "refgtracks": "225|538|667|980" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1634846.664631676394492, -141775.981056429183809 ], [ -1636930.891491398448125, -139845.161124279926298 ], [ -1637003.478418775368482, -139143.591089029039722 ], [ -1637004.344987970078364, -138145.116967582609504 ], [ -1635242.911262072855607, -135926.235560097382404 ], [ -1632589.684363800333813, -136103.483960465528071 ], [ -1631764.202044980134815, -136159.959719447622774 ], [ -1630527.172047570813447, -136258.081334974674974 ], [ -1630278.345764272613451, -136546.942211037210654 ], [ -1630003.250704129924998, -137173.836344753333833 ], [ -1629928.092897732276469, -139625.722554600564763 ], [ -1630150.341643279185519, -140293.580472361441934 ], [ -1630596.76664606644772, -140746.914295295864576 ], [ -1632089.85962127498351, -141108.755225312808761 ], [ -1633755.39074177807197, -141511.943880144011928 ], [ -1634846.664631676394492, -141775.981056429183809 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.5031135082244873, "refgtracks": "35|667|1109" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1651325.782812828198075, -133930.931555823452072 ], [ -1653470.232647069962695, -131325.145147093775449 ], [ -1653406.29161801491864, -130997.519028718525078 ], [ -1653270.313383798114955, -130578.680665600506472 ], [ -1647649.637899357359856, -129654.918722003756557 ], [ -1646785.918171912897378, -131071.018646635115147 ], [ -1646688.616534318076447, -131792.699862656620098 ], [ -1647123.742613640148193, -132951.171698594524059 ], [ -1648274.927410705946386, -133219.679698762163753 ], [ -1651325.782812828198075, -133930.931555823452072 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.4655982255935669, "refgtracks": "35|667|980|1109" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1637488.064594889758155, -134832.855831101100193 ], [ -1639258.656635447405279, -132293.336915908701485 ], [ -1639120.855338693363592, -131613.941511660144897 ], [ -1636937.134857697645202, -130574.6665442140511 ], [ -1636591.783107773400843, -130494.470289400080219 ], [ -1634118.250294768018648, -130609.212843707296997 ], [ -1633344.312741108937189, -132035.647838723059976 ], [ -1633187.176069425186142, -132722.210247684532078 ], [ -1633751.037622916745022, -133944.775503226846922 ], [ -1634210.280085011851043, -134056.944728825328639 ], [ -1636854.940838678507134, -134685.426204437302658 ], [ -1637488.064594889758155, -134832.855831101100193 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.7390377521514893, "refgtracks": "157|599|667|1109" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1521911.90200967551209, -142712.810300125420326 ], [ -1524476.5952760851942, -140038.700234339281451 ], [ -1524419.367923342855647, -139716.838130379735958 ], [ -1524362.2296016078908, -139702.314000843965914 ], [ -1518305.945893066935241, -138162.891970823984593 ], [ -1517791.746539522428066, -138032.2768555257353 ], [ -1517493.232594801113009, -138137.903831450210419 ], [ -1515946.758429827401415, -140599.169649714487605 ], [ -1516033.468092684168369, -141191.487032143137185 ], [ -1521911.90200967551209, -142712.810300125420326 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -2.7014813423156738, "refgtracks": "340|469|782|911" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1610597.029148116474971, -232080.862138869444607 ], [ -1610715.185882614925504, -232080.26662434564787 ], [ -1610892.414530312642455, -232078.663202528259717 ], [ -1611365.023924177512527, -232074.003786800487433 ], [ -1611424.10030210018158, -232073.401214991055895 ], [ -1611837.635146425105631, -232069.068810400902294 ], [ -1613373.654366781469434, -232052.839675628958503 ], [ -1615736.656091179698706, -232006.115745846007485 ], [ -1615857.08354704012163, -231824.623498964763712 ], [ -1615292.338603720534593, -228671.084887726901798 ], [ -1613603.310974647523835, -228092.594237608922413 ], [ -1613307.973006063140929, -228084.682050677656662 ], [ -1612066.879218011861667, -228092.41744407711667 ], [ -1611830.683007831918076, -228104.262529796804301 ], [ -1611653.80893455655314, -228120.854851163894637 ], [ -1609857.222432919312268, -228733.061433587077772 ], [ -1609604.481035915901884, -229297.130289043328958 ], [ -1609698.224789162166417, -229942.84226974734338 ], [ -1610597.029148116474971, -232080.862138869444607 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pine_Island", "maxabsdhdt": -1.713111400604248, "refgtracks": "35|667|1109" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1658127.151247223839164, -132099.168185002257815 ], [ -1660548.480188283370808, -130847.798174200201174 ], [ -1660582.367811453994364, -130041.327662052528467 ], [ -1660438.586493971291929, -129219.274725425115321 ], [ -1659402.25720794708468, -128976.921719279125682 ], [ -1659344.678054951829836, -128963.48348428303143 ], [ -1656177.887382141314447, -128224.6217667197634 ], [ -1656120.311070984927937, -128211.189717679881142 ], [ -1655314.245666662696749, -128023.164467179754865 ], [ -1655026.368251239648089, -127956.014490492307232 ], [ -1653329.276235342957079, -130574.352695390873123 ], [ -1653529.199782592942938, -131320.974728625820717 ], [ -1658127.151247223839164, -132099.168185002257815 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Thwaites", "maxabsdhdt": -17.611978530883789, "refgtracks": "188|256|1201" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1494853.399552725953981, -479843.656789882923476 ], [ -1495772.744640106568113, -476579.909046688931994 ], [ -1495720.220334192039445, -476552.79553949367255 ], [ -1492927.378349698148668, -475145.53737467864994 ], [ -1492769.133238025475293, -475066.296195503906347 ], [ -1487742.449248478049412, -476255.486923240765464 ], [ -1487577.156373742967844, -476799.279226551123429 ], [ -1487485.895569149637595, -477123.746095893671736 ], [ -1494853.399552725953981, -479843.656789882923476 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Thwaites", "maxabsdhdt": -12.120030403137207, "refgtracks": "249|378|820|1194" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1505664.712565046735108, -517266.853984515008051 ], [ -1511358.824522499926388, -516487.498477589979302 ], [ -1512077.927302259020507, -516340.859958884946536 ], [ -1511175.689618434524164, -513072.503179188875947 ], [ -1501534.036313213873655, -507075.295992689672858 ], [ -1501481.552151008276269, -507047.894034203374758 ], [ -1501272.265372762456536, -506938.630860901379492 ], [ -1501219.945095091825351, -506911.31623165513156 ], [ -1501010.658403595676646, -506802.056824714876711 ], [ -1500749.049584468826652, -506665.48557369154878 ], [ -1500696.729115108260885, -506638.172014823881909 ], [ -1500592.085380526026711, -506583.543930856860243 ], [ -1500173.515333043411374, -506365.037786566710565 ], [ -1499807.267126750666648, -506173.850966142548714 ], [ -1499545.664522972423583, -506037.290710274595767 ], [ -1499493.342866086168215, -506009.97849380003754 ], [ -1499231.739318996900693, -505873.421753885399085 ], [ -1498970.137041203444824, -505736.866144183615688 ], [ -1498813.176308690337464, -505654.935907527571544 ], [ -1498603.893360598245636, -505545.695744666212704 ], [ -1498342.291392548009753, -505409.148511236649938 ], [ -1498185.33103423495777, -505327.221006385749206 ], [ -1497819.090877991169691, -505136.060633315355517 ], [ -1497609.810842862585559, -505026.828523578296881 ], [ -1497243.570911774411798, -504835.678955877374392 ], [ -1497191.25079900585115, -504808.372523660131264 ], [ -1497034.289406235795468, -504726.456810958101414 ], [ -1496929.422379575436935, -504672.282577137229964 ], [ -1490576.729678685311228, -505110.719396724074613 ], [ -1490410.423244279343635, -505451.644896753598005 ], [ -1490236.156339515000582, -506032.408559898787644 ], [ -1493555.50629604421556, -510873.083777116786223 ], [ -1501052.828232505591586, -515172.660445102083031 ], [ -1505664.712565046735108, -517266.853984515008051 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Thwaites", "maxabsdhdt": -4.8131070137023926, "refgtracks": "386|515|1331" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1551913.12090796395205, -410245.803564294066746 ], [ -1552863.695193527266383, -409221.387186742853373 ], [ -1552789.290073899552226, -408622.678009733906947 ], [ -1552554.563826453639194, -406961.848883444035891 ], [ -1549559.685298802098259, -406648.461470278620254 ], [ -1548620.135585769778118, -406550.318345275474712 ], [ -1548240.014602469280362, -406647.717525884567294 ], [ -1547997.071332973660901, -407086.183571630390361 ], [ -1548740.055442812852561, -409931.022262359620072 ], [ -1550090.086461733095348, -410078.760882048343774 ], [ -1550324.984873708570376, -410103.437980468966998 ], [ -1550442.479781599948183, -410115.77645285759354 ], [ -1551617.061131148831919, -410238.066417505615391 ], [ -1551913.12090796395205, -410245.803564294066746 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Thwaites", "maxabsdhdt": 1.4683805704116821, "refgtracks": "180|295|737|1125" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1278852.736539892153814, -565249.886584544787183 ], [ -1279101.329934830078855, -565031.921909292461351 ], [ -1279649.940269351471215, -561919.040130792767741 ], [ -1278920.926457392517477, -561315.798541847267188 ], [ -1278441.106908977031708, -560975.258844358031638 ], [ -1278392.091538913315162, -560942.763711050036363 ], [ -1277509.376151580363512, -560358.210814547259361 ], [ -1277156.339824754046276, -560146.076136633753777 ], [ -1277104.876829985529184, -560117.276938070193864 ], [ -1276953.777210405096412, -560132.342402611277066 ], [ -1274945.993747564265504, -562252.964480529190041 ], [ -1274745.102006746921688, -562506.43526458600536 ], [ -1274831.687112199608237, -562588.663081768434495 ], [ -1275562.7858756640926, -563188.545418558758684 ], [ -1275709.272025689482689, -563286.841839405125938 ], [ -1276199.37695215176791, -563611.76230198063422 ], [ -1278454.951824440388009, -565104.893999019870535 ], [ -1278504.235441609518602, -565136.980018372880295 ], [ -1278612.056731330463663, -565187.199536578147672 ], [ -1278852.736539892153814, -565249.886584544787183 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Whillans", "maxabsdhdt": -1.5459640026092529, "refgtracks": "8|319|380|511|822|953|1264|1325" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -173425.448995316983201, -704971.265519167296588 ], [ -174836.759941077762051, -701003.778422124800272 ], [ -173924.523354241915513, -699512.888619155157357 ], [ -173893.730023890937446, -699463.432980320416391 ], [ -173604.998762358474778, -699025.546519577270374 ], [ -169481.304297338618198, -698275.418661123258062 ], [ -168414.071613626991166, -702468.659513061167672 ], [ -168803.011962275777478, -704572.669575337204151 ], [ -173425.448995316983201, -704971.265519167296588 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Whillans", "maxabsdhdt": -738.05035400390625, "refgtracks": "59|120|236|358|501|562|617|623|678|739|943|1004|1065|1120|1181|1242|1303|1385" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -319203.250313648837619, -526332.863648097147234 ], [ -319932.976213441928849, -526112.582485928200185 ], [ -331364.99835166905541, -520629.066490929806605 ], [ -332600.636760383145884, -519899.319652181002311 ], [ -332655.643477037490811, -518521.86033605854027 ], [ -332008.000130009429995, -516930.973045838880353 ], [ -330540.941882656363305, -516698.597038761596195 ], [ -312481.590946816664655, -514120.477462575829122 ], [ -309189.228406632842962, -514598.133719043398742 ], [ -308335.893489738286007, -514892.233876821177546 ], [ -308301.202752396115102, -515222.119401093339548 ], [ -308297.782170095480978, -516160.29530586686451 ], [ -308521.583293999079615, -518240.595684643252753 ], [ -310092.447440782736521, -522659.238141075591557 ], [ -311002.097639971179888, -524889.370051540434361 ], [ -317926.890809933596756, -526292.173938009538688 ], [ -319203.250313648837619, -526332.863648097147234 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Whillans", "maxabsdhdt": -3.0844080448150635, "refgtracks": "144|211|586|647|653|1089|1156|1217" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -454241.232024480646942, -491646.899442960391752 ], [ -461066.08371042314684, -490063.413961189391557 ], [ -460954.038765448145568, -486756.953131094807759 ], [ -460763.903839602251537, -486202.321898187452462 ], [ -458094.012328863958828, -482137.426810641714837 ], [ -457201.918952286068816, -481168.418926076032221 ], [ -457150.047539286955725, -481141.956694387306925 ], [ -457098.17551000189269, -481115.494598894088995 ], [ -456895.142721535405144, -481319.87433944165241 ], [ -455550.993691681651399, -484020.118828062957618 ], [ -453447.451099037658423, -488726.264307751029264 ], [ -453374.646560736873653, -489674.490552112984005 ], [ -453819.443663986166939, -490687.434650634240825 ], [ -454241.232024480646942, -491646.899442960391752 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Whillans", "maxabsdhdt": 11.06084156036377, "refgtracks": "135|196|327|388|577|638|1019|1080|1211|1272|1333" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -447444.859319593932014, -543751.358024253859185 ], [ -448596.013364973361604, -543481.823068320401944 ], [ -452718.661680236225948, -541299.737476670998149 ], [ -454260.128439468913712, -540449.187336765462533 ], [ -455385.897853999340441, -539099.835781286121346 ], [ -456175.166038003284484, -537757.023944976157509 ], [ -455989.230038218549453, -537346.939406191930175 ], [ -454018.686570251127705, -535694.306835590396076 ], [ -447856.957484721206129, -532335.885182601516135 ], [ -446776.839913370960858, -532023.575358333415352 ], [ -442791.153893358947244, -531687.133431011810899 ], [ -441882.130989352532197, -532151.93253182223998 ], [ -441614.190703744709026, -532362.217025525518693 ], [ -441380.160456000245176, -532591.813227049424313 ], [ -440329.322274583333638, -535444.67197540262714 ], [ -439962.21699974546209, -540248.852302643703297 ], [ -440883.513632582791615, -542823.187106482102536 ], [ -441216.367648815328721, -543635.62285137490835 ], [ -447444.859319593932014, -543751.358024253859185 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Whillans", "maxabsdhdt": 3.1939480304718018, "refgtracks": "166|526|587|669|730|968|1029|1111" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -228925.456220139109064, -544557.174555568257347 ], [ -230541.817450783768436, -543841.724848686717451 ], [ -231377.361278726632008, -543233.966928113019094 ], [ -235341.738161968562054, -539921.605886705336161 ], [ -235423.866858208086342, -539582.259459355846047 ], [ -235418.937575855758041, -539350.019232225953601 ], [ -235417.689747406548122, -539291.93269750114996 ], [ -235415.134895204479108, -539175.602474577957764 ], [ -235405.89249092162936, -538767.889889841433614 ], [ -235400.378953259001719, -538535.575361006660387 ], [ -235398.871931838832097, -538476.874838236137293 ], [ -235373.505400005553383, -537837.426246936898679 ], [ -235363.554809184191981, -537603.956723228911869 ], [ -234512.514963809488108, -537212.196099485037848 ], [ -233101.632426172291161, -537718.151077380636707 ], [ -230459.97914124262752, -539738.916052585351281 ], [ -228050.970774445886491, -541980.418444229988381 ], [ -228051.310550849419087, -542038.230647978256457 ], [ -228051.680754232860636, -542096.110806273412891 ], [ -228052.115534466487588, -542154.141896282089874 ], [ -228059.039264394290512, -542968.634474814054556 ], [ -228060.167238869616995, -543085.320973401190713 ], [ -228925.456220139109064, -544557.174555568257347 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Whillans", "maxabsdhdt": 3.3122162818908691, "refgtracks": "8|197|639|700|892|953|1081|1142" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -177941.877039760584012, -667128.612169651431032 ], [ -178489.844949621125124, -666738.334166663116775 ], [ -179175.877789515710901, -663068.93797442514915 ], [ -178418.118349785974715, -661316.702365725534037 ], [ -177745.769750453619054, -660942.538252068334259 ], [ -174901.633913002675399, -661291.454190646181814 ], [ -173787.401142156537389, -665003.869309024885297 ], [ -173819.254363695305074, -665052.623838857980445 ], [ -177941.877039760584012, -667128.612169651431032 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Whillans", "maxabsdhdt": 3.9733824729919434, "refgtracks": "235|409|677|738|851" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -608058.307165366015397, -509778.309192343440372 ], [ -610712.383064085035585, -508124.125457767222542 ], [ -612011.43753413145896, -505094.407251767930575 ], [ -612524.972796579939313, -503805.25312266376568 ], [ -612261.521973846131004, -503420.405664364108816 ], [ -612228.54081574967131, -503372.334569767932408 ], [ -611370.602186147822067, -502122.788864691799972 ], [ -607811.411212772713043, -502993.388084454636555 ], [ -607567.088867020327598, -503099.201521953218617 ], [ -605722.332730850903317, -506001.602082711353432 ], [ -605644.023517245193943, -506132.656666346127167 ], [ -606283.770109509583563, -507107.51601883827243 ], [ -607276.694691440090537, -508617.699369395093527 ], [ -607309.025714951683767, -508666.214998766547069 ], [ -608058.307165366015397, -509778.309192343440372 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Whillans", "maxabsdhdt": 2.3888800144195557, "refgtracks": "250|753|927" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -660075.261043243925087, -452009.151564641913865 ], [ -663105.901863127714023, -450502.280686344311107 ], [ -663240.827466161572374, -450065.713039099762682 ], [ -661446.37503082701005, -447918.347376887220889 ], [ -661371.588680232060142, -447828.89067715627607 ], [ -660735.895457153324969, -447068.523789562459569 ], [ -660294.161087361862883, -447077.033753764000721 ], [ -658551.771090244059451, -448907.339677289302927 ], [ -658113.158973978017457, -449621.862774619308766 ], [ -658187.14605613972526, -449711.991337022569496 ], [ -660075.261043243925087, -452009.151564641913865 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kamb", "maxabsdhdt": -4.0030517578125, "refgtracks": "212|404|654|846|1096" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -256013.245457755372627, -659026.260914364131168 ], [ -257950.636468547745608, -658763.75849586178083 ], [ -257953.086280387622537, -658367.775009075412527 ], [ -257159.013707974343561, -657020.787868990912102 ], [ -255505.893021297088126, -654274.006599214510061 ], [ -254425.76177395353443, -653687.961897798813879 ], [ -254024.356187908910215, -654073.342773665208369 ], [ -256013.245457755372627, -659026.260914364131168 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Scott", "maxabsdhdt": -5.7105302810668945, "refgtracks": "176|237|592|618|653|679|714|740|1060|1095|1121|1156|1217" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -153660.220414422947215, -326556.324170797248371 ], [ -154417.943846462847432, -326303.573774426826276 ], [ -155192.874504762614379, -324815.321636431966908 ], [ -154788.403048319567461, -323213.468602902488783 ], [ -153860.610057436773786, -322263.400925477792043 ], [ -151153.863452713791048, -322584.773387536464725 ], [ -150324.143917652661912, -324482.641760236234404 ], [ -150380.392814324790379, -325213.236513396492228 ], [ -150410.001245940511581, -325313.214000405976549 ], [ -150518.469000355922617, -325598.110556734318379 ], [ -151758.262946274800925, -326076.775576497137081 ], [ -153660.220414422947215, -326556.324170797248371 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Scott", "maxabsdhdt": 53.977787017822266, "refgtracks": "84|145|206|379|587|648|882|943|1090|1151|1324|1385" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -218782.217780112434411, -432535.850458922679536 ], [ -223907.190518795774551, -431416.08787114842562 ], [ -224632.683222215739079, -431156.949530339625198 ], [ -224965.575278453412466, -429757.619302649400197 ], [ -224969.091520864807535, -429276.137397329090163 ], [ -224968.604630005778745, -429217.814815701800399 ], [ -224967.03869829888572, -429042.639959835621994 ], [ -224833.6742442616669, -428049.86351631040452 ], [ -224242.094440402288456, -427178.473661964235362 ], [ -221838.152145098225446, -425839.792673537682276 ], [ -221630.345730035740416, -425731.670132234983612 ], [ -220305.986188954906538, -425944.529022090719081 ], [ -217790.784358474978944, -428323.32363169657765 ], [ -217212.566928223532159, -429957.842624375189189 ], [ -217209.361172488104785, -430189.286726897524204 ], [ -217206.67828858710709, -430481.205420649552252 ], [ -218033.730030998704024, -431985.170698095054831 ], [ -218782.217780112434411, -432535.850458922679536 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Scott", "maxabsdhdt": 3.9863743782043457, "refgtracks": "195|207|268|317|329|378|390|637|649|710|771|881|1079|1140|1152|1201|1384" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -123369.637169240930234, -249053.949461755779339 ], [ -126930.419358763872879, -248843.958458527165931 ], [ -128288.700546784413746, -248091.13373753419728 ], [ -128248.50369943487749, -245868.998702527664136 ], [ -127307.997412859243923, -244792.565982573723886 ], [ -124723.233999854521244, -242846.115021399047691 ], [ -121602.273450694803614, -245604.90137323513045 ], [ -121384.17286563309608, -246774.14041476053535 ], [ -122644.716366147054941, -248475.491809918981744 ], [ -122729.069664009817643, -248557.97263168025529 ], [ -123369.637169240930234, -249053.949461755779339 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Scott", "maxabsdhdt": 21.281949996948242, "refgtracks": "211|282|653|714|785|1095|1156|1166" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -249142.652051287412178, -374404.245214081718586 ], [ -249583.662878210248891, -374210.749040840892121 ], [ -250524.897690947342198, -373012.256768495659344 ], [ -251634.69976964010857, -371574.497837091388647 ], [ -247225.662320448522223, -369340.838039296097122 ], [ -246653.228335869382136, -369054.068724954209756 ], [ -245195.930513343628263, -370280.620011667837389 ], [ -245068.966069072252139, -371929.4919285402284 ], [ -246331.533599292539293, -373155.901355965354014 ], [ -247402.262423711625161, -373741.552514902548864 ], [ -247453.50806988007389, -373768.501549514650833 ], [ -249142.652051287412178, -374404.245214081718586 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Amundsen", "maxabsdhdt": -12.916450500488281, "refgtracks": "59|120|130|191|252|501|562|623|633|694|943|1065|1075|1136|1197|1385" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -175184.317132998694433, -413570.136486073082779 ], [ -176613.710688518680399, -412815.063074785750359 ], [ -178255.579984985524788, -411809.992807450646069 ], [ -179128.290026389237028, -407672.06335256312741 ], [ -178886.42241570117767, -407183.732909971033223 ], [ -178175.999616487591993, -405761.206111980194692 ], [ -176932.838184878200991, -405188.714525194605812 ], [ -175939.823065942007815, -405007.404626738571096 ], [ -174911.030353734298842, -405459.896051473042462 ], [ -171433.568154798354954, -407064.495032747625373 ], [ -171208.395475738361711, -407317.141697214217857 ], [ -170125.616354141122429, -409915.618161785940174 ], [ -170973.33897342873388, -411224.794121559360065 ], [ -174045.100908172666095, -413319.179426312621217 ], [ -175184.317132998694433, -413570.136486073082779 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Beardmore", "maxabsdhdt": 4.3627238273620605, "refgtracks": "30|238|299|472|533|741|914|975|1183|1244" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 79854.457041855057469, -473108.267392535344698 ], [ 78105.769151544984197, -472195.867088187485933 ], [ 77149.174112301217974, -470790.353293628781103 ], [ 77468.502460025061737, -467953.81113563006511 ], [ 81984.901726084543043, -469306.076685234671459 ], [ 82055.30451148620341, -469398.853771076886915 ], [ 82477.247929362245486, -469955.314915158145595 ], [ 82593.847896234088694, -470818.099208395113237 ], [ 79854.457041855057469, -473108.267392535344698 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Nimrod", "maxabsdhdt": -1.645607590675354, "refgtracks": "117|178|504|559|565|620|1062|1123" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 388799.6348326287698, -497843.164472184842452 ], [ 386162.232884049415588, -496241.828707962471526 ], [ 385457.030284702253994, -495095.929940760310274 ], [ 386077.990975906723179, -491633.523072792449966 ], [ 387665.246890055772383, -487883.235622993670404 ], [ 390405.572425628488418, -487026.429554115515202 ], [ 393234.175152550567873, -491160.782026133674663 ], [ 394216.396293113357387, -496622.654478268232197 ], [ 393778.915946570341475, -497252.604727072757669 ], [ 388799.6348326287698, -497843.164472184842452 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Nimrod", "maxabsdhdt": -1.5326927900314331, "refgtracks": "565|1068|1184|1245" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 389739.690599755849689, -474616.229250792122912 ], [ 386949.652627995703369, -470837.96280311740702 ], [ 386567.320914650859777, -469280.067736102209892 ], [ 387370.305139963107649, -467491.890225822338834 ], [ 388896.010975005687214, -466180.010752017318737 ], [ 390372.830760951561388, -466128.079271633992903 ], [ 392209.125934969983064, -471651.392831610515714 ], [ 391227.41890135128051, -473009.579251373652369 ], [ 389739.690599755849689, -474616.229250792122912 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Byrd", "maxabsdhdt": -4.6837091445922852, "refgtracks": "223|565|665|726|1068|1168" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 538581.944838198833168, -921567.505414488725364 ], [ 536760.952696302207187, -920688.366411768598482 ], [ 536563.367907996056601, -920472.731389726977795 ], [ 536523.851630953256972, -920429.604907582630403 ], [ 536405.303758734604344, -920300.22267476154957 ], [ 536326.271745153469965, -920213.967563546495512 ], [ 536010.508043531212024, -919868.619150026235729 ], [ 535707.347354510216974, -917467.393891801126301 ], [ 535068.389375708415173, -910592.61805603641551 ], [ 537557.75975444202777, -910520.390879956190474 ], [ 540131.678527772775851, -910496.681997559498996 ], [ 541528.032205015537329, -911993.615206790622324 ], [ 541844.279496739502065, -915081.488814465352334 ], [ 541593.786270410753787, -916891.925507673644461 ], [ 541023.945144355879165, -918367.39057420170866 ], [ 540755.557529148529284, -919053.124174808734097 ], [ 540063.90417161618825, -920079.286642420222051 ], [ 538581.944838198833168, -921567.505414488725364 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Byrd", "maxabsdhdt": -3.4910242557525635, "refgtracks": "77|421|580|863|1022|1366" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 416249.078474023728631, -871429.187408667057753 ], [ 414233.488833680632524, -871141.664992841891944 ], [ 412723.844998174696229, -869489.785831377026625 ], [ 412449.651333381829318, -869185.714599877712317 ], [ 412202.375695818511304, -868859.672217255807482 ], [ 413517.028602957900148, -867650.938392578158528 ], [ 415403.372033662511967, -866038.545314726419747 ], [ 415478.430567744362634, -866128.091811576159671 ], [ 416036.468870526994579, -866803.734768548747525 ], [ 416891.763400738476776, -867840.04255173355341 ], [ 416966.072761845774949, -867930.210431607207283 ], [ 416249.078474023728631, -871429.187408667057753 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Byrd", "maxabsdhdt": 3.4174818992614746, "refgtracks": "25|86|528|809|970|1251" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 559375.242188617121428, -854340.628813279094175 ], [ 559332.957509238738567, -854300.253324736957438 ], [ 559163.821510852430947, -854138.749030547798611 ], [ 558825.553120541269891, -853815.73946565343067 ], [ 558783.269866948248819, -853775.363315760158002 ], [ 558740.987187461927533, -853734.987008754629642 ], [ 558698.704332954366691, -853694.610516467597336 ], [ 558360.443543013650924, -853371.598705052165315 ], [ 558275.879400485893711, -853290.845729728811421 ], [ 557937.627288656309247, -852967.829941447358578 ], [ 557726.221897178911604, -852765.945115591632202 ], [ 557514.817434087512083, -852564.060924836085178 ], [ 557345.69734314607922, -852402.551159286289476 ], [ 557218.858212448540144, -852281.418907711049542 ], [ 556965.182177220354788, -852039.153040827950463 ], [ 556796.065667402930558, -851877.642395479837433 ], [ 556669.229224809678271, -851756.509432777180336 ], [ 556373.279113260563463, -851473.866343691479415 ], [ 556331.001264656311832, -851433.488232016563416 ], [ 556288.723787176189944, -851393.110283551854081 ], [ 556119.613757063169032, -851231.596982581540942 ], [ 556035.059312829049304, -851150.840768505353481 ], [ 555865.950997511274181, -850989.327240025857463 ], [ 555570.013410335290246, -850706.680092422291636 ], [ 555400.910057317931205, -850545.164159814594314 ], [ 553565.560008544824086, -845835.192852776264772 ], [ 553406.506031812517904, -844085.42620446102228 ], [ 554497.378245418542065, -840574.288570507895201 ], [ 555715.095529514714144, -837541.145494833122939 ], [ 557731.706340512027964, -837493.389627971220762 ], [ 562912.009588610148057, -842393.443936034338549 ], [ 562954.462053537252359, -842433.621730941114947 ], [ 563628.114924581488594, -845835.617200952488929 ], [ 563387.537745028617792, -848580.45443126547616 ], [ 561685.282119401497766, -853405.255145650473423 ], [ 561592.91629514109809, -853594.51140146900434 ], [ 559375.242188617121428, -854340.628813279094175 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Byrd", "maxabsdhdt": 4.8117752075195312, "refgtracks": "25|467|909|970|1251" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 565543.279041482601315, -868310.387711434508674 ], [ 561925.949101343983784, -866521.348993200925179 ], [ 561883.50844377069734, -866481.122625176678412 ], [ 561799.664161546854302, -866399.613859077217057 ], [ 561757.914080333663151, -866358.683427796000615 ], [ 561674.754152348148637, -866276.446902881143615 ], [ 559680.354874795302749, -860888.861491103889421 ], [ 558069.761057862313464, -856454.24190786993131 ], [ 558678.480021108291112, -855355.878318967181258 ], [ 559755.803787845303304, -854704.011877898708917 ], [ 562507.833660601638258, -855594.067932686652057 ], [ 567461.49346052040346, -865580.674852486466989 ], [ 567033.15273466182407, -867633.438253988395445 ], [ 566799.7870768281864, -867741.989682591403835 ], [ 565543.279041482601315, -868310.387711434508674 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "MacAyeal", "maxabsdhdt": -25.67308235168457, "refgtracks": "105|547|1089" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -628615.623317579273134, -897945.264747006236576 ], [ -631237.410053692990914, -897260.006905040470883 ], [ -631663.141172334435396, -896659.71765007392969 ], [ -631725.306562267476693, -895606.013992037042044 ], [ -630207.587657121592201, -893208.543929040315561 ], [ -629095.798085584188811, -893191.220673791365698 ], [ -627584.520547974389046, -893883.506467378232628 ], [ -627159.880041368422098, -894494.489499321905896 ], [ -628615.623317579273134, -897945.264747006236576 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "MacAyeal", "maxabsdhdt": -53.956638336181641, "refgtracks": "105|205|608|647|1050|1089|1150" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -619929.320375035516918, -906379.581235079094768 ], [ -621555.27621163148433, -906045.82961705327034 ], [ -627409.37815859366674, -903598.426438298309222 ], [ -629388.894054752890952, -899778.615842275787145 ], [ -629139.174851907999255, -899185.274208479211666 ], [ -628638.366763445083052, -897999.185922534088604 ], [ -624698.019117666059174, -895710.685452216421254 ], [ -624089.408672295394354, -895726.368317493703216 ], [ -618365.85792450059671, -902529.648661462008022 ], [ -618317.204825869179331, -902996.099542632699013 ], [ -619929.320375035516918, -906379.581235079094768 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "MacAyeal", "maxabsdhdt": -18.000175476074219, "refgtracks": "120|159|181|601|623|1065" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -735360.348930094507523, -858225.776033590896986 ], [ -738110.857131791184656, -858178.87761564005632 ], [ -740393.447975986637175, -857769.442500394419767 ], [ -742449.929338458226994, -857335.613829323789105 ], [ -744074.703088689246215, -856412.765347949462011 ], [ -744166.477760322741233, -854316.405145317083225 ], [ -742303.303196138935164, -850908.533492187038064 ], [ -741884.561697477824055, -850588.677325109601952 ], [ -741837.967413687496446, -850553.228267272235826 ], [ -740579.748223667731509, -849596.329041928052902 ], [ -739787.450010778615251, -848993.981701558688655 ], [ -739321.284908690489829, -848639.811558155110106 ], [ -734741.745080953696743, -851416.116255864850245 ], [ -731982.872902658767998, -855606.919907531235367 ], [ -732029.137177999247797, -855642.795060246135108 ], [ -732075.401495094993152, -855678.670145816286094 ], [ -734943.922911335481331, -857902.910960393375717 ], [ -734990.192553133005276, -857938.785414621583186 ], [ -735175.270064850687049, -858082.280865628621541 ], [ -735221.539619736606255, -858118.154743309947662 ], [ -735314.078923545661382, -858189.902353504206985 ], [ -735360.348930094507523, -858225.776033590896986 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "MacAyeal", "maxabsdhdt": -6.3361377716064453, "refgtracks": "425|464|867|906|1309" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -655922.695684493635781, -871768.880872787325643 ], [ -658258.183145284885541, -869412.715169016853906 ], [ -660289.977856415091082, -865787.384975978522561 ], [ -659994.483193304156885, -865150.676664320519194 ], [ -659674.015882959589362, -864460.581839795224369 ], [ -659574.847561490954831, -864248.705096701509319 ], [ -657558.629024766501971, -864254.188434953102842 ], [ -655785.678556740051135, -865252.274175103404559 ], [ -652995.008495962712914, -869249.524292204994708 ], [ -653437.971284750034101, -869631.927912471117452 ], [ -653792.87221944576595, -869937.270898959832266 ], [ -654236.529411064926535, -870318.963885791599751 ], [ -654591.411550381570123, -870624.271249430254102 ], [ -654680.147531869472004, -870700.610088663874194 ], [ -655123.779394612647593, -871082.259589838213287 ], [ -655212.562667248188518, -871158.638088495819829 ], [ -655256.952384872827679, -871196.825426077470183 ], [ -655301.327650846797042, -871235.000239139655605 ], [ -655922.695684493635781, -871768.880872787325643 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Rutford", "maxabsdhdt": 2.9710028171539307, "refgtracks": "255|697|1254" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1399060.268675056053326, 121378.358033210795838 ], [ -1399686.027632920537144, 121542.717352984385798 ], [ -1402359.745994082884863, 122245.23940576169116 ], [ -1402530.379375356249511, 122290.211161520637688 ], [ -1402975.376574835507199, 123468.120473542105174 ], [ -1402026.266018890310079, 125587.306714234029641 ], [ -1401112.790316842496395, 125360.767836356491898 ], [ -1400998.919190439395607, 125331.265731211475213 ], [ -1399292.522454851539806, 124882.513681372918654 ], [ -1399235.650652213953435, 124867.52821034185763 ], [ -1397475.555499118287116, 124391.976149420414004 ], [ -1397406.796065261820331, 123930.164142778143287 ], [ -1399060.268675056053326, 121378.358033210795838 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Evans", "maxabsdhdt": -3.3522734642028809, "refgtracks": "263|773|1208|1215" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1478711.561535435961559, 284171.757321820710786 ], [ -1478770.467432636534795, 284173.412040728435386 ], [ -1481970.645509238122031, 287355.613656170258764 ], [ -1481622.56780643411912, 287580.933400674024597 ], [ -1480784.14978980855085, 287639.803119017567951 ], [ -1480725.253160736290738, 287637.890550610725768 ], [ -1480548.562896464485675, 287632.150489301653579 ], [ -1480371.873338310979307, 287626.41022471775068 ], [ -1480312.977156764827669, 287624.496389612206258 ], [ -1480018.496286027366295, 287614.926550543925259 ], [ -1479959.600207404932007, 287613.011825348250568 ], [ -1479841.808189120842144, 287609.182018512510695 ], [ -1479665.121119965566322, 287603.436811160878278 ], [ -1479547.330217487877235, 287599.606135863228701 ], [ -1479252.853499299380928, 287590.02910148887895 ], [ -1479193.958598347846419, 287588.111587503051851 ], [ -1479135.063796681119129, 287586.194039533671457 ], [ -1479017.27765427948907, 287582.260602164431475 ], [ -1478722.816406483761966, 287572.295910582703073 ], [ -1478487.252289226977155, 287564.256446063693147 ], [ -1478428.362457662587985, 287562.204984069103375 ], [ -1476379.48580459668301, 287433.443723583302926 ], [ -1477329.048314845887944, 285633.794723307713866 ], [ -1477362.175036948174238, 285576.469404482631944 ], [ -1478711.561535435961559, 284171.757321820710786 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Slessor", "maxabsdhdt": -3.9088091850280762, "refgtracks": "154|557|596|1060" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -395149.686615472426638, 1035281.288390449481085 ], [ -398113.785716775397304, 1035798.824689577682875 ], [ -399061.803226892778184, 1038208.040125444531441 ], [ -398312.760059496446047, 1040136.991044836351648 ], [ -396504.795603305276018, 1040345.359159857151099 ], [ -395554.94830906635616, 1040329.048183239763603 ], [ -394538.358806639851537, 1037295.236598266288638 ], [ -395149.686615472426638, 1035281.288390449481085 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Slessor", "maxabsdhdt": -17.871707916259766, "refgtracks": "230|252|313|672|733|755|1175|1197|1258" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -335984.294806113408413, 1103554.989595356397331 ], [ -337507.454373367363587, 1103894.407558786682785 ], [ -345155.872119679814205, 1110513.513741184491664 ], [ -347990.443903398874681, 1117258.816940808901563 ], [ -348602.216790468781255, 1122381.736503477208316 ], [ -347996.863666408578865, 1124139.045564540661871 ], [ -346820.139675283338875, 1126016.381587396841496 ], [ -346713.975461662164889, 1126078.33975737541914 ], [ -344706.2730630740989, 1126150.876612816238776 ], [ -330045.968696635216475, 1115330.779412004631013 ], [ -328076.109937056840863, 1113858.034153934568167 ], [ -326350.708277059195098, 1110948.684700657613575 ], [ -326340.545747042226139, 1110831.983880586456507 ], [ -326330.384839285397902, 1110715.283181390259415 ], [ -326274.971172192133963, 1110073.39184307679534 ], [ -327598.416939089889638, 1108137.197477021021768 ], [ -334201.77784121670993, 1104021.550785544328392 ], [ -335984.294806113408413, 1103554.989595356397331 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Slessor", "maxabsdhdt": -6.5392298698425293, "refgtracks": "93|535|557|999" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -390255.548821540141944, 1048944.115658686030656 ], [ -390287.231038200028706, 1048993.343290777644143 ], [ -390350.156850338273216, 1049092.074128956766799 ], [ -390632.322318902239203, 1049537.001926170429215 ], [ -391226.976401624153368, 1050476.960413271561265 ], [ -392916.391353032842744, 1053148.949953197967261 ], [ -394480.442694020224735, 1055623.306649772217497 ], [ -393489.701262477028649, 1057243.401637204457074 ], [ -393130.350232400989626, 1057534.149267993867397 ], [ -391910.465101703361142, 1057734.669516764348373 ], [ -391483.749213160539512, 1057590.200376528315246 ], [ -387842.31718164589256, 1051301.910560104763135 ], [ -390175.345511616615113, 1048966.175914548104629 ], [ -390255.548821540141944, 1048944.115658686030656 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Slessor", "maxabsdhdt": -1.9146382808685303, "refgtracks": "176|215|618|718|1060|1160" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -402322.70111731137149, 1002392.492408972117119 ], [ -402399.858768701611552, 1002450.665992531110533 ], [ -403576.051640858466271, 1004229.015877332305536 ], [ -403109.549013437295798, 1006212.525913197896443 ], [ -401719.144632298324723, 1006803.663287178962491 ], [ -400108.761418281530496, 1006838.40946922742296 ], [ -399127.081565428059548, 1006222.515636898577213 ], [ -398963.151893726142589, 1005980.194367168587632 ], [ -399601.247565444733482, 1003719.621858750004321 ], [ -400749.719179285515565, 1002722.968441024888307 ], [ -402322.70111731137149, 1002392.492408972117119 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Slessor", "maxabsdhdt": -6.2539949417114258, "refgtracks": "855|877|1297" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -356786.77382767497329, 1077212.621036415919662 ], [ -358981.317905295523815, 1077890.362466237042099 ], [ -359655.257741417677607, 1079056.261045321356505 ], [ -361412.491741408535745, 1082098.381859092507511 ], [ -361421.444545639969874, 1085153.185550522524863 ], [ -359650.371807949384674, 1085396.423238400835544 ], [ -359490.726266444718931, 1085372.601486747851595 ], [ -359461.00817770906724, 1085322.139093408361077 ], [ -358874.744753174192738, 1084308.234170211479068 ], [ -356502.056901955395006, 1080201.182632541982457 ], [ -356297.075454066623934, 1079846.232447955058888 ], [ -356267.793045623751823, 1079795.525755820097402 ], [ -355974.97223064256832, 1079288.452918905531988 ], [ -355945.690885885735042, 1079237.746204688213766 ], [ -355711.440779327182099, 1078832.088720730273053 ], [ -355682.16050549125066, 1078781.382028292166069 ], [ -355652.880724464659579, 1078730.674629754852504 ], [ -355623.600947396829724, 1078679.966991881141439 ], [ -356786.77382767497329, 1077212.621036415919662 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Slessor", "maxabsdhdt": 2.9502627849578857, "refgtracks": "17|420|520|862|962|1365" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -460937.961875883920584, 978182.301593366661109 ], [ -462860.104323213046882, 978418.626678132335655 ], [ -464315.657341815647669, 981741.648266940494068 ], [ -462358.154810749110766, 984279.543210260337219 ], [ -459287.378126640338451, 984308.952210461371578 ], [ -457315.700213636446279, 980521.70153663912788 ], [ -458472.783658236789051, 979009.557406884501688 ], [ -460937.961875883920584, 978182.301593366661109 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Slessor", "maxabsdhdt": 27.572257995605469, "refgtracks": "176|215|618|657|679|1060|1099|1121|1160" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -402154.506114172399975, 1019431.009714087587781 ], [ -403759.911955751304049, 1019534.251287216204219 ], [ -415882.726978586521, 1023254.680964932078496 ], [ -415903.542685901164077, 1023369.867526530637406 ], [ -415945.170393592503387, 1023600.241873811814003 ], [ -415955.576513808860909, 1023657.835788040538318 ], [ -415976.381852759979665, 1023773.024996528052725 ], [ -416808.155012791277841, 1028380.832478052121587 ], [ -416849.271969719382469, 1028611.316449318197556 ], [ -412804.117674253124278, 1033972.118850377388299 ], [ -411273.101937574218027, 1034653.775210544117726 ], [ -401301.199259415618144, 1034808.316462433547713 ], [ -398909.833012722840067, 1026080.283319240785204 ], [ -399099.246750512975268, 1020352.648076303652488 ], [ -400460.414485717366915, 1019847.728463266626932 ], [ -402154.506114172399975, 1019431.009714087587781 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Support_Force", "maxabsdhdt": -3.235694408416748, "refgtracks": "79|521|878|1381" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -623671.982028229977004, 594015.004636397119612 ], [ -623723.383391363895498, 594042.60127761750482 ], [ -623774.78277002624236, 594070.200364682474174 ], [ -623928.281746758846566, 594154.279790071770549 ], [ -626971.748915262869559, 596953.069191580405459 ], [ -629557.033836700371467, 601002.994453170569614 ], [ -629523.18222174490802, 601138.085809415904805 ], [ -628902.94314195564948, 601200.716177224414423 ], [ -625520.632635317393579, 600910.984082208480686 ], [ -623927.677514946903102, 600167.746000311337411 ], [ -620630.651796558988281, 596629.17441313364543 ], [ -619885.569998957100324, 595705.439261946128681 ], [ -621542.915116281365044, 594384.624065546202473 ], [ -623671.982028229977004, 594015.004636397119612 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Lambert", "maxabsdhdt": 2.7719552516937256, "refgtracks": "340|592|782|1034" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 1597030.738175747450441, 599990.008433922892436 ], [ 1596409.914590489119291, 600768.93894487596117 ], [ 1596257.325609351042658, 601274.830578736262396 ], [ 1600058.221328132320195, 606619.961621637106873 ], [ 1602280.579240702791139, 607860.50307766860351 ], [ 1605330.426519019296393, 609562.100598731311038 ], [ 1606519.429789365967736, 610225.44303808233235 ], [ 1607398.331276375800371, 610715.662664420087822 ], [ 1610731.48988505708985, 608900.887407970498316 ], [ 1610832.181919304654002, 608816.373694312525913 ], [ 1610940.445022573927417, 607863.372121859807521 ], [ 1609850.838621966307983, 604217.999666968011297 ], [ 1597030.738175747450441, 599990.008433922892436 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Mellor", "maxabsdhdt": -6.0150566101074219, "refgtracks": "584|836|1026|1278" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 1565762.323008445091546, 688145.170644318219274 ], [ 1565138.439707030775025, 688242.823860800825059 ], [ 1564748.726648938609287, 688708.797002662206069 ], [ 1563458.921476184157655, 691236.502880102721974 ], [ 1563370.346840881276876, 691777.565875057480298 ], [ 1563393.96104507218115, 692255.312875528354198 ], [ 1570426.315180971752852, 696234.750972038367763 ], [ 1571831.832054357277229, 694605.208779618376866 ], [ 1572535.704788776813075, 692383.637303743511438 ], [ 1571189.418244231957942, 689899.283322625793517 ], [ 1565762.323008445091546, 688145.170644318219274 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Institute", "maxabsdhdt": -3.6519312858581543, "refgtracks": "187|422|483|629|925|1071|1367" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -814428.074677266995423, 195151.443029646703508 ], [ -814479.201799924485385, 195179.515932285168674 ], [ -814837.080058063846081, 195376.034228664269904 ], [ -815246.083059619762935, 195600.632468258176232 ], [ -815655.088363686576486, 195825.232820714445552 ], [ -816064.090032566222362, 196049.843274718441535 ], [ -816677.589808332966641, 196386.771971680806018 ], [ -816728.714501466834918, 196414.850077520764899 ], [ -816294.508649886818603, 197725.295421450427966 ], [ -814802.18576056137681, 199558.680538561631693 ], [ -812954.144344640080817, 199331.064290938462364 ], [ -810231.332553186221048, 197900.340098601358477 ], [ -810450.354146194527857, 197491.141903910931433 ], [ -810972.542185043101199, 197129.800819533178583 ], [ -814428.074677266995423, 195151.443029646703508 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Institute", "maxabsdhdt": -4.0540738105773926, "refgtracks": "233|346|407|675|736|849|1178|1291" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -786560.666094548767433, 103513.263454408952384 ], [ -786772.082766897394322, 103627.326190556763322 ], [ -788890.195255104335956, 106629.52226058319502 ], [ -788903.950157667743042, 106966.246076969022397 ], [ -788712.595221074065194, 107423.854249780619284 ], [ -786083.337721010670066, 109682.043785728819785 ], [ -785365.044564956566319, 109665.7532794924482 ], [ -781227.30634933989495, 107817.889549665618688 ], [ -781168.561363519984297, 107766.931703209120315 ], [ -779455.636911745648831, 104762.418773466270068 ], [ -780368.680822582216933, 103659.066340813616989 ], [ -780928.049542274791747, 103637.17205022800772 ], [ -786560.666094548767433, 103513.263454408952384 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Institute", "maxabsdhdt": 2.0852985382080078, "refgtracks": "361|803|949|1245" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -827328.285323885385878, 224720.586920740257483 ], [ -827830.705991975381039, 225017.267941599915503 ], [ -828031.575997759238817, 225136.104998867725953 ], [ -827992.971283771912567, 226427.418131930055097 ], [ -827817.452749162446707, 228319.732681217166828 ], [ -827757.221446536364965, 228807.832181025267346 ], [ -827246.861027055187151, 229029.241692157112993 ], [ -824842.495810106629506, 227593.034612652292708 ], [ -823890.931081220973283, 227024.309868254349567 ], [ -823790.772932266118005, 226964.437479950895067 ], [ -823590.467358481488191, 226844.670150245219702 ], [ -823589.374650339712389, 225842.376487249712227 ], [ -827328.285323885385878, 224720.586920740257483 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pope", "maxabsdhdt": -3.2067925930023193, "refgtracks": "51|493|1064" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1474421.585006696172059, -596322.5331730308244 ], [ -1477398.003202657448128, -595708.236351723899134 ], [ -1477869.129887134535238, -595444.043437979766168 ], [ -1478911.397206475259736, -593939.523354871198535 ], [ -1478554.264165889937431, -591977.142315513105132 ], [ -1478503.500807459931821, -591947.009035172988661 ], [ -1478249.682778001530096, -591796.346989660989493 ], [ -1478198.919287485769019, -591766.214824817958288 ], [ -1477945.10194541933015, -591615.555851840181276 ], [ -1477742.048620723420754, -591495.030391633277759 ], [ -1477488.232218267163262, -591344.37619471445214 ], [ -1477234.416440842906013, -591193.724417389254086 ], [ -1476777.549605181440711, -590922.557437645504251 ], [ -1476726.786596776684746, -590892.428494508378208 ], [ -1472386.866862194146961, -592190.82911020249594 ], [ -1472238.917700944002718, -592399.49647115287371 ], [ -1472131.860070683062077, -592552.132908535306342 ], [ -1473559.486101359827444, -596120.035817681578919 ], [ -1474073.121150244493037, -596257.179546523606405 ], [ -1474421.585006696172059, -596322.5331730308244 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pope", "maxabsdhdt": -4.3450984954833984, "refgtracks": "51|493|622|1064" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1486665.270296661648899, -601263.944511945359409 ], [ -1487360.734869595151395, -601082.18785273632966 ], [ -1489231.094068338628858, -600278.800235416390933 ], [ -1489437.489457780029625, -599790.907643536804244 ], [ -1489368.384207293158397, -598396.146684106672183 ], [ -1489317.616215476067737, -598365.991356502636336 ], [ -1489266.848294367315248, -598335.836106710368767 ], [ -1487794.588213194394484, -597461.377679960336536 ], [ -1487185.381512674270198, -597099.559636822436005 ], [ -1487083.847863517235965, -597039.257466046139598 ], [ -1486880.780380234355107, -596918.654881234746426 ], [ -1486271.073104610899463, -596557.710291307070293 ], [ -1486118.520453470991924, -596467.689523042994551 ], [ -1481625.282295546494424, -597674.398362028296106 ], [ -1481449.39797361777164, -598136.170182229718193 ], [ -1482006.109759272541851, -598470.671743032988161 ], [ -1482056.742446554126218, -598501.044160701218061 ], [ -1482816.257998541695997, -598956.608601875486784 ], [ -1486563.736529626185074, -601203.621802221401595 ], [ -1486665.270296661648899, -601263.944511945359409 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pope", "maxabsdhdt": -4.4665088653564453, "refgtracks": "180|554|622|996" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1462871.790909743169323, -603528.511456081178039 ], [ -1465264.254826518939808, -601910.340128136333078 ], [ -1465227.75953486864455, -600553.896713173598982 ], [ -1465177.528888894012198, -600522.89802607416641 ], [ -1465127.043137517059222, -600492.322817637468688 ], [ -1465076.558308776235208, -600461.74856411840301 ], [ -1465026.071352525614202, -600431.173732563620433 ], [ -1464167.813602180918679, -599911.422857466852292 ], [ -1464066.843385462416336, -599850.277829257305712 ], [ -1463864.900312061654404, -599727.988053929526359 ], [ -1463511.502966369502246, -599513.987151008099318 ], [ -1463461.016869832063094, -599483.415350776049308 ], [ -1463208.591760960640386, -599330.560510716401041 ], [ -1463107.621392944362015, -599269.419048865325749 ], [ -1462703.739722811849788, -599024.857694945414551 ], [ -1462350.345842493698001, -598810.873087701154873 ], [ -1462097.921965224435553, -598658.02948173834011 ], [ -1461694.043102356838062, -598413.486952103441581 ], [ -1461492.105060321977362, -598291.217016015783884 ], [ -1461189.197514941683039, -598107.814770902739838 ], [ -1460987.258894353639334, -597985.551075292169116 ], [ -1460936.775235955836251, -597954.985913455137052 ], [ -1458051.812316050520167, -600099.158547738334164 ], [ -1457892.164144307142124, -600471.442229894571938 ], [ -1461411.324828887125477, -602635.790915566612966 ], [ -1462468.714946113759652, -603282.540258433087729 ], [ -1462871.790909743169323, -603528.511456081178039 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Pope", "maxabsdhdt": -3.8213927745819092, "refgtracks": "432|561|1003|1377" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1499108.983262808062136, -583809.096187873627059 ], [ -1499570.54498238582164, -583203.998424085322767 ], [ -1501097.653333374299109, -580834.457029922748916 ], [ -1501099.871764929965138, -580285.900113603798673 ], [ -1500518.273123782128096, -579901.88537827716209 ], [ -1500415.92428247211501, -579842.948615021072328 ], [ -1500364.749544098973274, -579813.480675077997148 ], [ -1494171.849644157569855, -576250.243873220519163 ], [ -1494120.430782661307603, -576221.217579538933933 ], [ -1487646.396745576057583, -576344.236148959957063 ], [ -1487306.876962234498933, -577295.506147308275104 ], [ -1487722.136294982396066, -577878.432688864530064 ], [ -1491844.201682011829689, -582201.919768845662475 ], [ -1491959.394778557587415, -582227.935805594781414 ], [ -1499108.983262808062136, -583809.096187873627059 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Haynes", "maxabsdhdt": -13.155643463134766, "refgtracks": "249|378|820|1194" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1516618.030438910936937, -521357.843802715884522 ], [ -1519655.878897091140971, -520323.632495161844417 ], [ -1521274.520867792656645, -518800.712889934540726 ], [ -1521480.60010894248262, -518254.57615099317627 ], [ -1521470.350422843825072, -517490.015087899228092 ], [ -1521000.089381572557613, -517242.493512343557086 ], [ -1519744.551446188008413, -516585.228273411805276 ], [ -1519587.566983795491979, -516503.15756506647449 ], [ -1518907.262933118268847, -516147.605082324938849 ], [ -1517860.46235011308454, -515600.99719372286927 ], [ -1517493.922903555212542, -515410.003189814684447 ], [ -1514325.224034712649882, -517006.004124458413571 ], [ -1514066.171429957496002, -517379.912034325825516 ], [ -1514010.315588089404628, -517495.584095630794764 ], [ -1514584.155920314602554, -520995.89965921791736 ], [ -1514758.693013488082215, -521027.199691060348414 ], [ -1515571.69655881728977, -521172.182704571576323 ], [ -1516443.48345847777091, -521327.603380369720981 ], [ -1516618.030438910936937, -521357.843802715884522 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kohler", "maxabsdhdt": -2.0643744468688965, "refgtracks": "43|417|485" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1405615.324839518405497, -705008.074465778190643 ], [ -1407874.960881995270029, -703269.775578489527106 ], [ -1407969.657348963432014, -702888.313808698789217 ], [ -1407983.476732397684827, -702612.201947226189077 ], [ -1402175.577471180353314, -698458.81510777620133 ], [ -1399075.783923246432096, -700089.191190926358104 ], [ -1398994.15498813171871, -700433.578705556923524 ], [ -1405615.324839518405497, -705008.074465778190643 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kohler", "maxabsdhdt": -7.8827877044677734, "refgtracks": "173|302|744|1118" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1469346.648294450249523, -649710.03243743092753 ], [ -1469695.327051709871739, -649312.97732535074465 ], [ -1471311.368316896026954, -647137.546671969234012 ], [ -1471188.628289892338216, -646312.968990468769334 ], [ -1469048.35458993120119, -644946.844638170092367 ], [ -1467853.767519903136417, -644184.487631032592617 ], [ -1467555.053604351822287, -643994.015834409859963 ], [ -1467007.415576554136351, -643644.823301011696458 ], [ -1466708.704647755715996, -643454.359645208111033 ], [ -1466658.919754992704839, -643422.615885785897262 ], [ -1458593.436889043310657, -638282.406507286126725 ], [ -1458543.654780004872009, -638250.680100644822232 ], [ -1458444.090165360365063, -638187.228348076227121 ], [ -1458145.397213665070012, -637996.874678494408727 ], [ -1457896.486505473963916, -637838.250160777475685 ], [ -1457398.666171313961968, -637521.008200517739169 ], [ -1457249.320623252540827, -637425.838854991248809 ], [ -1457199.537980419816449, -637394.116413266165182 ], [ -1452130.157576208934188, -635155.893109824741259 ], [ -1452073.142956990748644, -635140.432979801553302 ], [ -1450076.565473741618916, -634610.219370981445536 ], [ -1449449.066116732778028, -634444.308975041727535 ], [ -1447848.286323109641671, -634033.084310253732838 ], [ -1447791.111182345310226, -634018.441020833328366 ], [ -1445553.667297275736928, -633924.509275685413741 ], [ -1445019.216156518086791, -634056.99643377889879 ], [ -1444028.716024359688163, -636463.817392331315205 ], [ -1443964.376995359081775, -637333.626463487627916 ], [ -1458905.469422037247568, -646947.865779475891031 ], [ -1469346.648294450249523, -649710.03243743092753 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kohler", "maxabsdhdt": -4.1660590171813965, "refgtracks": "295|363|1240|1308" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1415408.742943953257054, -664996.570986212231219 ], [ -1416164.161290838383138, -661755.731523376773112 ], [ -1416234.493702619103715, -661429.714882778935134 ], [ -1415517.177035774569958, -660685.49602420325391 ], [ -1413859.451071837218478, -659556.180291712400503 ], [ -1408861.615502120228484, -659315.525467238388956 ], [ -1408628.678447405807674, -659570.256143947830424 ], [ -1408382.959314895095304, -662957.475527976057492 ], [ -1408836.100306383566931, -663089.472513169981539 ], [ -1408949.387682578293607, -663122.35018237947952 ], [ -1415408.742943953257054, -664996.570986212231219 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kohler", "maxabsdhdt": -4.5159692764282227, "refgtracks": "295|737|866|1308" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1455951.619960685726255, -684228.123079246492125 ], [ -1459270.129620171617717, -681731.002795620006509 ], [ -1459309.303722229087725, -681576.454549474641681 ], [ -1459337.965959900291637, -681172.182288230047561 ], [ -1456742.865562289953232, -676962.159278071252629 ], [ -1454814.281185957835987, -676403.799384513287805 ], [ -1441939.628744601039216, -672678.456410572514869 ], [ -1441769.500527990981936, -672629.237660705577582 ], [ -1441599.372519744792953, -672580.02043398632668 ], [ -1440924.33798559801653, -673270.433158655418083 ], [ -1440788.975443874020129, -675805.732035354129039 ], [ -1441085.329704807838425, -676389.208506952738389 ], [ -1442361.118840849492699, -678827.397604871890508 ], [ -1442898.764904480660334, -679191.598001784295775 ], [ -1442947.668229077942669, -679224.669019831344485 ], [ -1443730.386141881579533, -679753.429051404120401 ], [ -1448699.522263913415372, -682097.057288371492177 ], [ -1448756.033640954177827, -682114.184280254063196 ], [ -1449379.063768600346521, -682297.832349902717397 ], [ -1449435.716272035147995, -682314.484504889696836 ], [ -1455951.619960685726255, -684228.123079246492125 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kohler", "maxabsdhdt": -975.620361328125, "refgtracks": "173|302|615|744" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1481196.071790213463828, -652697.541752339689992 ], [ -1483067.845466925529763, -650868.230524073471315 ], [ -1483244.811469014501199, -650298.166877427254803 ], [ -1483237.275584866758436, -650047.853198690689169 ], [ -1482278.187700283015147, -648753.000543024856597 ], [ -1480786.151169957360253, -647797.282483419054188 ], [ -1480487.337890503695235, -647606.907987442100421 ], [ -1477957.920366049744189, -646682.031292299623601 ], [ -1476166.843305304879323, -649011.552323554060422 ], [ -1476167.900552118197083, -649487.997030518017709 ], [ -1476316.998656727606431, -649583.70708147494588 ], [ -1480449.223150305217132, -652220.931970973033458 ], [ -1481096.49081366113387, -652633.994166222051717 ], [ -1481196.071790213463828, -652697.541752339689992 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kohler", "maxabsdhdt": -7.570432186126709, "refgtracks": "302|676|744|1118" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1445315.853892508428544, -644141.964275001781061 ], [ -1446481.735377131728455, -642910.547509708208963 ], [ -1447738.951092550996691, -640894.064856989542022 ], [ -1443914.741964304354042, -637301.700115683604963 ], [ -1438094.431776152458042, -637514.825071844621561 ], [ -1437812.849283796269447, -638258.445955057512037 ], [ -1441441.18437516130507, -643095.67820365272928 ], [ -1441498.161449136678129, -643111.067467463901266 ], [ -1441783.050138400867581, -643188.009924768353812 ], [ -1442181.897823216626421, -643295.726410850067623 ], [ -1442238.876667458331212, -643311.114018936175853 ], [ -1442694.708329402143136, -643434.214222092879936 ], [ -1443150.54629617696628, -643557.310162172536366 ], [ -1444062.238762856693938, -643803.491594479535706 ], [ -1444461.111951388418674, -643911.191579327220097 ], [ -1445030.937710858881474, -644065.043748073163442 ], [ -1445087.920803793473169, -644080.428093379479833 ], [ -1445315.853892508428544, -644141.964275001781061 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kohler", "maxabsdhdt": -3.5722761154174805, "refgtracks": "356|424|798|1369" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1424153.543768178671598, -688123.595620085019618 ], [ -1424768.953898027073592, -688018.870647220173851 ], [ -1426156.661467759404331, -686314.953452815418132 ], [ -1426366.660707715433091, -685076.630608229432255 ], [ -1426318.063684708671644, -685043.136756159015931 ], [ -1426269.465601925039664, -685009.64242087432649 ], [ -1425297.474298551212996, -684339.819024113705382 ], [ -1424082.537789032794535, -683502.638380283955485 ], [ -1423402.156482186634094, -683033.836856840644032 ], [ -1423353.55411480483599, -683000.349054623860866 ], [ -1423061.954167320625857, -682799.455648922361434 ], [ -1418743.830440602032468, -683866.130186146940105 ], [ -1418465.584137034369633, -684016.293913752539083 ], [ -1418390.366549549857154, -684109.405255205114372 ], [ -1418299.692348684184253, -684329.457526783808134 ], [ -1424153.543768178671598, -688123.595620085019618 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kohler", "maxabsdhdt": -3.7142751216888428, "refgtracks": "424|798|866|1240" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1427192.656968248076737, -685645.869632396963425 ], [ -1430334.895977096166462, -684089.937050109962001 ], [ -1430539.598137800116092, -683910.253647136618383 ], [ -1430718.317945065442473, -683509.221754009835422 ], [ -1431627.681509735062718, -681005.120170612121001 ], [ -1431730.756124330451712, -680575.414355741464533 ], [ -1428546.379296128405258, -678001.964613641495816 ], [ -1428254.944079299923033, -677800.800572038977407 ], [ -1426060.685028054984286, -676304.667955442215316 ], [ -1421895.955923672998324, -677487.356302997563034 ], [ -1421673.984685361851007, -678035.938831591862254 ], [ -1423264.197300173575059, -681982.923733370611444 ], [ -1426415.257937470218167, -685110.122268682694994 ], [ -1426463.857083627488464, -685143.615364890196361 ], [ -1427192.656968248076737, -685645.869632396963425 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kohler", "maxabsdhdt": -4.3520150184631348, "refgtracks": "173|615|744" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1462853.208309343317524, -638014.961561388103291 ], [ -1462919.774828768102452, -637096.12385836429894 ], [ -1462416.384213643148541, -636191.658859877497889 ], [ -1461817.858178733615205, -635812.744623750098981 ], [ -1461019.127684400882572, -635308.657328978995793 ], [ -1460619.765517483931035, -635056.621935785748065 ], [ -1460370.164409222314134, -634899.104045983403921 ], [ -1460220.402606591349468, -634804.596404959796928 ], [ -1460169.954430475598201, -634773.92994238936808 ], [ -1459385.053462091367692, -634843.094967610668391 ], [ -1457602.760020055342466, -636625.833172001992352 ], [ -1457887.949948756955564, -636701.874333007843234 ], [ -1457945.013440570794046, -636716.986679506022483 ], [ -1458401.526540741557255, -636837.880621436284855 ], [ -1458800.980108510237187, -636943.659217441105284 ], [ -1458858.045303231338039, -636958.770421302877367 ], [ -1458972.176721164723858, -636988.989370798226446 ], [ -1460741.371398609830067, -637456.926586115849204 ], [ -1462853.208309343317524, -638014.961561388103291 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Kohler", "maxabsdhdt": -2.957134485244751, "refgtracks": "485|859|1301" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1416850.392562578432262, -702191.973389108781703 ], [ -1418414.399722923059016, -700855.422290113172494 ], [ -1418635.634005525615066, -699266.394372053444386 ], [ -1413875.756968731991947, -697665.99376028648112 ], [ -1413792.164046903140843, -697749.590202293125913 ], [ -1413647.120857664849609, -697997.906060710549355 ], [ -1414033.628978514578193, -701311.317048662458546 ], [ -1414089.932807893259451, -701329.014739141915925 ], [ -1416286.910786310676485, -702015.91874254506547 ], [ -1416850.392562578432262, -702191.973389108781703 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Ross_East2", "maxabsdhdt": -19.786056518554688, "refgtracks": "228|289|390|451|731|832|893|1173|1234|1274|1335" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 27353.229138533388323, -550073.570372670306824 ], [ 25417.235821608548576, -549343.496787883108482 ], [ 24626.360757157781336, -548029.259912835201249 ], [ 24804.041648866488686, -545158.824407508829609 ], [ 25502.256788883143599, -544439.285988563322462 ], [ 26517.847505498542887, -544137.554121583350934 ], [ 29372.208042720201775, -544801.511829914641567 ], [ 29910.49178888028473, -545373.815072994329967 ], [ 30922.024435445480776, -547338.92589166329708 ], [ 30948.644810580593912, -547390.837680508266203 ], [ 31107.894803222992778, -547701.930582878761925 ], [ 30477.350381551372266, -549270.177205672836863 ], [ 29380.042361359101051, -549924.281459026271477 ], [ 27353.229138533388323, -550073.570372670306824 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Bowman_Strom_Live_Axel-Heigerg", "maxabsdhdt": 6.243192195892334, "refgtracks": "29|54|90|115|471|496|532|938|999" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -134062.308365216391394, -486729.604184928291943 ], [ -135592.552485600084765, -485325.831247782101855 ], [ -136073.383187243598513, -484004.421418842452113 ], [ -136222.65019751386717, -483203.504045071895234 ], [ -135827.540585229842691, -482900.3581673472072 ], [ -133333.626103554386646, -481499.280475409061182 ], [ -132354.196002830984071, -481592.306851820088923 ], [ -130691.732244900442311, -482802.496062216814607 ], [ -130593.636796239297837, -484581.409181614406407 ], [ -131098.482669118558988, -486126.834703026048373 ], [ -131454.631056366284611, -486255.676043413986918 ], [ -132371.261142437550006, -486499.151748887321446 ], [ -134062.308365216391394, -486729.604184928291943 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Sulzberger", "maxabsdhdt": -4.6110467910766602, "refgtracks": "22|349|967" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -761958.121022721636109, -1119923.757371809799224 ], [ -764105.586510513792746, -1119613.082198861520737 ], [ -764985.29516586009413, -1118269.620593136642128 ], [ -761755.486767383408733, -1114355.219996347324923 ], [ -761310.120670593343675, -1115068.608577985316515 ], [ -759779.066590374917723, -1117698.152134711621329 ], [ -761793.439571995520964, -1119755.998669974273071 ], [ -761958.121022721636109, -1119923.757371809799224 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Sulzberger", "maxabsdhdt": -10.984376907348633, "refgtracks": "891|1157|1333" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -651489.430548224132508, -1144382.533390542725101 ], [ -651844.708870450849645, -1144318.795247945701703 ], [ -652232.158381970366463, -1144222.664168376242742 ], [ -653893.5997229446657, -1141602.215226115426049 ], [ -652731.924546452471986, -1140200.673849048325792 ], [ -652618.66383474227041, -1140065.749980084132403 ], [ -651854.243225271464325, -1139174.119240711443126 ], [ -649437.215771576622501, -1138568.690815266454592 ], [ -647502.993872122722678, -1139146.173610802972689 ], [ -647170.250497883651406, -1139766.877743700053543 ], [ -647826.793135448126122, -1141652.346809011884034 ], [ -648483.735415478004143, -1143537.747528717387468 ], [ -648522.814749729586765, -1143648.5046785059385 ], [ -651489.430548224132508, -1144382.533390542725101 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Lucchitta_Velasco", "maxabsdhdt": -2.5221989154815674, "refgtracks": "20|210|462" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1674295.376817366341129, -307441.545335490372963 ], [ -1675453.416643803706393, -306054.184626164787915 ], [ -1675296.814663496566936, -304138.370278371730819 ], [ -1671120.902232138905674, -303588.719084008655045 ], [ -1670862.062062577577308, -303980.731546772527508 ], [ -1670694.895552271278575, -304424.975690842489712 ], [ -1671752.569283698685467, -307351.626987885334529 ], [ -1671870.823490485781804, -307356.191096117370762 ], [ -1673822.270189383765683, -307425.287711695884354 ], [ -1674177.100385104771703, -307437.482417735736817 ], [ -1674236.239114629104733, -307439.514250561245717 ], [ -1674295.376817366341129, -307441.545335490372963 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -7.6495366096496582, "refgtracks": "28|341|470|783" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1402617.596398923546076, -876984.015357102849521 ], [ -1406231.976254713023081, -876064.177762255771086 ], [ -1406493.710467488737777, -875916.918696971610188 ], [ -1407030.46005743322894, -875401.292492638807744 ], [ -1407875.356578349135816, -873145.303583733038977 ], [ -1408067.109934637323022, -871901.788320527528413 ], [ -1407886.995047229807824, -871748.595426085987128 ], [ -1405094.901350296335295, -869374.700352019164711 ], [ -1403068.302600059658289, -867652.080668978975154 ], [ -1402887.862848598975688, -867499.305972382542677 ], [ -1393885.826354393735528, -865104.781667585601099 ], [ -1392405.259778775973246, -866482.216241354704835 ], [ -1392300.277866845484823, -868133.486535600037314 ], [ -1393376.436692165676504, -869057.241251233615912 ], [ -1400149.659716748865321, -874867.913240194553509 ], [ -1402617.596398923546076, -876984.015357102849521 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -9.0822639465332031, "refgtracks": "28|341|973|1286" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1402251.917262742528692, -882436.355298576527275 ], [ -1406783.323619521921501, -880772.030924414983019 ], [ -1406833.249980249674991, -880604.075592037872411 ], [ -1406322.761909214081243, -879735.981226717471145 ], [ -1398938.605634436244145, -873828.669096573605202 ], [ -1394183.457305037416518, -870865.26783123309724 ], [ -1393803.549527847208083, -870700.87065305840224 ], [ -1390765.168864429695532, -869389.161426299950108 ], [ -1390331.153740009525791, -869202.471878538723104 ], [ -1389277.692538799950853, -869932.798775045550428 ], [ -1388738.860627910355106, -872146.625610772985965 ], [ -1389231.869665791047737, -873112.279720118851401 ], [ -1395425.917003626003861, -879456.180623329128139 ], [ -1396129.955778956180438, -879764.052064676303416 ], [ -1399976.273861398687586, -881443.345922461827286 ], [ -1400030.454194141551852, -881466.990155769512057 ], [ -1400084.633566682692617, -881490.633935232530348 ], [ -1400680.619968849699944, -881750.715309412684292 ], [ -1401330.800757936900482, -882034.433016942581162 ], [ -1401601.714464371092618, -882152.647279048687778 ], [ -1402251.917262742528692, -882436.355298576527275 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -9.5233659744262695, "refgtracks": "59|372|501|1317" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1169371.996159728383645, -1139356.128865900682285 ], [ -1169878.182578042615205, -1139224.965461729560047 ], [ -1170448.967904103687033, -1138929.401144574396312 ], [ -1171102.176251920871437, -1135691.291850954759866 ], [ -1171219.023814059561118, -1134414.567590148653835 ], [ -1170897.990190032403916, -1133990.695163407595828 ], [ -1169934.083378578536212, -1132719.733792593004182 ], [ -1169862.654884884832427, -1132625.61239237524569 ], [ -1169648.364083364373073, -1132343.254510444123298 ], [ -1169612.648934228112921, -1132296.195222821086645 ], [ -1169505.502788591664284, -1132155.018049075268209 ], [ -1169255.493917042156681, -1131825.609688623109832 ], [ -1169041.201786946272478, -1131543.262111997231841 ], [ -1168826.906301268143579, -1131260.920442547881976 ], [ -1168684.043285145657137, -1131072.693952149245888 ], [ -1168505.459829537430778, -1130837.416427894728258 ], [ -1168433.635578699409962, -1130743.602792920311913 ], [ -1164254.287172887241468, -1126755.651812731055543 ], [ -1164206.13160579954274, -1126721.490490975324064 ], [ -1164157.989583894610405, -1126687.364235959015787 ], [ -1162712.019097618293017, -1125662.881145045394078 ], [ -1162567.409428007667884, -1125560.47160394419916 ], [ -1162471.003117905464023, -1125492.198979539098218 ], [ -1162181.504141107900068, -1125287.779969489201903 ], [ -1160077.221180115826428, -1126193.945972687797621 ], [ -1159533.156383923254907, -1127493.993893169797957 ], [ -1166647.421879725530744, -1137376.423061883775517 ], [ -1166693.187152545200661, -1137413.995048753451556 ], [ -1167161.211632832884789, -1137775.209465756779537 ], [ -1167881.984451639698818, -1138290.844003378413618 ], [ -1169275.851034401450306, -1139287.452832405921072 ], [ -1169371.996159728383645, -1139356.128865900682285 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -4.0927863121032715, "refgtracks": "82|211|524|653" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1352035.573988420655951, -928071.565038688131608 ], [ -1353813.356232015648857, -927804.942916370928288 ], [ -1354250.096635739086196, -927277.847321928013116 ], [ -1354910.197868015151471, -925774.359052571584471 ], [ -1349540.962104577803984, -919486.501511365291663 ], [ -1346515.759841374587268, -917327.962190839461982 ], [ -1346462.39877042430453, -917302.590306368423626 ], [ -1344961.934049931587651, -917285.210415523150004 ], [ -1343962.504757836693898, -917692.09855892683845 ], [ -1343604.836011573206633, -918218.626358469948173 ], [ -1343094.008978090947494, -920078.46336614061147 ], [ -1350917.501951003447175, -927532.904322594637051 ], [ -1351231.011836741119623, -927699.011707898345776 ], [ -1351817.152285835007206, -927980.286136178998277 ], [ -1351870.658744487445801, -928005.460801000590436 ], [ -1352035.573988420655951, -928071.565038688131608 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -3.4939711093902588, "refgtracks": "82|211" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1352887.343047401402146, -928482.047720882575959 ], [ -1353899.501385817071423, -927885.868589926860295 ], [ -1353856.429477295605466, -927845.405224247486331 ], [ -1349106.449808378238231, -926662.619950875174254 ], [ -1349153.481495417188853, -926701.164026849670336 ], [ -1350005.841735590016469, -927110.495270853629336 ], [ -1350432.143756798701361, -927314.927113984711468 ], [ -1352887.343047401402146, -928482.047720882575959 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -8.1778106689453125, "refgtracks": "82|211|524|1156" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1362116.381420469144359, -940200.723610140383244 ], [ -1366909.537165259709582, -940108.35306540702004 ], [ -1367203.536268904572353, -939900.786077539087273 ], [ -1367650.554301944095641, -936409.375102391466498 ], [ -1367595.440188545268029, -935149.093878196086735 ], [ -1367465.749809677014127, -935028.139461717917584 ], [ -1366082.141382772941142, -933738.282299562706612 ], [ -1363400.112230177270249, -931240.882213558885269 ], [ -1362491.4728894517757, -930395.157817672821693 ], [ -1358011.789303627097979, -930782.224101527943276 ], [ -1357303.456920343916863, -931081.477840650826693 ], [ -1357069.376566677587107, -931272.654470884357579 ], [ -1358589.71790112182498, -935716.39169693319127 ], [ -1359199.5362594188191, -937435.301060948288068 ], [ -1359273.117641778895631, -937529.612329586874694 ], [ -1359359.248007180634886, -937610.578851546859369 ], [ -1360479.260636446066201, -938662.848331687273458 ], [ -1362116.381420469144359, -940200.723610140383244 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -4.7941761016845703, "refgtracks": "21|150|463|1095" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1378554.945878698024899, -919514.356391110923141 ], [ -1380992.708340930286795, -918068.601971877273172 ], [ -1381265.554829459637403, -917364.170371923828498 ], [ -1374973.581544199725613, -911760.706216356833465 ], [ -1372032.283393788384274, -913597.419227102538571 ], [ -1372792.116398876532912, -915275.619839540449902 ], [ -1375515.82857841369696, -919165.587089865352027 ], [ -1375622.862814946798608, -919215.8027485240018 ], [ -1375836.932950320187956, -919316.228052110061981 ], [ -1378554.945878698024899, -919514.356391110923141 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -2.4555149078369141, "refgtracks": "211|524|653|966" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1358253.29076592088677, -926447.643494901130907 ], [ -1361844.917777108959854, -925396.808736783103086 ], [ -1362049.784652666654438, -924705.90607413649559 ], [ -1356679.742573205381632, -919513.143580183968879 ], [ -1356636.319792954949662, -919473.071626782417297 ], [ -1354944.175238893134519, -918814.555631174938753 ], [ -1352469.942037380998954, -920894.005966971977614 ], [ -1352413.450575908878818, -921012.877503922907636 ], [ -1353062.172961566364393, -921616.802329773083329 ], [ -1357863.722818950889632, -926085.527690940070897 ], [ -1358253.29076592088677, -926447.643494901130907 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -5.4568405151367188, "refgtracks": "265|455|897|1210" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1328070.463847356382757, -1011054.615091229206882 ], [ -1331937.574345415923744, -1010326.605487627210096 ], [ -1331987.904247097205371, -1010301.00033260602504 ], [ -1332425.550645438721403, -1009574.792145574931055 ], [ -1329902.142173688625917, -1004494.56966095068492 ], [ -1328083.355151398805901, -1003507.345074005424976 ], [ -1325536.797134069027379, -1002126.026027755462565 ], [ -1325252.84813814307563, -1002283.293827429530211 ], [ -1324613.156810199143365, -1002708.964640117948875 ], [ -1322980.93351913173683, -1005387.072381067904644 ], [ -1322881.069693970493972, -1005709.726914033293724 ], [ -1328070.463847356382757, -1011054.615091229206882 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -14.977787971496582, "refgtracks": "372|501|943|1317" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1157163.962637783726677, -1122324.783815973671153 ], [ -1157559.986195531440899, -1121942.633130869362503 ], [ -1159930.635499983793125, -1119548.923902555601671 ], [ -1160211.761649567633867, -1119126.743669806513935 ], [ -1158934.884621507953852, -1114160.632156086852774 ], [ -1146129.19686506036669, -1105169.759241067804396 ], [ -1146032.293584682745859, -1105102.302031149622053 ], [ -1143984.437106157187372, -1104825.861336049390957 ], [ -1141173.273744957288727, -1105754.646704769227654 ], [ -1140938.122120952932164, -1106144.100464996648952 ], [ -1140904.601614362327382, -1106259.041401227004826 ], [ -1141401.435683851828799, -1106919.440403723856434 ], [ -1152232.809483390999958, -1121305.419929112074897 ], [ -1157163.962637783726677, -1122324.783815973671153 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -2.2187709808349609, "refgtracks": "372" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1160905.102233905345201, -1126372.996536629274487 ], [ -1160778.5814320796635, -1126172.442600089590997 ], [ -1160742.475699061062187, -1126125.70163001678884 ], [ -1160474.355234394548461, -1125810.190163818653673 ], [ -1160509.679223411018029, -1125857.52445239154622 ], [ -1160545.190195745090023, -1125904.71644626930356 ], [ -1160688.639575799461454, -1126092.418365073157474 ], [ -1160905.102233905345201, -1126372.996536629274487 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -8.0158901214599609, "refgtracks": "531|844|973|1286" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1397323.73634944926016, -888385.558428433607332 ], [ -1399428.361015145899728, -885669.086757913581096 ], [ -1399617.708440214861184, -884925.2724126460962 ], [ -1397913.973783595021814, -882716.706374698784202 ], [ -1397690.90189847885631, -882522.837766050710343 ], [ -1397467.829116848995909, -882328.97127720376011 ], [ -1397423.150629329727963, -882290.271940886974335 ], [ -1397378.472290347097442, -882251.573090328020044 ], [ -1396797.644348566886038, -881748.510450642788783 ], [ -1395009.828692048089579, -880201.502365035470575 ], [ -1387822.05941226053983, -878390.92885271331761 ], [ -1386726.089116133516654, -879292.416264943312854 ], [ -1386502.794345489935949, -879971.381077948841266 ], [ -1391537.981231858255342, -885830.195802394184284 ], [ -1391591.845135908108205, -885854.539831800037064 ], [ -1393208.991100607439876, -886582.164791908231564 ], [ -1393316.925270008388907, -886630.3976480436977 ], [ -1393587.258734215749428, -886749.855908962665126 ], [ -1393803.527770201209933, -886845.421222013072111 ], [ -1394182.003630462568253, -887012.655668638646603 ], [ -1394993.038605835987255, -887371.008304628659971 ], [ -1395155.248177330708131, -887442.676431221538223 ], [ -1396885.544404904358089, -888207.129545348114334 ], [ -1396939.616796320769936, -888231.017336121527478 ], [ -1397264.060114660067484, -888374.347332142060623 ], [ -1397323.73634944926016, -888385.558428433607332 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -4.727442741394043, "refgtracks": "52|181|494|623" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1137141.496178234461695, -1167619.029220388736576 ], [ -1138444.179770693881437, -1166904.008634848985821 ], [ -1139021.958080880576745, -1166527.715559687465429 ], [ -1138919.84187882207334, -1164381.456233626697212 ], [ -1137784.310002973536029, -1162796.799111315282062 ], [ -1137612.084807468345389, -1162556.586438531987369 ], [ -1137440.081002136226743, -1162316.708345572231337 ], [ -1137405.655201896093786, -1162268.706440523033962 ], [ -1137371.203509998274967, -1162220.67914887261577 ], [ -1137336.387367497198284, -1162172.965384438866749 ], [ -1134200.529393475502729, -1162912.189682183088735 ], [ -1133871.731716972775757, -1163057.862856821157038 ], [ -1133970.818893023766577, -1163515.16611382109113 ], [ -1134700.547454695217311, -1165284.643275018781424 ], [ -1135829.133984371554106, -1166874.454682657495141 ], [ -1136068.613962223054841, -1167211.641133024357259 ], [ -1137141.496178234461695, -1167619.029220388736576 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Getz", "maxabsdhdt": -9.9814071655273438, "refgtracks": "82|714|1156" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1361997.479798421496525, -944641.42100391513668 ], [ -1365020.838943717535585, -943375.733196461806074 ], [ -1365143.0887199153658, -943022.413336325436831 ], [ -1365214.372763451188803, -942642.685241134604439 ], [ -1358424.375414329813793, -936706.608209773199633 ], [ -1355856.930534781655297, -938093.73309528036043 ], [ -1355668.255736778257415, -938689.231823109439574 ], [ -1355727.108754181768745, -938833.996963296202011 ], [ -1361997.479798421496525, -944641.42100391513668 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Abbot", "maxabsdhdt": 8.1773452758789062, "refgtracks": "584|896|1026|1338" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1835281.179311134619638, -405787.428339567966759 ], [ -1836109.7110276944004, -405192.861505454988219 ], [ -1837560.555813123472035, -401964.181621457682922 ], [ -1837502.617112087784335, -401186.168579047720414 ], [ -1836954.317612774437293, -400144.879625494591892 ], [ -1827799.498995580943301, -397621.018654346989933 ], [ -1825697.999878778588027, -399291.572291059594136 ], [ -1824238.411235146457329, -400876.975420471746475 ], [ -1823884.710363107267767, -401491.274437946325634 ], [ -1824620.999860768672079, -404270.620880134112667 ], [ -1835219.245024270843714, -405783.466135539114475 ], [ -1835281.179311134619638, -405787.428339567966759 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Abbot", "maxabsdhdt": 6.7902145385742188, "refgtracks": "454|584|896" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1839734.433326564496383, -411024.280025864136405 ], [ -1840363.212689439067617, -410666.11649669270264 ], [ -1841113.444620897993445, -408960.449623463558964 ], [ -1840960.952418976230547, -407903.728583653399255 ], [ -1837175.608445613412187, -406489.450226669549011 ], [ -1837119.923815899062902, -406468.706465595168993 ], [ -1836450.329706579679623, -406223.51420560060069 ], [ -1834596.527174666291103, -408449.699153124820441 ], [ -1834629.738934793043882, -409067.617528556846082 ], [ -1835626.723148436285555, -410264.966159310017247 ], [ -1839734.433326564496383, -411024.280025864136405 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "George_VI", "maxabsdhdt": -3.4389832019805908, "refgtracks": "864|1055|1306" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1651982.079568123212084, 704450.266457106219605 ], [ -1655648.65537975449115, 705430.266429260605946 ], [ -1659200.79387942655012, 706380.333558718906716 ], [ -1660428.32070227782242, 709932.745344360126182 ], [ -1660288.046815402572975, 710122.240520030143671 ], [ -1659297.101852606283501, 710917.225204481626861 ], [ -1652438.426212199963629, 708022.644883858272806 ], [ -1651449.21351345255971, 706129.308398139546625 ], [ -1651799.724411838222295, 704718.76876912789885 ], [ -1651982.079568123212084, 704450.266457106219605 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "George_VI", "maxabsdhdt": -4.1508378982543945, "refgtracks": "110|422|1055|1367" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1713833.626477518118918, 722764.500552538200282 ], [ -1713884.693214345024899, 722794.813690732349642 ], [ -1713935.760042660636827, 722825.127124541788362 ], [ -1713986.826437453739345, 722855.441301121027209 ], [ -1714037.892608563881367, 722885.756111997994594 ], [ -1714293.222070392454043, 723037.335031342809089 ], [ -1718274.512541945325211, 725405.516083374270238 ], [ -1717437.267346902051941, 726838.029858980095014 ], [ -1717138.747092827223241, 727089.143307802383788 ], [ -1713698.843833259306848, 726159.532809084863402 ], [ -1713641.522375768283382, 726144.017924636951648 ], [ -1712905.539867612766102, 724116.122398909879848 ], [ -1713833.626477518118918, 722764.500552538200282 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Recovery", "maxabsdhdt": -7.3313446044921875, "refgtracks": "130|191|413|633|855|916|1075|1358" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -291636.253355770430062, 941659.549996954388916 ], [ -292691.107504485582467, 941660.075947078643367 ], [ -296074.820927562774159, 942833.837921428726986 ], [ -296635.85456993652042, 943190.182686188956723 ], [ -297527.700722999230493, 944699.260684316395782 ], [ -297824.579506158304866, 945202.535127353155985 ], [ -297385.07054529589368, 946207.595537200686522 ], [ -295525.567081155488268, 947783.090932936407626 ], [ -290038.097379697137512, 950861.571663266164251 ], [ -286511.543446431751363, 949723.254254206782207 ], [ -285496.428644168423489, 948903.091065944405273 ], [ -285212.880355063069146, 947369.072336846729741 ], [ -285138.755350968567654, 943305.099386606365442 ], [ -288351.759522716223728, 941928.474187785875984 ], [ -291636.253355770430062, 941659.549996954388916 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Recovery", "maxabsdhdt": 3.5948548316955566, "refgtracks": "78|139|359|520|581|740|801|1023|1243" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -422629.978694569785148, 896154.247844482655637 ], [ -424970.830707224609796, 898327.086925218696706 ], [ -425115.934762118558865, 898510.322550787241198 ], [ -425586.759961506351829, 899106.448860492557287 ], [ -424323.012150550843216, 904159.032542887609452 ], [ -421385.06423968472518, 906214.437275925069116 ], [ -415144.994504725560546, 908699.424111841479316 ], [ -412948.727926282328553, 904313.587988981977105 ], [ -413896.802037683490198, 902055.576489783590659 ], [ -418443.516219247248955, 897483.287250648136251 ], [ -419413.294644885347225, 896895.376896479050629 ], [ -422629.978694569785148, 896154.247844482655637 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Recovery", "maxabsdhdt": 1.8184157609939575, "refgtracks": "250|640|692|1082|1143|1195" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 328214.235134409856983, 694846.423893383587711 ], [ 325579.224698436213657, 695265.699626198736951 ], [ 324926.143647433898877, 696172.498112619505264 ], [ 325158.130404822819401, 697846.924201144604012 ], [ 325174.131489877298009, 697962.402753320639022 ], [ 325333.533881688897964, 698146.545297534554265 ], [ 328371.706563600338995, 698397.987269300967455 ], [ 328749.961221538251266, 698361.110306136193685 ], [ 328636.396870871656574, 697611.975683177588508 ], [ 328618.848058135481551, 697496.736351023660973 ], [ 328223.492940034950152, 694903.968579669832252 ], [ 328214.235134409856983, 694846.423893383587711 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Hull", "maxabsdhdt": -4.5349311828613281, "refgtracks": "52|181|1126" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -1126671.441226984141394, -1167085.91324718715623 ], [ -1127314.929517743410543, -1166828.634183941874653 ], [ -1127526.873802014160901, -1166684.451477868715301 ], [ -1128194.633302840171382, -1163317.107760961633176 ], [ -1127252.939402585849166, -1162603.759535773657262 ], [ -1126169.700750613352284, -1161783.852990713436157 ], [ -1124551.242769490461797, -1162495.159078515134752 ], [ -1123359.108986478066072, -1163832.951220362680033 ], [ -1123338.826852685539052, -1164540.203021816443652 ], [ -1123855.096409572288394, -1164934.636026168474928 ], [ -1124934.663888903800398, -1165759.282026544678956 ], [ -1126624.533786239568144, -1167050.095998763339594 ], [ -1126671.441226984141394, -1167085.91324718715623 ] ] ] } }, +{ "type": "Feature", "properties": { "basin_name": "Coats_Coast", "maxabsdhdt": -26.990604400634766, "refgtracks": "451|627|893|1069" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -759188.486735700978898, 1087982.1629650357645 ], [ -762068.665563712478615, 1088025.920644424622878 ], [ -762093.240406692144461, 1088079.270779216662049 ], [ -763802.106320905964822, 1091819.175736645236611 ], [ -763706.264748862246051, 1092055.665786029305309 ], [ -763302.43181618093513, 1092634.772222332656384 ], [ -761881.911741494201124, 1093920.493077465333045 ], [ -760946.30482427123934, 1093990.826285855378956 ], [ -760904.718666849308647, 1093949.340417908970267 ], [ -760364.114241086412221, 1093410.038856319850311 ], [ -759657.193933589733206, 1092704.79574697650969 ], [ -759615.61145258473698, 1092663.310838804813102 ], [ -759407.700308402534574, 1092455.884984384523705 ], [ -759366.118308847770095, 1092414.399881279561669 ], [ -758991.885249237413518, 1092041.031519669806585 ], [ -758908.722973090363666, 1091958.060986452968791 ], [ -758742.40081195498351, 1091792.12063962733373 ], [ -758617.659811710705981, 1091667.662955752806738 ], [ -758368.180317620746791, 1091418.749928867211565 ], [ -758160.282426074612886, 1091211.32290033553727 ], [ -758118.703494144487195, 1091169.837410197826102 ], [ -757993.966694949544035, 1091045.380579123506323 ], [ -757702.919981080456637, 1090754.978917251573876 ], [ -757495.030655223759823, 1090547.549646832048893 ], [ -757453.453251338913105, 1090506.064062462653965 ], [ -757287.144764517783187, 1090340.119724359828979 ], [ -758551.971737041603774, 1088352.365353680681437 ], [ -759188.486735700978898, 1087982.1629650357645 ] ] ] } } +] +} diff --git a/atlxi_lake.ipynb b/atlxi_lake.ipynb index 78c663b..04add6b 100644 --- a/atlxi_lake.ipynb +++ b/atlxi_lake.ipynb @@ -1,5 +1,277 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# **ICESat-2 Active Subglacial Lakes in Antarctica**\n", + "\n", + "Finding subglacial lakes that are draining or filling under the ice!\n", + "They can be detected with ICESat-2 data, as significant changes in height\n", + "(> 1 metre) over a relatively short duration (< 1 year), i.e. a high rate of\n", + "elevation change over time (dhdt).\n", + "\n", + "In this notebook, we'll use some neat tools to help us examine the lakes:\n", + "- To find active subglacial lake boundaries,\n", + "use an *unsupervised clustering* technique\n", + "- To see ice surface elevation trends at a higher temporal resolution,\n", + "perform *crossover track error analysis* on intersecting ICESat-2 tracks\n", + "\n", + "To speed up analysis on millions of points,\n", + "we will use state of the art GPU algorithms enabled by RAPIDS AI libraries,\n", + "or parallelize the processing across our HPC's many CPU cores using Dask." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import numpy as np\n", + "\n", + "import cudf\n", + "import cuml\n", + "import dask\n", + "import dask.array\n", + "import deepicedrain\n", + "import geopandas as gpd\n", + "import hvplot.cudf\n", + "import panel as pn\n", + "import pygmt\n", + "import scipy.spatial\n", + "import shapely.geometry\n", + "import tqdm\n", + "import zarr" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Data Preparation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if not os.path.exists(\"ATLXI/df_dhdt_antarctica.parquet\"):\n", + " zarrarray = zarr.open_consolidated(store=f\"ATLXI/ds_dhdt_antarctica.zarr\", mode=\"r\")\n", + " _ = deepicedrain.zarr_to_parquet(\n", + " zarrarray=zarrarray,\n", + " parquetpath=\"ATLXI/df_dhdt_antarctica.parquet\",\n", + " variables=[\"x\", \"y\", \"dhdt_slope\", \"referencegroundtrack\", \"h_corr\"],\n", + " dropnacols=[\"dhdt_slope\"],\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load in ICESat-2 data (x, y, dhdt) and do initial trimming" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Read in raw x, y, dhdt_slope and referencegroundtrack data into the GPU\n", + "cudf_raw: cudf.DataFrame = cudf.read_parquet(\n", + " filepath_or_buffer=\"ATLXI/df_dhdt_antarctica.parquet\",\n", + " columns=[\"x\", \"y\", \"dhdt_slope\", \"referencegroundtrack\"],\n", + ")\n", + "# Filter to points with dhdt that is less than -1 m/yr or more than +1 m/yr\n", + "cudf_many = cudf_raw.loc[abs(cudf_raw.dhdt_slope) > 1]\n", + "print(f\"Trimmed {len(cudf_raw)} -> {len(cudf_many)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lines_to_next_cell": 2 + }, + "source": [ + "## Label ICESat-2 points according to their drainage basin\n", + "\n", + "Uses Point in Polygon.\n", + "For each point, find out which Antarctic Drainage Basin they are in.\n", + "This will also remove the points on floating (FR) ice shelves and islands (IS),\n", + "so that we keep only points on the grounded (GR) ice regions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "# Read in Antarctic Drainage Basin Boundaries shapefile into a GeoDataFrame\n", + "ice_boundaries: gpd.GeoDataFrame = gpd.read_file(\n", + " filename=\"Quantarctica3/Glaciology/MEaSUREs Antarctic Boundaries/IceBoundaries_Antarctica_v2.shp\"\n", + ")\n", + "drainage_basins: gpd.GeoDataFrame = ice_boundaries.query(expr=\"TYPE == 'GR'\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "lines_to_next_cell": 1 + }, + "outputs": [], + "source": [ + "# Use point in polygon to label points according to the drainage basins they fall in\n", + "cudf_many[\"drainage_basin\"]: cudf.Series = deepicedrain.point_in_polygon_gpu(\n", + " points_df=cudf_many, poly_df=drainage_basins\n", + ")\n", + "X_many = cudf_many.dropna() # drop points that are not in a drainage basin" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Active Subglacial Lake clusters\n", + "\n", + "Uses Density-based spatial clustering of applications with noise (DBSCAN)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series:\n", + " \"\"\"\n", + " Density-based spatial clustering of applications with noise (DBSCAN)\n", + " See also https://www.naftaliharris.com/blog/visualizing-dbscan-clustering\n", + " \"\"\"\n", + " # Run DBSCAN using 2500 m distance, and minimum of 250 points\n", + " dbscan = cuml.DBSCAN(eps=2500, min_samples=250)\n", + " dbscan.fit(X=X)\n", + "\n", + " return dbscan.labels_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Subglacial lake finder\n", + "activelakes: dict = {\n", + " \"basin_name\": [],\n", + " \"maxabsdhdt\": [],\n", + " \"refgtracks\": [],\n", + " \"geometry\": [],\n", + "}\n", + "for basin_index in tqdm.tqdm(iterable=drainage_basins.index):\n", + " # Initial data cleaning, filter to rows that are in the drainage basin\n", + " basin = drainage_basins.loc[basin_index]\n", + " X = X_many.loc[X_many.drainage_basin == basin.NAME] # .reset_index(drop=True)\n", + " if len(X) <= 1000: # don't run on too few points\n", + " continue\n", + " print(f\"{len(X)} rows at {basin.NAME}\")\n", + "\n", + " # Run unsupervised clustering separately on draining and filling lakes\n", + " for activity, X_ in (\n", + " (\"draining\", X.loc[X.dhdt_slope < -1]),\n", + " (\"filling\", X.loc[X.dhdt_slope > 1]),\n", + " ):\n", + " labels_ = find_clusters(X=X_[[\"x\", \"y\", \"dhdt_slope\"]])\n", + " n_clusters_ = len(labels_.unique()) - 1 # No. of clusters minus noise (-1)\n", + " print(f\"{n_clusters_} {activity} lakes found\")\n", + "\n", + " # Store attribute and geometry information of each active lake\n", + " for i in range(n_clusters_):\n", + " lake_points: cudf.DataFrame = X_.loc[labels_ == i]\n", + "\n", + " try:\n", + " assert len(lake_points) > 2\n", + " except AssertionError:\n", + " continue\n", + "\n", + " multipoint: shapely.geometry.MultiPoint = shapely.geometry.MultiPoint(\n", + " points=lake_points[[\"x\", \"y\"]].as_matrix()\n", + " )\n", + " convexhull: shapely.geometry.Polygon = multipoint.convex_hull\n", + "\n", + " maxabsdhdt: float = (\n", + " lake_points.dhdt_slope.max()\n", + " if activity == \"filling\"\n", + " else lake_points.dhdt_slope.min()\n", + " )\n", + " refgtracks: str = \"|\".join(\n", + " map(str, lake_points.referencegroundtrack.unique().to_pandas())\n", + " )\n", + "\n", + " activelakes[\"basin_name\"].append(basin.NAME)\n", + " activelakes[\"maxabsdhdt\"].append(maxabsdhdt)\n", + " activelakes[\"refgtracks\"].append(refgtracks)\n", + " activelakes[\"geometry\"].append(convexhull)\n", + "\n", + "if len(activelakes[\"geometry\"]) >= 1:\n", + " gdf = gpd.GeoDataFrame(activelakes, crs=\"EPSG:3031\")\n", + " gdf.to_file(filename=\"antarctic_subglacial_lakes.geojson\", driver=\"GeoJSON\")\n", + "\n", + "print(f\"Total of {len(gdf)} subglacial lakes founds\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualize lakes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "# Plot clusters on a map in colour, noise points/outliers as small dots\n", + "X_cpu = X_.to_pandas()\n", + "\n", + "fig = pygmt.Figure()\n", + "labels_cpu = labels_.to_pandas().astype(np.int32)\n", + "sizes = (labels_cpu < 0).map(arg={True: 0.02, False: 0.2})\n", + "if n_clusters_:\n", + " pygmt.makecpt(cmap=\"categorical\", series=(-0.5, n_clusters_ - 0.5, 1))\n", + "else:\n", + " pygmt.makecpt(cmap=\"gray\")\n", + "fig.plot(\n", + " x=X_cpu.x,\n", + " y=X_cpu.y,\n", + " sizes=sizes,\n", + " style=\"cc\",\n", + " color=labels_cpu,\n", + " cmap=True,\n", + " frame=[\n", + " f'WSne+t\"Estimated number of clusters at {basin.NAME}: {n_clusters_}\"',\n", + " 'xaf+l\"Polar Stereographic X (km)\"',\n", + " 'yaf+l\"Polar Stereographic Y (km)\"',\n", + " ],\n", + ")\n", + "fig.colorbar(frame='af+l\"Cluster Number\"')\n", + "fig.savefig(fname=f\"figures/subglacial_lakes_at_{basin.NAME}.png\")\n", + "fig.show()" + ] + }, { "cell_type": "markdown", "metadata": { diff --git a/atlxi_lake.py b/atlxi_lake.py index 18b4e79..3e5093f 100644 --- a/atlxi_lake.py +++ b/atlxi_lake.py @@ -14,6 +14,202 @@ # name: deepicedrain # --- +# %% [markdown] +# # **ICESat-2 Active Subglacial Lakes in Antarctica** +# +# Finding subglacial lakes that are draining or filling under the ice! +# They can be detected with ICESat-2 data, as significant changes in height +# (> 1 metre) over a relatively short duration (< 1 year), i.e. a high rate of +# elevation change over time (dhdt). +# +# In this notebook, we'll use some neat tools to help us examine the lakes: +# - To find active subglacial lake boundaries, +# use an *unsupervised clustering* technique +# - To see ice surface elevation trends at a higher temporal resolution, +# perform *crossover track error analysis* on intersecting ICESat-2 tracks +# +# To speed up analysis on millions of points, +# we will use state of the art GPU algorithms enabled by RAPIDS AI libraries, +# or parallelize the processing across our HPC's many CPU cores using Dask. + +# %% +import os + +import numpy as np + +import cudf +import cuml +import dask +import dask.array +import deepicedrain +import geopandas as gpd +import hvplot.cudf +import panel as pn +import pygmt +import scipy.spatial +import shapely.geometry +import tqdm +import zarr + +# %% [markdown] +# # Data Preparation + +# %% +if not os.path.exists("ATLXI/df_dhdt_antarctica.parquet"): + zarrarray = zarr.open_consolidated(store=f"ATLXI/ds_dhdt_antarctica.zarr", mode="r") + _ = deepicedrain.zarr_to_parquet( + zarrarray=zarrarray, + parquetpath="ATLXI/df_dhdt_antarctica.parquet", + variables=["x", "y", "dhdt_slope", "referencegroundtrack", "h_corr"], + dropnacols=["dhdt_slope"], + ) + +# %% [markdown] +# ## Load in ICESat-2 data (x, y, dhdt) and do initial trimming + +# %% +# Read in raw x, y, dhdt_slope and referencegroundtrack data into the GPU +cudf_raw: cudf.DataFrame = cudf.read_parquet( + filepath_or_buffer="ATLXI/df_dhdt_antarctica.parquet", + columns=["x", "y", "dhdt_slope", "referencegroundtrack"], +) +# Filter to points with dhdt that is less than -1 m/yr or more than +1 m/yr +cudf_many = cudf_raw.loc[abs(cudf_raw.dhdt_slope) > 1] +print(f"Trimmed {len(cudf_raw)} -> {len(cudf_many)}") + +# %% [markdown] +# ## Label ICESat-2 points according to their drainage basin +# +# Uses Point in Polygon. +# For each point, find out which Antarctic Drainage Basin they are in. +# This will also remove the points on floating (FR) ice shelves and islands (IS), +# so that we keep only points on the grounded (GR) ice regions. + + +# %% +# Read in Antarctic Drainage Basin Boundaries shapefile into a GeoDataFrame +ice_boundaries: gpd.GeoDataFrame = gpd.read_file( + filename="Quantarctica3/Glaciology/MEaSUREs Antarctic Boundaries/IceBoundaries_Antarctica_v2.shp" +) +drainage_basins: gpd.GeoDataFrame = ice_boundaries.query(expr="TYPE == 'GR'") + + +# %% +# Use point in polygon to label points according to the drainage basins they fall in +cudf_many["drainage_basin"]: cudf.Series = deepicedrain.point_in_polygon_gpu( + points_df=cudf_many, poly_df=drainage_basins +) +X_many = cudf_many.dropna() # drop points that are not in a drainage basin + +# %% [markdown] +# # Find Active Subglacial Lake clusters +# +# Uses Density-based spatial clustering of applications with noise (DBSCAN). + +# %% +def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series: + """ + Density-based spatial clustering of applications with noise (DBSCAN) + See also https://www.naftaliharris.com/blog/visualizing-dbscan-clustering + """ + # Run DBSCAN using 2500 m distance, and minimum of 250 points + dbscan = cuml.DBSCAN(eps=2500, min_samples=250) + dbscan.fit(X=X) + + return dbscan.labels_ + + +# %% +# Subglacial lake finder +activelakes: dict = { + "basin_name": [], + "maxabsdhdt": [], + "refgtracks": [], + "geometry": [], +} +for basin_index in tqdm.tqdm(iterable=drainage_basins.index): + # Initial data cleaning, filter to rows that are in the drainage basin + basin = drainage_basins.loc[basin_index] + X = X_many.loc[X_many.drainage_basin == basin.NAME] # .reset_index(drop=True) + if len(X) <= 1000: # don't run on too few points + continue + print(f"{len(X)} rows at {basin.NAME}") + + # Run unsupervised clustering separately on draining and filling lakes + for activity, X_ in ( + ("draining", X.loc[X.dhdt_slope < -1]), + ("filling", X.loc[X.dhdt_slope > 1]), + ): + labels_ = find_clusters(X=X_[["x", "y", "dhdt_slope"]]) + n_clusters_ = len(labels_.unique()) - 1 # No. of clusters minus noise (-1) + print(f"{n_clusters_} {activity} lakes found") + + # Store attribute and geometry information of each active lake + for i in range(n_clusters_): + lake_points: cudf.DataFrame = X_.loc[labels_ == i] + + try: + assert len(lake_points) > 2 + except AssertionError: + continue + + multipoint: shapely.geometry.MultiPoint = shapely.geometry.MultiPoint( + points=lake_points[["x", "y"]].as_matrix() + ) + convexhull: shapely.geometry.Polygon = multipoint.convex_hull + + maxabsdhdt: float = ( + lake_points.dhdt_slope.max() + if activity == "filling" + else lake_points.dhdt_slope.min() + ) + refgtracks: str = "|".join( + map(str, lake_points.referencegroundtrack.unique().to_pandas()) + ) + + activelakes["basin_name"].append(basin.NAME) + activelakes["maxabsdhdt"].append(maxabsdhdt) + activelakes["refgtracks"].append(refgtracks) + activelakes["geometry"].append(convexhull) + +if len(activelakes["geometry"]) >= 1: + gdf = gpd.GeoDataFrame(activelakes, crs="EPSG:3031") + gdf.to_file(filename="antarctic_subglacial_lakes.geojson", driver="GeoJSON") + +print(f"Total of {len(gdf)} subglacial lakes founds") + +# %% [markdown] +# ## Visualize lakes + +# %% +# Plot clusters on a map in colour, noise points/outliers as small dots +X_cpu = X_.to_pandas() + +fig = pygmt.Figure() +labels_cpu = labels_.to_pandas().astype(np.int32) +sizes = (labels_cpu < 0).map(arg={True: 0.02, False: 0.2}) +if n_clusters_: + pygmt.makecpt(cmap="categorical", series=(-0.5, n_clusters_ - 0.5, 1)) +else: + pygmt.makecpt(cmap="gray") +fig.plot( + x=X_cpu.x, + y=X_cpu.y, + sizes=sizes, + style="cc", + color=labels_cpu, + cmap=True, + frame=[ + f'WSne+t"Estimated number of clusters at {basin.NAME}: {n_clusters_}"', + 'xaf+l"Polar Stereographic X (km)"', + 'yaf+l"Polar Stereographic Y (km)"', + ], +) +fig.colorbar(frame='af+l"Cluster Number"') +fig.savefig(fname=f"figures/subglacial_lakes_at_{basin.NAME}.png") +fig.show() + + # %% [markdown] # # Crossover Track Analysis # From 9142e8756688b1f73050c76af1535ddab5d2ba61 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Sun, 13 Sep 2020 23:38:07 +1200 Subject: [PATCH 12/12] :recycle: Refactor to enable plotting drain/fill lakes in one figure Combining draining/filling active lake cluster labels, which allows us to reduce the number of for-loop nesting in the active subglacial lake finder code, and plot both draining/filling lakes in the same figure! Cluster labels are now negative integers for draining lakes, positive integers for filling lakes, and NaN for noise points. Lake cluster plot now uses red (draining) and blue (filling) 'polar' colormap, with unclassified noise points in black as before. Code still takes 11 seconds to run for the entire Antarctic continent which is awesome! Also made a minor change to deepicedrain/__init__.py script to disable loading IceSat2Explorer dashboard script otherwise `import deepicedrain` will load stuff into GPU memory! --- atlxi_dhdt.ipynb | 3 +- atlxi_dhdt.py | 3 +- atlxi_lake.ipynb | 110 ++++++++++++++++++++++--------------- atlxi_lake.py | 113 +++++++++++++++++++++++---------------- deepicedrain/__init__.py | 3 +- 5 files changed, 139 insertions(+), 93 deletions(-) diff --git a/atlxi_dhdt.ipynb b/atlxi_dhdt.ipynb index aea20db..37b502c 100644 --- a/atlxi_dhdt.ipynb +++ b/atlxi_dhdt.ipynb @@ -44,6 +44,7 @@ "import dask\n", "import datashader\n", "import deepicedrain\n", + "import deepicedrain.vizplots\n", "import holoviews as hv\n", "import hvplot.cudf # comment out if no GPU\n", "import hvplot.pandas\n", @@ -942,7 +943,7 @@ "source": [ "# Interactive holoviews scatter plot to find referencegroundtrack needed\n", "# Tip: Hover over the points, and find those with high 'dhdt_slope' values\n", - "viewer = deepicedrain.IceSat2Explorer(name=\"ICESat-2 Explorer\")\n", + "viewer = deepicedrain.vizplots.IceSat2Explorer(name=\"ICESat-2 Explorer\")\n", "dashboard: pn.layout.Column = pn.Column(viewer.widgets, viewer.view)\n", "# dashboard" ] diff --git a/atlxi_dhdt.py b/atlxi_dhdt.py index 0cac79d..67c4896 100644 --- a/atlxi_dhdt.py +++ b/atlxi_dhdt.py @@ -47,6 +47,7 @@ import dask import datashader import deepicedrain +import deepicedrain.vizplots import holoviews as hv import hvplot.cudf # comment out if no GPU import hvplot.pandas @@ -431,7 +432,7 @@ # %% # Interactive holoviews scatter plot to find referencegroundtrack needed # Tip: Hover over the points, and find those with high 'dhdt_slope' values -viewer = deepicedrain.IceSat2Explorer(name="ICESat-2 Explorer") +viewer = deepicedrain.vizplots.IceSat2Explorer(name="ICESat-2 Explorer") dashboard: pn.layout.Column = pn.Column(viewer.widgets, viewer.view) # dashboard diff --git a/atlxi_lake.ipynb b/atlxi_lake.ipynb index 04add6b..431c30b 100644 --- a/atlxi_lake.ipynb +++ b/atlxi_lake.ipynb @@ -161,7 +161,11 @@ " dbscan = cuml.DBSCAN(eps=2500, min_samples=250)\n", " dbscan.fit(X=X)\n", "\n", - " return dbscan.labels_" + " cluster_labels = dbscan.labels_ + 1 # noise points -1 becomes 0\n", + " cluster_labels = cluster_labels.mask(cond=cluster_labels == 0) # turn 0 to NaN\n", + " cluster_labels.index = X.index # let labels have same index as input data\n", + "\n", + " return cluster_labels" ] }, { @@ -177,6 +181,9 @@ " \"refgtracks\": [],\n", " \"geometry\": [],\n", "}\n", + "# for basin_index in tqdm.tqdm(\n", + "# iterable=drainage_basins[drainage_basins.NAME.str.startswith(\"Whillans\")].index\n", + "# ):\n", "for basin_index in tqdm.tqdm(iterable=drainage_basins.index):\n", " # Initial data cleaning, filter to rows that are in the drainage basin\n", " basin = drainage_basins.loc[basin_index]\n", @@ -186,41 +193,47 @@ " print(f\"{len(X)} rows at {basin.NAME}\")\n", "\n", " # Run unsupervised clustering separately on draining and filling lakes\n", - " for activity, X_ in (\n", - " (\"draining\", X.loc[X.dhdt_slope < -1]),\n", - " (\"filling\", X.loc[X.dhdt_slope > 1]),\n", - " ):\n", - " labels_ = find_clusters(X=X_[[\"x\", \"y\", \"dhdt_slope\"]])\n", - " n_clusters_ = len(labels_.unique()) - 1 # No. of clusters minus noise (-1)\n", - " print(f\"{n_clusters_} {activity} lakes found\")\n", + " # Draining lake points have negative labels (e.g. -1, -2, 3),\n", + " # Filling lake points have positive labels (e.g. 1, 2, 3),\n", + " # Noise points have NaN labels (i.e. NaN)\n", + " cluster_vars = [\"x\", \"y\", \"dhdt_slope\"]\n", + " draining_lake_labels = -find_clusters(X=X.loc[X.dhdt_slope < 0][cluster_vars])\n", + " filling_lake_labels = find_clusters(X=X.loc[X.dhdt_slope > 0][cluster_vars])\n", + " lake_labels = cudf.concat(objs=[draining_lake_labels, filling_lake_labels])\n", + " lake_labels.name = \"cluster_label\"\n", + "\n", + " clusters: cudf.Series = lake_labels.unique()\n", + " print(\n", + " f\"{(clusters < 0).sum()} draining and {(clusters > 0).sum()} filling lakes found\"\n", + " )\n", "\n", + " for cluster_label in clusters.to_array():\n", " # Store attribute and geometry information of each active lake\n", - " for i in range(n_clusters_):\n", - " lake_points: cudf.DataFrame = X_.loc[labels_ == i]\n", + " lake_points: cudf.DataFrame = X.loc[lake_labels == cluster_label]\n", "\n", - " try:\n", - " assert len(lake_points) > 2\n", - " except AssertionError:\n", - " continue\n", + " try:\n", + " assert len(lake_points) > 2\n", + " except AssertionError:\n", + " continue\n", "\n", - " multipoint: shapely.geometry.MultiPoint = shapely.geometry.MultiPoint(\n", - " points=lake_points[[\"x\", \"y\"]].as_matrix()\n", - " )\n", - " convexhull: shapely.geometry.Polygon = multipoint.convex_hull\n", + " multipoint: shapely.geometry.MultiPoint = shapely.geometry.MultiPoint(\n", + " points=lake_points[[\"x\", \"y\"]].as_matrix()\n", + " )\n", + " convexhull: shapely.geometry.Polygon = multipoint.convex_hull\n", "\n", - " maxabsdhdt: float = (\n", - " lake_points.dhdt_slope.max()\n", - " if activity == \"filling\"\n", - " else lake_points.dhdt_slope.min()\n", - " )\n", - " refgtracks: str = \"|\".join(\n", - " map(str, lake_points.referencegroundtrack.unique().to_pandas())\n", - " )\n", + " maxabsdhdt: float = (\n", + " lake_points.dhdt_slope.max()\n", + " if cluster_label > 0 # positive label = filling\n", + " else lake_points.dhdt_slope.min() # negative label = draining\n", + " )\n", + " refgtracks: str = \"|\".join(\n", + " map(str, lake_points.referencegroundtrack.unique().to_pandas())\n", + " )\n", "\n", - " activelakes[\"basin_name\"].append(basin.NAME)\n", - " activelakes[\"maxabsdhdt\"].append(maxabsdhdt)\n", - " activelakes[\"refgtracks\"].append(refgtracks)\n", - " activelakes[\"geometry\"].append(convexhull)\n", + " activelakes[\"basin_name\"].append(basin.NAME)\n", + " activelakes[\"maxabsdhdt\"].append(maxabsdhdt)\n", + " activelakes[\"refgtracks\"].append(refgtracks)\n", + " activelakes[\"geometry\"].append(convexhull)\n", "\n", "if len(activelakes[\"geometry\"]) >= 1:\n", " gdf = gpd.GeoDataFrame(activelakes, crs=\"EPSG:3031\")\n", @@ -243,32 +256,43 @@ "lines_to_next_cell": 2 }, "outputs": [], + "source": [ + "# Concatenate XY points with labels, and move data from GPU to CPU\n", + "X: cudf.DataFrame = cudf.concat(objs=[X, lake_labels], axis=\"columns\")\n", + "X_ = X.to_pandas()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Plot clusters on a map in colour, noise points/outliers as small dots\n", - "X_cpu = X_.to_pandas()\n", - "\n", "fig = pygmt.Figure()\n", - "labels_cpu = labels_.to_pandas().astype(np.int32)\n", - "sizes = (labels_cpu < 0).map(arg={True: 0.02, False: 0.2})\n", + "n_clusters_ = len(X_.cluster_label.unique()) - 1 # No. of clusters minus noise (NaN)\n", + "sizes = (X_.cluster_label.isna()).map(arg={True: 0.01, False: 0.1})\n", "if n_clusters_:\n", - " pygmt.makecpt(cmap=\"categorical\", series=(-0.5, n_clusters_ - 0.5, 1))\n", + " pygmt.makecpt(cmap=\"polar+h0\", series=(-1.5, 1.5, 1), reverse=True, D=True)\n", "else:\n", " pygmt.makecpt(cmap=\"gray\")\n", "fig.plot(\n", - " x=X_cpu.x,\n", - " y=X_cpu.y,\n", + " x=X_.x,\n", + " y=X_.y,\n", " sizes=sizes,\n", " style=\"cc\",\n", - " color=labels_cpu,\n", + " color=X_.cluster_label,\n", " cmap=True,\n", " frame=[\n", - " f'WSne+t\"Estimated number of clusters at {basin.NAME}: {n_clusters_}\"',\n", - " 'xaf+l\"Polar Stereographic X (km)\"',\n", - " 'yaf+l\"Polar Stereographic Y (km)\"',\n", + " f'WSne+t\"Estimated number of lake clusters at {basin.NAME}: {n_clusters_}\"',\n", + " 'xaf+l\"Polar Stereographic X (m)\"',\n", + " 'yaf+l\"Polar Stereographic Y (m)\"',\n", " ],\n", ")\n", - "fig.colorbar(frame='af+l\"Cluster Number\"')\n", - "fig.savefig(fname=f\"figures/subglacial_lakes_at_{basin.NAME}.png\")\n", + "basinx, basiny = basin.geometry.exterior.coords.xy\n", + "fig.plot(x=basinx, y=basiny, pen=\"thinnest,-\")\n", + "fig.colorbar(frame='af+l\"Draining/Filling\"', position='JBC+n\"Unclassified\"')\n", + "fig.savefig(fname=f\"figures/subglacial_lake_clusters_at_{basin.NAME}.png\")\n", "fig.show()" ] }, diff --git a/atlxi_lake.py b/atlxi_lake.py index 3e5093f..abf7f38 100644 --- a/atlxi_lake.py +++ b/atlxi_lake.py @@ -116,7 +116,11 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series: dbscan = cuml.DBSCAN(eps=2500, min_samples=250) dbscan.fit(X=X) - return dbscan.labels_ + cluster_labels = dbscan.labels_ + 1 # noise points -1 becomes 0 + cluster_labels = cluster_labels.mask(cond=cluster_labels == 0) # turn 0 to NaN + cluster_labels.index = X.index # let labels have same index as input data + + return cluster_labels # %% @@ -127,6 +131,9 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series: "refgtracks": [], "geometry": [], } +# for basin_index in tqdm.tqdm( +# iterable=drainage_basins[drainage_basins.NAME.str.startswith("Whillans")].index +# ): for basin_index in tqdm.tqdm(iterable=drainage_basins.index): # Initial data cleaning, filter to rows that are in the drainage basin basin = drainage_basins.loc[basin_index] @@ -136,41 +143,47 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series: print(f"{len(X)} rows at {basin.NAME}") # Run unsupervised clustering separately on draining and filling lakes - for activity, X_ in ( - ("draining", X.loc[X.dhdt_slope < -1]), - ("filling", X.loc[X.dhdt_slope > 1]), - ): - labels_ = find_clusters(X=X_[["x", "y", "dhdt_slope"]]) - n_clusters_ = len(labels_.unique()) - 1 # No. of clusters minus noise (-1) - print(f"{n_clusters_} {activity} lakes found") + # Draining lake points have negative labels (e.g. -1, -2, 3), + # Filling lake points have positive labels (e.g. 1, 2, 3), + # Noise points have NaN labels (i.e. NaN) + cluster_vars = ["x", "y", "dhdt_slope"] + draining_lake_labels = -find_clusters(X=X.loc[X.dhdt_slope < 0][cluster_vars]) + filling_lake_labels = find_clusters(X=X.loc[X.dhdt_slope > 0][cluster_vars]) + lake_labels = cudf.concat(objs=[draining_lake_labels, filling_lake_labels]) + lake_labels.name = "cluster_label" + + clusters: cudf.Series = lake_labels.unique() + print( + f"{(clusters < 0).sum()} draining and {(clusters > 0).sum()} filling lakes found" + ) + for cluster_label in clusters.to_array(): # Store attribute and geometry information of each active lake - for i in range(n_clusters_): - lake_points: cudf.DataFrame = X_.loc[labels_ == i] - - try: - assert len(lake_points) > 2 - except AssertionError: - continue - - multipoint: shapely.geometry.MultiPoint = shapely.geometry.MultiPoint( - points=lake_points[["x", "y"]].as_matrix() - ) - convexhull: shapely.geometry.Polygon = multipoint.convex_hull - - maxabsdhdt: float = ( - lake_points.dhdt_slope.max() - if activity == "filling" - else lake_points.dhdt_slope.min() - ) - refgtracks: str = "|".join( - map(str, lake_points.referencegroundtrack.unique().to_pandas()) - ) - - activelakes["basin_name"].append(basin.NAME) - activelakes["maxabsdhdt"].append(maxabsdhdt) - activelakes["refgtracks"].append(refgtracks) - activelakes["geometry"].append(convexhull) + lake_points: cudf.DataFrame = X.loc[lake_labels == cluster_label] + + try: + assert len(lake_points) > 2 + except AssertionError: + continue + + multipoint: shapely.geometry.MultiPoint = shapely.geometry.MultiPoint( + points=lake_points[["x", "y"]].as_matrix() + ) + convexhull: shapely.geometry.Polygon = multipoint.convex_hull + + maxabsdhdt: float = ( + lake_points.dhdt_slope.max() + if cluster_label > 0 # positive label = filling + else lake_points.dhdt_slope.min() # negative label = draining + ) + refgtracks: str = "|".join( + map(str, lake_points.referencegroundtrack.unique().to_pandas()) + ) + + activelakes["basin_name"].append(basin.NAME) + activelakes["maxabsdhdt"].append(maxabsdhdt) + activelakes["refgtracks"].append(refgtracks) + activelakes["geometry"].append(convexhull) if len(activelakes["geometry"]) >= 1: gdf = gpd.GeoDataFrame(activelakes, crs="EPSG:3031") @@ -182,31 +195,37 @@ def find_clusters(X: cudf.core.dataframe.DataFrame) -> cudf.core.series.Series: # ## Visualize lakes # %% -# Plot clusters on a map in colour, noise points/outliers as small dots -X_cpu = X_.to_pandas() +# Concatenate XY points with labels, and move data from GPU to CPU +X: cudf.DataFrame = cudf.concat(objs=[X, lake_labels], axis="columns") +X_ = X.to_pandas() + +# %% +# Plot clusters on a map in colour, noise points/outliers as small dots fig = pygmt.Figure() -labels_cpu = labels_.to_pandas().astype(np.int32) -sizes = (labels_cpu < 0).map(arg={True: 0.02, False: 0.2}) +n_clusters_ = len(X_.cluster_label.unique()) - 1 # No. of clusters minus noise (NaN) +sizes = (X_.cluster_label.isna()).map(arg={True: 0.01, False: 0.1}) if n_clusters_: - pygmt.makecpt(cmap="categorical", series=(-0.5, n_clusters_ - 0.5, 1)) + pygmt.makecpt(cmap="polar+h0", series=(-1.5, 1.5, 1), reverse=True, D=True) else: pygmt.makecpt(cmap="gray") fig.plot( - x=X_cpu.x, - y=X_cpu.y, + x=X_.x, + y=X_.y, sizes=sizes, style="cc", - color=labels_cpu, + color=X_.cluster_label, cmap=True, frame=[ - f'WSne+t"Estimated number of clusters at {basin.NAME}: {n_clusters_}"', - 'xaf+l"Polar Stereographic X (km)"', - 'yaf+l"Polar Stereographic Y (km)"', + f'WSne+t"Estimated number of lake clusters at {basin.NAME}: {n_clusters_}"', + 'xaf+l"Polar Stereographic X (m)"', + 'yaf+l"Polar Stereographic Y (m)"', ], ) -fig.colorbar(frame='af+l"Cluster Number"') -fig.savefig(fname=f"figures/subglacial_lakes_at_{basin.NAME}.png") +basinx, basiny = basin.geometry.exterior.coords.xy +fig.plot(x=basinx, y=basiny, pen="thinnest,-") +fig.colorbar(frame='af+l"Draining/Filling"', position='JBC+n"Unclassified"') +fig.savefig(fname=f"figures/subglacial_lake_clusters_at_{basin.NAME}.png") fig.show() diff --git a/deepicedrain/__init__.py b/deepicedrain/__init__.py index 97b12a2..d029e66 100644 --- a/deepicedrain/__init__.py +++ b/deepicedrain/__init__.py @@ -11,7 +11,8 @@ lonlat_to_xy, point_in_polygon_gpu, ) -from deepicedrain.vizplots import IceSat2Explorer + +# from deepicedrain.vizplots import IceSat2Explorer __version__: str = "0.2.1"