def add_scale_bar(ax, provmap, location="bl", height_scale=0.016, width_scale=0.2): """在地图左下角添加比例尺。""" bounds = provmap.total_bounds map_width_m = bounds[2] - bounds[0] target_km = map_width_m * width_scale / 1000 nice_values = [500, 1000, 2000, 3000, 5000, 10000, 20000, 50000, 100000] bar_km = min(nice_values, key=lambda x: abs(x - target_km)) bar_m = bar_km * 1000
x0_frac = 0.08 if location == "bl" else 0.62 xlim = ax.get_xlim() ylim = ax.get_ylim() ax_width = xlim[1] - xlim[0] x0_data = xlim[0] + x0_frac * ax_width y0_data = ylim[0] + 0.04 * (ylim[1] - ylim[0]) bar_height_m = map_width_m * height_scale n_seg = 4 seg_w = bar_m / n_seg for i in range(n_seg): color = "black" if i % 2 == 0 else "white" rect = Rectangle( (x0_data + i * seg_w, y0_data), seg_w, bar_height_m, facecolor=color, edgecolor="black", linewidth=0.5, clip_on=False, zorder=10, ) ax.add_patch(rect) ax.text(x0_data + bar_m / 2, y0_data - bar_height_m * 0.3, f"{bar_km:,} km", fontsize=7, ha="center", va="top", color="black", zorder=10)
def add_north_arrow(ax): """在地图右上角添加指北针。""" ax.annotate("N", xy=(0.98, 0.95), xycoords="axes fraction", fontsize=10, fontweight="bold", ha="center", va="bottom") ax.annotate("", xy=(0.98, 0.95), xycoords="axes fraction", xytext=(0.98, 0.88), textcoords="axes fraction", arrowprops=dict(arrowstyle="->", color="black", lw=1.5))
def draw_lines(ax, provlinemap): """绘制线条元素。""" for _, row in provlinemap.iterrows(): cls = row["class"] color = LINE_COLORS.get(cls, "gray") lw = LINE_WIDTHS.get(cls, 0.3) geom = row["geometry"] if geom.geom_type == "MultiLineString": for line in geom.geoms: xs, ys = line.xy ax.plot(xs, ys, color=color, linewidth=lw, zorder=3) elif geom.geom_type == "LineString": xs, ys = geom.xy ax.plot(xs, ys, color=color, linewidth=lw, zorder=3)
def draw_province_labels(ax, provmap, centroids): """绘制省名标注。""" for i, row in provmap.iterrows(): cx, cy = centroids.iloc[i].x, centroids.iloc[i].y ax.text(cx, cy, row["省"], fontsize=5, color="gray", ha="center", va="center", zorder=4)
def add_vertical_colorbar(ax, fig, cmap, norm, label=None, ticks=None, ticklabels=None, n_segments=4): """在地图左下角、比例尺上方绘制竖向分段色条。""" xlim = ax.get_xlim() ylim = ax.get_ylim() ax_width = xlim[1] - xlim[0] ax_height = ylim[1] - ylim[0]
cb_width_m = ax_width * 0.0075 cb_height_m = ax_height * 0.15 seg_height_m = cb_height_m / n_segments
x0_frac = 0.08 x0_data = xlim[0] + x0_frac * ax_width scale_y0 = ylim[0] + 0.04 * ax_height bar_height_m = ax_width * 0.016 cb_bottom = scale_y0 + bar_height_m + ax_height * 0.025
vmin, vmax = norm.vmin, norm.vmax for i in range(n_segments): frac_lo = i / n_segments frac_hi = (i + 1) / n_segments color = cmap((frac_lo + frac_hi) / 2) y_bottom = cb_bottom + i * seg_height_m rect = Rectangle( (x0_data, y_bottom), cb_width_m, seg_height_m, facecolor=color, edgecolor="black", linewidth=0.5, clip_on=False, zorder=10, ) ax.add_patch(rect)
if ticks is not None and ticklabels is not None: for tick_val, tick_lbl in zip(ticks, ticklabels): frac = (tick_val - vmin) / (vmax - vmin) if vmax > vmin else 0 frac = max(0, min(1, frac)) y_pos = cb_bottom + frac * cb_height_m ax.plot( [x0_data + cb_width_m, x0_data + cb_width_m + cb_width_m * 0.3], [y_pos, y_pos], color="black", linewidth=0.5, clip_on=False, zorder=10, ) ax.text( x0_data + cb_width_m + cb_width_m * 0.4, y_pos, tick_lbl, fontsize=6, ha="left", va="center", color="black", zorder=10, )
if label: ax.text( x0_data + cb_width_m / 2, cb_bottom + cb_height_m + ax_height * 0.01, label, fontsize=7, ha="center", va="bottom", color="black", zorder=10, )
|
评论