I have created a Dash app using Python with an interactive map using mapbox. I want to generate drawdown contours at 10-ft intervals out to a 1-ft drawdown around a specified lat/lon using the Theis equation. I just started learning to code a few months ago so I'm sure there's a simple fix but I'm really struggling with this one. I would greatly appreciate any assistance on this!
So far, my map displays the drawdown contours, but they aren't true circles. The code currently generates points in a circular pattern around the lat/lon at 10-ft intervals out to 10,000 ft, calculates drawdown at each point using Theis, then connects the points using straight lines and plots them on the map with Scattermapbox.
The problem is, I don't want to have to plug in that exact distance each time to calculate drawdown out to. What I really need it to do is generate concentric circles around the lat/lon out to a 1-ft drawdown contour, no matter how far that drawdown circle needs to be away from the original lat/lon point. Some advice I was given was to look at using matplotlib instead of plotly but I'm not sure if this is possible in Dash.
Above the @app.callback section I'm using this:
# Contour lines
distance = np.append(np.arange(1,1000,10), np.append(np.arange(100, 2500, 100), np.arange(2500,10000,250)))
distance = np.append(distance, np.arange(10000,105000,1000)) # distance, feet
ci = 10 # default contour interval, feet
def generate_contour_points(lat, lon, distance, angles):
"""
Generate points in a circular pattern around a given lat/lon for the contour lines.
Parameters:
- lat: Latitude of the center point (decimal degrees).
- lon: Longitude of the center point (decimal degrees).
- distance: Array of radial distances for contour levels (in feet).
- angles: Array of angles (in degrees) to calculate the radial points.
Returns:
- List of points representing contour lines.
"""
contour_points = []
for i in distance: # Loop over each distance (for each contour level)
contour_level_points = [] # List to store points for each contour level
for angle in angles: # Loop over each angle
# Convert angle to radians
angle_rad = np.deg2rad(angle)
# Calculate the new x, y coordinates using polar to Cartesian conversion
x_offset = i * np.sin(angle_rad)
y_offset = i * np.cos(angle_rad)
# Convert to geographic coordinates (lat/lon) using distance
lat_offset = x_offset / 364000.0 # Approximate conversion from feet to degrees latitude
lon_offset = y_offset / (364000.0 * np.cos(np.radians(lat))) # Approximate conversion for longitude
contour_level_points.append([lat + lat_offset, lon + lon_offset])
contour_points.append(contour_level_points)
return contour_points
def calculate_drawdown_at_points(lat, lon, distance, angles, transmissivity, storativity, discharge, time):
"""
Calculate the drawdown at each point generated by generate_contour_points.
"""
contour_points = generate_contour_points(lat, lon, distance, angles)
drawdowns = []
for contour_level_points in contour_points:
drawdown_level = []
for point in contour_level_points:
r = np.sqrt((point[0] - lat)**2 + (point[1] - lon)**2) * 3963.0 # Convert to miles
drawdown = theis_con(transmissivity, storativity, r, discharge, time)
drawdown_level.append(drawdown)
drawdowns.append(drawdown_level)
return drawdowns, contour_points
def plot_contours_on_map(contour_points, drawdowns):
"""
Plot the contour lines on the map.
"""
contour_traces = []
for contour_level, drawdown_level in zip(contour_points, drawdowns):
contour_traces.append(go.Scattermapbox(
lat=[point[0] for point in contour_level],
lon=[point[1] for point in contour_level],
mode='lines',
line=dict(width=2, color='blue'),
name=f"Drawdown: {min(drawdown_level)} to {max(drawdown_level)} ft"
))
return contour_traces
In my @app.callback section I'm currently using this:
inc = 20
angles = np.arange(inc,360+inc, inc)
# Generate points for contour lines
distanceone = np.arange(10, 10000, 1000) # Example distance array (1 ft to 1000 ft)
angles = np.linspace(0, 360, 36) # Angles from 0° to 360° (every 10 degrees)
# Calculate drawdowns at the generated points
drawdowns, contour_points = calculate_drawdown_at_points(lat, lon, distanceone, angles, transmissivity, storativity, discharge, time=365)
# Plot contour lines on the map
contour_traces = plot_contours_on_map(contour_points, drawdowns)
GeoJSON was effective in producing the contours I needed.