You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python Dash的交互式Mapbox地图上展示基于Theis方程计算的降深等值线?

如何在Python Dash的交互式Mapbox地图上展示基于Theis方程计算的降深等值线?

嘿,很高兴看到你在学习用Dash做水文相关的可视化!你的问题其实有两个核心:一是让等值线呈现真正的同心圆,二是自动计算到1ft降深的范围而不用手动指定距离。咱们一步步来解决这两个问题——完全不用切换到Matplotlib,Dash+Plotly完全能搞定!

一、先修复非正圆的问题

你现在的等值线不是正圆,核心原因是用了近似的经纬度转换公式,而且计算距离r的时候用了经纬度差直接转英里的粗略方法,这会导致在不同纬度或者距离较远时出现明显变形。我们可以用更准确的地理距离计算工具来生成点:

替换坐标生成逻辑

推荐用pyproj库的Geod类来做精确的 geodesic 计算,它能根据给定的起点、方位角和距离,准确计算出目标点的经纬度,避免手动转换的误差。先安装pyproj:

pip install pyproj

然后修改你的generate_contour_points函数:

from pyproj import Geod

def generate_contour_points(lat, lon, distances, angles):
    """
    生成同心圆上的精确地理坐标点
    """
    contour_points = []
    # 使用WGS84坐标系的椭球参数(和Mapbox使用的坐标系一致)
    geod = Geod(ellps='WGS84')
    
    for dist_ft in distances:
        contour_level_points = []
        dist_meters = dist_ft * 0.3048  # 转换为米(pyproj默认用米作为距离单位)
        for angle in angles:
            # 计算从(lat, lon)出发,沿angle方向走dist_meters后的坐标
            lon_new, lat_new, _ = geod.fwd(lon, lat, angle, dist_meters)
            contour_level_points.append([lat_new, lon_new])
        contour_points.append(contour_level_points)
    
    return contour_points

修复降深计算中的距离r

原来计算r的方法不准确,现在用geod.inv来计算两点之间的实际距离(转换为英里,适配你的Theis方程):

def calculate_drawdown_at_points(lat, lon, distances, angles, transmissivity, storativity, discharge, time):
    contour_points = generate_contour_points(lat, lon, distances, angles)
    drawdowns = []
    geod = Geod(ellps='WGS84')
    
    for contour_level_points in contour_points:
        drawdown_level = []
        for point in contour_level_points:
            # 计算当前点到中心的实际距离(英里)
            _, _, dist_meters = geod.inv(lon, lat, point[1], point[0])
            r = dist_meters / 1609.34  # 米转英里
            drawdown = theis_con(transmissivity, storativity, r, discharge, time)
            drawdown_level.append(drawdown)
        drawdowns.append(drawdown_level)
    
    return drawdowns, contour_points

这样生成的点就会是真正的同心圆,不会因为纬度或者近似转换变形了。

二、自动计算到1ft降深的最大距离

现在你手动指定了distanceone的范围,我们可以通过反解Theis方程来自动找到降深为1ft时对应的距离r_max,然后生成从10ft到r_max的10ft间隔距离数组。

Theis方程的形式是:
$$ s = \frac{Q}{4\pi T} W(u), \quad u = \frac{r^2 S}{4 T t} $$
其中W(u)是井函数(指数积分函数)。因为W(u)没有解析逆函数,我们可以用数值方法求解,比如用scipy.optimize.bisect来找到满足s=1ft的r值。

先安装scipy(如果没装的话):

pip install scipy

然后添加一个反解函数:

from scipy.optimize import bisect
import numpy as np

def theis_inverse(s_target, T, S, Q, t):
    """
    反解Theis方程,给定目标降深s_target,求解对应的距离r(英里)
    """
    def equation(r):
        u = (r**2 * S) / (4 * T * t)
        # 井函数W(u) = -exp1(-u),numpy的exp1是指数积分E1(u)
        W_u = -np.exp1(-u)
        s_calculated = (Q / (4 * np.pi * T)) * W_u
        return s_calculated - s_target
    
    # 设定搜索区间:从0.01英里到足够大的上限(比如100英里,可根据实际场景调整)
    try:
        r_max = bisect(equation, 0.01, 100)
        return r_max
    except ValueError:
        # 处理降深永远达不到1ft的极端情况,返回一个默认最大距离
        return 100

然后在你的callback里替换原来的distanceone生成逻辑:

# 在@app.callback中
inc = 20
angles = np.arange(inc, 360+inc, inc)
# 也可以用更密集的角度让等值线更平滑:angles = np.linspace(0, 360, 72)

# 1. 反解得到1ft降深对应的最大距离(转换为英尺)
r_max_miles = theis_inverse(s_target=1, T=transmissivity, S=storativity, Q=discharge, t=365)
r_max_ft = r_max_miles * 5280  # 英里转英尺

# 2. 生成10ft间隔的距离数组,从10ft到r_max_ft
distanceone = np.arange(10, r_max_ft + 10, 10)  # 加10确保包含r_max_ft

# 剩下的计算和绘图逻辑不变
drawdowns, contour_points = calculate_drawdown_at_points(lat, lon, distanceone, angles, transmissivity, storativity, discharge, time=365)
contour_traces = plot_contours_on_map(contour_points, drawdowns)

三、额外优化建议

  • 可以给等值线添加颜色渐变,比如用颜色映射表示降深大小,修改plot_contours_on_map里的color参数,比如根据降深值从红(大降深)到蓝(小降深)渐变。
  • 角度数量可以调整,角度越多,等值线越平滑,但计算量也会稍大,平衡即可。
  • 可以给theis_inverse函数添加日志或提示,当触发异常时告知用户当前参数下降深无法达到1ft。

这样修改后,你的Dash应用就能自动生成从10ft间隔到1ft降深的完美同心圆等值线啦!

备注:内容来源于stack exchange,提问作者user29044372

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 12:25:29