Skip to content

Commit ea99e14

Browse files
fix: add data converter widget and section thickness calculator
* test: add unit tests for ConfigurationModel * feat: add DataConversionWidget * feat: add data conversion GUI components * feat: implement data conversion configuration helpers * feat: DataConversionWidget implemenation * fix: improve ui handling * fix: update config file * refactor: renamed layers * refactor: update geodataframe conversion * feat: add run functionality to manual conversion * fix: remove manual conversion code * fix: add loopdataconverter to requirements * fix: added data converter as dialog * feat: added along section thickness calc * refactor: added along section code * feat: add layer guessing and filters to conversion widget * fix: applying some copilot changes --------- Co-authored-by: Lachlan Grose <lachlan.grose@monash.edu>
1 parent 7d6cbb3 commit ea99e14

File tree

11 files changed

+1247
-5
lines changed

11 files changed

+1247
-5
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Data conversion GUI components."""
2+
3+
from .data_conversion_widget import AutomaticConversionDialog, AutomaticConversionWidget
4+
5+
__all__ = ["AutomaticConversionDialog", "AutomaticConversionWidget"]
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
"""Configuration helpers used by the data conversion UI."""
2+
3+
from __future__ import annotations
4+
5+
from copy import deepcopy
6+
from typing import Any, Dict, Iterable, MutableMapping
7+
8+
9+
class Config:
10+
"""Container for the default NTGS configuration."""
11+
12+
def __init__(self) -> None:
13+
self.fold_config = {
14+
"structtype_column": "FoldType",
15+
"fold_text": "'Anticline','Syncline','Antiform','Synform','Monocline','Monoform','Neutral','Fold axis','Overturned syncline'",
16+
"description_column": "Desc",
17+
"synform_text": "FoldType",
18+
"foldname_column": "FoldName",
19+
"objectid_column": "OBJECTID",
20+
"tightness_column": "IntlimbAng",
21+
"axial_plane_dipdir_column": "AxPlDipDir",
22+
"axial_plane_dip_column": "AxPlDip",
23+
}
24+
25+
self.fault_config = {
26+
"orientation_type": "dip direction",
27+
"structtype_column": "FaultType",
28+
"fault_text": "'Thrust','Reverse','Normal','Shear zone','Strike-slip','Thrust','Unknown'",
29+
"dip_null_value": "-999",
30+
"dipdir_flag": "num",
31+
"dipdir_column": "DipDir",
32+
"dip_column": "Dip",
33+
"dipestimate_column": "DipEstimate",
34+
"dipestimate_text": "'NORTH_EAST','NORTH',<rest of cardinals>,'NOT ACCESSED'",
35+
"displacement_column": "Displace",
36+
"displacement_text": "'1m-100m', '100m-1km', '1km-5km', '>5km'",
37+
"fault_length_column": "FaultLen",
38+
"fault_length_text": "Small (0-5km),Medium (5-30km),Large (30-100km),Regional (>100km),Unclassified",
39+
"name_column": "FaultName",
40+
"objectid_column": "OBJECTID",
41+
}
42+
43+
self.geology_config = {
44+
"unitname_column": "Formation",
45+
"alt_unitname_column": "Formation",
46+
"group_column": "Group",
47+
"supergroup_column": "Supergroup",
48+
"description_column": "LithDescn1",
49+
"minage_column": "AgeMin",
50+
"maxage_column": "AgeMax",
51+
"rocktype_column": "LithClass",
52+
"alt_rocktype_column": "RockCat",
53+
"sill_text": "RockCat",
54+
"intrusive_text": "RockCat",
55+
"volcanic_text": "RockCat",
56+
"objectid_column": "OBJECTID",
57+
"ignore_lithology_codes": ["cover", "Unknown"],
58+
}
59+
60+
self.structure_config = {
61+
"orientation_type": "dip direction",
62+
"dipdir_column": "DipDir",
63+
"dip_column": "Dip",
64+
"description_column": "FeatDesc",
65+
"bedding_text": "ObsType",
66+
"overturned_column": "Desc",
67+
"overturned_text": "overturned",
68+
"objectid_column": "OBJECTID",
69+
}
70+
71+
self.config_map = {
72+
"geology": self.geology_config,
73+
"structure": self.structure_config,
74+
"fault": self.fault_config,
75+
"fold": self.fold_config,
76+
}
77+
78+
def __getitem__(self, datatype: str) -> Dict[str, Any]:
79+
return self.config_map[datatype]
80+
81+
def as_dict(self) -> Dict[str, Dict[str, Any]]:
82+
"""Return a deep copy of the configuration map."""
83+
return deepcopy(self.config_map)
84+
85+
86+
def _coerce_config_value(template_value: Any, new_value: Any) -> Any:
87+
"""Coerce user supplied values into the template format."""
88+
if isinstance(template_value, list):
89+
if isinstance(new_value, list):
90+
return new_value
91+
if new_value in (None, ""):
92+
return []
93+
if isinstance(new_value, str):
94+
return [item.strip() for item in new_value.split(",") if item.strip()]
95+
return [str(new_value)]
96+
97+
if isinstance(template_value, (int, float)):
98+
try:
99+
return type(template_value)(new_value)
100+
except (TypeError, ValueError):
101+
return template_value
102+
103+
if template_value is None:
104+
return new_value
105+
106+
if new_value is None:
107+
return ""
108+
109+
return str(new_value)
110+
111+
112+
class ConfigurationState:
113+
"""State holder for the NTGS configuration mapping."""
114+
115+
def __init__(self, *, base_config: MutableMapping[str, Dict[str, Any]] | None = None):
116+
self._config = deepcopy(base_config) if base_config is not None else Config().as_dict()
117+
118+
def data_types(self) -> Iterable[str]:
119+
"""Return the supported data types."""
120+
return self._config.keys()
121+
122+
def get_config_for_type(self, data_type: str) -> Dict[str, Any]:
123+
"""Return a copy of the configuration for a single data type."""
124+
self._ensure_data_type(data_type)
125+
return deepcopy(self._config[data_type])
126+
127+
def set_value(self, data_type: str, key: str, value: Any) -> None:
128+
"""Update a single configuration entry."""
129+
self._ensure_data_type(data_type)
130+
template_value = self._config[data_type].get(key)
131+
self._config[data_type][key] = _coerce_config_value(template_value, value)
132+
133+
def update_values(self, data_type: str, updates: Dict[str, Any]) -> None:
134+
"""Bulk update configuration entries for a type."""
135+
for key, value in updates.items():
136+
self.set_value(data_type, key, value)
137+
138+
def get_value(self, data_type: str, key: str) -> Any:
139+
"""Return the stored value for a configuration entry."""
140+
self._ensure_data_type(data_type)
141+
return self._config[data_type].get(key)
142+
143+
def as_dict(self) -> Dict[str, Dict[str, Any]]:
144+
"""Return a deep copy of the entire configuration map."""
145+
return deepcopy(self._config)
146+
147+
def _ensure_data_type(self, data_type: str) -> None:
148+
if data_type not in self._config:
149+
raise KeyError(f"Unknown data type '{data_type}'")

0 commit comments

Comments
 (0)