My goal is to get a 2d-array (i.e. a matrix) of floats which can be visualized as a digital elevation model (i.e. a contour plot) with smooth contours as shown on the figure below provided that I am able to control how many white mountains are generated and their distance to each other every time I run the script.
The white mountains represent the clusters of cells in the CSV file containing values greater than 0.9 as shown on the color scale. On the figure we see two mountains divided by areas with values lower than 0.9.
Here is my full code with detailed comments where I use convolutional filters to create such an image:
import numpy as np
import pandas as pd
from scipy import signal
import plotly
import plotly.graph_objs as go
def rndmtx():
"""Generate random 2d-array as a digital elevation model."""
nx = 100
ny = 100
dem1 = np.random.rand(nx, ny)
# Save array to csv file befor Gaussian filter.
# Comment the next two lines if reading from the csv file.
dafr = pd.DataFrame(dem1)
dafr.to_csv('G_dem1.csv', header=False, index=False)
# Uncomment the next two lines to read from csv file.
# dafr = pd.read_csv('G_dem1.csv', header=None)
# dem1 = dafr.values
# Apply the first Gaussian filter.
sizex = 5 # The less sizex and sizey the more highlands.
sizey = 5 # The more sizex and sizey the more water.
x, y = np.mgrid[-sizex:sizex+1, -sizey:sizey+1]
scale = 0.33 # The more scale the bigger the difference in elevation.
g = np.exp(-scale*(x**2/sizex+y**2/sizey))
filter1 = g/g.sum() # Normalise the Gaussian function.
dem_smooth = signal.convolve(dem1, filter1, mode='valid')
# Rescale so it lies between 0 and 1.
dem_smooth = ((dem_smooth - dem_smooth.min())
/ (dem_smooth.max() - dem_smooth.min()))
# Apply the second Gaussian filter to make the boundaries smoother.
sizex = 5
sizey = 5
x, y = np.mgrid[-sizex:sizex+1, -sizey:sizey+1]
g = np.exp(-0.33*(x**2/sizex+y**2/sizey))
filter2 = g/g.sum()
dem_smooth1 = signal.convolve(dem_smooth, filter2, mode='valid')
dem_smooth1 = ((dem_smooth1 - dem_smooth1.min())
/ (dem_smooth1.max() - dem_smooth1.min()))
return dem_smooth1
# Get the raw random array of the digital elevation model
# and assign it to the variable.
contour_xy = rndmtx()
# Save the array into CSV file in the working directory.
df = pd.DataFrame(contour_xy)
df.to_csv('last_data.csv', header=False, index=False)
data = [
go.Contour(
z=contour_xy,
colorscale=[
[0, 'rgb(0, 161, 233)'], [0.28, 'rgb(0, 161, 233)'],
[0.28, 'rgb(29, 210, 108)'], [0.50, 'rgb(29, 210, 108)'],
[0.50, 'rgb(141, 232, 130)'], [0.65, 'rgb(141, 232, 130)'],
[0.65, 'rgb(254, 254, 152)'], [0.75, 'rgb(254, 254, 152)'],
[0.75, 'rgb(192, 182, 122)'], [0.82, 'rgb(192, 182, 122)'],
[0.82, 'rgb(142, 110, 92)'], [0.88, 'rgb(142, 110, 92)'],
[0.88, 'rgb(171, 147, 142)'], [0.93, 'rgb(171, 147, 142)'],
[0.93, 'rgb(227, 219, 217)'], [0.97, 'rgb(227, 219, 217)'],
[0.97, 'rgb(255, 255, 255)'], [1, 'rgb(255, 255, 255)']
],
),
]
layout = go.Layout(
yaxis=dict(
autorange='reversed'
)
)
figure = go.Figure(data=data, layout=layout)
plotly.offline.plot(figure, filename='dem.html')
Every time I run the script I get a unique random array of numbers.
How to control over the number and proximity of white mountains to be generated?
For example, now I want 10 white mountains to be placed on the plot, then I want only 2 mountains, and so on.
Currently, I use a Brute Force algorithm that eventually build the plot with the desired number of mountains, but I am not able to set specific coordinates of mountains to control the distance between them.
Generous answers and pointing out the right direction are highly appreciated. Enjoy playing with the code!