Skip to content

Commit 25203e3

Browse files
feat(plotly): implement column-stratigraphic
1 parent 4a54060 commit 25203e3

1 file changed

Lines changed: 159 additions & 0 deletions

File tree

  • plots/column-stratigraphic/implementations
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
"""pyplots.ai
2+
column-stratigraphic: Stratigraphic Column with Lithology Patterns
3+
Library: plotly | Python 3.13
4+
Quality: pending | Created: 2026-03-15
5+
"""
6+
7+
import plotly.graph_objects as go
8+
9+
10+
# Data - synthetic sedimentary section with 10 layers
11+
layers = [
12+
{"top": 0, "bottom": 15, "lithology": "Sandstone", "formation": "Dakota Fm", "age": "Late Cretaceous"},
13+
{"top": 15, "bottom": 30, "lithology": "Shale", "formation": "Graneros Sh", "age": "Late Cretaceous"},
14+
{"top": 30, "bottom": 50, "lithology": "Limestone", "formation": "Greenhorn Ls", "age": "Late Cretaceous"},
15+
{"top": 50, "bottom": 62, "lithology": "Shale", "formation": "Carlile Sh", "age": "Late Cretaceous"},
16+
{"top": 62, "bottom": 78, "lithology": "Siltstone", "formation": "Niobrara Fm", "age": "Late Cretaceous"},
17+
{"top": 78, "bottom": 100, "lithology": "Limestone", "formation": "Fort Hays Ls", "age": "Late Cretaceous"},
18+
{"top": 100, "bottom": 125, "lithology": "Sandstone", "formation": "Fox Hills Ss", "age": "Maastrichtian"},
19+
{"top": 125, "bottom": 148, "lithology": "Conglomerate", "formation": "Lance Fm", "age": "Maastrichtian"},
20+
{"top": 148, "bottom": 170, "lithology": "Shale", "formation": "Fort Union Fm", "age": "Paleocene"},
21+
{"top": 170, "bottom": 195, "lithology": "Sandstone", "formation": "Wasatch Fm", "age": "Eocene"},
22+
]
23+
24+
lithology_styles = {
25+
"Sandstone": {"color": "#F5DEB3", "pattern_shape": ".", "pattern_size": 8},
26+
"Shale": {"color": "#8B8682", "pattern_shape": "-", "pattern_size": 6},
27+
"Limestone": {"color": "#87CEEB", "pattern_shape": "+", "pattern_size": 8},
28+
"Siltstone": {"color": "#C4A882", "pattern_shape": "/", "pattern_size": 6},
29+
"Conglomerate": {"color": "#D2691E", "pattern_shape": "x", "pattern_size": 10},
30+
}
31+
32+
# Plot
33+
fig = go.Figure()
34+
35+
for layer in layers:
36+
style = lithology_styles[layer["lithology"]]
37+
thickness = layer["bottom"] - layer["top"]
38+
mid_depth = (layer["top"] + layer["bottom"]) / 2
39+
40+
fig.add_trace(
41+
go.Bar(
42+
x=[1],
43+
y=[thickness],
44+
base=layer["top"],
45+
orientation="v",
46+
marker={
47+
"color": style["color"],
48+
"pattern": {
49+
"shape": style["pattern_shape"],
50+
"size": style["pattern_size"],
51+
"solidity": 0.6,
52+
"fgcolor": "rgba(0,0,0,0.5)",
53+
},
54+
"line": {"color": "black", "width": 1.5},
55+
},
56+
width=0.6,
57+
showlegend=False,
58+
hovertemplate=(
59+
f"<b>{layer['formation']}</b><br>"
60+
f"Lithology: {layer['lithology']}<br>"
61+
f"Depth: {layer['top']}{layer['bottom']} m<br>"
62+
f"Age: {layer['age']}"
63+
"<extra></extra>"
64+
),
65+
)
66+
)
67+
68+
# Formation name annotation (right side)
69+
fig.add_annotation(
70+
x=1.42,
71+
y=mid_depth,
72+
text=f"<b>{layer['formation']}</b><br><i>{layer['lithology']}</i>",
73+
showarrow=False,
74+
font={"size": 13},
75+
xanchor="left",
76+
yanchor="middle",
77+
)
78+
79+
# Group consecutive layers by age for left-side labels
80+
age_groups = []
81+
current_age = layers[0]["age"]
82+
current_top = layers[0]["top"]
83+
prev_bottom = layers[0]["bottom"]
84+
for layer in layers:
85+
if layer["age"] != current_age:
86+
age_groups.append({"age": current_age, "top": current_top, "bottom": prev_bottom})
87+
current_age = layer["age"]
88+
current_top = layer["top"]
89+
prev_bottom = layer["bottom"]
90+
age_groups.append({"age": current_age, "top": current_top, "bottom": prev_bottom})
91+
92+
for group in age_groups:
93+
mid = (group["top"] + group["bottom"]) / 2
94+
fig.add_annotation(
95+
x=0.58,
96+
y=mid,
97+
text=f"<i>{group['age']}</i>",
98+
showarrow=False,
99+
font={"size": 12, "color": "#444444"},
100+
xanchor="right",
101+
yanchor="middle",
102+
)
103+
104+
# Legend for lithology types
105+
for lithology, style in lithology_styles.items():
106+
fig.add_trace(
107+
go.Bar(
108+
x=[None],
109+
y=[None],
110+
marker={
111+
"color": style["color"],
112+
"pattern": {
113+
"shape": style["pattern_shape"],
114+
"size": style["pattern_size"],
115+
"solidity": 0.6,
116+
"fgcolor": "rgba(0,0,0,0.5)",
117+
},
118+
"line": {"color": "black", "width": 1},
119+
},
120+
name=lithology,
121+
showlegend=True,
122+
)
123+
)
124+
125+
# Style
126+
fig.update_layout(
127+
title={"text": "column-stratigraphic · plotly · pyplots.ai", "font": {"size": 28}, "x": 0.5, "xanchor": "center"},
128+
yaxis={
129+
"title": {"text": "Depth (m)", "font": {"size": 22}},
130+
"tickfont": {"size": 16},
131+
"autorange": "reversed",
132+
"dtick": 20,
133+
"gridcolor": "rgba(0,0,0,0.1)",
134+
"gridwidth": 1,
135+
"zeroline": False,
136+
},
137+
xaxis={"showticklabels": False, "showgrid": False, "zeroline": False, "range": [0.2, 2.0], "fixedrange": True},
138+
template="plotly_white",
139+
plot_bgcolor="white",
140+
paper_bgcolor="white",
141+
barmode="overlay",
142+
bargap=0,
143+
legend={
144+
"title": {"text": "Lithology", "font": {"size": 18}},
145+
"font": {"size": 14},
146+
"x": 0.92,
147+
"y": 0.98,
148+
"bgcolor": "rgba(255,255,255,0.9)",
149+
"bordercolor": "rgba(0,0,0,0.2)",
150+
"borderwidth": 1,
151+
},
152+
margin={"l": 140, "r": 200, "t": 80, "b": 40},
153+
height=900,
154+
width=1600,
155+
)
156+
157+
# Save
158+
fig.write_image("plot.png", width=1600, height=900, scale=3)
159+
fig.write_html("plot.html", include_plotlyjs="cdn")

0 commit comments

Comments
 (0)