44import click
55import geopandas as gpd
66import pandas as pd
7+ import warnings
78from shapely .geometry import Point
89from schimpy .schism_sources_sinks import yaml2df
910import yaml
@@ -22,27 +23,69 @@ def points_to_shp(fpath, points):
2223 gdf .to_file (fpath , driver = "ESRI Shapefile" )
2324
2425def points_to_yaml (fpath , points ):
26+ # Ensure columns exist
27+ if "sites" not in points .columns or "stype" not in points .columns :
28+ raise ValueError ("Shapefile must contain 'sites' and 'stype' fields." )
2529
2630 dict_file = {}
2731 for _ , row in points .iterrows ():
2832 dict_file [str (row .sites )] = [round (float (row .x ), 2 ), round (float (row .y ), 2 )]
2933 with open (fpath , "w" ) as file :
3034 yaml .dump (dict_file , file , default_flow_style = False , allow_unicode = True )
35+
36+ def points_to_bp (fpath , points ):
37+ """Convert a DataFrame to a SCHISM build points (.bp) file."""
38+ import numpy as np
39+
40+ # Calculate cumulative distance along the transect
41+ points = points .reset_index (drop = True )
42+ coords = points [['x' , 'y' ]].values
43+
44+ # Calculate distances between consecutive points
45+ distances = np .sqrt (np .sum (np .diff (coords , axis = 0 )** 2 , axis = 1 ))
46+
47+ # Cumulative distance starting at 0
48+ cumulative_distances = np .concatenate (([0 ], np .cumsum (distances )))
49+
50+ # Write to file
51+ with open (fpath , 'w' ) as f :
52+ # Write header
53+ f .write ("point x y z distance\n " )
54+ f .write (f"{ len (points )} ! Number of points\n " )
55+
56+ # Write each point
57+ for i , (_ , row ) in enumerate (points .iterrows (), start = 1 ):
58+ x = float (row ['x' ])
59+ y = float (row ['y' ])
60+ z = - 10000.0
61+ distance = float (cumulative_distances [i - 1 ])
62+ f .write (f"{ i } { x } { y } { z } { distance } \n " )
63+
64+ print (f"\n Build points file written to { fpath } ." )
65+
3166
3267def shp_to_df (fpath ):
3368 """
3469 Read a point shapefile using geopandas and return a DataFrame
3570 with 'sites', 'stype', 'x', and 'y' columns.
3671 """
3772 gdf = gpd .read_file (fpath )
38- # Ensure columns exist
39- if "sites" not in gdf .columns or "stype" not in gdf .columns :
40- raise ValueError ("Shapefile must contain 'sites' and 'stype' fields." )
73+
4174 # Extract coordinates
4275 gdf ["x" ] = gdf .geometry .x
4376 gdf ["y" ] = gdf .geometry .y
77+
4478 # Select relevant columns
45- df = gdf [["sites" , "stype" , "x" , "y" ]].copy ()
79+ cols = ["x" , "y" ]
80+ if "sites" in gdf .columns :
81+ cols .insert (0 , "sites" )
82+ if "stype" in gdf .columns :
83+ cols .insert (1 if "sites" in gdf .columns else 0 , "stype" )
84+
85+ if "sites" not in gdf .columns or "stype" not in gdf .columns :
86+ warnings .warn ("Shapefile must contain 'sites' and 'stype' fields for conversion to yaml" )
87+
88+ df = gdf [cols ].copy ()
4689 return df
4790
4891
@@ -62,14 +105,15 @@ def write_points(fpath, points):
62105 return points_to_shp (fpath , points )
63106 elif fpath .endswith (".yaml" ):
64107 return points_to_yaml (fpath , points )
108+ elif fpath .endswith (".bp" ):
109+ return points_to_bp (fpath , points )
65110 else :
66111 raise ValueError ("Not supported output file type. Only Shapefile (.shp) is supported." )
67112
68113
69114@click .command (
70115 help = "Convert SCHISM points (source and sink) between YAML and Shapefile formats."
71116)
72- @click .help_option ("-h" , "--help" )
73117@click .option (
74118 "--input" ,
75119 required = True ,
@@ -80,7 +124,7 @@ def write_points(fpath, points):
80124 "--output" ,
81125 required = True ,
82126 type = click .Path (),
83- help = "Output file (.yml, .yaml, .in, or .shp)." ,
127+ help = "Output file (.yml, .yaml, .in, .bp, or .shp)." ,
84128)
85129@click .help_option ("-h" , "--help" )
86130def convert_points_cli (input , output ):
0 commit comments