55import os
66import utm
77import numpy as np
8+ import pandas as pd
89from osgeo import gdal , gdal_array , osr
910import matplotlib .pyplot as plt
1011
@@ -38,10 +39,145 @@ def pick(c, r, mask): # (c_number, r_number, an array of 1 amd 0)
3839 fill .add (south )
3940 return picked
4041
42+ def expand (array , n ): # (an array of 1 and 0, number of additional pixels)
43+ expand = array - array
44+ for i in range (len (array )):
45+ for j in range (len (array [i ])):
46+ if array [i ][j ] == 1 :
47+ for k in range (max (0 , i - n ), min (i + n , len (array )- 1 )):
48+ for l in range (max (0 , j - n ), min (j + n , len (array [i ])- 1 )):
49+ expand [k ][l ] = 1
50+ continue
51+ else :
52+ continue
53+ return expand
4154
55+ # Check for best fit curve
56+ def best_fit_degree (xdata , ydata ):
57+ import numpy as np
58+ import matplotlib .pyplot as plt
59+ from sklearn .metrics import r2_score
60+
61+ # Maximum degree for polynomial regression
62+ max_degree = 5
63+
64+ # Lists to store R-squared values and RMSEs for each degree
65+ r_squared_values = []
66+ rmse_values = []
67+
68+ for degree in range (1 , max_degree + 1 ):
69+ # Perform polynomial regression
70+ coefficients = np .polyfit (xdata , ydata , degree )
71+ p = np .poly1d (coefficients )
72+
73+ # Calculate fitted values and residuals
74+ ydata_fit = p (xdata )
75+ residuals = ydata - ydata_fit
76+
77+ # Calculate R-squared using NumPy
78+ r_squared = r2_score (ydata , ydata_fit )
79+ r_squared_values .append (r_squared )
80+
81+ # Calculate RMSE
82+ rmse = np .sqrt (np .mean (residuals ** 2 ))
83+ rmse_values .append (rmse )
84+
85+ # # Plotting R-squared values and RMSEs for different degrees
86+ # plt.figure(figsize=(10, 4))
87+
88+ # # Plot R-squared values
89+ # plt.subplot(1, 2, 1)
90+ # plt.plot(range(1, max_degree + 1), r_squared_values, marker='o')
91+ # plt.xlabel('Polynomial Degree')
92+ # plt.ylabel('R-squared')
93+ # plt.title('R-squared vs Polynomial Degree')
94+
95+ # # Plot RMSE values
96+ # plt.subplot(1, 2, 2)
97+ # plt.plot(range(1, max_degree + 1), rmse_values, marker='o', color='orange')
98+ # plt.xlabel('Polynomial Degree')
99+ # plt.ylabel('RMSE')
100+ # plt.title('RMSE vs Polynomial Degree')
101+
102+ # plt.tight_layout()
103+ # plt.show()
104+
105+ # score1 = np.diff((rmse_values[0]-rmse_values)/rmse_values[0]*100)
106+ # score2 = -np.diff((r_squared_values[0]-r_squared_values)/r_squared_values[0]*100)
107+ # rmse_pos = int(np.where(score1 == min(score1))[0]) + 1
108+ # r_squared_pos = int(np.where(score2 == min(score2))[0]) + 1
109+
110+ rmse_pos = int (np .where (rmse_values == min (rmse_values ))[0 ]) + 1
111+ r_squared_pos = int (np .where (r_squared_values == max (r_squared_values ))[0 ]) + 1
112+
113+ return round ((rmse_pos + r_squared_pos )/ 2 )- 1
114+
115+
116+ # Curve fitting ========================>
117+ def curve_fit (xdata , ydata , bf_degree ):
118+ import numpy as np
119+ import matplotlib .pyplot as plt
120+
121+ # Perform polynomial regression (adjust the degree as needed)
122+ degree = bf_degree # Example degree of the polynomial
123+ coefficients = np .polyfit (xdata , ydata , degree )
124+ p = np .poly1d (coefficients )
125+
126+ # Generate area values for smoother curve plotting
127+ xdata_values = np .linspace (min (xdata ), max (xdata ), 1000 )
128+
129+ # Calculate corresponding elevation values using the fitted polynomial
130+ ydata_fit = p (xdata_values )
131+
132+ # # Plotting the original data and the fitted curve
133+ # plt.figure()
134+ # plt.scatter(ydata, xdata, label='Original Data')
135+ # plt.plot(ydata_fit, xdata_values, label='Fitted Curve', color='red')
136+ # plt.xlabel('Elevation')
137+ # plt.ylabel('Area')
138+ # plt.title('Area vs Elevation with Fitted Curve')
139+ # plt.legend()
140+ # plt.grid(True)
141+ # plt.show()
142+ # plt.savefig('Fitted_areaVSelevation.png', dpi=600, bbox_inches='tight')
143+
144+ out = np .column_stack ((ydata_fit , xdata_values ))
145+ return out
42146
147+ # Curve extrapolation (area tends to zero) =================================================================
148+ def curve_extrapolate (xdata , ydata , bf_degree ):
149+ import numpy as np
150+ import matplotlib .pyplot as plt
151+
152+ # Perform polynomial regression (using a 4th-degree polynomial as an example)
153+ degree = bf_degree
154+ coefficients = np .polyfit (xdata , ydata , degree )
155+ p = np .poly1d (coefficients )
156+
157+ # Generate area values approaching zero for extrapolation
158+ extrapolated_xdata_values = np .linspace (0 , max (xdata ), 1000 )
159+ #extrapolated_area_values = np.linspace(max(area), 1.5*max(area), 1000)
160+ # Use the polynomial function to extrapolate elevation values for small area values
161+ extrapolated_ydata_values = p (extrapolated_xdata_values )
162+
163+ # Plotting the fitted curve and extrapolated values
164+ plt .figure ()
165+ plt .plot (ydata , xdata , 'o' , label = 'Original Data' )
166+ plt .plot (extrapolated_ydata_values , extrapolated_xdata_values , label = 'Extrapolated Curve' , color = 'red' )
167+ plt .ylabel ('Area' )
168+ plt .xlabel ('Elevation' )
169+ plt .title ('Extrapolation of Elevation for Small Area Values' )
170+ plt .legend ()
171+ plt .grid (True )
172+ plt .show ()
173+ #plt.savefig('Extrapolated_areaVSelevation.png', dpi=600, bbox_inches='tight')
174+
175+ out = np .column_stack ((extrapolated_ydata_values , extrapolated_xdata_values ))
176+ return out
177+
178+ # DEM-based reservoir isolation =============================
43179def res_isolation (res_name , max_wl , point , boundary , res_directory ):
44- # ===================================================== INPUT PARAMETERS
180+
45181 os .chdir (res_directory + "/Outputs" )
46182 res_dem_file = (res_name + "_DEM_UTM_CLIP.tif" )
47183
@@ -52,7 +188,7 @@ def res_isolation(res_name, max_wl, point, boundary, res_directory):
52188
53189 dem_bin = gdal_array .LoadFile (res_dem_file ).astype (np .float32 )
54190 dem_bin [dem_bin == 32767 ] = np .nan
55- dem_bin [np .where (dem_bin > max_wl + 10 )] = 0 #to expand the reservoir extent for accounting uncertainity in max_wl
191+ dem_bin [np .where (dem_bin > max_wl + 10 )] = 0 #to expand the reservoir extent (10m extra) for accounting uncertainity in max_wl
56192 dem_bin [np .where (dem_bin > 0 )] = 1
57193 res_iso = pick (xp , yp , dem_bin )
58194 aa = sum (sum (res_iso ))
@@ -85,8 +221,8 @@ def res_isolation(res_name, max_wl, point, boundary, res_directory):
85221
86222 #------------------ Visualization <Start>
87223 plt .figure ()
88- plt .imshow (res_iso , cmap = 'viridis' )
89- plt .scatter ([xp ], [yp ], c = 'r' , s = 10 )
224+ # plt.imshow(res_iso, cmap='viridis')
225+ # plt.scatter([xp], [yp], c='r', s=10)
90226 plt .imshow (dem_bin , cmap = 'viridis' )
91227 plt .scatter ([xp ], [yp ], c = 'r' , s = 20 )
92228 plt .title ('DEM-based reservoir isolation' )
@@ -97,8 +233,90 @@ def res_isolation(res_name, max_wl, point, boundary, res_directory):
97233 "res_iso.tif" , format = "GTiff" ,
98234 prototype = res_dem_file )
99235
236+ return xp , yp
237+
238+
100239#============================================================== E-A relationship
101- def curve (res_name , res_directory ):
240+ def curve_preDEM (res_name , point_loc , res_directory ):
241+ # caculating reservoir surface area and storage volume coresponding to each water level
242+ os .chdir (res_directory + "/Outputs" )
243+ res_dem_file = (res_name + "_DEM_UTM_CLIP.tif" )
244+ dem_bin = gdal_array .LoadFile (res_dem_file ).astype (np .float32 )
245+ dem_bin [dem_bin == 32767 ] = np .nan
246+ [xp , yp ]= point_loc
247+ dem_val = int (dem_bin [yp ,xp ])
248+
249+ results = [["Level (m)" , "Area (skm)" , "Storage (mcm)" ]]
250+ pre_area = 0
251+ tot_stor = 0
252+ for i in range (dem_val , dem_val + 30 ):
253+ level = i
254+ water_px = gdal_array .LoadFile (res_dem_file ).astype (np .float32 )
255+ water_px [water_px == 32767 ] = np .nan
256+ water_px [np .where (water_px < dem_val )] = np .nan
257+ water_px [np .where (water_px > i )] = 0
258+ water_px [np .where (water_px > 0 )] = 1
259+ res_iso = pick (xp , yp , water_px )
260+ area = np .nansum (res_iso )* 9 / 10000
261+ storage = (area + pre_area )/ 2
262+ tot_stor += storage
263+ pre_area = area
264+ results = np .append (results , [[level , round (area ,4 ), round (tot_stor ,4 )]],
265+ axis = 0 )
266+
267+ # Extract column 2 and 3 from the array
268+ data = results [1 :, :]
269+ data = np .array (data , dtype = np .float32 )
270+ area = data [:, 1 ] # area
271+ elevation = data [:, 0 ] # elevation (ydata)
272+
273+ # Function calling to get best-fit degree of polinomial
274+ bf_degree = best_fit_degree (area , elevation )
275+
276+ # Function calling to get best-fit E-A-S curve values
277+ #EA_curve = curve_fit(area, elevation, bf_degree) # This curve represents the characteristics of DEM outside the reservoir
278+
279+ # Function calling to extrapolate the best-fit to get E-A-S values
280+ EA_curve_extrapolated = curve_extrapolate (area , elevation , bf_degree )
281+
282+ updated_results = [["Level (m)" , "Area (skm)" , "Storage (mcm)" ]]
283+ pre_area = 0
284+ tot_stor = 0
285+ initial_elevation = EA_curve_extrapolated [0 ,0 ]
286+ for i in range (0 , len (EA_curve_extrapolated )):
287+ level = EA_curve_extrapolated [i ,0 ]
288+ area = EA_curve_extrapolated [i ,1 ]
289+ effective_height = EA_curve_extrapolated [i ,0 ] - initial_elevation
290+ storage = (area + pre_area )/ 2 * (effective_height )
291+ tot_stor += storage
292+ pre_area = area
293+ initial_elevation = EA_curve_extrapolated [i ,0 ]
294+ updated_results = np .append (updated_results , [[level , round (area ,4 ), round (tot_stor ,4 )]],
295+ axis = 0 )
296+
297+ # saving output as a csv file
298+ with open ('Curve_' + res_name + '.csv' ,"w" , newline = '' ) as my_csv :
299+ csvWriter = csv .writer (my_csv )
300+ csvWriter .writerows (updated_results )
301+
302+ # ==================== Plot the DEM-based Level-Storage curve
303+ data = updated_results [1 :, :]
304+ data = np .array (data , dtype = np .float32 )
305+ # Create the scatter plot
306+ plt .figure ()
307+ plt .scatter (data [:,0 ], data [:,2 ], s = 5 , c = 'red' )
308+ # Set labels and title
309+ plt .xlabel ('Level (m)' )
310+ plt .ylabel ('Storage (mcm)' )
311+ plt .title (res_name )
312+ plt .savefig (res_name + '_storageVSelevation.png' , dpi = 600 , bbox_inches = 'tight' )
313+
314+ return round (data [0 ,0 ])
315+
316+
317+
318+ #============================================================== E-A relationship
319+ def curve_postDEM (res_name , res_directory ):
102320 # caculating reservoir surface area and storage volume coresponding to each water level
103321 os .chdir (res_directory + "/Outputs" )
104322 res_dem_file = (res_name + "_DEM_UTM_CLIP.tif" )
@@ -107,9 +325,10 @@ def curve(res_name, res_directory):
107325
108326 exp_mask = gdal_array .LoadFile ("Expanded_Mask.tif" ).astype (np .float32 )
109327 res_dem [np .where (exp_mask == 0 )] = np .nan
110- gdal_array .SaveArray (res_dem .astype (gdal_array .numpy .float32 ),
328+ output = gdal_array .SaveArray (res_dem .astype (gdal_array .numpy .float32 ),
111329 "DEM_Landsat_res_iso.tif" ,
112330 format = "GTiff" , prototype = res_dem_file )
331+ output = None
113332 # plt.figure()
114333 # plt.imshow(res_dem, cmap='jet')
115334 # plt.colorbar()
@@ -133,27 +352,80 @@ def curve(res_name, res_directory):
133352 results = np .append (results , [[level , round (area ,4 ), round (tot_stor ,4 )]],
134353 axis = 0 )
135354
355+ # Extract column 2 and 3 from the array
356+ data = results [1 :, :]
357+ data = np .array (data , dtype = np .float32 )
358+ area = data [:, 1 ] # area
359+ elevation = data [:, 0 ] # elevation (ydata)
360+
361+ # Function calling to get best-fit degree of polinomial
362+ from scipy .interpolate import interp1d
363+ interpolation_function = interp1d (area , elevation , kind = 'linear' , fill_value = 'extrapolate' )
364+ new_area_values = np .linspace (min (area ), max (area ), 500 )
365+ interpolated_elevation_values = interpolation_function (new_area_values )
366+
367+ # Function calling to get best-fit E-A-S curve values
368+ EA_curve = np .column_stack ((interpolated_elevation_values , new_area_values ))
369+
370+ updated_results = [["Level (m)" , "Area (skm)" , "Storage (mcm)" ]]
371+ pre_area = 0
372+ tot_stor = 0
373+ initial_elevation = EA_curve [0 ,0 ]
374+ for i in range (0 , len (EA_curve )):
375+ level = EA_curve [i ,0 ]
376+ area = EA_curve [i ,1 ]
377+ effective_height = EA_curve [i ,0 ] - initial_elevation
378+ storage = (area + pre_area )/ 2 * (effective_height )
379+ tot_stor += storage
380+ pre_area = area
381+ initial_elevation = EA_curve [i ,0 ]
382+ updated_results = np .append (updated_results , [[level , round (area ,4 ), round (tot_stor ,4 )]],
383+ axis = 0 )
384+
136385 # saving output as a csv file
137386 with open ('Curve_' + res_name + '.csv' ,"w" , newline = '' ) as my_csv :
138387 csvWriter = csv .writer (my_csv )
139- csvWriter .writerows (results )
388+ csvWriter .writerows (updated_results )
140389
141390 # ==================== Plot the DEM-based Level-Storage curve
142- data = results [1 :, :]
391+ data = updated_results [1 :, :]
143392 data = np .array (data , dtype = np .float32 )
144- # Extract column 2 and 3 from the array
145- column_1 = data [:, 0 ]
146- column_3 = data [:, 2 ]
147393 # Create the scatter plot
148394 plt .figure ()
149- plt .scatter (column_1 , column_3 , s = 5 , c = 'red' )
395+ plt .scatter (data [:, 0 ], data [:, 2 ] , s = 5 , c = 'red' )
150396 # Set labels and title
151397 plt .xlabel ('Level (m)' )
152398 plt .ylabel ('Storage (mcm)' )
153399 plt .title (res_name )
154400 plt .savefig (res_name + '_storageVSelevation.png' , dpi = 600 , bbox_inches = 'tight' )
155401
156402 return min_dem
403+
404+
405+ #=================================== Update Landsat-based E-A database with DEM-based E-A-S relationship
406+ def one_tile (res_name , max_wl , dead_wl , res_directory ):
407+
408+ os .chdir (res_directory + "/Outputs" )
409+ curve = pd .read_csv ('Curve_' + res_name + '.csv' )
410+ landsat_wsa = pd .read_csv ('WSA_' + res_name + '.csv' )
411+
412+ Wsa = landsat_wsa
413+ Wsa ["dem_value_m" ] = None
414+ Wsa ["Tot_res_volume_mcm" ] = None
415+ for i in range (0 ,len (Wsa )):
416+ diff = np .abs (curve .iloc [:, 1 ] - Wsa .Fn_area [i ])
417+ closest_index = np .argmin (diff )
418+ closest_elev = curve .iloc [closest_index , 0 ]
419+ closest_vol = curve .iloc [closest_index , 2 ]
420+ Wsa .dem_value_m [i ] = closest_elev
421+ Wsa .Tot_res_volume_mcm [i ] = closest_vol
422+
423+ delete_id = Wsa [(Wsa ['Quality' ] == 0 ) | (Wsa ['dem_value_m' ] < dead_wl ) | (Wsa ['dem_value_m' ] > max_wl + 20 )].index
424+ Wsa = Wsa .drop (delete_id )
425+ # ========================================== EXPORT RESULTS AS A CSV FILE
426+ Wsa .to_csv ('WSA_updated_' + res_name + '.csv' , index = False )
427+ print ("Done" )
428+ print (" " )
157429
158430
159431
0 commit comments