github-actions[bot] commited on
Commit
69abaa3
·
1 Parent(s): 2dc4d04

Deploy from GitHub Actions

Browse files
Files changed (2) hide show
  1. plots/map.py +223 -200
  2. ui/pages/seasonal_maps.py +1 -0
plots/map.py CHANGED
@@ -13,204 +13,203 @@ from osgeo import gdal
13
 
14
  from utils.data_loading import timer
15
 
16
-
17
- def plot_seasonal_salinity(
18
- salinity_data: pd.DataFrame,
19
- year: str,
20
- basemap_provider,
21
- alpha=0.5,
22
- shapefile_path="data/SAB/SAB.shp",
23
- reporting_end_month: int = 10,
24
- ):
25
- """
26
- Create seasonal plots of mean salinity values by WBID with basemap.
27
- Uses configurable Reporting Year with meteorological seasons.
28
-
29
- Args:
30
- salinity_data: DataFrame containing salinity measurements
31
- year: Reporting Year to filter data for (str)
32
- reporting_end_month: Last month of the reporting year (1-12, default=10 for October)
33
- """
34
- # Read and filter WBIDs
35
- wbids = gpd.read_file(shapefile_path)
36
- relevant_wbids = salinity_data["WBID"].unique()
37
- wbids = wbids[wbids["WBID"].isin(relevant_wbids)]
38
- wbids = wbids.to_crs(epsg=3857)
39
-
40
- # Process data - create a copy to avoid SettingWithCopyWarning
41
- year_data = salinity_data[salinity_data["Reporting_Year"] == int(year)].copy()
42
-
43
- # Function to determine quarter based on date and reporting year end
44
- def get_quarter(date, reporting_end_month):
45
- month = date.month
46
-
47
- # Calculate month offset to align with reporting year
48
- month_offset = (12 - reporting_end_month) % 12
49
-
50
- # Adjust month to align with reporting year
51
- adjusted_month = ((month + month_offset) % 12) or 12
52
-
53
- # Determine quarter (1-4)
54
- return f"Q{((adjusted_month - 1) // 3) + 1}"
55
-
56
- # Add quarter column
57
- year_data.loc[:, "quarter"] = year_data["Activity_Start_Date_Time"].apply(
58
- lambda x: get_quarter(x, reporting_end_month)
59
- )
60
-
61
- # Calculate quarterly means
62
- seasonal_means = (
63
- year_data.groupby(["WBID", "quarter"], observed=True)["Salinity"]
64
- .mean()
65
- .reset_index()
66
- )
67
-
68
- fig = plt.figure(figsize=(20, 14))
69
-
70
- # Create custom colormap with focused range
71
- colors = ["#08519c", "#73a9cf", "#fee090", "#fc8d59", "#d73027"]
72
- cmap = LinearSegmentedColormap.from_list("custom", colors, N=100)
73
-
74
- # Get global min/max for consistent colormap
75
- vmin = seasonal_means["Salinity"].min()
76
- vmax = 40
77
-
78
- # Calculate map extent
79
- bounds = wbids.total_bounds
80
- x_buffer = (bounds[2] - bounds[0]) * 0.05
81
- y_buffer = (bounds[3] - bounds[1]) * 0.05
82
- extent = [
83
- bounds[0] - x_buffer,
84
- bounds[2] + x_buffer,
85
- bounds[1] - y_buffer,
86
- bounds[3] + y_buffer,
87
- ]
88
-
89
- # Create subplots with tighter spacing
90
- gs = fig.add_gridspec(
91
- 2,
92
- 2,
93
- width_ratios=[1, 1],
94
- wspace=0.05, # Minimal horizontal space between plots
95
- hspace=-0.15, # More negative value to further reduce vertical space
96
- left=0.02, # Left margin
97
- right=0.98, # Right margin
98
- top=0.95, # Slightly reduced top margin to give more space
99
- bottom=0.05, # Slightly increased bottom margin to give more space
100
- )
101
-
102
- # Function to get quarter date range
103
- def get_quarter_dates(quarter: str, year: int, reporting_end_month: int) -> str:
104
- # Calculate first month of reporting year
105
- first_month = (reporting_end_month % 12) + 1
106
-
107
- # Calculate start month for each quarter
108
- quarter_num = int(quarter[1])
109
- start_month = ((first_month - 1 + ((quarter_num - 1) * 3)) % 12) + 1
110
- end_month = ((start_month + 2) % 12) or 12
111
-
112
- # For Reporting Year X, the start date is actually in year X-1 if the month
113
- # is after the reporting end month
114
- start_year = int(year) - 1 if start_month > reporting_end_month else int(year)
115
- end_year = start_year
116
- if end_month < start_month:
117
- end_year += 1
118
-
119
- start_date = pd.Timestamp(f"{start_year}-{start_month:02d}-01")
120
- end_date = pd.Timestamp(
121
- f"{end_year}-{end_month:02d}-{pd.Timestamp(f'{end_year}-{end_month:02d}').days_in_month}"
122
- )
123
-
124
- return f"{start_date.strftime('%b %d, %Y')} - {end_date.strftime('%b %d, %Y')}"
125
-
126
- # Use quarters instead of seasons
127
- quarters = ["Q1", "Q2", "Q3", "Q4"]
128
-
129
- for idx, quarter in enumerate(quarters):
130
- ax = fig.add_subplot(gs[idx // 2, idx % 2])
131
-
132
- quarter_data = seasonal_means[seasonal_means["quarter"] == quarter]
133
- merged = wbids.merge(quarter_data, on="WBID", how="left")
134
-
135
- # Plot WBIDs
136
- merged.plot(
137
- column="Salinity",
138
- ax=ax,
139
- cmap=cmap,
140
- vmin=vmin,
141
- vmax=vmax,
142
- alpha=0.7,
143
- missing_kwds={"color": "lightgrey", "alpha": 0.5},
144
- )
145
-
146
- ctx.add_basemap(ax, source=basemap_provider, zoom=11, alpha=alpha) # type: ignore
147
-
148
- ax.set_xlim(extent[0], extent[1])
149
- ax.set_ylim(extent[2], extent[3])
150
-
151
- # Get date range for this quarter
152
- date_range = get_quarter_dates(quarter, int(year), reporting_end_month)
153
-
154
- # Create title with two lines
155
- if idx < 2: # Top row
156
- ax.set_title(
157
- f"Quarter {quarter[1]} Mean Salinity\n{date_range}",
158
- pad=15,
159
- fontsize=10,
160
- )
161
- else: # Bottom row
162
- ax.set_title(
163
- f"Quarter {quarter[1]} Mean Salinity\n{date_range}",
164
- pad=5,
165
- fontsize=10,
166
- )
167
- ax.set_axis_off()
168
-
169
- # Add colorbar
170
- norm = plt.Normalize(vmin=vmin, vmax=vmax) # type: ignore
171
- sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
172
- sm.set_array([])
173
- fig.colorbar(
174
- sm,
175
- ax=fig.axes,
176
- orientation="vertical",
177
- label="Salinity (ppt)",
178
- pad=0.01,
179
- fraction=0.015,
180
- ticks=np.arange(0, 45, 5), # Add ticks every 5 units
181
- )
182
-
183
- return fig
184
-
185
-
186
- def plot_seasonal_salinity_for_bays(
187
- salinity_data: pd.DataFrame,
188
- year: str,
189
- basemap_provider=ctx.providers.USGS.USTopo, # type: ignore
190
- alpha=0.5,
191
- shapefile_path="data/SAB/SAB.shp",
192
- wbids=None,
193
- reporting_end_month: int = 10,
194
- ):
195
- """
196
- Create seasonal plots of mean salinity values by WBID for N, E, W, SAB, GL and Lake Powell.
197
- """
198
- if wbids is None:
199
- wbids = gpd.read_file(shapefile_path)
200
- if wbids.crs is None:
201
- wbids.set_crs(epsg=6439, inplace=True)
202
- wbids = wbids.to_crs(epsg=3857)
203
- fig = plot_seasonal_salinity(
204
- salinity_data.query(
205
- "WBID.isin(['1061A', '1061B', '1061C', '1061D', '1061E', '1061F', '1061G', '1061H', '1055A'])"
206
- ),
207
- year=year,
208
- basemap_provider=basemap_provider,
209
- alpha=alpha,
210
- shapefile_path=shapefile_path,
211
- reporting_end_month=reporting_end_month,
212
- )
213
- return fig
214
 
215
 
216
  @timer(include_params=True)
@@ -490,9 +489,10 @@ def add_colorbar(
490
  # Get value range
491
  vmin = seasonal_means[parameter].min()
492
  vmax = get_parameter_max_value(parameter, seasonal_means[parameter].max())
 
493
 
494
  # Create colorbar
495
- norm = plt.Normalize(vmin=vmin, vmax=vmax) # type: ignore
496
  sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
497
  sm.set_array([])
498
 
@@ -500,6 +500,29 @@ def add_colorbar(
500
  unit = get_parameter_unit(parameter)
501
  label = f"{parameter} ({unit})" if unit else parameter
502
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
  # Add colorbar to figure
504
  fig.colorbar(
505
  sm,
@@ -508,7 +531,7 @@ def add_colorbar(
508
  label=label,
509
  pad=0.01,
510
  fraction=0.015,
511
- ticks=np.arange(0, vmax + 5, 5), # Add ticks every 5 units
512
  )
513
 
514
 
 
13
 
14
  from utils.data_loading import timer
15
 
16
+ # def plot_seasonal_salinity(
17
+ # salinity_data: pd.DataFrame,
18
+ # year: str,
19
+ # basemap_provider,
20
+ # alpha=0.5,
21
+ # shapefile_path="data/SAB/SAB.shp",
22
+ # reporting_end_month: int = 10,
23
+ # ):
24
+ # """
25
+ # Create seasonal plots of mean salinity values by WBID with basemap.
26
+ # Uses configurable Reporting Year with meteorological seasons.
27
+
28
+ # Args:
29
+ # salinity_data: DataFrame containing salinity measurements
30
+ # year: Reporting Year to filter data for (str)
31
+ # reporting_end_month: Last month of the reporting year (1-12, default=10 for October)
32
+ # """
33
+ # # Read and filter WBIDs
34
+ # wbids = gpd.read_file(shapefile_path)
35
+ # relevant_wbids = salinity_data["WBID"].unique()
36
+ # wbids = wbids[wbids["WBID"].isin(relevant_wbids)]
37
+ # wbids = wbids.to_crs(epsg=3857)
38
+
39
+ # # Process data - create a copy to avoid SettingWithCopyWarning
40
+ # year_data = salinity_data[salinity_data["Reporting_Year"] == int(year)].copy()
41
+
42
+ # # Function to determine quarter based on date and reporting year end
43
+ # def get_quarter(date, reporting_end_month):
44
+ # month = date.month
45
+
46
+ # # Calculate month offset to align with reporting year
47
+ # month_offset = (12 - reporting_end_month) % 12
48
+
49
+ # # Adjust month to align with reporting year
50
+ # adjusted_month = ((month + month_offset) % 12) or 12
51
+
52
+ # # Determine quarter (1-4)
53
+ # return f"Q{((adjusted_month - 1) // 3) + 1}"
54
+
55
+ # # Add quarter column
56
+ # year_data.loc[:, "quarter"] = year_data["Activity_Start_Date_Time"].apply(
57
+ # lambda x: get_quarter(x, reporting_end_month)
58
+ # )
59
+
60
+ # # Calculate quarterly means
61
+ # seasonal_means = (
62
+ # year_data.groupby(["WBID", "quarter"], observed=True)["Salinity"]
63
+ # .mean()
64
+ # .reset_index()
65
+ # )
66
+
67
+ # fig = plt.figure(figsize=(20, 14))
68
+
69
+ # # Create custom colormap with focused range
70
+ # colors = ["#08519c", "#73a9cf", "#fee090", "#fc8d59", "#d73027"]
71
+ # cmap = LinearSegmentedColormap.from_list("custom", colors, N=100)
72
+
73
+ # # Get global min/max for consistent colormap
74
+ # vmin = seasonal_means["Salinity"].min()
75
+ # vmax = 40
76
+
77
+ # # Calculate map extent
78
+ # bounds = wbids.total_bounds
79
+ # x_buffer = (bounds[2] - bounds[0]) * 0.05
80
+ # y_buffer = (bounds[3] - bounds[1]) * 0.05
81
+ # extent = [
82
+ # bounds[0] - x_buffer,
83
+ # bounds[2] + x_buffer,
84
+ # bounds[1] - y_buffer,
85
+ # bounds[3] + y_buffer,
86
+ # ]
87
+
88
+ # # Create subplots with tighter spacing
89
+ # gs = fig.add_gridspec(
90
+ # 2,
91
+ # 2,
92
+ # width_ratios=[1, 1],
93
+ # wspace=0.05, # Minimal horizontal space between plots
94
+ # hspace=-0.15, # More negative value to further reduce vertical space
95
+ # left=0.02, # Left margin
96
+ # right=0.98, # Right margin
97
+ # top=0.95, # Slightly reduced top margin to give more space
98
+ # bottom=0.05, # Slightly increased bottom margin to give more space
99
+ # )
100
+
101
+ # # Function to get quarter date range
102
+ # def get_quarter_dates(quarter: str, year: int, reporting_end_month: int) -> str:
103
+ # # Calculate first month of reporting year
104
+ # first_month = (reporting_end_month % 12) + 1
105
+
106
+ # # Calculate start month for each quarter
107
+ # quarter_num = int(quarter[1])
108
+ # start_month = ((first_month - 1 + ((quarter_num - 1) * 3)) % 12) + 1
109
+ # end_month = ((start_month + 2) % 12) or 12
110
+
111
+ # # For Reporting Year X, the start date is actually in year X-1 if the month
112
+ # # is after the reporting end month
113
+ # start_year = int(year) - 1 if start_month > reporting_end_month else int(year)
114
+ # end_year = start_year
115
+ # if end_month < start_month:
116
+ # end_year += 1
117
+
118
+ # start_date = pd.Timestamp(f"{start_year}-{start_month:02d}-01")
119
+ # end_date = pd.Timestamp(
120
+ # f"{end_year}-{end_month:02d}-{pd.Timestamp(f'{end_year}-{end_month:02d}').days_in_month}"
121
+ # )
122
+
123
+ # return f"{start_date.strftime('%b %d, %Y')} - {end_date.strftime('%b %d, %Y')}"
124
+
125
+ # # Use quarters instead of seasons
126
+ # quarters = ["Q1", "Q2", "Q3", "Q4"]
127
+
128
+ # for idx, quarter in enumerate(quarters):
129
+ # ax = fig.add_subplot(gs[idx // 2, idx % 2])
130
+
131
+ # quarter_data = seasonal_means[seasonal_means["quarter"] == quarter]
132
+ # merged = wbids.merge(quarter_data, on="WBID", how="left")
133
+
134
+ # # Plot WBIDs
135
+ # merged.plot(
136
+ # column="Salinity",
137
+ # ax=ax,
138
+ # cmap=cmap,
139
+ # vmin=vmin,
140
+ # vmax=vmax,
141
+ # alpha=0.7,
142
+ # missing_kwds={"color": "lightgrey", "alpha": 0.5},
143
+ # )
144
+
145
+ # ctx.add_basemap(ax, source=basemap_provider, zoom=11, alpha=alpha) # type: ignore
146
+
147
+ # ax.set_xlim(extent[0], extent[1])
148
+ # ax.set_ylim(extent[2], extent[3])
149
+
150
+ # # Get date range for this quarter
151
+ # date_range = get_quarter_dates(quarter, int(year), reporting_end_month)
152
+
153
+ # # Create title with two lines
154
+ # if idx < 2: # Top row
155
+ # ax.set_title(
156
+ # f"Quarter {quarter[1]} Mean Salinity\n{date_range}",
157
+ # pad=15,
158
+ # fontsize=10,
159
+ # )
160
+ # else: # Bottom row
161
+ # ax.set_title(
162
+ # f"Quarter {quarter[1]} Mean Salinity\n{date_range}",
163
+ # pad=5,
164
+ # fontsize=10,
165
+ # )
166
+ # ax.set_axis_off()
167
+
168
+ # # Add colorbar
169
+ # norm = plt.Normalize(vmin=vmin, vmax=vmax) # type: ignore
170
+ # sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
171
+ # sm.set_array([])
172
+ # fig.colorbar(
173
+ # sm,
174
+ # ax=fig.axes,
175
+ # orientation="vertical",
176
+ # label="Salinity (ppt)",
177
+ # pad=0.01,
178
+ # fraction=0.015,
179
+ # ticks=np.arange(0, 45, 5), # Add ticks every 5 units
180
+ # )
181
+
182
+ # return fig
183
+
184
+
185
+ # def plot_seasonal_salinity_for_bays(
186
+ # salinity_data: pd.DataFrame,
187
+ # year: str,
188
+ # basemap_provider=ctx.providers.USGS.USTopo, # type: ignore
189
+ # alpha=0.5,
190
+ # shapefile_path="data/SAB/SAB.shp",
191
+ # wbids=None,
192
+ # reporting_end_month: int = 10,
193
+ # ):
194
+ # """
195
+ # Create seasonal plots of mean salinity values by WBID for N, E, W, SAB, GL and Lake Powell.
196
+ # """
197
+ # if wbids is None:
198
+ # wbids = gpd.read_file(shapefile_path)
199
+ # if wbids.crs is None:
200
+ # wbids.set_crs(epsg=6439, inplace=True)
201
+ # wbids = wbids.to_crs(epsg=3857)
202
+ # fig = plot_seasonal_salinity(
203
+ # salinity_data.query(
204
+ # "WBID.isin(['1061A', '1061B', '1061C', '1061D', '1061E', '1061F', '1061G', '1061H', '1055A'])"
205
+ # ),
206
+ # year=year,
207
+ # basemap_provider=basemap_provider,
208
+ # alpha=alpha,
209
+ # shapefile_path=shapefile_path,
210
+ # reporting_end_month=reporting_end_month,
211
+ # )
212
+ # return fig
 
213
 
214
 
215
  @timer(include_params=True)
 
489
  # Get value range
490
  vmin = seasonal_means[parameter].min()
491
  vmax = get_parameter_max_value(parameter, seasonal_means[parameter].max())
492
+ data_max = seasonal_means[parameter].max()
493
 
494
  # Create colorbar
495
+ norm = plt.Normalize(vmin=vmin, vmax=vmax if vmax is not None else data_max) # type: ignore
496
  sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
497
  sm.set_array([])
498
 
 
500
  unit = get_parameter_unit(parameter)
501
  label = f"{parameter} ({unit})" if unit else parameter
502
 
503
+ # Calculate appropriate tick spacing based on data range
504
+ if vmax is not None:
505
+ if vmax <= 1:
506
+ tick_spacing = 0.1
507
+ elif vmax <= 10:
508
+ tick_spacing = 1
509
+ elif vmax <= 50:
510
+ tick_spacing = 5
511
+ else:
512
+ tick_spacing = 10
513
+ ticks = np.arange(0, vmax + tick_spacing, tick_spacing)
514
+ else:
515
+ # Use data_max with automatic tick spacing
516
+ if data_max <= 1:
517
+ tick_spacing = 0.1
518
+ elif data_max <= 10:
519
+ tick_spacing = 1
520
+ elif data_max <= 50:
521
+ tick_spacing = 5
522
+ else:
523
+ tick_spacing = 10
524
+ ticks = np.arange(0, data_max + tick_spacing, tick_spacing)
525
+
526
  # Add colorbar to figure
527
  fig.colorbar(
528
  sm,
 
531
  label=label,
532
  pad=0.01,
533
  fraction=0.015,
534
+ ticks=ticks,
535
  )
536
 
537
 
ui/pages/seasonal_maps.py CHANGED
@@ -70,6 +70,7 @@ if not raw_df.empty:
70
  "Activity_Start_Date_Time",
71
  "Reporting_Year",
72
  "WBID",
 
73
  "Station_Number",
74
  "Sample_Position",
75
  "Org_Analyte_Name",
 
70
  "Activity_Start_Date_Time",
71
  "Reporting_Year",
72
  "WBID",
73
+ "Sector",
74
  "Station_Number",
75
  "Sample_Position",
76
  "Org_Analyte_Name",