|
1 | | -""" pyplots.ai |
| 1 | +"""pyplots.ai |
2 | 2 | column-stratigraphic: Stratigraphic Column with Lithology Patterns |
3 | 3 | Library: bokeh 3.9.0 | Python 3.14.3 |
4 | 4 | Quality: 85/100 | Created: 2026-03-15 |
|
23 | 23 | {"top": 180, "bottom": 200, "lithology": "Siltstone", "formation": "Uinta Fm", "age": "Eocene"}, |
24 | 24 | ] |
25 | 25 |
|
26 | | -# Lithology style mapping: color, hatch_pattern (improved color differentiation) |
| 26 | +# Lithology style mapping: improved colorblind-safe palette |
| 27 | +# Sandstone: warm yellow, Siltstone: olive green (high contrast vs sandstone) |
27 | 28 | lithology_styles = { |
28 | 29 | "Sandstone": {"color": "#F5DEB3", "hatch_pattern": ".", "hatch_color": "#8B7355"}, |
29 | 30 | "Shale": {"color": "#A9A9A9", "hatch_pattern": "-", "hatch_color": "#4A4A4A"}, |
30 | 31 | "Limestone": {"color": "#87CEEB", "hatch_pattern": "+", "hatch_color": "#2E5A88"}, |
31 | | - "Siltstone": {"color": "#8B6F5E", "hatch_pattern": "/", "hatch_color": "#3E2F23"}, |
| 32 | + "Siltstone": {"color": "#7B9971", "hatch_pattern": "/", "hatch_color": "#3B5335"}, |
32 | 33 | "Conglomerate": {"color": "#E8923F", "hatch_pattern": "o", "hatch_color": "#6B3A00"}, |
33 | 34 | } |
34 | 35 |
|
35 | | -# K-Pg boundary depth (between Fox Hills Fm / Late Cretaceous and Dawson Fm / Paleocene) |
| 36 | +# K-Pg boundary depth |
36 | 37 | KPG_DEPTH = 88 |
37 | 38 |
|
38 | | -# Column x-position and width (wider column for better canvas use) |
39 | | -col_center = 0.5 |
40 | | -col_width = 1.0 |
| 39 | +# Column geometry — wider for better canvas fill |
| 40 | +col_center = 0.55 |
| 41 | +col_width = 1.1 |
41 | 42 |
|
42 | | -# Plot (tighter x_range for better horizontal utilization) |
| 43 | +# Plot — tighter x_range for better horizontal utilization |
43 | 44 | p = figure( |
44 | 45 | width=4800, |
45 | 46 | height=2700, |
46 | 47 | title="column-stratigraphic · bokeh · pyplots.ai", |
47 | 48 | y_axis_label="Depth (m)", |
48 | 49 | toolbar_location=None, |
49 | | - x_range=Range1d(-0.7, 2.0), |
| 50 | + x_range=Range1d(-0.55, 1.85), |
50 | 51 | y_range=Range1d(210, -10), |
51 | 52 | ) |
52 | 53 |
|
|
102 | 103 | ) |
103 | 104 | p.add_tools(hover) |
104 | 105 |
|
105 | | -# K-Pg boundary emphasis — bold red dashed line with annotation |
106 | | -kpg_span = Span(location=KPG_DEPTH, dimension="width", line_color="#CC0000", line_width=4, line_dash="dashed") |
| 106 | +# Depth tick marks at each layer boundary for polished geological appearance |
| 107 | +boundary_depths = sorted({layer["top"] for layer in layers} | {layer["bottom"] for layer in layers}) |
| 108 | +for depth in boundary_depths: |
| 109 | + x_left = col_center - col_width / 2 |
| 110 | + p.line(x=[x_left - 0.04, x_left], y=[depth, depth], line_color="#555555", line_width=1.5, line_alpha=0.6) |
| 111 | + # Small depth label at boundary |
| 112 | + label = Label( |
| 113 | + x=x_left - 0.06, |
| 114 | + y=depth, |
| 115 | + text=f"{depth:.0f}", |
| 116 | + text_font_size="13pt", |
| 117 | + text_align="right", |
| 118 | + text_baseline="middle", |
| 119 | + text_color="#777777", |
| 120 | + ) |
| 121 | + p.add_layout(label) |
| 122 | + |
| 123 | +# K-Pg boundary emphasis — bold red dashed line with prominent annotation |
| 124 | +kpg_span = Span(location=KPG_DEPTH, dimension="width", line_color="#CC0000", line_width=5, line_dash="dashed") |
107 | 125 | p.add_layout(kpg_span) |
108 | 126 |
|
109 | 127 | kpg_label = Label( |
110 | 128 | x=col_center, |
111 | 129 | y=KPG_DEPTH, |
112 | 130 | text="K-Pg Boundary (~66 Ma)", |
113 | | - text_font_size="16pt", |
| 131 | + text_font_size="20pt", |
114 | 132 | text_font_style="bold", |
115 | 133 | text_color="#CC0000", |
116 | 134 | text_align="center", |
117 | 135 | text_baseline="bottom", |
118 | | - y_offset=8, |
| 136 | + y_offset=10, |
| 137 | + background_fill_color="white", |
| 138 | + background_fill_alpha=0.8, |
119 | 139 | ) |
120 | 140 | p.add_layout(kpg_label) |
121 | 141 |
|
122 | | -# Formation labels on the right side |
| 142 | +# Formation labels on the right side — closer to column |
123 | 143 | for layer in layers: |
124 | 144 | mid_y = (layer["top"] + layer["bottom"]) / 2 |
125 | 145 | label = Label( |
126 | | - x=1.08, |
| 146 | + x=col_center + col_width / 2 + 0.04, |
127 | 147 | y=mid_y, |
128 | 148 | text=layer["formation"], |
129 | | - text_font_size="18pt", |
| 149 | + text_font_size="19pt", |
| 150 | + text_font_style="bold", |
130 | 151 | text_align="left", |
131 | 152 | text_baseline="middle", |
132 | 153 | text_color="#2C2C2C", |
|
143 | 164 | age_groups[age]["bottom"] = max(age_groups[age]["bottom"], layer["bottom"]) |
144 | 165 | age_groups[age]["top"] = min(age_groups[age]["top"], layer["top"]) |
145 | 166 |
|
| 167 | +bracket_x = -0.12 |
146 | 168 | for age, bounds in age_groups.items(): |
147 | 169 | mid_y = (bounds["top"] + bounds["bottom"]) / 2 |
148 | 170 | label = Label( |
149 | | - x=-0.08, |
| 171 | + x=bracket_x - 0.04, |
150 | 172 | y=mid_y, |
151 | 173 | text=age, |
152 | | - text_font_size="18pt", |
| 174 | + text_font_size="19pt", |
153 | 175 | text_align="right", |
154 | 176 | text_baseline="middle", |
155 | 177 | text_color="#2C2C2C", |
156 | 178 | text_font_style="italic", |
157 | 179 | ) |
158 | 180 | p.add_layout(label) |
159 | 181 |
|
160 | | - p.line(x=[-0.05, -0.05], y=[bounds["top"], bounds["bottom"]], line_color="#2C2C2C", line_width=2) |
161 | | - p.line(x=[-0.07, -0.05], y=[bounds["top"], bounds["top"]], line_color="#2C2C2C", line_width=2) |
162 | | - p.line(x=[-0.07, -0.05], y=[bounds["bottom"], bounds["bottom"]], line_color="#2C2C2C", line_width=2) |
| 182 | + # Bracket lines |
| 183 | + p.line(x=[bracket_x, bracket_x], y=[bounds["top"] + 1, bounds["bottom"] - 1], line_color="#2C2C2C", line_width=2.5) |
| 184 | + p.line( |
| 185 | + x=[bracket_x - 0.025, bracket_x], y=[bounds["top"] + 1, bounds["top"] + 1], line_color="#2C2C2C", line_width=2.5 |
| 186 | + ) |
| 187 | + p.line( |
| 188 | + x=[bracket_x - 0.025, bracket_x], |
| 189 | + y=[bounds["bottom"] - 1, bounds["bottom"] - 1], |
| 190 | + line_color="#2C2C2C", |
| 191 | + line_width=2.5, |
| 192 | + ) |
163 | 193 |
|
164 | | -# Legend — positioned adjacent to column, not in far corner |
| 194 | +# Legend — positioned adjacent to column on right side |
165 | 195 | legend_items = [LegendItem(label=lith, renderers=[rend]) for lith, rend in legend_items_dict.items()] |
166 | 196 | legend = Legend( |
167 | 197 | items=legend_items, |
168 | 198 | location="top_right", |
169 | 199 | label_text_font_size="20pt", |
170 | | - spacing=12, |
| 200 | + spacing=14, |
171 | 201 | padding=20, |
172 | | - background_fill_alpha=0.85, |
173 | | - glyph_height=30, |
174 | | - glyph_width=30, |
| 202 | + margin=10, |
| 203 | + background_fill_color="#F5F5F0", |
| 204 | + background_fill_alpha=0.9, |
| 205 | + border_line_color="#CCCCCC", |
| 206 | + border_line_width=1, |
| 207 | + glyph_height=32, |
| 208 | + glyph_width=32, |
175 | 209 | title="Lithology", |
176 | 210 | title_text_font_size="22pt", |
177 | 211 | title_text_font_style="bold", |
178 | 212 | ) |
179 | 213 | p.add_layout(legend, "right") |
180 | 214 |
|
181 | | -# Style |
| 215 | +# Typography hierarchy |
182 | 216 | p.title.text_font_size = "36pt" |
| 217 | +p.title.text_color = "#1A1A1A" |
| 218 | +p.title.offset = 10 |
183 | 219 | p.yaxis.axis_label_text_font_size = "28pt" |
| 220 | +p.yaxis.axis_label_text_font_style = "bold" |
| 221 | +p.yaxis.axis_label_text_color = "#333333" |
184 | 222 | p.yaxis.major_label_text_font_size = "22pt" |
| 223 | +p.yaxis.major_label_text_color = "#444444" |
185 | 224 |
|
| 225 | +# Visual refinement |
186 | 226 | p.xaxis.visible = False |
187 | 227 | p.xgrid.grid_line_color = None |
188 | | -p.ygrid.grid_line_alpha = 0.15 |
189 | | -p.ygrid.grid_line_dash = "dashed" |
| 228 | +p.ygrid.grid_line_alpha = 0.12 |
| 229 | +p.ygrid.grid_line_dash = [4, 4] |
| 230 | +p.ygrid.grid_line_color = "#999999" |
190 | 231 |
|
191 | 232 | p.yaxis.minor_tick_line_color = None |
| 233 | +p.yaxis.major_tick_line_color = "#AAAAAA" |
| 234 | +p.yaxis.axis_line_color = "#888888" |
192 | 235 | p.outline_line_color = None |
193 | | -p.background_fill_color = "#FAFAFA" |
| 236 | +p.background_fill_color = "#FAFAF6" |
| 237 | +p.border_fill_color = "#FFFFFF" |
| 238 | + |
| 239 | +# Padding |
| 240 | +p.min_border_left = 100 |
| 241 | +p.min_border_right = 40 |
| 242 | +p.min_border_top = 60 |
| 243 | +p.min_border_bottom = 60 |
194 | 244 |
|
195 | 245 | # Save |
196 | 246 | export_png(p, filename="plot.png") |
|
0 commit comments