Skip to content

Commit ef602d2

Browse files
chrislyonsKYclaude
andcommitted
Add REM, mine volume, notebooks, and web explorer examples
New real-world analysis scripts: - kentucky_river_rem.py: Relative Elevation Model of Kentucky River in Frankfort using KyGeoNet river centerline + abovepy DEM - mine_volume_estimate.py: Surface mine cut/fill volume estimation for eastern KY using KyGeoNet mine permits + abovepy DEM New Jupyter notebooks: - quickstart.ipynb: Interactive abovepy walkthrough - dem_analysis.ipynb: DEM mosaic, hillshade, elevation visualization - county_explorer.ipynb: Explore all 120 KY counties New web example: - kentucky_explorer.html: Interactive MapLibre viewer with product selector, color ramps, opacity control, KY boundary overlay Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7b0d248 commit ef602d2

7 files changed

Lines changed: 1737 additions & 2 deletions

File tree

examples/README.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Examples
22

3-
Working examples for abovepy.
3+
Working examples for abovepy — KyFromAbove data access for Python.
44

55
## Scripts
66

@@ -11,12 +11,29 @@ Working examples for abovepy.
1111
| `scripts/county_ortho_download.py` | Download orthoimagery by county name |
1212
| `scripts/stream_window.py` | Windowed read from a remote COG (no download) |
1313
| `scripts/explore_products.py` | List all products and tile counts |
14+
| `scripts/compare_dem_phases.py` | Compare DEM Phase 1 (5ft) vs Phase 3 (2ft) |
15+
| `scripts/batch_county_search.py` | Search multiple counties, aggregate results |
16+
| `scripts/inspect_remote_tile.py` | Inspect tile metadata without downloading |
17+
| `scripts/ortho_rgb_extract.py` | Extract RGB bands from orthoimagery |
18+
| `scripts/titiler_urls.py` | Generate TiTiler URLs for web maps |
19+
| `scripts/export_search_results.py` | Export search results to GeoJSON/GPKG/CSV |
20+
| `scripts/kentucky_river_rem.py` | Relative Elevation Model of the Kentucky River |
21+
| `scripts/mine_volume_estimate.py` | Surface mine cut/fill volume estimation |
22+
23+
## Notebooks
24+
25+
| Notebook | Description |
26+
|---|---|
27+
| `notebooks/quickstart.ipynb` | Interactive walkthrough of core abovepy features |
28+
| `notebooks/dem_analysis.ipynb` | DEM mosaic, hillshade, and elevation visualization |
29+
| `notebooks/county_explorer.ipynb` | Explore all 120 Kentucky counties and their data |
1430

1531
## Web
1632

1733
| File | Description |
1834
|---|---|
19-
| `web/titiler_map.html` | MapLibre GL JS viewer for COGs via TiTiler |
35+
| `web/titiler_map.html` | Simple COG viewer via TiTiler |
36+
| `web/kentucky_explorer.html` | Interactive KyFromAbove data explorer with layer controls |
2037

2138
## Docker
2239

@@ -36,6 +53,17 @@ python scripts/quickstart.py
3653
pip install abovepy[viz]
3754
python scripts/frankfort_hillshade.py
3855

56+
# REM analysis (needs scipy, shapely, matplotlib)
57+
pip install abovepy[all]
58+
python scripts/kentucky_river_rem.py
59+
60+
# Mine volume estimation (needs scipy, rasterio, geopandas)
61+
python scripts/mine_volume_estimate.py
62+
3963
# County ortho download
4064
python scripts/county_ortho_download.py --county Pike
65+
66+
# Notebooks
67+
pip install jupyter
68+
jupyter notebook notebooks/quickstart.ipynb
4169
```
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "7fb27b941602401d91542211134fc71a",
6+
"metadata": {},
7+
"source": [
8+
"# Kentucky County Data Explorer\n",
9+
"\n",
10+
"Explore KyFromAbove data availability across Kentucky's 120 counties.\n",
11+
"This notebook demonstrates how to:\n",
12+
"- List all available counties\n",
13+
"- Search multiple products for a single county\n",
14+
"- Compare tile coverage across products\n",
15+
"- Export results to GeoJSON"
16+
]
17+
},
18+
{
19+
"cell_type": "code",
20+
"execution_count": null,
21+
"id": "acae54e37e7d407bbb7b55eff062a284",
22+
"metadata": {},
23+
"outputs": [],
24+
"source": [
25+
"import matplotlib.pyplot as plt\n",
26+
"\n",
27+
"import abovepy\n",
28+
"from abovepy.utils.bbox import get_county_bbox, list_counties"
29+
]
30+
},
31+
{
32+
"cell_type": "markdown",
33+
"id": "9a63283cbaf04dbcab1f6479b197f3a8",
34+
"metadata": {},
35+
"source": [
36+
"## List All Counties\n",
37+
"\n",
38+
"abovepy ships with bounding boxes for all 120 Kentucky counties.\n",
39+
"These are used internally when you pass `county=` to `abovepy.search()`."
40+
]
41+
},
42+
{
43+
"cell_type": "code",
44+
"execution_count": null,
45+
"id": "8dd0d8092fe74a7c96281538738b07e2",
46+
"metadata": {},
47+
"outputs": [],
48+
"source": [
49+
"counties = list_counties()\n",
50+
"print(f\"Total counties: {len(counties)}\\n\")\n",
51+
"\n",
52+
"# Display in columns for readability\n",
53+
"cols = 4\n",
54+
"for i in range(0, len(counties), cols):\n",
55+
" row = counties[i:i + cols]\n",
56+
" print(\" \".join(f\"{c:<16}\" for c in row))"
57+
]
58+
},
59+
{
60+
"cell_type": "markdown",
61+
"id": "72eea5119410473aa328ad9291626812",
62+
"metadata": {},
63+
"source": [
64+
"## Search Multiple Products for One County\n",
65+
"\n",
66+
"Let's see what data is available for Pike County — the largest county by area\n",
67+
"in eastern Kentucky. We'll search across DEM, orthoimagery, and LiDAR products."
68+
]
69+
},
70+
{
71+
"cell_type": "code",
72+
"execution_count": null,
73+
"id": "8edb47106e1a46a883d545849b8ab81b",
74+
"metadata": {},
75+
"outputs": [],
76+
"source": [
77+
"county_name = \"Pike\"\n",
78+
"print(f\"Searching {county_name} County...\")\n",
79+
"print(f\"Bounding box: {get_county_bbox(county_name)}\\n\")\n",
80+
"\n",
81+
"products_to_search = [\n",
82+
" \"dem_phase1\",\n",
83+
" \"dem_phase2\",\n",
84+
" \"dem_phase3\",\n",
85+
" \"ortho_phase1\",\n",
86+
" \"ortho_phase2\",\n",
87+
" \"ortho_phase3\",\n",
88+
" \"laz_phase1\",\n",
89+
" \"laz_phase2\",\n",
90+
" \"laz_phase3\",\n",
91+
"]\n",
92+
"\n",
93+
"results = {}\n",
94+
"for product in products_to_search:\n",
95+
" try:\n",
96+
" tiles = abovepy.search(county=county_name, product=product)\n",
97+
" results[product] = tiles\n",
98+
" print(f\" {product:<16} {len(tiles):>5} tiles\")\n",
99+
" except Exception as e:\n",
100+
" print(f\" {product:<16} Error: {e}\")\n",
101+
"\n",
102+
"print(f\"\\nTotal products with data: {len(results)}\")"
103+
]
104+
},
105+
{
106+
"cell_type": "markdown",
107+
"id": "10185d26023b46108eb7d9f57d49d2b3",
108+
"metadata": {},
109+
"source": [
110+
"## Compare Tile Coverage\n",
111+
"\n",
112+
"Visualize the spatial coverage of different products over Pike County\n",
113+
"by plotting the tile boundaries from each search result."
114+
]
115+
},
116+
{
117+
"cell_type": "code",
118+
"execution_count": null,
119+
"id": "8763a12b2bbd4a93a75aff182afb95dc",
120+
"metadata": {},
121+
"outputs": [],
122+
"source": [
123+
"# Pick up to 3 products that returned results for comparison\n",
124+
"products_to_plot = [k for k in [\"dem_phase3\", \"ortho_phase3\", \"laz_phase3\"] if k in results]\n",
125+
"\n",
126+
"if products_to_plot:\n",
127+
" fig, axes = plt.subplots(\n",
128+
" 1, len(products_to_plot),\n",
129+
" figsize=(6 * len(products_to_plot), 6),\n",
130+
" squeeze=False,\n",
131+
" )\n",
132+
"\n",
133+
" for idx, product in enumerate(products_to_plot):\n",
134+
" ax = axes[0][idx]\n",
135+
" gdf = results[product]\n",
136+
" gdf.plot(ax=ax, edgecolor=\"steelblue\", facecolor=\"lightblue\", alpha=0.5, linewidth=0.5)\n",
137+
" ax.set_title(f\"{product}\\n({len(gdf)} tiles)\")\n",
138+
" ax.set_xlabel(\"Easting\")\n",
139+
" ax.set_ylabel(\"Northing\")\n",
140+
"\n",
141+
" plt.suptitle(f\"{county_name} County — Tile Coverage by Product\", fontsize=14, y=1.02)\n",
142+
" plt.tight_layout()\n",
143+
" plt.show()\n",
144+
"else:\n",
145+
" print(\"No products returned results for plotting.\")"
146+
]
147+
},
148+
{
149+
"cell_type": "markdown",
150+
"id": "7623eae2785240b9bd12b16a66d81610",
151+
"metadata": {},
152+
"source": [
153+
"## Export Results\n",
154+
"\n",
155+
"Export the tile index for a product to GeoJSON for use in other tools\n",
156+
"(QGIS, ArcGIS Pro, web maps, etc.)."
157+
]
158+
},
159+
{
160+
"cell_type": "code",
161+
"execution_count": null,
162+
"id": "7cdc8c89c7104fffa095e18ddfef8986",
163+
"metadata": {},
164+
"outputs": [],
165+
"source": [
166+
"from pathlib import Path\n",
167+
"\n",
168+
"output_dir = Path(\"./output\")\n",
169+
"output_dir.mkdir(exist_ok=True)\n",
170+
"\n",
171+
"# Export each product's tile index to GeoJSON\n",
172+
"for product, gdf in results.items():\n",
173+
" output_path = output_dir / f\"{county_name.lower()}_{product}.geojson\"\n",
174+
" gdf.to_file(output_path, driver=\"GeoJSON\")\n",
175+
" print(f\"Exported {product}: {output_path} ({len(gdf)} tiles)\")\n",
176+
"\n",
177+
"print(f\"\\nAll exports saved to {output_dir.resolve()}\")"
178+
]
179+
}
180+
],
181+
"metadata": {
182+
"kernelspec": {
183+
"display_name": "Python 3",
184+
"language": "python",
185+
"name": "python3"
186+
},
187+
"language_info": {
188+
"name": "python",
189+
"version": "3.10.0"
190+
}
191+
},
192+
"nbformat": 4,
193+
"nbformat_minor": 5
194+
}

0 commit comments

Comments
 (0)