|
| 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