33import os
44
55from PyQt5 .QtWidgets import QMessageBox , QWidget
6+ from qgis .core import QgsProject , QgsVectorFileWriter
67from qgis .PyQt import uic
78
89from ...main .helpers import ColumnMatcher , get_layer_names
@@ -17,7 +18,7 @@ class BasalContactsWidget(QWidget):
1718 from geology layers.
1819 """
1920
20- def __init__ (self , parent = None , data_manager = None ):
21+ def __init__ (self , parent = None , data_manager = None , debug_manager = None ):
2122 """Initialize the basal contacts widget.
2223
2324 Parameters
@@ -29,6 +30,7 @@ def __init__(self, parent=None, data_manager=None):
2930 """
3031 super ().__init__ (parent )
3132 self .data_manager = data_manager
33+ self ._debug = debug_manager
3234
3335 # Load the UI file
3436 ui_path = os .path .join (os .path .dirname (__file__ ), "basal_contacts_widget.ui" )
@@ -62,6 +64,66 @@ def __init__(self, parent=None, data_manager=None):
6264 # Set up field combo boxes
6365 self ._setup_field_combo_boxes ()
6466
67+ def set_debug_manager (self , debug_manager ):
68+ """Attach a debug manager instance."""
69+ self ._debug = debug_manager
70+
71+ def _export_layer_for_debug (self , layer , name_prefix : str ):
72+ if not (self ._debug and self ._debug .is_debug ()):
73+ return None
74+ try :
75+ debug_dir = self ._debug .get_effective_debug_dir ()
76+ out_path = debug_dir / f"{ name_prefix } .gpkg"
77+ options = QgsVectorFileWriter .SaveVectorOptions ()
78+ options .driverName = "GPKG"
79+ options .layerName = layer .name ()
80+ res = QgsVectorFileWriter .writeAsVectorFormatV3 (
81+ layer ,
82+ str (out_path ),
83+ QgsProject .instance ().transformContext (),
84+ options ,
85+ )
86+ if res [0 ] == QgsVectorFileWriter .NoError :
87+ return str (out_path )
88+ except Exception as err :
89+ self ._debug .plugin .log (
90+ message = f"[map2loop] Failed to export layer '{ name_prefix } ': { err } " ,
91+ log_level = 2 ,
92+ )
93+ return None
94+
95+ def _serialize_layer (self , layer , name_prefix : str ):
96+ try :
97+ export_path = self ._export_layer_for_debug (layer , name_prefix )
98+ return {
99+ "name" : layer .name (),
100+ "id" : layer .id (),
101+ "provider" : layer .providerType () if hasattr (layer , "providerType" ) else None ,
102+ "source" : layer .source () if hasattr (layer , "source" ) else None ,
103+ "export_path" : export_path ,
104+ }
105+ except Exception :
106+ return str (layer )
107+
108+ def _serialize_params_for_logging (self , params , context_label : str ):
109+ serialized = {}
110+ for key , value in params .items ():
111+ if hasattr (value , "source" ) or hasattr (value , "id" ):
112+ serialized [key ] = self ._serialize_layer (value , f"{ context_label } _{ key } " )
113+ else :
114+ serialized [key ] = value
115+ return serialized
116+
117+ def _log_params (self , context_label : str ):
118+ if getattr (self , "_debug" , None ):
119+ try :
120+ self ._debug .log_params (
121+ context_label = context_label ,
122+ params = self ._serialize_params_for_logging (self .get_parameters (), context_label ),
123+ )
124+ except Exception :
125+ pass
126+
65127 def _guess_layers (self ):
66128 """Attempt to auto-select layers based on common naming conventions."""
67129 if not self .data_manager :
@@ -113,53 +175,39 @@ def _on_geology_layer_changed(self):
113175
114176 def _run_extractor (self ):
115177 """Run the basal contacts extraction algorithm."""
178+ self ._log_params ("basal_contacts_widget_run" )
179+
116180 # Validate inputs
117181 if not self .geologyLayerComboBox .currentLayer ():
118182 QMessageBox .warning (self , "Missing Input" , "Please select a geology layer." )
119183 return
120184
121- # Parse ignore units
122- ignore_units = []
123- if self .ignoreUnitsLineEdit .text ().strip ():
124- ignore_units = [
125- unit .strip () for unit in self .ignoreUnitsLineEdit .text ().split (',' ) if unit .strip ()
126- ]
127- geology = self .geologyLayerComboBox .currentLayer ()
128- unit_name_field = self .unitNameFieldComboBox .currentField ()
129- faults = self .faultsLayerComboBox .currentLayer ()
130- stratigraphic_order = (
131- self .data_manager .get_stratigraphic_unit_names () if self .data_manager else []
132- )
133-
134- # Check if user wants all contacts or just basal contacts
135- all_contacts = self .allContactsCheckBox .isChecked ()
136- if all_contacts :
137- stratigraphic_order = list ({g [unit_name_field ] for g in geology .getFeatures ()})
138- result = extract_basal_contacts (
139- geology = geology ,
140- stratigraphic_order = stratigraphic_order ,
141- faults = faults ,
142- ignore_units = ignore_units ,
143- unit_name_field = unit_name_field ,
144- all_contacts = all_contacts ,
145- updater = lambda message : QMessageBox .information (self , "Extraction Progress" , message ),
146- )
147-
148- # Show success message based on what was extracted
149- if all_contacts and result :
150- addGeoDataFrameToproject (result ['all_contacts' ], "All contacts" )
151- contact_type = "all contacts and basal contacts"
152- else :
153- addGeoDataFrameToproject (result ['basal_contacts' ], "Basal contacts" )
154-
155- contact_type = "basal contacts"
156-
157- if result :
158- QMessageBox .information (
159- self ,
160- "Success" ,
161- f"Successfully extracted { contact_type } !" ,
162- )
185+ try :
186+ result , contact_type = self ._extract_contacts ()
187+ if result :
188+ QMessageBox .information (
189+ self ,
190+ "Success" ,
191+ f"Successfully extracted { contact_type } !" ,
192+ )
193+ if self ._debug and self ._debug .is_debug ():
194+ try :
195+ self ._debug .save_debug_file (
196+ "basal_contacts_result.txt" , str (result ).encode ("utf-8" )
197+ )
198+ except Exception as err :
199+ self ._debug .plugin .log (
200+ message = f"[map2loop] Failed to save basal contacts debug output: { err } " ,
201+ log_level = 2 ,
202+ )
203+ except Exception as err :
204+ if self ._debug :
205+ self ._debug .plugin .log (
206+ message = f"[map2loop] Basal contacts extraction failed: { err } " ,
207+ log_level = 2 ,
208+ )
209+ raise err
210+ QMessageBox .critical (self , "Error" , f"An error occurred: { err } " )
163211
164212 def get_parameters (self ):
165213 """Get current widget parameters.
@@ -199,3 +247,44 @@ def set_parameters(self, params):
199247 self .ignoreUnitsLineEdit .setText (', ' .join (params ['ignore_units' ]))
200248 if 'all_contacts' in params :
201249 self .allContactsCheckBox .setChecked (params ['all_contacts' ])
250+
251+ def _extract_contacts (self ):
252+ """Execute basal contacts extraction."""
253+ # Parse ignore units
254+ ignore_units = []
255+ if self .ignoreUnitsLineEdit .text ().strip ():
256+ ignore_units = [
257+ unit .strip () for unit in self .ignoreUnitsLineEdit .text ().split (',' ) if unit .strip ()
258+ ]
259+ geology = self .geologyLayerComboBox .currentLayer ()
260+ unit_name_field = self .unitNameFieldComboBox .currentField ()
261+ faults = self .faultsLayerComboBox .currentLayer ()
262+ stratigraphic_order = (
263+ self .data_manager .get_stratigraphic_unit_names () if self .data_manager else []
264+ )
265+
266+ # Check if user wants all contacts or just basal contacts
267+ all_contacts = self .allContactsCheckBox .isChecked ()
268+ if all_contacts :
269+ stratigraphic_order = list ({g [unit_name_field ] for g in geology .getFeatures ()})
270+ self .data_manager .logger (f"Extracting all contacts for units: { stratigraphic_order } " )
271+
272+ result = extract_basal_contacts (
273+ geology = geology ,
274+ stratigraphic_order = stratigraphic_order ,
275+ faults = faults ,
276+ ignore_units = ignore_units ,
277+ unit_name_field = unit_name_field ,
278+ all_contacts = all_contacts ,
279+ updater = lambda message : QMessageBox .information (self , "Extraction Progress" , message ),
280+ debug_manager = self ._debug ,
281+ )
282+ self .data_manager .logger (f'All contacts extracted: { all_contacts } ' )
283+ contact_type = "basal contacts"
284+ if result :
285+ if all_contacts and result ['all_contacts' ].empty is False :
286+ addGeoDataFrameToproject (result ['all_contacts' ], "All contacts" )
287+ contact_type = "all contacts and basal contacts"
288+ elif not all_contacts and result ['basal_contacts' ].empty is False :
289+ addGeoDataFrameToproject (result ['basal_contacts' ], "Basal contacts" )
290+ return result , contact_type
0 commit comments