Skip to content

Commit 13918db

Browse files
committed
feat: add stratigraphic age application functionality with color ramp selection
1 parent 8e38c34 commit 13918db

2 files changed

Lines changed: 134 additions & 1 deletion

File tree

loopstructural/gui/modelling/stratigraphic_column/stratigraphic_column.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from LoopStructural.modelling.core.stratigraphic_column import StratigraphicColumnElementType
2-
from qgis.core import QgsMapLayerProxyModel
2+
from qgis.core import QgsMapLayerProxyModel, QgsStyle
33
from qgis.gui import QgsFieldComboBox, QgsMapLayerComboBox
44
from qgis.PyQt.QtWidgets import (
55
QAbstractItemView,
6+
QComboBox,
67
QHBoxLayout,
8+
QLabel,
79
QListWidget,
810
QListWidgetItem,
911
QMessageBox,
@@ -94,6 +96,26 @@ def __init__(self, parent=None, data_manager=None):
9496
applyColoursButton.clicked.connect(self.apply_colours_to_layer)
9597
layout.addWidget(applyColoursButton)
9698

99+
# Colour ramp picker + apply stratigraphic age button
100+
ageRow = QHBoxLayout()
101+
ageRow.addWidget(QLabel("Colour ramp:"))
102+
self.strat_ageColorRampComboBox = QComboBox()
103+
ramp_names = sorted(QgsStyle().defaultStyle().colorRampNames())
104+
self.strat_ageColorRampComboBox.addItems(ramp_names)
105+
default_ramp_index = self.strat_ageColorRampComboBox.findText('Viridis')
106+
if default_ramp_index >= 0:
107+
self.strat_ageColorRampComboBox.setCurrentIndex(default_ramp_index)
108+
ageRow.addWidget(self.strat_ageColorRampComboBox)
109+
layout.addLayout(ageRow)
110+
111+
applyAgeButton = QPushButton("Apply Stratigraphic Age to Map Layer")
112+
applyAgeButton.setToolTip(
113+
"Write a 'strat_order' field (0 = first unit in the column) onto "
114+
"the selected layer above and style it with a graduated colour ramp."
115+
)
116+
applyAgeButton.clicked.connect(self.apply_age_to_layer)
117+
layout.addWidget(applyAgeButton)
118+
97119
self._guess_units_layer()
98120
self._restore_units_layer_selection()
99121

@@ -272,6 +294,38 @@ def apply_colours_to_layer(self):
272294
"Could not apply colours. The stratigraphic column may have no units.",
273295
)
274296

297+
def apply_age_to_layer(self):
298+
"""Write the stratigraphic order onto the selected units layer and style it by a graduated ramp."""
299+
if not self.data_manager:
300+
print("Error: Data manager is not initialized.")
301+
return
302+
layer = self.unitsLayerComboBox.currentLayer()
303+
field_name = self.unitsLayerFieldComboBox.currentField()
304+
if layer is None or not field_name:
305+
QMessageBox.warning(
306+
self,
307+
"Apply Stratigraphic Age to Map Layer",
308+
"Please select a units layer and unit name field above.",
309+
)
310+
return
311+
ramp_name = self.strat_ageColorRampComboBox.currentText()
312+
applied = self.data_manager.apply_stratigraphic_age_to_layer(
313+
layer, field_name, ramp_name=ramp_name
314+
)
315+
if applied:
316+
QMessageBox.information(
317+
self,
318+
"Apply Stratigraphic Age to Map Layer",
319+
f"Applied stratigraphic age and graduated styling to layer '{layer.name()}'.",
320+
)
321+
else:
322+
QMessageBox.warning(
323+
self,
324+
"Apply Stratigraphic Age to Map Layer",
325+
"Could not apply stratigraphic age. The stratigraphic column may have no "
326+
"units, or no features matched a stratigraphic unit.",
327+
)
328+
275329
def add_unit(self, *, unit_data=None, create_new=True):
276330
if unit_data is None:
277331
unit_data = {'type': 'unit', 'name': ''}

loopstructural/main/data_manager.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
from qgis.core import (
66
QgsCategorizedSymbolRenderer,
77
QgsCoordinateReferenceSystem,
8+
QgsGraduatedSymbolRenderer,
89
QgsPointXY,
910
QgsProject,
1011
QgsRendererCategory,
12+
QgsRendererRange,
13+
QgsStyle,
1114
QgsSymbol,
1215
QgsVectorLayer,
1316
)
@@ -17,6 +20,7 @@
1720
from LoopStructural.datatypes import BoundingBox
1821
from LoopStructural.modelling.core.stratigraphic_column import StratigraphicColumnElementType
1922

23+
from .m2l_api import paint_stratigraphic_order
2024
from .vectorLayerWrapper import qgsLayerToGeoDataFrame
2125

2226

@@ -417,6 +421,81 @@ def apply_stratigraphic_colours_to_layer(self, layer, field_name):
417421
self.logger(message=f"Applied stratigraphic column colours to layer '{layer.name()}'.")
418422
return True
419423

424+
def apply_stratigraphic_age_to_layer(self, layer, field_name, ramp_name=None):
425+
"""Write the stratigraphic order onto a layer and style it with a graduated colour ramp.
426+
427+
Writes an integer 'strat_order' field to ``layer`` (0 = first unit in
428+
the stratigraphic column) matched via ``field_name``, then applies a
429+
graduated renderer over that field.
430+
431+
Parameters
432+
----------
433+
layer : QgsVectorLayer
434+
The layer to update (e.g. the geological units/geology layer).
435+
field_name : str
436+
Name of the field on ``layer`` holding the stratigraphic unit name.
437+
ramp_name : str, optional
438+
Name of a QGIS colour ramp (from QgsStyle) to use for the
439+
graduated renderer. Falls back to any available ramp if not found.
440+
441+
Returns
442+
-------
443+
bool
444+
True if the field was written and the renderer applied, False otherwise.
445+
"""
446+
if layer is None or not field_name:
447+
self.logger(message="No layer/unit name field set, cannot apply stratigraphic age.")
448+
return False
449+
450+
unit_names = self.get_stratigraphic_unit_names()
451+
if not unit_names:
452+
self.logger(message="Stratigraphic column has no units, cannot apply stratigraphic age.")
453+
return False
454+
455+
age_field_name = "strat_order"
456+
try:
457+
paint_stratigraphic_order(layer, unit_names, field_name)
458+
except Exception as err:
459+
self.logger(message=f"Failed to write stratigraphic order onto layer: {err}")
460+
return False
461+
462+
unique_values = set()
463+
for feature in layer.getFeatures():
464+
value = feature[age_field_name]
465+
if value is None or (hasattr(value, 'isNull') and value.isNull()):
466+
continue
467+
unique_values.add(int(value))
468+
unique_values = sorted(unique_values)
469+
470+
if not unique_values:
471+
self.logger(
472+
message="No features matched a stratigraphic unit, cannot style layer by age."
473+
)
474+
return False
475+
476+
style = QgsStyle().defaultStyle()
477+
ramp = style.colorRamp(ramp_name) if ramp_name else None
478+
if ramp is None:
479+
ramp_names = style.colorRampNames()
480+
if ramp_names:
481+
ramp = style.colorRamp(ramp_names[0])
482+
483+
n = len(unique_values)
484+
ranges = []
485+
for i, value in enumerate(unique_values):
486+
symbol = QgsSymbol.defaultSymbol(layer.geometryType())
487+
if ramp is not None:
488+
symbol.setColor(ramp.color(i / (n - 1) if n > 1 else 0))
489+
ranges.append(QgsRendererRange(value - 0.5, value + 0.5, symbol, str(value)))
490+
491+
layer.setRenderer(QgsGraduatedSymbolRenderer(age_field_name, ranges))
492+
layer.triggerRepaint()
493+
self.logger(
494+
message=f"Applied stratigraphic age field '{age_field_name}' and graduated "
495+
f"styling to layer '{layer.name()}'."
496+
)
497+
return True
498+
420499
def get_stratigraphic_unit_names(self):
421500
"""Get the names of the stratigraphic units in the column."""
422501
units = []

0 commit comments

Comments
 (0)