@@ -150,188 +150,194 @@ def _on_geology_layer_changed(self):
150150 break
151151
152152 def _run_painter (self ):
153- """Run the paint stratigraphic order algorithm."""
153+ """Run the paint stratigraphic order algorithm.
154+
155+ Returns
156+ -------
157+ bool
158+ True if operation completed without unhandled exceptions, False otherwise.
159+ """
154160
155- geology_layer = self .geologyLayerComboBox .currentLayer ()
156- unit_name_field = self .unitNameFieldComboBox .currentField ()
157- stratigraphic_order = self .data_manager .stratigraphic_order = (
158- self .data_manager .get_stratigraphic_unit_names () if self .data_manager else []
159- )
160- paint_stratigraphic_order (
161- geology_layer , stratigraphic_order , unit_name_field , debug_manager = self ._debug
162- )
163-
164- # If requested, duplicate layer and apply style using selected colour ramp
165161 try :
166- duplicate = (
167- getattr (self , 'duplicateLayerCheckBox' , None )
168- and self .duplicateLayerCheckBox .isChecked ()
162+ geology_layer = self .geologyLayerComboBox .currentLayer ()
163+ unit_name_field = self .unitNameFieldComboBox .currentField ()
164+ stratigraphic_order = self .data_manager .stratigraphic_order = (
165+ self .data_manager .get_stratigraphic_unit_names () if self .data_manager else []
166+ )
167+ paint_stratigraphic_order (
168+ geology_layer , stratigraphic_order , unit_name_field , debug_manager = self ._debug
169169 )
170- except Exception :
171- duplicate = False
172170
173- if duplicate :
174- # Get chosen ramp name
171+ # If requested, duplicate layer and apply style using selected colour ramp
175172 try :
176- ramp_name = self .colorRampComboBox .currentText ()
173+ duplicate = (
174+ getattr (self , 'duplicateLayerCheckBox' , None )
175+ and self .duplicateLayerCheckBox .isChecked ()
176+ )
177177 except Exception :
178- ramp_name = None
178+ duplicate = False
179+
180+ if duplicate :
181+ # Get chosen ramp name
182+ try :
183+ ramp_name = self .colorRampComboBox .currentText ()
184+ except Exception :
185+ ramp_name = None
186+
187+ # Step 1: create a memory copy of the geology layer and copy attributes/geometry
188+ try :
189+ from PyQt5 .QtCore import QVariant
190+ from qgis .core import (
191+ QgsFeature ,
192+ QgsField ,
193+ QgsGraduatedSymbolRenderer ,
194+ QgsProject ,
195+ QgsRendererRange ,
196+ QgsStyle ,
197+ QgsSymbol ,
198+ QgsVectorLayer ,
199+ QgsWkbTypes ,
200+ )
179201
180- # Step 1: create a memory copy of the geology layer and copy attributes/geometry
181- try :
182- from PyQt5 .QtCore import QVariant
183- from qgis .core import (
184- QgsFeature ,
185- QgsField ,
186- QgsGraduatedSymbolRenderer ,
187- QgsProject ,
188- QgsRendererRange ,
189- QgsStyle ,
190- QgsSymbol ,
191- QgsVectorLayer ,
192- QgsWkbTypes ,
193- )
202+ geom_type = QgsWkbTypes .displayString (geology_layer .wkbType ())
203+ crs_auth = (
204+ geology_layer .crs ().authid () if hasattr (geology_layer , 'crs' ) else None
205+ )
206+ uri = f"{ geom_type } ?crs={ crs_auth } " if crs_auth else f"{ geom_type } "
207+ mem_layer = QgsVectorLayer (uri , f"{ geology_layer .name ()} _strat" , "memory" )
194208
195- geom_type = QgsWkbTypes .displayString (geology_layer .wkbType ())
196- crs_auth = geology_layer .crs ().authid () if hasattr (geology_layer , 'crs' ) else None
197- uri = f"{ geom_type } ?crs={ crs_auth } " if crs_auth else f"{ geom_type } "
198- mem_layer = QgsVectorLayer (uri , f"{ geology_layer .name ()} _strat" , "memory" )
199-
200- mem_dp = mem_layer .dataProvider ()
201- mem_dp .addAttributes (list (geology_layer .fields ()))
202- mem_layer .updateFields ()
203-
204- # copy each feature and its attributes explicitly
205- src_field_names = [f .name () for f in geology_layer .fields ()]
206- new_feats = []
207- for src_feat in geology_layer .getFeatures ():
208- nf = QgsFeature ()
209- nf .setGeometry (src_feat .geometry ())
210- nf .setFields (mem_layer .fields ())
211- attrs = []
212- for f in mem_layer .fields ():
213- fname = f .name ()
214- if fname in src_field_names :
215- try :
216- attrs .append (src_feat [fname ])
217- except Exception :
218- attrs .append (None )
219- else :
220- attrs .append (None )
221- nf .setAttributes (attrs )
222- new_feats .append (nf )
223- mem_dp .addFeatures (new_feats )
224- mem_layer .updateExtents ()
225- QgsProject .instance ().addMapLayer (mem_layer )
226- except Exception as e :
227- QMessageBox .warning (self , 'Duplicate Layer' , f'Failed to create copy: { e } ' )
228- return
229-
230- # Step 2: ensure 'strat_order' exists on memory layer and populate numeric values by matching geometries
231- field_name = 'strat_order'
232- try :
233- if field_name not in [f .name () for f in mem_layer .fields ()]:
234- mem_layer .startEditing ()
235- mem_dp .addAttributes ([QgsField (field_name , QVariant .Int )])
209+ mem_dp = mem_layer .dataProvider ()
210+ mem_dp .addAttributes (list (geology_layer .fields ()))
236211 mem_layer .updateFields ()
237- mem_layer .commitChanges ()
238-
239- # build mapping from geometry WKB -> numeric strat value from original layer
240- geom_to_val = {}
241- for of in geology_layer .getFeatures ():
242- try :
243- raw = of [field_name ]
244- except Exception :
245- raw = None
246- if raw is None :
247- continue
248- try :
249- val = int (raw )
250- except Exception :
212+
213+ # copy each feature and its attributes explicitly
214+ src_field_names = [f .name () for f in geology_layer .fields ()]
215+ new_feats = []
216+ for src_feat in geology_layer .getFeatures ():
217+ nf = QgsFeature ()
218+ nf .setGeometry (src_feat .geometry ())
219+ nf .setFields (mem_layer .fields ())
220+ attrs = []
221+ for f in mem_layer .fields ():
222+ fname = f .name ()
223+ if fname in src_field_names :
224+ try :
225+ attrs .append (src_feat [fname ])
226+ except Exception :
227+ attrs .append (None )
228+ else :
229+ attrs .append (None )
230+ nf .setAttributes (attrs )
231+ new_feats .append (nf )
232+ mem_dp .addFeatures (new_feats )
233+ mem_layer .updateExtents ()
234+ QgsProject .instance ().addMapLayer (mem_layer )
235+ except Exception as e :
236+ QMessageBox .warning (self , 'Duplicate Layer' , f'Failed to create copy: { e } ' )
237+ return False
238+
239+ # Step 2: ensure 'strat_order' exists on memory layer and populate numeric values by matching geometries
240+ field_name = 'strat_order'
241+ try :
242+ if field_name not in [f .name () for f in mem_layer .fields ()]:
243+ mem_layer .startEditing ()
244+ mem_dp .addAttributes ([QgsField (field_name , QVariant .Int )])
245+ mem_layer .updateFields ()
246+ mem_layer .commitChanges ()
247+
248+ # build mapping from geometry WKB -> numeric strat value from original layer
249+ geom_to_val = {}
250+ for of in geology_layer .getFeatures ():
251251 try :
252- val = int ( float ( raw ))
252+ raw = of [ field_name ]
253253 except Exception :
254+ raw = None
255+ if raw is None :
254256 continue
255- try :
256- geom_to_val [of .geometry ().asWkb ()] = val
257- except Exception :
258- # fallback to WKT if asWkb unavailable
259257 try :
260- geom_to_val [ of . geometry (). asWkt ()] = val
258+ val = int ( raw )
261259 except Exception :
262- continue
263-
264- if geom_to_val :
265- mem_layer .startEditing ()
266- strat_idx = mem_layer .fields ().indexFromName (field_name )
267- for mf in mem_layer .getFeatures ():
260+ try :
261+ val = int (float (raw ))
262+ except Exception :
263+ continue
268264 try :
269- key = mf .geometry ().asWkb ()
265+ geom_to_val [ of .geometry ().asWkb ()] = val
270266 except Exception :
271267 try :
272- key = mf .geometry ().asWkt ()
268+ geom_to_val [ of .geometry ().asWkt ()] = val
273269 except Exception :
274- key = None
275- if key is None :
276- continue
277- val = geom_to_val .get (key , None )
278- if val is not None :
279- mem_layer .changeAttributeValue (mf .id (), strat_idx , int (val ))
280- mem_layer .commitChanges ()
281- except Exception :
282- # continue; styling will fallback if numeric data missing
283- pass
270+ continue
284271
285- # Step 3: build graduated renderer using explicit ranges per unique strat value
286- try :
287- vals = set ()
288- for f in mem_layer .getFeatures ():
289- try :
290- v = f [field_name ]
291- except Exception :
292- v = None
293- if v is None :
294- continue
295- try :
296- vals .add (float (v ))
297- except Exception :
298- continue
299- unique_vals = sorted (vals )
300-
301- if not unique_vals :
302- QMessageBox .information (
303- self ,
304- 'Styling' ,
305- "No 'strat_order' values found on duplicated layer; leaving default styling." ,
306- )
307- else :
308- from qgis .core import QgsRendererRange
309-
310- ramp = QgsStyle ().defaultStyle ().colorRamp (ramp_name ) if ramp_name else None
311- ranges = []
312- n = len (unique_vals )
313- for i , v in enumerate (unique_vals ):
314- lower = v - 0.5
315- upper = v + 0.5
316- symbol = QgsSymbol .defaultSymbol (mem_layer .geometryType ())
317- if ramp :
272+ if geom_to_val :
273+ mem_layer .startEditing ()
274+ strat_idx = mem_layer .fields ().indexFromName (field_name )
275+ for mf in mem_layer .getFeatures ():
318276 try :
319- color = ramp .color (i / (n - 1 ) if n > 1 else 0 )
320- symbol .setColor (color )
277+ key = mf .geometry ().asWkb ()
321278 except Exception :
322- pass
323- label = str (int (v )) if float (v ).is_integer () else str (v )
324- ranges .append (QgsRendererRange (lower , upper , symbol , label ))
325- renderer = QgsGraduatedSymbolRenderer (field_name , ranges )
326- mem_layer .setRenderer (renderer )
327- mem_layer .triggerRepaint ()
328- except Exception as e :
329- QMessageBox .warning (
330- self , 'Duplicate Layer' , f'Failed to apply graduated styling: { e } '
331- )
279+ try :
280+ key = mf .geometry ().asWkt ()
281+ except Exception :
282+ key = None
283+ if key is None :
284+ continue
285+ val = geom_to_val .get (key , None )
286+ if val is not None :
287+ mem_layer .changeAttributeValue (mf .id (), strat_idx , int (val ))
288+ mem_layer .commitChanges ()
289+ except Exception :
290+ # continue; styling will fallback if numeric data missing
291+ pass
292+
293+ # Step 3: build graduated renderer using explicit ranges per unique strat value
294+ try :
295+ vals = set ()
296+ for f in mem_layer .getFeatures ():
297+ try :
298+ v = f [field_name ]
299+ except Exception :
300+ v = None
301+ if v is None :
302+ continue
303+ try :
304+ vals .add (float (v ))
305+ except Exception :
306+ continue
307+ unique_vals = sorted (vals )
308+
309+ if not unique_vals :
310+ QMessageBox .information (
311+ self ,
312+ 'Styling' ,
313+ "No 'strat_order' values found on duplicated layer; leaving default styling." ,
314+ )
315+ else :
316+ ramp = QgsStyle ().defaultStyle ().colorRamp (ramp_name ) if ramp_name else None
317+ ranges = []
318+ n = len (unique_vals )
319+ for i , v in enumerate (unique_vals ):
320+ lower = v - 0.5
321+ upper = v + 0.5
322+ symbol = QgsSymbol .defaultSymbol (mem_layer .geometryType ())
323+ if ramp :
324+ try :
325+ color = ramp .color (i / (n - 1 ) if n > 1 else 0 )
326+ symbol .setColor (color )
327+ except Exception :
328+ pass
329+ label = str (int (v )) if float (v ).is_integer () else str (v )
330+ ranges .append (QgsRendererRange (lower , upper , symbol , label ))
331+ renderer = QgsGraduatedSymbolRenderer (field_name , ranges )
332+ mem_layer .setRenderer (renderer )
333+ mem_layer .triggerRepaint ()
334+ except Exception as e :
335+ QMessageBox .warning (
336+ self , 'Duplicate Layer' , f'Failed to apply graduated styling: { e } '
337+ )
338+
339+ except Exception as e :
340+ QMessageBox .warning (self , 'Paint Stratigraphic Order' , f'Operation failed: { e } ' )
341+ return False
332342
333- # QMessageBox.information(
334- # self,
335- # "Paint Stratigraphic Order",
336- # "Stratigraphic order has been painted onto the geology layer.",
337- # )
343+ return True
0 commit comments