From 567b6eefad433ff6eaf0ecb85c51030540242d9b Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sat, 28 Sep 2024 00:49:13 -0400 Subject: [PATCH] Add nhd notebook example --- docs/notebooks/98_watershed.ipynb | 124 ++++++++++++++++++++++++++++++ docs/tutorials.md | 1 + examples/README.md | 1 + leafmap/common.py | 84 ++++++++++++-------- mkdocs.yml | 1 + 5 files changed, 179 insertions(+), 32 deletions(-) create mode 100644 docs/notebooks/98_watershed.ipynb diff --git a/docs/notebooks/98_watershed.ipynb b/docs/notebooks/98_watershed.ipynb new file mode 100644 index 0000000000..c03ff7a508 --- /dev/null +++ b/docs/notebooks/98_watershed.ipynb @@ -0,0 +1,124 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "[![image](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://demo.leafmap.org/lab/index.html?path=notebooks/98_watershed.ipynb)\n", + "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/leafmap/blob/master/docs/notebooks/98_watershed.ipynb)\n", + "[![image](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/opengeos/leafmap/HEAD)\n", + "\n", + "**Retrieving watershed boundaries from the National Hydrography Dataset (NHD)**\n", + "\n", + "Uncomment the following line to install [leafmap](https://leafmap.org) if needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install -U leafmap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import leafmap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "point_geometry = {\"x\": -114.452985, \"y\": 36.13323}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "gdf = leafmap.get_wbd(point_geometry, digit=8, return_geometry=True)\n", + "gdf.explore()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "bbox_geometry = {\"xmin\": -115.0954, \"ymin\": 36.1000, \"xmax\": -114.6658, \"ymax\": 36.1986}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "gdf = leafmap.get_wbd(bbox_geometry, digit=8, return_geometry=True)\n", + "gdf.explore()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "huc_id = \"10160002\"\n", + "gdf = leafmap.get_wbd(searchText=huc_id, digit=8)\n", + "gdf.explore()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "gdf = leafmap.get_wbd(searchText=huc_id, digit=10)\n", + "gdf.explore()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "geo", + "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.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials.md b/docs/tutorials.md index 57440c18d7..016df026a8 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -109,6 +109,7 @@ 95. Editing vector data interactively ([notebook](https://leafmap.org/notebooks/95_edit_vector)) 96. Batch editing vector data attributes interactively ([notebook](https://leafmap.org/notebooks/96_batch_edit_vector)) 97. Downloading Overture Maps data ([notebook](https://leafmap.org/notebooks/97_overture_data)) +98. Retrieving watershed boundaries from the National Hydrography Dataset (NHD) ([notebook](https://leafmap.org/notebooks/98_watershed)) ## Demo diff --git a/examples/README.md b/examples/README.md index 6c02488628..73c33d9b8b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -116,6 +116,7 @@ 95. Editing vector data interactively ([notebook](https://leafmap.org/notebooks/95_edit_vector)) 96. Batch editing vector data attributes interactively ([notebook](https://leafmap.org/notebooks/96_batch_edit_vector)) 97. Downloading Overture Maps data ([notebook](https://leafmap.org/notebooks/97_overture_data)) +98. Retrieving watershed boundaries from the National Hydrography Dataset (NHD) ([notebook](https://leafmap.org/notebooks/98_watershed)) ## Demo diff --git a/leafmap/common.py b/leafmap/common.py index 0063084f5f..6140383f44 100644 --- a/leafmap/common.py +++ b/leafmap/common.py @@ -14303,7 +14303,7 @@ def construct_bbox( return geometry -def get_nhd_wbd( +def get_nhd( geometry: Union[ "gpd.GeoDataFrame", str, List[float], Tuple[float, float, float, float] ], @@ -14545,7 +14545,8 @@ def detect_geometry_type(geometry): def get_wbd( - geometry: Union["gpd.GeoDataFrame", Dict[str, Any]], + geometry: Union["gpd.GeoDataFrame", Dict[str, Any]] = None, + searchText: Optional[str] = None, inSR: str = "4326", outSR: str = "3857", digit: int = 8, @@ -14600,6 +14601,14 @@ def detect_geometry_type(geometry): else: raise ValueError("Unsupported geometry type or invalid geometry structure.") + allowed_digit_values = [2, 4, 6, 8, 10, 12, 14, 16] + if digit not in allowed_digit_values: + raise ValueError( + f"Invalid digit value. Allowed values are {allowed_digit_values}" + ) + + layer = allowed_digit_values.index(digit) + 1 + # Convert GeoDataFrame to a dictionary if needed if isinstance(geometry, gpd.GeoDataFrame): geometry_dict = _convert_geodataframe_to_esri_format(geometry)[0] @@ -14609,38 +14618,44 @@ def detect_geometry_type(geometry): geometry_dict = geometry elif isinstance(geometry, str): geometry_dict = geometry - else: + elif searchText is None: raise ValueError( "Invalid geometry input. Must be a GeoDataFrame or a dictionary." ) - - # Convert geometry to a JSON string (required by the API) - if isinstance(geometry_dict, dict): - geometry_json = json.dumps(geometry_dict) else: - geometry_json = geometry_dict - - allowed_digit_values = [2, 4, 6, 8, 10, 12, 14, 16] - if digit not in allowed_digit_values: - raise ValueError( - f"Invalid digit value. Allowed values are {allowed_digit_values}" - ) + geometry_dict = None - layer = allowed_digit_values.index(digit) + 1 - - # API URL for querying the WBD - url = f"https://hydro.nationalmap.gov/arcgis/rest/services/wbd/MapServer/{layer}/query" - - # Construct the query parameters - params = { - "geometry": geometry_json, - "geometryType": geometry_type, - "inSR": inSR, - "spatialRel": spatialRel, - "outFields": outFields, - "returnGeometry": str(return_geometry).lower(), - "f": "json", - } + if geometry_dict is not None: + # Convert geometry to a JSON string (required by the API) + if isinstance(geometry_dict, dict): + geometry_json = json.dumps(geometry_dict) + else: + geometry_json = geometry_dict + + # Construct the query parameters + params = { + "geometry": geometry_json, + "geometryType": geometry_type, + "inSR": inSR, + "spatialRel": spatialRel, + "outFields": outFields, + "returnGeometry": str(return_geometry).lower(), + "f": "json", + } + # API URL for querying the WBD + url = f"https://hydro.nationalmap.gov/arcgis/rest/services/wbd/MapServer/{layer}/query" + else: + # Construct the query parameters + params = { + "searchText": searchText, + "contains": "true", + "layers": str(layer), + "inSR": inSR, + "outFields": outFields, + "returnGeometry": str(return_geometry).lower(), + "f": "json", + } + url = f"https://hydro.nationalmap.gov/arcgis/rest/services/wbd/MapServer/find" # Add additional keyword arguments for key, value in kwargs.items(): @@ -14654,8 +14669,13 @@ def detect_geometry_type(geometry): data = response.json() - # Extract features from the API response - features = data.get("features", []) + if geometry_dict is not None: + # Extract features from the API response + features = data.get("features", []) + crs = f"EPSG:{data['spatialReference']['latestWkid']}" + else: + features = data.get("results", []) + crs = f"EPSG:{data['results'][0]['geometry']['spatialReference']['latestWkid']}" # Prepare attribute data and geometries attributes = [feature["attributes"] for feature in features] @@ -14678,7 +14698,7 @@ def detect_geometry_type(geometry): gdf = gpd.GeoDataFrame( df, geometry=geometries, - crs=f"EPSG:{data['spatialReference']['latestWkid']}", + crs=crs, ) if outSR != "3857": gdf = gdf.to_crs(outSR) diff --git a/mkdocs.yml b/mkdocs.yml index 40ffd6f7e3..b29bc5e6f8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -322,3 +322,4 @@ nav: - notebooks/95_edit_vector.ipynb - notebooks/96_batch_edit_vector.ipynb - notebooks/97_overture_data.ipynb + - notebooks/98_watershed.ipynb