@@ -68,7 +68,6 @@ def build_distribution_fig(self, image):
6868 if self .df .empty :
6969 return None
7070 row_heights = [0.14 , 0.14 , 0.14 , 0.14 , 0.14 , 0.30 ]
71- # Создаем 3 строки и 2 колонки (последний график будет один в третьей строке)
7271 fig = make_subplots (
7372 rows = 6 , cols = 2 , row_heights = row_heights ,
7473 subplot_titles = (
@@ -126,13 +125,36 @@ def build_distribution_fig(self, image):
126125
127126 for row , col , col_name , label , color in params :
128127 self ._add_distribution (fig , row , col , self .df [col_name ], label , color )
128+ fig .update_traces (
129+ showlegend = False ,
130+ )
129131
130132 if 'centroid_x' in self .df .columns and 'centroid_y' in self .df .columns :
131133 self ._add_vector_field (fig , self .df , row = 6 , col = 1 , image = image )
134+
135+ # Включаем легенду только для векторного поля
136+ fig .update_traces (
137+ showlegend = True ,
138+ selector = dict (row = 6 , col = 1 )
139+ )
140+ # Настраиваем позицию легенды только для этого subplot
141+ # Настраиваем позицию легенды только для этого subplot
142+ fig .update_layout (
143+ legend = dict (
144+ itemsizing = 'constant' ,
145+ itemclick = 'toggle' ,
146+ itemdoubleclick = False ,
147+ orientation = "h" , # Горизонтальная ориентация
148+ yanchor = "top" , # Якорь по верхнему краю
149+ y = - 0.025 , # Позиция ниже графика (отрицательное значение)
150+ xanchor = "center" , # Центрирование по горизонтали
151+ x = 0.475 , # Центр по оси X
152+ font = dict (size = 16 )
153+ )
154+ )
132155
133156 fig .update_layout (
134157 height = 1600 ,
135- showlegend = False ,
136158 plot_bgcolor = "white" ,
137159 margin = dict (l = 50 , r = 50 , b = 50 , t = 50 ),
138160 modebar = {
@@ -190,8 +212,6 @@ def _add_distribution(self, fig, row, col, data, title, color):
190212 showgrid = False
191213 )
192214
193-
194-
195215 def _add_vector_field (self , fig , df , row , col , image ):
196216 image = np .flipud (image )
197217 image_pil = Image .fromarray (image .astype (np .uint8 ))
@@ -213,52 +233,84 @@ def _add_vector_field(self, fig, df, row, col, image):
213233 row = row ,
214234 col = col
215235 )
216-
217- if not {'centroid_x' , 'centroid_y' }.issubset (df .columns ):
236+
237+ required_columns = {'centroid_x' , 'centroid_y' }
238+ if not required_columns .issubset (df .columns ):
218239 return
219240
220- angle_column = self ._get_translation ("θₘₐₓ [°]" )
221- if angle_column not in df .columns :
241+ angle_max_col = self ._get_translation ("θₘₐₓ [°]" )
242+ angle_min_col = self ._get_translation ("θₘᵢₙ [°]" )
243+
244+ if not all (col in df .columns for col in [angle_max_col , angle_min_col ]):
222245 return
223246
224247 if self .scale_selector == self ._get_translation ('Instrument scale in µm' ):
225- diameter_col = self ._get_translation ("Dₘₐₓ [мкм]" )
248+ diameter_max_col = self ._get_translation ("Dₘₐₓ [мкм]" )
249+ diameter_min_col = self ._get_translation ("Dₘᵢₙ [мкм]" )
226250 else :
227- diameter_col = self ._get_translation ("Dₘₐₓ [пикс]" )
251+ diameter_max_col = self ._get_translation ("Dₘₐₓ [пикс]" )
252+ diameter_min_col = self ._get_translation ("Dₘᵢₙ [пикс]" )
228253
229- if diameter_col not in df .columns :
254+ if not all ( col in df .columns for col in [ diameter_max_col , diameter_min_col ]) :
230255 return
231256
232257 x = df ['centroid_x' ].values
233258 y = df ['centroid_y' ].values
234- theta_deg = df [angle_column ].values
235- theta_rad = np .deg2rad (theta_deg )
236- diameters = df [diameter_col ].values
237-
238- min_length = 20 # минимальная длина стрелки
239- max_length = 60 # максимальная длина стрелки
240- if len (diameters ) > 1 :
241- normalized_lengths = min_length + (max_length - min_length ) * (diameters - min (diameters )) / (max (diameters ) - min (diameters ))
259+
260+ theta_max_deg = df [angle_max_col ].values
261+ theta_max_rad = np .deg2rad (theta_max_deg )
262+ diameters_max = df [diameter_max_col ].values
263+
264+ min_length = 20
265+ max_length = 60
266+ if len (diameters_max ) > 1 :
267+ lengths_max = min_length + (max_length - min_length ) * (diameters_max - min (diameters_max )) / (max (diameters_max ) - min (diameters_max ))
242268 else :
243- normalized_lengths = [min_length ]
244-
245- u = np .cos (theta_rad ) * normalized_lengths
246- v = np .sin (theta_rad ) * normalized_lengths
247-
248- quiver_fig = ff .create_quiver (
249- x , y , u , v ,
250- scale = 1 ,
251- arrow_scale = 0.3 ,
252- line = dict (
253- width = 2 ,
254- color = 'yellow' ,
255- shape = 'spline'
256- )
257- )
258-
259- for trace in quiver_fig .data :
269+ lengths_max = [min_length ]
270+
271+ u_max = np .cos (theta_max_rad ) * lengths_max
272+ v_max = np .sin (theta_max_rad ) * lengths_max
273+
274+ theta_min_deg = df [angle_min_col ].values
275+ theta_min_rad = np .deg2rad (theta_min_deg )
276+ diameters_min = df [diameter_min_col ].values
277+
278+ if len (diameters_min ) > 1 :
279+ lengths_min = min_length + (max_length - min_length ) * (diameters_min - min (diameters_min )) / (max (diameters_min ) - min (diameters_min ))
280+ else :
281+ lengths_min = [min_length ]
282+
283+ u_min = np .cos (theta_min_rad ) * lengths_min
284+ v_min = np .sin (theta_min_rad ) * lengths_min
285+
286+ quiver_max = ff .create_quiver (
287+ x , y , u_max , v_max ,
288+ scale = 1 ,
289+ arrow_scale = 0.3 ,
290+ line = dict (
291+ width = 2 ,
292+ color = 'yellow' ,
293+ shape = 'spline'
294+ ),
295+ name = self ._get_translation ('Удлинение' )
296+ )
297+
298+ quiver_min = ff .create_quiver (
299+ x , y , u_min , v_min ,
300+ scale = 1 ,
301+ arrow_scale = 0.25 ,
302+ line = dict (
303+ width = 1.5 ,
304+ color = 'cyan' ,
305+ shape = 'spline'
306+ ),
307+ name = self ._get_translation ('Утолщение' ),
308+ visible = 'legendonly'
309+ )
310+
311+ for trace in quiver_max .data + quiver_min .data :
260312 fig .add_trace (trace , row = row , col = col )
261-
313+
262314 fig .update_xaxes (
263315 title_text = self ._get_translation ("X" ),
264316 row = row ,
@@ -267,7 +319,7 @@ def _add_vector_field(self, fig, df, row, col, image):
267319 constrain = 'domain' ,
268320 showgrid = False
269321 )
270-
322+
271323 fig .update_yaxes (
272324 title_text = self ._get_translation ("Y" ),
273325 row = row ,
0 commit comments