diff --git a/docs/user-guide/from_file.ipynb b/docs/user-guide/from_file.ipynb new file mode 100644 index 000000000..720bd27d6 --- /dev/null +++ b/docs/user-guide/from_file.ipynb @@ -0,0 +1,251 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4a432a8bf95d9cdb", + "metadata": {}, + "source": [ + "# Reading & Working with Geometry Files\n", + "\n", + "This notebooks demonstrates how to use the `Grid.from_file()` class method to load in geometry files such as:\n", + "\n", + "1. Shapefile\n", + "2. GeoJSON\n", + "\n", + "Highlighted is a workflow showcasing how to remap a variable from an unstructured grid to a Shapefile." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b35ba4a2c30750e4", + "metadata": { + "ExecuteTime": { + "end_time": "2024-10-09T17:50:50.244285Z", + "start_time": "2024-10-09T17:50:50.239653Z" + } + }, + "outputs": [], + "source": [ + "import uxarray as ux\n", + "import warnings\n", + "import geocat.datafiles as geodf\n", + "\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "markdown", + "id": "395a3db7-495c-4cff-b733-06bbe522a604", + "metadata": {}, + "source": [ + "## Load a shapefile and plot \n", + "\n", + "* This section demonstrates how to load a shapefile using uxarray's Grid.from_file() function\n", + "* The shapefile used in this example is the US national boundary file from the US Census Bureau. It is a 20m resolution shapefile that represents the national boundary of the United States. \n", + "* The data plotted is subset to a specific bounding box, which is defined by the latitude and longitude bounds. The result is plotted using the matplotlib backend." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b4160275c09fe6b0", + "metadata": { + "ExecuteTime": { + "end_time": "2024-10-09T17:50:51.217211Z", + "start_time": "2024-10-09T17:50:50.540946Z" + } + }, + "outputs": [], + "source": [ + "shp_filename = (\n", + " \"../../test/meshfiles/shp/cb_2018_us_nation_20m/cb_2018_us_nation_20m.shp\"\n", + ")\n", + "uxds = ux.Grid.from_file(shp_filename)\n", + "lat_bounds = [-90, -70]\n", + "lon_bounds = [20, 55]\n", + "uxds = uxds.subset.bounding_box(lon_bounds, lat_bounds)\n", + "uxds.plot(\n", + " title=\"US 20m Focus on Mainland (cb_2018_us_nation_20m.shp)\",\n", + " backend=\"matplotlib\",\n", + " width=500,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "9808189d", + "metadata": {}, + "source": [ + "## Load a Geojson file and plot\n", + "\n", + " * This section demonstrates how to load a Geojson file using uxarray's Grid.from_file() function\n", + " * The Geojson file used in this example is a few buildings around downtown Chicago. The plot is shown using the \"matplotlib\" backend for bounds specific to the region.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31d92527", + "metadata": {}, + "outputs": [], + "source": [ + "geojson_filename = \"../../test/meshfiles/geojson/sample_chicago_buildings.geojson\"\n", + "uxgeojson = ux.Grid.from_file(geojson_filename)\n", + "lat_bounds = [41.6, 42.1]\n", + "lon_bounds = [-87.7, -87.5]\n", + "uxgeojson.subset.bounding_box(lon_bounds, lat_bounds).plot(backend=\"matplotlib\")" + ] + }, + { + "cell_type": "markdown", + "id": "f2f14b3a", + "metadata": {}, + "source": [ + "## Open NetCDF mesh file using the Grid.from_file() function\n", + "\n", + "* Regular NetCDF files can also be opened using this function. Backend options available are:\n", + " * xarray\n", + " * geopandas (default for opening shapefile, geojson file and other file formats supported by geopandas read_file function)\n", + "* In the following code, we load a NetCDF mesh file: scrip/outCSne8/outCSne8.nc and print out the grid contents." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aba5d650", + "metadata": {}, + "outputs": [], + "source": [ + "nc_filename = \"../../test/meshfiles/scrip/outCSne8/outCSne8.nc\"\n", + "uxgrid = ux.Grid.from_file(nc_filename, backend=\"xarray\")\n", + "uxgrid" + ] + }, + { + "cell_type": "markdown", + "id": "f27481e2-5c1c-4189-b0c7-39737c4e47f8", + "metadata": {}, + "source": [ + "## Remapping from Shapefile\n", + "\n", + "The following steps are needed for Remapping Global Relative Humidity Data on to a specific region defined by Shapefile using UXarray\n", + "\n", + "1. **Read the shapefile** (uxds)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "002b3f66-11ed-4f3d-905f-967802b9fff2", + "metadata": {}, + "outputs": [], + "source": [ + "shp_filename = (\n", + " \"../../test/meshfiles/shp/chicago_neighborhoods/chicago_neighborhoods.shp\"\n", + ")\n", + "uxds = ux.Grid.from_file(shp_filename)\n", + "lat_bounds = [41, 43]\n", + "lon_bounds = [-89, -90]\n", + "uxds = uxds.subset.bounding_box(lon_bounds, lat_bounds)\n", + "uxds.plot(\n", + " title=\"Chicago Neighborhoods\",\n", + " backend=\"bokeh\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "c9447221-aaba-4155-868d-f88b791e559e", + "metadata": {}, + "source": [ + "\n", + "2. **Initialize Input Data Files**\n", + " - The input datasets consist of NetCDF files that include global relative humidity data.\n", + " - The `datafiles` variable points to two NetCDF files using the `geodf.get` function, specifying the paths:\n", + " - The first file contains meteorological diagnostic data: \n", + " `netcdf_files/MPAS/FalkoJudt/dyamond_1/30km/diag.2016-08-20_00.00.00_subset.nc`.\n", + " - The second file provides the MPAS grid specification: \n", + " `netcdf_files/MPAS/FalkoJudt/dyamond_1/30km/x1.655362.grid_subset.nc`.\n", + "\n", + "2. **Open the Datasets with UXarray**\n", + " - The `ux.open_dataset()` function is used to load these files, making them accessible as a UXarray Dataset.\n", + " - `uxds_source` is the opened dataset that holds the meteorological data, such as relative humidity, structured over the MPAS grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b629686-8286-4336-b188-8a1b12c0fcd2", + "metadata": {}, + "outputs": [], + "source": [ + "datafiles = (\n", + " geodf.get(\n", + " \"netcdf_files/MPAS/FalkoJudt/dyamond_1/30km/diag.2016-08-20_00.00.00_subset.nc\"\n", + " ),\n", + " geodf.get(\"netcdf_files/MPAS/FalkoJudt/dyamond_1/30km/x1.655362.grid_subset.nc\"),\n", + ")\n", + "\n", + "uxds_source = ux.open_dataset(datafiles[1], datafiles[0])" + ] + }, + { + "cell_type": "markdown", + "id": "ab2f5f8c", + "metadata": {}, + "source": [ + "4. **Remap Relative Humidity Data**\n", + " - The `relhum_200hPa` variable is accessed from `uxds_source` to extract relative humidity data at 200 hPa pressure level.\n", + " - **Inverse Distance Weighted Remapping**:\n", + " - The data is remapped using UXarray’s `inverse_distance_weighted` method.\n", + " - The remapping is done to \"face centers,\" adapting the data from its original grid to align with a new shape or structure.\n", + "\n", + "5. **Plot the Remapped Data**\n", + " - The remapped data for Chicago neighborhoods is plotted using UXarray's plotting utilities.\n", + " - The plot uses the `sequential_blue` colormap and is rendered with the `bokeh` backend.\n", + " - The title of the plot is \"Chicago Neighborhoods Relative Humidity,\" giving a clear representation of how relative humidity varies spatially." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af78a1ed-e9e4-4dd0-a58f-87640e7d5f11", + "metadata": {}, + "outputs": [], + "source": [ + "chicago_relative_humidty = uxds_source[\"relhum_200hPa\"].remap.inverse_distance_weighted(\n", + " uxds, remap_to=\"face centers\"\n", + ")\n", + "\n", + "chicago_relative_humidty[0].plot(\n", + " cmap=ux.cmaps.sequential_blue,\n", + " title=\"Chicago Neighborhoods Relative Humidty\",\n", + " backend=\"bokeh\",\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/userguide.rst b/docs/userguide.rst index 2f1f85cbb..b4e6249a0 100644 --- a/docs/userguide.rst +++ b/docs/userguide.rst @@ -82,7 +82,8 @@ These user guides provide additional details about specific features in UXarray. `Compatibility with HoloViz Tools `_ Use UXarray with HoloViz tools - +`Reading & Working with Geometry Files `_ + Load and work with geometry files (i.e. Shapefile, GeoJSON) .. toctree:: :hidden: @@ -106,3 +107,4 @@ These user guides provide additional details about specific features in UXarray. user-guide/structured.ipynb user-guide/from-points.ipynb user-guide/holoviz.ipynb + user-guide/from_file.ipynb diff --git a/test/meshfiles/shp/chicago_neighborhoods/chicago_neighborhoods.shp b/test/meshfiles/shp/chicago_neighborhoods/chicago_neighborhoods.shp new file mode 100644 index 000000000..eca9082a2 Binary files /dev/null and b/test/meshfiles/shp/chicago_neighborhoods/chicago_neighborhoods.shp differ diff --git a/test/meshfiles/shp/chicago_neighborhoods/chicago_neighborhoods.shx b/test/meshfiles/shp/chicago_neighborhoods/chicago_neighborhoods.shx new file mode 100644 index 000000000..1a3970ebe Binary files /dev/null and b/test/meshfiles/shp/chicago_neighborhoods/chicago_neighborhoods.shx differ diff --git a/test/test_geopandas.py b/test/test_geopandas.py index 84d790019..279a0349e 100644 --- a/test/test_geopandas.py +++ b/test/test_geopandas.py @@ -9,6 +9,7 @@ shp_filename_5poly = current_path / "meshfiles" / "shp" / "5poly/5poly.shp" shp_filename_multi = current_path / "meshfiles" / "shp" / "multipoly/multipoly.shp" geojson_filename = current_path / "meshfiles" / "geojson" / "sample_chicago_buildings.geojson" +nc_filename = current_path / "meshfiles" / "scrip" / "outCSne8" / "outCSne8.nc" def test_read_shpfile(): """Read a shapefile.""" @@ -43,3 +44,8 @@ def test_read_geojson(): uxgrid = ux.Grid.from_file(geojson_filename) assert uxgrid.n_face == 10 assert uxgrid.n_max_face_nodes == 36 + +def test_load_xarray_with_from_file(): + """ Use backend xarray to call the from_file method.""" + uxgrid = ux.Grid.from_file(nc_filename, backend="xarray") + uxgrid.validate() diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index c975ebff8..7f51c47c5 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -365,7 +365,8 @@ def from_file( grid_ds, source_dims_dict = _read_geodataframe(filename) elif backend == "xarray": - grid_ds, source_dims_dict = cls.from_dataset(filename) + dataset = xr.open_dataset(filename, **kwargs) + return cls.from_dataset(dataset) else: raise ValueError("Backend not supported")