88* *
99***************************************************************************
1010"""
11+
1112# Python imports
1213from typing import Any , Optional
14+
1315import pandas as pd
16+ from map2loop .thickness_calculator import InterpolatedStructure , StructuralPoint
1417
1518# QGIS imports
1619from qgis import processing
2124 QgsProcessingContext ,
2225 QgsProcessingException ,
2326 QgsProcessingFeedback ,
27+ QgsProcessingParameterEnum ,
2428 QgsProcessingParameterFeatureSink ,
2529 QgsProcessingParameterFeatureSource ,
26- QgsProcessingParameterEnum ,
27- QgsProcessingParameterNumber ,
2830 QgsProcessingParameterField ,
29- QgsProcessingParameterMatrix ,
30- QgsSettings ,
31+ QgsProcessingParameterMatrix ,
32+ QgsProcessingParameterNumber ,
3133 QgsProcessingParameterRasterLayer ,
34+ QgsSettings ,
3235)
36+
3337# Internal imports
3438from ...main .vectorLayerWrapper import (
35- qgsLayerToGeoDataFrame ,
36- GeoDataFrameToQgsLayer ,
37- qgsLayerToDataFrame ,
38- dataframeToQgsLayer ,
39- qgsRasterToGdalDataset ,
39+ GeoDataFrameToQgsLayer ,
40+ dataframeToQgsLayer ,
41+ dataframeToQgsTable ,
4042 matrixToDict ,
41- dataframeToQgsTable
42- )
43- from map2loop .thickness_calculator import InterpolatedStructure , StructuralPoint
43+ qgsLayerToDataFrame ,
44+ qgsLayerToGeoDataFrame ,
45+ qgsRasterToGdalDataset ,
46+ )
4447
4548
4649class ThicknessCalculatorAlgorithm (QgsProcessingAlgorithm ):
@@ -56,6 +59,7 @@ class ThicknessCalculatorAlgorithm(QgsProcessingAlgorithm):
5659 INPUT_STRUCTURE_DATA = 'STRUCTURE_DATA'
5760 INPUT_DIPDIR_FIELD = 'DIPDIR_FIELD'
5861 INPUT_DIP_FIELD = 'DIP_FIELD'
62+ INPUT_STRUCTURE_UNIT_FIELD = 'STRUCTURE_UNIT_FIELD'
5963 INPUT_GEOLOGY = 'GEOLOGY'
6064 INPUT_ORIENTATION_TYPE = 'ORIENTATION_TYPE'
6165 INPUT_UNIT_NAME_FIELD = 'UNIT_NAME_FIELD'
@@ -82,14 +86,14 @@ def groupId(self) -> str:
8286
8387 def initAlgorithm (self , config : Optional [dict [str , Any ]] = None ) -> None :
8488 """Initialize the algorithm parameters."""
85-
89+
8690 self .addParameter (
8791 QgsProcessingParameterEnum (
8892 self .INPUT_THICKNESS_CALCULATOR_TYPE ,
8993 "Thickness Calculator Type" ,
90- options = ['InterpolatedStructure' ,'StructuralPoint' ],
94+ options = ['InterpolatedStructure' , 'StructuralPoint' ],
9195 allowMultiple = False ,
92- defaultValue = 'InterpolatedStructure'
96+ defaultValue = 'InterpolatedStructure' ,
9397 )
9498 )
9599 self .addParameter (
@@ -100,38 +104,35 @@ def initAlgorithm(self, config: Optional[dict[str, Any]] = None) -> None:
100104 optional = True ,
101105 )
102106 )
103-
107+
104108 self .addParameter (
105109 QgsProcessingParameterEnum (
106110 self .INPUT_BOUNDING_BOX_TYPE ,
107111 "Bounding Box Type" ,
108112 options = ['Extract from geology layer' , 'User defined' ],
109113 allowMultiple = False ,
110- defaultValue = 1
114+ defaultValue = 1 ,
111115 )
112116 )
113-
117+
114118 bbox_settings = QgsSettings ()
115119 last_bbox = bbox_settings .value ("m2l/bounding_box" , "" )
116120 self .addParameter (
117121 QgsProcessingParameterMatrix (
118122 self .INPUT_BOUNDING_BOX ,
119123 description = "Static Bounding Box" ,
120- headers = ['minx' ,'miny' ,'maxx' ,'maxy' ],
124+ headers = ['minx' , 'miny' , 'maxx' , 'maxy' ],
121125 numberRows = 1 ,
122126 defaultValue = last_bbox ,
123- optional = True
127+ optional = True ,
124128 )
125129 )
126-
130+
127131 self .addParameter (
128132 QgsProcessingParameterNumber (
129- self .INPUT_MAX_LINE_LENGTH ,
130- "Max Line Length" ,
131- minValue = 0 ,
132- defaultValue = 1000
133+ self .INPUT_MAX_LINE_LENGTH , "Max Line Length" , minValue = 0 , defaultValue = 1000
133134 )
134- )
135+ )
135136 self .addParameter (
136137 QgsProcessingParameterFeatureSource (
137138 self .INPUT_BASAL_CONTACTS ,
@@ -147,14 +148,14 @@ def initAlgorithm(self, config: Optional[dict[str, Any]] = None) -> None:
147148 [QgsProcessing .TypeVectorPolygon ],
148149 )
149150 )
150-
151+
151152 self .addParameter (
152153 QgsProcessingParameterField (
153154 'UNIT_NAME_FIELD' ,
154155 'Unit Name Field e.g. Formation' ,
155156 parentLayerParameterName = self .INPUT_GEOLOGY ,
156157 type = QgsProcessingParameterField .String ,
157- defaultValue = 'Formation'
158+ defaultValue = 'Formation' ,
158159 )
159160 )
160161
@@ -163,10 +164,10 @@ def initAlgorithm(self, config: Optional[dict[str, Any]] = None) -> None:
163164 'STRATIGRAPHIC_COLUMN_LAYER' ,
164165 'Stratigraphic Column Layer (from sorter)' ,
165166 [QgsProcessing .TypeVector ],
166- optional = True
167+ optional = True ,
167168 )
168169 )
169-
170+
170171 strati_settings = QgsSettings ()
171172 last_strati_column = strati_settings .value ("m2l/strati_column" , "" )
172173 self .addParameter (
@@ -176,7 +177,7 @@ def initAlgorithm(self, config: Optional[dict[str, Any]] = None) -> None:
176177 headers = ["Unit" ],
177178 numberRows = 0 ,
178179 defaultValue = last_strati_column ,
179- optional = True
180+ optional = True ,
180181 )
181182 )
182183 self .addParameter (
@@ -198,7 +199,7 @@ def initAlgorithm(self, config: Optional[dict[str, Any]] = None) -> None:
198199 self .INPUT_ORIENTATION_TYPE ,
199200 'Orientation Type' ,
200201 options = ['Dip Direction' , 'Strike' ],
201- defaultValue = 0 # Default to Dip Direction
202+ defaultValue = 0 , # Default to Dip Direction
202203 )
203204 )
204205 self .addParameter (
@@ -207,7 +208,7 @@ def initAlgorithm(self, config: Optional[dict[str, Any]] = None) -> None:
207208 "Dip Direction Column" ,
208209 parentLayerParameterName = self .INPUT_STRUCTURE_DATA ,
209210 type = QgsProcessingParameterField .Numeric ,
210- defaultValue = 'DIPDIR'
211+ defaultValue = 'DIPDIR' ,
211212 )
212213 )
213214 self .addParameter (
@@ -216,7 +217,18 @@ def initAlgorithm(self, config: Optional[dict[str, Any]] = None) -> None:
216217 "Dip Column" ,
217218 parentLayerParameterName = self .INPUT_STRUCTURE_DATA ,
218219 type = QgsProcessingParameterField .Numeric ,
219- defaultValue = 'DIP'
220+ defaultValue = 'DIP' ,
221+ )
222+ )
223+ # New parameter: choose the field in the structure layer that contains the unit name
224+ self .addParameter (
225+ QgsProcessingParameterField (
226+ self .INPUT_STRUCTURE_UNIT_FIELD ,
227+ "Structure Unit Name Field" ,
228+ parentLayerParameterName = self .INPUT_STRUCTURE_DATA ,
229+ type = QgsProcessingParameterField .String ,
230+ defaultValue = 'unit_name' ,
231+ optional = True ,
220232 )
221233 )
222234 self .addParameter (
@@ -234,7 +246,9 @@ def processAlgorithm(
234246 ) -> dict [str , Any ]:
235247
236248 feedback .pushInfo ("Initialising Thickness Calculation Algorithm..." )
237- thickness_type_index = self .parameterAsEnum (parameters , self .INPUT_THICKNESS_CALCULATOR_TYPE , context )
249+ thickness_type_index = self .parameterAsEnum (
250+ parameters , self .INPUT_THICKNESS_CALCULATOR_TYPE , context
251+ )
238252 thickness_type = ['InterpolatedStructure' , 'StructuralPoint' ][thickness_type_index ]
239253 dtm_data = self .parameterAsRasterLayer (parameters , self .INPUT_DTM , context )
240254 bounding_box_type = self .parameterAsEnum (parameters , self .INPUT_BOUNDING_BOX_TYPE , context )
@@ -243,9 +257,12 @@ def processAlgorithm(
243257 geology_data = self .parameterAsSource (parameters , self .INPUT_GEOLOGY , context )
244258 structure_data = self .parameterAsSource (parameters , self .INPUT_STRUCTURE_DATA , context )
245259 orientation_type = self .parameterAsEnum (parameters , self .INPUT_ORIENTATION_TYPE , context )
246- is_strike = (orientation_type == 1 )
247- structure_dipdir_field = self .parameterAsString (parameters , self .INPUT_DIPDIR_FIELD , context )
260+ is_strike = orientation_type == 1
261+ structure_dipdir_field = self .parameterAsString (
262+ parameters , self .INPUT_DIPDIR_FIELD , context
263+ )
248264 structure_dip_field = self .parameterAsString (parameters , self .INPUT_DIP_FIELD , context )
265+
249266 sampled_contacts = self .parameterAsSource (parameters , self .INPUT_SAMPLED_CONTACTS , context )
250267 unit_name_field = self .parameterAsString (parameters , self .INPUT_UNIT_NAME_FIELD , context )
251268
@@ -256,33 +273,41 @@ def processAlgorithm(
256273 'minx' : extent .xMinimum (),
257274 'miny' : extent .yMinimum (),
258275 'maxx' : extent .xMaximum (),
259- 'maxy' : extent .yMaximum ()
276+ 'maxy' : extent .yMaximum (),
260277 }
261278 feedback .pushInfo ("Using bounding box from geology layer" )
262279 else :
263- static_bbox_matrix = self .parameterAsMatrix (parameters , self .INPUT_BOUNDING_BOX , context )
280+ static_bbox_matrix = self .parameterAsMatrix (
281+ parameters , self .INPUT_BOUNDING_BOX , context
282+ )
264283 if not static_bbox_matrix or len (static_bbox_matrix ) == 0 :
265284 raise QgsProcessingException ("Bounding box is required" )
266-
285+
267286 bounding_box = matrixToDict (static_bbox_matrix )
268-
287+
269288 bbox_settings = QgsSettings ()
270289 bbox_settings .setValue ("m2l/bounding_box" , static_bbox_matrix )
271290 feedback .pushInfo ("Using bounding box from user input" )
272291
273- stratigraphic_column_source = self .parameterAsSource (parameters , self .INPUT_STRATIGRAPHIC_COLUMN_LAYER , context )
292+ stratigraphic_column_source = self .parameterAsSource (
293+ parameters , self .INPUT_STRATIGRAPHIC_COLUMN_LAYER , context
294+ )
274295 stratigraphic_order = []
275296 if stratigraphic_column_source is not None :
276- ordered_pairs = []
297+ ordered_pairs = []
277298 for feature in stratigraphic_column_source .getFeatures ():
278299 order = feature .attribute ('order' )
279300 unit_name = feature .attribute ('unit_name' )
280301 ordered_pairs .append ((order , unit_name ))
281302 ordered_pairs .sort (key = lambda x : x [0 ])
282303 stratigraphic_order = [pair [1 ] for pair in ordered_pairs ]
283- feedback .pushInfo (f"DEBUG: parameterAsVectorLayer Stratigraphic order: { stratigraphic_order } " )
304+ feedback .pushInfo (
305+ f"DEBUG: parameterAsVectorLayer Stratigraphic order: { stratigraphic_order } "
306+ )
284307 else :
285- matrix_stratigraphic_order = self .parameterAsMatrix (parameters , self .INPUT_STRATI_COLUMN , context )
308+ matrix_stratigraphic_order = self .parameterAsMatrix (
309+ parameters , self .INPUT_STRATI_COLUMN , context
310+ )
286311 if matrix_stratigraphic_order :
287312 stratigraphic_order = [str (row ) for row in matrix_stratigraphic_order if row ]
288313 else :
@@ -313,61 +338,60 @@ def processAlgorithm(
313338 rename_map [structure_dip_field ] = 'DIP'
314339 else :
315340 missing_fields .append (structure_dip_field )
341+
316342 if missing_fields :
317343 raise QgsProcessingException (
318344 f"Orientation data missing required field(s): { ', ' .join (missing_fields )} "
319345 )
320346 if rename_map :
321347 structure_data = structure_data .rename (columns = rename_map )
322-
348+
323349 sampled_contacts = qgsLayerToDataFrame (sampled_contacts )
324350 sampled_contacts ['X' ] = sampled_contacts ['X' ].astype (float )
325351 sampled_contacts ['Y' ] = sampled_contacts ['Y' ].astype (float )
326352 sampled_contacts ['Z' ] = sampled_contacts ['Z' ].astype (float )
327353 dtm_data = qgsRasterToGdalDataset (dtm_data )
328354 if thickness_type == "InterpolatedStructure" :
329355 thickness_calculator = InterpolatedStructure (
330- dtm_data = dtm_data ,
331- bounding_box = bounding_box ,
332- is_strike = is_strike
356+ dtm_data = dtm_data , bounding_box = bounding_box , is_strike = is_strike
333357 )
334358 thicknesses = thickness_calculator .compute (
335- units ,
336- stratigraphic_order ,
337- basal_contacts ,
338- structure_data ,
339- geology_data ,
340- sampled_contacts
359+ units ,
360+ stratigraphic_order ,
361+ basal_contacts ,
362+ structure_data ,
363+ geology_data ,
364+ sampled_contacts ,
341365 )
342366
343367 if thickness_type == "StructuralPoint" :
344368 thickness_calculator = StructuralPoint (
345369 dtm_data = dtm_data ,
346370 bounding_box = bounding_box ,
347371 max_line_length = max_line_length ,
348- is_strike = is_strike
372+ is_strike = is_strike ,
349373 )
350- thicknesses = thickness_calculator .compute (
374+ thicknesses = thickness_calculator .compute (
351375 units ,
352376 stratigraphic_order ,
353377 basal_contacts ,
354378 structure_data ,
355379 geology_data ,
356- sampled_contacts
380+ sampled_contacts ,
357381 )
358382
359383 thicknesses = thicknesses [
360- ["name" ,"ThicknessMean" ,"ThicknessMedian" , "ThicknessStdDev" ]
384+ ["name" , "ThicknessMean" , "ThicknessMedian" , "ThicknessStdDev" ]
361385 ].copy ()
362-
386+
363387 feedback .pushInfo ("Exporting Thickness Table..." )
364388 thicknesses = dataframeToQgsTable (
365389 self ,
366390 thicknesses ,
367391 parameters = parameters ,
368392 context = context ,
369393 feedback = feedback ,
370- param_name = self .OUTPUT
394+ param_name = self .OUTPUT ,
371395 )
372396
373397 return {self .OUTPUT : thicknesses [1 ]}
0 commit comments