11from LoopStructural .modelling .core .stratigraphic_column import StratigraphicColumnElementType
2- from qgis .core import QgsApplication , QgsMapLayerProxyModel , QgsStyle
2+ from qgis .core import QgsApplication , QgsExpression , QgsMapLayerProxyModel , QgsStyle
33from qgis .gui import QgsFieldComboBox , QgsMapLayerComboBox
44from qgis .PyQt .QtCore import QSize
55from qgis .PyQt .QtGui import QIcon
@@ -67,10 +67,15 @@ def __init__(self, parent=None, data_manager=None):
6767 # layer/field is selected, so the name-match warning is skipped.
6868 self ._known_unit_names = None
6969
70+ # The layer whose features were selected to highlight the selected
71+ # unit on the map, so the selection can be cleared later.
72+ self ._highlighted_layer = None
73+
7074 # Main list widget
7175 self .unitList = QListWidget ()
7276 self .unitList .setDragDropMode (QAbstractItemView .DragDropMode .InternalMove )
7377 self .unitList .model ().rowsMoved .connect (self .update_order )
78+ self .unitList .itemSelectionChanged .connect (self .highlight_selected_unit )
7479 layout .addWidget (self .unitList )
7580
7681 # A single row of icon-only actions for building/clearing the column.
@@ -170,6 +175,18 @@ def __init__(self, parent=None, data_manager=None):
170175 )
171176 applyAgeButton .clicked .connect (self .apply_age_to_layer )
172177 ageRow .addWidget (applyAgeButton )
178+
179+ applyThicknessButton = self ._make_tool_button (
180+ "mActionMeasure.svg" , "Apply Stratigraphic Thickness to Map Layer"
181+ )
182+ applyThicknessButton .setToolTip (
183+ "Apply Stratigraphic Thickness to Map Layer\n "
184+ "Write a 'strat_thickness' field (the thickness of each unit in the "
185+ "column) onto the selected layer above and style it with a graduated "
186+ "colour ramp."
187+ )
188+ applyThicknessButton .clicked .connect (self .apply_thickness_to_layer )
189+ ageRow .addWidget (applyThicknessButton )
173190 layout .addLayout (ageRow )
174191
175192 self ._guess_units_layer ()
@@ -413,6 +430,7 @@ def _on_units_field_changed(self, _field_name):
413430 """Persist and re-validate when the unit-name field selection changes."""
414431 self ._persist_units_layer_selection ()
415432 self ._revalidate_unit_names ()
433+ self .highlight_selected_unit ()
416434
417435 def _on_units_layer_changed (self , layer ):
418436 """Update the field combo box when the units layer changes."""
@@ -424,6 +442,7 @@ def _on_units_layer_changed(self, layer):
424442 self .unitsLayerFieldComboBox .setField (unit_match )
425443 self ._persist_units_layer_selection ()
426444 self ._revalidate_unit_names ()
445+ self .highlight_selected_unit ()
427446
428447 def _get_known_unit_names (self ):
429448 """Return the set of exact unit-name values in the selected geology layer/field.
@@ -454,6 +473,47 @@ def _revalidate_unit_names(self):
454473 if hasattr (widget , 'set_known_unit_names' ):
455474 widget .set_known_unit_names (self ._known_unit_names )
456475
476+ def highlight_selected_unit (self ):
477+ """Select the features of the selected unit in the units layer, so
478+ that QGIS highlights them on the map.
479+
480+ Clears the highlight when no unit row is selected (for example an
481+ unconformity row) or when no units layer/field is set.
482+ """
483+ try :
484+ items = self .unitList .selectedItems ()
485+ except RuntimeError :
486+ # Widget was deleted
487+ return
488+ widget = self .unitList .itemWidget (items [0 ]) if items else None
489+ layer = self .unitsLayerComboBox .currentLayer ()
490+ field_name = self .unitsLayerFieldComboBox .currentField ()
491+
492+ self ._clear_unit_highlight ()
493+ if (
494+ not isinstance (widget , StratigraphicUnitWidget )
495+ or not widget .name
496+ or layer is None
497+ or not field_name
498+ or layer .fields ().indexFromName (field_name ) < 0
499+ ):
500+ return
501+ expression = f"{ QgsExpression .quotedColumnRef (field_name )} = { QgsExpression .quotedValue (widget .name )} "
502+ layer .selectByExpression (expression )
503+ self ._highlighted_layer = layer
504+
505+ def _clear_unit_highlight (self ):
506+ """Remove the feature selection made by highlight_selected_unit."""
507+ layer = self ._highlighted_layer
508+ self ._highlighted_layer = None
509+ if layer is None :
510+ return
511+ try :
512+ layer .removeSelection ()
513+ except RuntimeError :
514+ # Layer was removed from the project
515+ pass
516+
457517 def apply_colours_to_layer (self ):
458518 """Push the stratigraphic column's colours onto the selected units layer."""
459519 if not self .data_manager :
@@ -514,6 +574,39 @@ def apply_age_to_layer(self):
514574 "units, or no features matched a stratigraphic unit." ,
515575 )
516576
577+ def apply_thickness_to_layer (self ):
578+ """Write each unit's thickness onto the selected units layer and style it by a graduated ramp."""
579+ if not self .data_manager :
580+ print ("Error: Data manager is not initialized." )
581+ return
582+ layer = self .unitsLayerComboBox .currentLayer ()
583+ field_name = self .unitsLayerFieldComboBox .currentField ()
584+ if layer is None or not field_name :
585+ QMessageBox .warning (
586+ self ,
587+ "Apply Stratigraphic Thickness to Map Layer" ,
588+ "Please select a units layer and unit name field above." ,
589+ )
590+ return
591+ ramp_name = self .strat_ageColorRampComboBox .currentText ()
592+ applied = self .data_manager .apply_stratigraphic_thickness_to_layer (
593+ layer , field_name , ramp_name = ramp_name
594+ )
595+ if applied :
596+ QMessageBox .information (
597+ self ,
598+ "Apply Stratigraphic Thickness to Map Layer" ,
599+ f"Applied stratigraphic thickness and graduated styling to layer "
600+ f"'{ layer .name ()} '." ,
601+ )
602+ else :
603+ QMessageBox .warning (
604+ self ,
605+ "Apply Stratigraphic Thickness to Map Layer" ,
606+ "Could not apply stratigraphic thickness. The stratigraphic column may "
607+ "have no units, or no features matched a stratigraphic unit." ,
608+ )
609+
517610 def add_unit (self , * , unit_data = None , create_new = True ):
518611 if unit_data is None :
519612 unit_data = {'type' : 'unit' , 'name' : '' }
@@ -545,6 +638,7 @@ def add_unit(self, *, unit_data=None, create_new=True):
545638 unit_widget .nameChanged .connect (
546639 lambda : self .update_element (unit_widget )
547640 ) # Connect name change signal
641+ unit_widget .nameChanged .connect (lambda : self ._on_unit_name_changed (unit_widget ))
548642
549643 unit_widget .thicknessChanged .connect (
550644 lambda : self .update_element (unit_widget )
@@ -561,6 +655,7 @@ def add_unit(self, *, unit_data=None, create_new=True):
561655 item .setSizeHint (unit_widget .sizeHint ())
562656 self ._add_list_item (item , at_top = create_new )
563657 self .unitList .setItemWidget (item , unit_widget )
658+ unit_widget .focused .connect (lambda : self .unitList .setCurrentItem (item ))
564659 unit_widget .setData (unit_data ) # Set data for the unit widget
565660 unit_widget .set_known_unit_names (self ._known_unit_names )
566661
@@ -608,6 +703,15 @@ def add_unconformity(self, *, unconformity_data=None, create_new=True):
608703 # Cache the widget for efficient updates
609704 self ._widget_cache [unconformity .uuid ] = (unconformity_widget , item )
610705
706+ def _on_unit_name_changed (self , unit_widget ):
707+ """Update the map highlight when the selected unit is renamed."""
708+ try :
709+ items = self .unitList .selectedItems ()
710+ except RuntimeError :
711+ return
712+ if items and self .unitList .itemWidget (items [0 ]) is unit_widget :
713+ self .highlight_selected_unit ()
714+
611715 def _add_list_item (self , item , * , at_top ):
612716 """Add a row to the list. A new element goes on top of the column
613717 (the end of `order`), so it is shown in the first row."""
0 commit comments