Skip to content

Commit be50f7f

Browse files
fix(plotnine): address review feedback for column-stratigraphic
Attempt 3/3 - fixes based on AI review
1 parent 51311e1 commit be50f7f

1 file changed

Lines changed: 63 additions & 75 deletions

File tree

plots/column-stratigraphic/implementations/plotnine.py

Lines changed: 63 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
""" pyplots.ai
1+
"""pyplots.ai
22
column-stratigraphic: Stratigraphic Column with Lithology Patterns
33
Library: plotnine 0.15.3 | Python 3.14.3
44
Quality: 86/100 | Created: 2026-03-15
@@ -85,45 +85,50 @@
8585
col_left = 0.0
8686
col_right = 4.0
8787

88-
# Lithology colors - improved contrast (Shale lighter gray, Mudstone warm brown)
88+
# Lithology colors - enhanced contrast, colorblind-friendly palette
8989
lith_colors = {
90-
"Sandstone": "#F5D76E",
91-
"Shale": "#A8A8A8",
92-
"Limestone": "#6DAEDB",
93-
"Siltstone": "#C8DCC0",
90+
"Sandstone": "#EEC946",
91+
"Shale": "#B0B0B0",
92+
"Limestone": "#5A9BD5",
93+
"Siltstone": "#7EBF8E",
9494
"Conglomerate": "#D4785C",
9595
"Mudstone": "#8B6914",
9696
}
9797

9898
# Unconformity depth (J/K boundary between Kootenai Fm and Morrison Fm)
9999
unconformity_depth = 95.0
100100

101-
# Generate pattern overlay data for each lithology
102-
pattern_rows = []
101+
# Generate pattern overlay data with helper functions to reduce verbosity
103102
np.random.seed(42)
103+
pattern_rows = []
104+
105+
106+
def _add_random_points(rows, top_val, bot_val, lith, n, ptype, x_pad=0.3, y_pad=0.5):
107+
xs = np.random.uniform(col_left + x_pad, col_right - x_pad, n)
108+
ys = np.random.uniform(top_val + y_pad, bot_val - y_pad, n)
109+
for px, py in zip(xs, ys, strict=True):
110+
rows.append({"x": px, "y": py, "xend": px, "yend": py, "ptype": ptype, "lithology": lith})
111+
112+
113+
def _add_horiz_dashes(rows, top_val, bot_val, lith, spacing, ptype, dash_len=0.5, x_step=0.8):
114+
y_pos = top_val + spacing * 0.4
115+
while y_pos < bot_val - 0.3:
116+
for x_start in np.arange(col_left + 0.3, col_right - 0.3, x_step):
117+
rows.append(
118+
{"x": x_start, "y": y_pos, "xend": x_start + dash_len, "yend": y_pos, "ptype": ptype, "lithology": lith}
119+
)
120+
y_pos += spacing
121+
104122

105123
for _, row in layers.iterrows():
106-
top_val = row["top"]
107-
bot_val = row["bottom"]
108-
lith = row["lithology"]
124+
top_val, bot_val, lith = row["top"], row["bottom"], row["lithology"]
109125
thickness = bot_val - top_val
110126

111127
if lith == "Sandstone":
112-
n_dots = int(thickness * 4)
113-
for _ in range(n_dots):
114-
px = np.random.uniform(col_left + 0.3, col_right - 0.3)
115-
py = np.random.uniform(top_val + 0.5, bot_val - 0.5)
116-
pattern_rows.append({"x": px, "y": py, "xend": px, "yend": py, "ptype": "dot", "lithology": lith})
128+
_add_random_points(pattern_rows, top_val, bot_val, lith, int(thickness * 5), "dot")
117129

118130
elif lith == "Shale":
119-
spacing = 2.5
120-
y_pos = top_val + 1.0
121-
while y_pos < bot_val - 0.5:
122-
for x_start in np.arange(col_left + 0.3, col_right - 0.5, 0.8):
123-
pattern_rows.append(
124-
{"x": x_start, "y": y_pos, "xend": x_start + 0.5, "yend": y_pos, "ptype": "dash", "lithology": lith}
125-
)
126-
y_pos += spacing
131+
_add_horiz_dashes(pattern_rows, top_val, bot_val, lith, spacing=2.5, ptype="dash", dash_len=0.5, x_step=0.8)
127132

128133
elif lith == "Limestone":
129134
spacing = 4.0
@@ -150,38 +155,22 @@
150155
row_idx += 1
151156

152157
elif lith == "Siltstone":
153-
n_dashes = int(thickness * 6)
154-
for _ in range(n_dashes):
155-
px = np.random.uniform(col_left + 0.3, col_right - 0.3)
156-
py = np.random.uniform(top_val + 0.5, bot_val - 0.5)
157-
dx = np.random.uniform(-0.15, 0.15)
158+
n_dashes = int(thickness * 8)
159+
xs = np.random.uniform(col_left + 0.3, col_right - 0.3, n_dashes)
160+
ys = np.random.uniform(top_val + 0.5, bot_val - 0.5, n_dashes)
161+
dxs = np.random.uniform(-0.2, 0.2, n_dashes)
162+
for px, py, dx in zip(xs, ys, dxs, strict=True):
158163
pattern_rows.append(
159-
{"x": px, "y": py, "xend": px + dx, "yend": py + 0.2, "ptype": "short_dash", "lithology": lith}
164+
{"x": px, "y": py, "xend": px + dx, "yend": py + 0.3, "ptype": "short_dash", "lithology": lith}
160165
)
161166

162167
elif lith == "Conglomerate":
163-
n_circles = int(thickness * 2)
164-
for _ in range(n_circles):
165-
px = np.random.uniform(col_left + 0.5, col_right - 0.5)
166-
py = np.random.uniform(top_val + 1.0, bot_val - 1.0)
167-
pattern_rows.append({"x": px, "y": py, "xend": px, "yend": py, "ptype": "circle", "lithology": lith})
168+
_add_random_points(pattern_rows, top_val, bot_val, lith, int(thickness * 2.5), "circle", x_pad=0.5, y_pad=1.0)
168169

169170
elif lith == "Mudstone":
170-
spacing = 2.0
171-
y_pos = top_val + 0.8
172-
while y_pos < bot_val - 0.3:
173-
for x_start in np.arange(col_left + 0.2, col_right - 0.3, 0.5):
174-
pattern_rows.append(
175-
{
176-
"x": x_start,
177-
"y": y_pos,
178-
"xend": x_start + 0.25,
179-
"yend": y_pos,
180-
"ptype": "fine_dash",
181-
"lithology": lith,
182-
}
183-
)
184-
y_pos += spacing
171+
_add_horiz_dashes(
172+
pattern_rows, top_val, bot_val, lith, spacing=1.8, ptype="fine_dash", dash_len=0.25, x_step=0.5
173+
)
185174

186175
pattern_df = pd.DataFrame(pattern_rows)
187176

@@ -210,7 +199,7 @@
210199
)
211200

212201
# Unconformity wavy line data
213-
wavy_x = np.linspace(col_left, col_right, 40)
202+
wavy_x = np.linspace(col_left - 0.15, col_right + 0.15, 45)
214203
wavy_y = unconformity_depth + np.sin(wavy_x * 8) * 0.8
215204
wavy_df = pd.DataFrame({"x": wavy_x[:-1], "y": wavy_y[:-1], "xend": wavy_x[1:], "yend": wavy_y[1:]})
216205

@@ -221,32 +210,32 @@
221210
+ geom_tile(
222211
data=layers,
223212
mapping=aes(x="x_center", y="mid", width=col_right - col_left, height="thickness", fill="lithology"),
224-
color="#2C2C2C",
225-
size=0.8,
226-
alpha=0.6,
213+
color="#3A3A3A",
214+
size=0.9,
215+
alpha=0.7,
227216
)
228-
# Pattern overlays - line segments
217+
# Pattern overlays - line segments (thicker for visibility)
229218
+ geom_segment(
230-
data=lines_df, mapping=aes(x="x", y="y", xend="xend", yend="yend"), color="#2C2C2C", size=0.6, alpha=0.75
219+
data=lines_df, mapping=aes(x="x", y="y", xend="xend", yend="yend"), color="#2C2C2C", size=0.65, alpha=0.8
231220
)
232221
# Pattern overlays - stipple dots (sandstone)
233-
+ geom_point(data=dots_df, mapping=aes(x="x", y="y"), color="#2C2C2C", size=1.0, alpha=0.65)
222+
+ geom_point(data=dots_df, mapping=aes(x="x", y="y"), color="#2C2C2C", size=1.2, alpha=0.7)
234223
# Pattern overlays - open circles (conglomerate)
235224
+ geom_point(
236225
data=circles_df,
237226
mapping=aes(x="x", y="y"),
238227
color="#2C2C2C",
239228
size=3.5,
240-
alpha=0.55,
229+
alpha=0.6,
241230
shape="o",
242231
fill="none",
243232
stroke=0.9,
244233
)
245234
# Layer boundary lines
246-
+ geom_segment(data=boundary_df, mapping=aes(x="x", y="y", xend="xend", yend="yend"), color="#2C2C2C", size=0.8)
247-
# Unconformity wavy line (red - focal point for data storytelling)
235+
+ geom_segment(data=boundary_df, mapping=aes(x="x", y="y", xend="xend", yend="yend"), color="#3A3A3A", size=0.9)
236+
# Unconformity wavy line (firebrick red - focal point for data storytelling)
248237
+ geom_segment(
249-
data=wavy_df, mapping=aes(x="x", y="y", xend="xend", yend="yend"), color="#B22222", size=1.8, alpha=0.9
238+
data=wavy_df, mapping=aes(x="x", y="y", xend="xend", yend="yend"), color="#B22222", size=2.2, alpha=0.95
250239
)
251240
# Unconformity annotation
252241
+ annotate(
@@ -255,38 +244,37 @@
255244
y=unconformity_depth,
256245
label="~ Unconformity ~",
257246
ha="left",
258-
size=14,
247+
size=15,
259248
color="#B22222",
260249
fontstyle="italic",
261250
fontweight="bold",
262251
)
263252
# Age bracket lines using geom_linerange (idiomatic plotnine for vertical ranges)
264-
+ geom_linerange(data=age_groups, mapping=aes(x="bracket_x", ymin="ymin", ymax="ymax"), color="#444444", size=0.8)
253+
+ geom_linerange(data=age_groups, mapping=aes(x="bracket_x", ymin="ymin", ymax="ymax"), color="#444444", size=0.9)
265254
# Formation labels using geom_label (plotnine-native styled text with background)
266255
+ geom_label(
267256
data=form_groups,
268257
mapping=aes(x="x", y="mid", label="formation"),
269258
ha="left",
270-
size=16,
259+
size=17,
271260
fontstyle="italic",
272261
color="#1A1A1A",
273-
fill="#FFFFFF",
274-
alpha=0.7,
262+
fill="#FFFFFFCC",
275263
label_padding=0.3,
276-
label_size=0,
264+
label_size=0.3,
277265
)
278-
# Age labels (left side)
266+
# Age labels (left side) - increased size for legibility
279267
+ geom_text(
280268
data=age_groups,
281269
mapping=aes(x="x", y="mid", label="age"),
282270
ha="right",
283-
size=15,
271+
size=17,
284272
fontweight="bold",
285-
color="#333333",
273+
color="#2A2A2A",
286274
)
287275
# Scales - grammar-driven fill mapping with manual color values
288276
+ scale_fill_manual(values=lith_colors, name="Lithology")
289-
+ scale_x_continuous(limits=(-4.5, 9.0), breaks=[])
277+
+ scale_x_continuous(limits=(-4.0, 8.5), breaks=[])
290278
+ scale_y_continuous(trans="reverse", name="Depth (m)", breaks=list(range(0, 200, 20)))
291279
# Clipping for clean edges
292280
+ coord_cartesian(ylim=(185, -5))
@@ -301,22 +289,22 @@
301289
plot_title=element_text(size=26, face="bold", ha="center"),
302290
axis_title_y=element_text(size=22),
303291
axis_title_x=element_blank(),
304-
axis_text_y=element_text(size=18),
292+
axis_text_y=element_text(size=18, color="#333333"),
305293
axis_text_x=element_blank(),
306294
axis_ticks_major_x=element_blank(),
307295
legend_title=element_text(size=18, face="bold"),
308296
legend_text=element_text(size=16),
309297
legend_position="bottom",
310298
legend_direction="horizontal",
311299
legend_key_size=22,
312-
legend_background=element_rect(fill="#FAFAFA", color="#E0E0E0", size=0.3),
300+
legend_background=element_rect(fill="#F5F5F5", color="#CCCCCC", size=0.4),
313301
legend_margin=10,
314302
panel_grid_major_x=element_blank(),
315303
panel_grid_minor_x=element_blank(),
316-
panel_grid_major_y=element_line(color="#E0E0E0", size=0.3, alpha=0.4),
304+
panel_grid_major_y=element_line(color="#D8D8D8", size=0.3, alpha=0.35),
317305
panel_grid_minor_y=element_blank(),
318306
panel_background=element_rect(fill="#FAFAFA", color="none"),
319-
plot_background=element_rect(fill="white", color="none"),
307+
plot_background=element_rect(fill="#FFFFFF", color="none"),
320308
)
321309
)
322310

0 commit comments

Comments
 (0)