Skip to content

Commit

Permalink
errorhandling for v0.0.2 release
Browse files Browse the repository at this point in the history
  • Loading branch information
cyschneck committed Mar 15, 2023
1 parent 9e8081a commit 604e47c
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 34 deletions.
107 changes: 84 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,25 @@

Find the centerline and width of rivers based on the latitude and longitude of the right and left bank

| Points on River Bank | Centerline of River Bank |
| River Outlined in ArcGIS | Generated Centerline for the River Bank |
| ------------- | ------------- |
| ![river_google_earth+png](https://raw.githubusercontent.com/cyschneck/centerline-width/main/data/doc_examples/river_example_google_earth.png) | ![river_centerline+png](https://raw.githubusercontent.com/cyschneck/centerline-width/main/data/doc_examples/river_example.png) |


Python implementation of [R-Code CMGO](https://github.com/AntoniusGolly/cmgo) (with modification)

## Data
Scripts can accept a text file that can be converted to a .csv file
## Requirements
Currently running on Python 3.7+

```
pip install -r requirments.txt
```

## Running Script

### Converted Data Text File to CSV

Convert a text file with coordinates for a left and right bank's latitude/longitude

```
llat llon rlat rlon
Expand All @@ -21,29 +31,58 @@ Scripts can accept a text file that can be converted to a .csv file
30.037648 -92.868546 30.119746 -92.907917
30.037674 -92.868536 30.119721 -92.907909
30.037702 -92.868533 30.119706 -92.907905
...
```

Scripts expect data as a list of point:
Scripts expect data as a list of point for left and right banks:
- Header: llat, llon, rlat, rlon

## Requirements
Currently running on Python 3.7+

```
pip install -r requirments.txt
convertColumnsToCSV(text_file=None, flipBankDirection=False)
```

## Running Script

### Converted Text File to CSV
* **[REQUIRED]** text_file (string): File location of the text file to convert
* [OPTIONAL] flipBankDirection (boolean): If the latitude/longitude of the banks are generated in reverse order, flip the final values so left/right bank are in order

```python
import centerline_width
centerline_width.convertColumnsToCSV(text_file="data/river_coords.txt", flipBankDirection=True)
```
Converts text file:
```
llat llon rlat rlon
30.037581 -92.868569 30.119804 -92.907933
30.037613 -92.868549 30.119772 -92.907924
30.037648 -92.868546 30.119746 -92.907917
30.037674 -92.868536 30.119721 -92.907909
30.037702 -92.868533 30.119706 -92.907905
```
To a CSV file:
```
llat,llon,rlat,rlon
30.037581,-92.868569,30.037441,-92.867476
30.037613,-92.868549,30.037448,-92.867474
30.037648,-92.868546,30.037482,-92.867449
30.037674,-92.868536,30.037506,-92.867432
30.037702,-92.868533,30.037525,-92.867430
```

### Plot Centerline in Matplotlib
Plot the centerline created from a list of right and left banks with Voronoi vertices

```
plotCenterline(csv_data=None,
display_all_possible_paths=False,
plot_title=None,
save_plot_name=None,
displayVoronoi=False,
optional_cutoff=None)
```
* **[REQUIRED]** csv_data (string): File location of the text file to convert
* [OPTIONAL] display_all_possible_paths (boolean): Display all possible paths, not just the centerline (useful for debugging)
* [OPTIONAL] plot_title (string): Change plot title, defaults to "River Coordinates: Valid Centerline = True/False, Valid Polygon = True/False"
* [OPTIONAL] save_plot_name (string): Save the plot with a given name and location
* [OPTIONAL] displayVoronoi (boolean): Overlay Voronoi diagram used to generate centerline
* [OPTIONAL] optional_cutoff (int): Include only the first x amount of the data to chart (useful for debugging)

```python
import centerline_width
centerline_width.plotCenterline(csv_data="data/river_coords.csv",
Expand All @@ -56,12 +95,33 @@ Output:
![river_coords+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/river_coords.png)

### Return Latitude/Longitude Coordinates of Centerline
Return a list of lists for each latitude/longtiude coordinate of the centerline
```
centerlineLatitudeLongitude(csv_data=None, optional_cutoff=None)
```
* **[REQUIRED]** csv_data (string): File location of the text file to convert
* [OPTIONAL] optional_cutoff (int): Include only the first x amount of the data to chart (useful for debugging)

```python
import centerline_width
centerline_long_lat_coordinates = centerline_width.centerlineLatitudeLongitude(csv_data="data/river_coords.csv", optional_cutoff=cutoff)
```
Output: `[(-92.86788596499872, 30.03786596717931), (-92.86789573751797, 30.037834641974108), (-92.8679141386283, 30.037789636848878), (-92.8679251193248, 30.037756853899904), (-92.86796903819089, 30.03765423778148), (-92.86797335733262, 30.037643336049054), (-92.8679920356456, 30.037592224469797), (-92.86800576063828, 30.037555441489403), (-92.86800841510367, 30.037546512833107), (-92.8680119498663, 30.03753043193875)]`
### Return Width of River
Return the width of the river based on the centerline
```
riverWidthFromCenterline(csv_data=None,
centerline_coordinates=None,
save_to_csv=None,
optional_cutoff=None)
```

* **[REQUIRED]** csv_data (string): File location of the text file to convert
* **[REQUIRED]** centerline_coordinates (list): A list of centerline coordinates (via centerlineLatitudeLongitude())
* [OPTIONAL] plot_title (string): Change plot title, defaults to "River Coordinates: Valid Centerline = True/False, Valid Polygon = True/False"
* [OPTIONAL] save_to_csv (string): Save the csv with a given name and location
* [OPTIONAL] optional_cutoff (int): Include only the first x amount of the data to chart (useful for debugging)

```python
import centerline_width
river_width_dict = centerline_width.riverWidthFromCenterline(csv_data="data/river_coords.csv", centerline_coordinates=centerline_long_lat_coordinates,
Expand All @@ -84,38 +144,39 @@ The centerline is defined by the greatest distance from the right and left bank,
### Display Voronoi ridge vertices that lie within the polygon (within the river banks)
![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/example4.png)

### Filter out any point pairs that only have one connections to filter out the short dead end paths

### Find the starting and ending node based on distance from the top and bottom of polygon
### Filter out any point pairs that only have one connections to filter out the short dead end paths and find the starting and ending node based on distance from the top and bottom of polygon
![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/example6.png)
![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/example7.png)

### Find the centerline: shortest path from the starting node to the ending node ([Dijkstra's Algorithm](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.generic.shortest_path.html#networkx.algorithms.shortest_paths.generic.shortest_path))
### Find the shortest path from the starting node to the ending node ([Dijkstra's Algorithm](https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.shortest_paths.generic.shortest_path.html#networkx.algorithms.shortest_paths.generic.shortest_path))
| Points on River Bank | NetworkX Graph of Points on River Bank |
| ------------- | ------------- |
| ![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/example10.png) | ![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/example9.png) |

### Display the centerline found by connecting the starting/ending node with the shortest path
![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/example8.png)

This is an attempt at a more robust algorithm working from raw data to ensure that all dead ends are removed and no gaps exist in the centerline

Points that only have one connection are removed, but by limiting the number of connections for a point to just two will create gaps. The Voronoi vertices connect to other vertex values, but some connect to more and some only connect to one other point. Removing additional values will create gaps, so this is avoided in this code by not applying additional filters.

All vertices:
**All vertices:**
![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/example4.png)

Vertices that have at least two connections (that would create gaps):
**Vertices that have at least two connections (that would create gaps):**
![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/example5.png)

## Edge Cases
Invalid Polygon
## Debugging and Edge Cases
A polygon is invalid if it overlaps within itself:
![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/invalid_example1.png)
In this example, the polygon is invalid, but with such a small overlap it is still able to find a valid path

Invalid Polygon
With limited data, the polygon will overlap more dramatically and will no longer be able to find a valid centerline:
![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/invalid_example4.png)

Invalid Starting Node
If the data starts with a large width, it is possible for the starting node to be invalid
![example+png](https://raw.githubusercontent.com/cyschneck/river-geometry/main/data/doc_examples/invalid_example3.png)
Currently, the starting node is determined by the closest node to the top of the bank (in green) and the ending node is determined by the closest node to the bottom of the bank (in red)

## Citations
Based on the work:
Expand Down
2 changes: 1 addition & 1 deletion centerline_width/error_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def errorHandlingRiverWidthFromCenterline(csv_data=None,
exit()

if type(centerline_coordinates) != list:
logger.critical("\nCRITICAL ERROR, [centerline_coordinates]: Must be a dict, current type = '{0}'".format(type(centerline_coordinates)))
logger.critical("\nCRITICAL ERROR, [centerline_coordinates]: Must be a list of lists, current type = '{0}'".format(type(centerline_coordinates)))
exit()

if save_to_csv is not None and type(save_to_csv) != str:
Expand Down
3 changes: 3 additions & 0 deletions centerline_width/preprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ def generatePolygon(left_bank_lst, right_bank_lst):

if not river_polygon.is_valid:
logger.critical("Invalid Polygon needs to be corrected")
else:
logger.info("Valid polygon generated")

return river_polygon, top_river, bottom_river

Expand All @@ -83,6 +85,7 @@ def generateVoronoi(left_bank_lst, right_bank_lst):
all_banks_points = np.array(all_banks_points)

river_voronoi = Voronoi(all_banks_points)
logger.info("Voronoi diagram generated")
return river_voronoi

def pointsFromVoronoi(river_voronoi, river_polygon):
Expand Down
Binary file modified data/doc_examples/river_example_google_earth.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified data/river_coords.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 9 additions & 8 deletions river_centerline_width_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@

# Valid Examples
cutoff = None
cutoff = 15 # valid centerline, valid path, valid polygon, valid starting node, valid ending node
#cutoff = 15 # valid centerline, valid path, valid polygon, valid starting node, valid ending node
#cutoff = 100 # valid centerline, valid path, valid polygon, valid starting node, valid ending node
#cutoff = 550 # valid centerline, valid path, valid polygon, valid starting node, valid ending node
cutoff = 550 # valid centerline, valid path, valid polygon, valid starting node, valid ending node
# Invalid Examples
#cutoff = 250 # valid centerline, valid path, invalid polygon, valid starting node, valid ending nodes
#cutoff = 40 # invalid centerline, valid path, valid polgyon, invalid starting node, valid ending node
#cutoff = 700 # invalid centerline, valid path, valid polgyon, invalid starting node, valid ending node
#cutoff = 1000 # invalid centerline, invalid path, invalid polgyon, invalid starting node, valid ending node

# Plot river banks
#centerline_width.plotCenterline(csv_data="data/river_coords.csv",
# save_plot_name="data/river_coords.png",
# display_all_possible_paths=True,
# displayVoronoi=False,
# optional_cutoff=cutoff)
centerline_width.plotCenterline(csv_data="data/river_coords.csv",
save_plot_name="data/river_coords.png",
display_all_possible_paths=True,
displayVoronoi=False,
optional_cutoff=cutoff)

# Return the latitude/longtiude coordinates for the centerline
centerline_long_lat_coordinates = centerline_width.centerlineLatitudeLongitude(csv_data="data/river_coords.csv",
Expand All @@ -30,4 +30,5 @@
# Return the width of the river for each centerline vertex (distance from right, left, total)
river_width_dict = centerline_width.riverWidthFromCenterline(csv_data="data/river_coords.csv",
centerline_coordinates=centerline_long_lat_coordinates,
save_to_csv="data/river_width.csv")
save_to_csv="data/river_width.csv",
optional_cutoff=cutoff)
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Python Package Setup
from setuptools import setup, find_namespace_packages

VERSION="0.0.1"
VERSION="0.0.2"
DESCRIPTION="A Python package to find the centerline and width of rivers based on the latitude and longitude from a right and left bank"

with open("README.md", "r") as f:
Expand All @@ -17,7 +17,7 @@
long_description_content_type='text/markdown',
url="https://github.com/cyschneck/centerline-width",
download_url="https://github.com/cyschneck/centerline-width/archive/refs/tags/v{0}.tar.gz".format(VERSION),
author="cyschneck (C. Y. Schneck)",
author="Una Schneck (unaschneck), C. Y. Schneck (cyschneck)",
keywords=["geophysics", "python", "voronoi", "centerline", "centerline-extraction", "river-bank", "limnology", "hydrology"],
license="MIT",
classifiers=[
Expand Down

0 comments on commit 604e47c

Please sign in to comment.