Place and insert plane image along path using matplotlib - python-3.x

My code is a fair bit more advanced, but in simple terms I am looking to place and rotate an image of a plane along a path using matplotlib. Ideally I would be able to select the angle and how far along the path the image should be placed. Any ideas? My ideal output would be something like this (ignoring the coordinates I already fixed that in my real code).
Image of Norway used:
Code
import matplotlib.pyplot as plt
import matplotlib.image as img
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def x2map(x, x_scale):
return x * x_scale
def y2map(y, y_scale):
return (1 - y) * y_scale
if __name__ == "__main__":
image_url = "Norge2.png"
# Obtains the scaling for the figure
map = img.imread(image_url)
fig, ax = plt.subplots()
im = ax.imshow(map)
_, x_scale = plt.xlim()
y_scale, _ = plt.ylim()
# Fixes the axis to 0-1 and 0-1
positions_x = [i * x_scale / 10 for i in range(0, 11)]
positions_y = [i * y_scale / 10 for i in range(0, 11)]
labels = [i / 10 for i in range(0, 11)]
ax.set_xticks(positions_x)
ax.set_xticklabels([i / 10 for i in range(0, 11)])
ax.set_yticks(positions_y)
ax.set_yticklabels([(10 - i) / 10 for i in range(0, 11)])
route_color = "red"
route_ls = "-"
city_marker ="o"
city_color = "red"
A = [x2map(0.125,x_scale), y2map(0.14,y_scale)]
B = [x2map(0.772,x_scale), y2map(0.92,y_scale)]
plt.plot(
[A[0], B[0]], [A[1], B[1]], marker='o', color=route_color, ls=route_ls
)
plt.show()

Related

Slider is not updating my diagram correctly

I am trying to plot the biffurcation diagram and its equation.
My problem is that I want to put a slider for when I change the rate in the logistic map equation, but I can't seem to understand what I need to code in the update function.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
rate = np.linspace(1, 4, 1000)
N = 1000
x = np.zeros(N) + 0.5
count = np.arange(round(N*0.9), N)
y = np.zeros(N) + 0.5
#t = 1
# Biffurcation
for rs in range(len(rate)):
for n in range(N-1):
x[n+1] = rate[rs] * x[n] * (1-x[n])
u = np.unique(x[count])
r = rate[rs] * np.ones(len(u))
for i in range(N - 1):
y[i + 1] = rate[rs] * y[i] * (1 - y[i])
# plotting
plt.plot(r, u, '.', markersize=2)
plt.ylabel(ylabel='X')
plt.xlabel(xlabel='r')
plt.title('Biffurcation')
# Plotting
fig, ax = plt.subplots()
axes, = ax.plot(y, 'o-')
ax.set_ylabel(ylabel='X')
ax.set_xlabel(xlabel='Time')
ax.set_title('$x_{n+1}$ = r * $x_{n}$ * (1-$x_{n}$)')
# defining axSlider
fig.subplots_adjust(bottom=0.25)
ax_slider = fig.add_axes([0.15, 0.1, 0.65, 0.03])
slider = Slider(ax_slider, label='r', valmin=1, valmax=4, valinit=1, valstep=rate)
# updating the plot
def update(val):
current_v = slider.val
rate[rs] = current_v
axes.set_ydata(rate[rs])
fig.canvas.draw()
slider.on_changed(update)
plt.show()
I tried to update my plot for when I change the rate on my slider, but it is not working properly.
def update(val):
current_v = slider.val
rate[rs] = current_v
axes.set_ydata(rate[rs])
fig.canvas.draw()

How can i make a continuous python slider discrete?

Recently I have been developing a code to create a function approximation through Bernstein's polynomial.
The problem i have is that I want to represent the Bernstein's polynomial for different values for 'n'. I found an example on Matplotlib how to make sliders so I copied to see if it worked with my function. The result is that for a starting 'n' value it works but as soon as I change it, it stops working because the slider is only using integers but if you move it the function changes as if 'n' could have any value for an interval. The code:
import sympy as sy
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
def f(x):
return np.abs(x)
def fac(x):
return np.math.factorial(x)
def ecuacion(n,k):
result = ((fac(n)) / (fac(k) * fac(n - k))) * ((x + 1) ** k * (1 - x) ** (n - k)) / (2 ** n) * f(2 * k / n - 1)
return result
def bernstein(x, k, n):
#p = ((fac(n)) / (fac(k) * fac(n - k))) * ((x + 1) ** k * (1 - x) ** (n - k)) / (2 ** n) * f(2 * k / n - 1)
resultado = 0
for k in range(0,n+1):
resultado = ecuacion(n,k) + resultado
return resultado
axis_color = 'lightgoldenrodyellow'
fig = plt.figure()
ax = fig.add_subplot(111)
fig.subplots_adjust(left=0.25, bottom=0.25)
x = np.arange(-1, 1, 0.001)
freq_0 = 3
# Draw the initial plot
# The 'line' variable is used for modifying the line later
[line] = ax.plot(x, bernstein(x,0,2), linewidth=2, color='red')
ax.set_xlim([-1, 1])
ax.set_ylim([0, 1])
# Add two sliders for tweaking the parameters
# Define an axes area and draw a slider in it
amp_slider_ax = fig.add_axes([0.25, 0.15, 0.65, 0.03])
amp_slider = Slider(amp_slider_ax, 'n', 2, 50, valinit=2)
# Draw another slider
freq_slider_ax = fig.add_axes([0.25, 0.1, 0.65, 0.03])
freq_slider = Slider(freq_slider_ax, 'Freq', 3, 30.0, valinit=6)
# Define an action for modifying the line when any slider's value changes
def sliders_on_changed(val):
line.set_ydata(bernstein(x,amp_slider.val, freq_slider.val))
fig.canvas.draw_idle()
amp_slider.on_changed(sliders_on_changed)
freq_slider.on_changed(sliders_on_changed)
# Add a button for resetting the parameters
reset_button_ax = fig.add_axes([0.8, 0.025, 0.1, 0.04])
reset_button = Button(reset_button_ax, 'Reset', color=axis_color, hovercolor='0.975')
def reset_button_on_clicked(mouse_event):
freq_slider.reset()
amp_slider.reset()
reset_button.on_clicked(reset_button_on_clicked)
#Add a set of radio buttons for changing color
# color_radios_ax = fig.add_axes([0.025, 0.5, 0.15, 0.15])
# color_radios = RadioButtons(color_radios_ax, ('red', 'blue', 'green'), active=0)
# def color_radios_on_clicked(label):
# line.set_color(label)
# fig.canvas.draw_idle()
# color_radios.on_clicked(color_radios_on_clicked)
plt.show()

How to plot the figure in the desired fashion?

How to edit this code to have the same width and colour map as in the following figure? The script is based on this question.
import numpy as np
import matplotlib.pyplot as plt
dpi = 100
def offset(x,y, o):
""" Offset coordinates given by array x,y by o """
X = np.c_[x,y].T
m = np.array([[0,-1],[1,0]])
R = np.zeros_like(X)
S = X[:,2:]-X[:,:-2]
R[:,1:-1] = np.dot(m, S)
R[:,0] = np.dot(m, X[:,1]-X[:,0])
R[:,-1] = np.dot(m, X[:,-1]-X[:,-2])
On = R/np.sqrt(R[0,:]**2+R[1,:]**2)*o
Out = On+X
return Out[0,:], Out[1,:]
def offset_curve(ax, x,y, o):
""" Offset array x,y in data coordinates
by o in points """
trans = ax.transData.transform
inv = ax.transData.inverted().transform
X = np.c_[x,y]
Xt = trans(X)
xto, yto = offset(Xt[:,0],Xt[:,1],o*dpi/72. )
Xto = np.c_[xto, yto]
Xo = inv(Xto)
return Xo[:,0], Xo[:,1]
x = np.linspace(-3, 3, 100)
y = -(1/4*x**4 - 1.6*x**2)
fig, ax=plt.subplots(figsize=(4,2.5), dpi=dpi)
cmap = plt.get_cmap('Greys_r')
lw = 2.
lines = []
width_l = ax.get_ylim()[1] - ax.get_ylim()[0]
for t in np.linspace(0, 1, 40):
l, = ax.plot(x, y - t * 0.1 * width_l, color=cmap(t/2 + 0.25))
lines.append(l)
def plot_rainbow(event=None):
# initialization of lists
xr, yr = 6*[None], 6*[None]
xr[0],yr[0] = offset_curve(ax, x,y, lw/2.)
xr[1],yr[1] = offset_curve(ax, x,y, -lw/2.)
xr[2],yr[2] = offset_curve(ax, xr[0],yr[0], lw)
xr[3],yr[3] = offset_curve(ax, xr[1],yr[1], -lw)
xr[4],yr[4] = offset_curve(ax, xr[2],yr[2], lw)
xr[5],yr[5] = offset_curve(ax, xr[3],yr[3], -lw)
for i in range(6):
lines[i].set_data(xr[i], yr[i])
plot_rainbow()
fig.canvas.mpl_connect("resize_event", plot_rainbow)
fig.canvas.mpl_connect("button_release_event", plot_rainbow)
plt.show()
The figure above was created by the following script:
import numpy as np
import matplotlib.pyplot as plt
import math
dpi = 100
# Function for plotting parallels to curves
def get_parallels(length=.1):
px, py = [], []
for idx in range(len(x)-1):
x0, y0, xa, ya = x[idx], y[idx], x[idx+1], y[idx+1]
dx, dy = xa-x0, ya-y0
norm = math.hypot(dx, dy) * 1/length
dx /= norm
dy /= norm
px.append(x0-dy)
py.append(y0+dx)
return px, py
fig, ax=plt.subplots(figsize=(4,2.5), dpi=dpi)
cmap = plt.get_cmap('Greys_r')
x = np.linspace(-1, 1, 100)
y = -x**2
ax.set_ylim(-1.02, 0.3)
ax.scatter(1/2*(ax.get_xlim()[0] + ax.get_xlim()[1]), 0.145, marker = 'o', s=900, facecolors='none')
width_l = ax.get_ylim()[1] - ax.get_ylim()[0]
for t in np.linspace(0, 1, 40):
length = -0.1*width_l*t
ax.plot(*get_parallels(length=length), color=cmap(t/2 + 0.25))
plt.tight_layout()
plt.show()
Several curves are plotted in camp and the length is set.
I would like to have the same "shadow" for the curve in the first scrip. How to do that, please?

Matplotlib animation not showing (Gradient Descent Test)

I tried to create a matplotlib animation to practice using gradient descent to do linear regression. However I can't get the animation to work.
I managed to get the animation to work by using anim.show() but this caused an AttributeError as the animation class does not have a method. No idea why this actually causes the animation to work
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
def main():
# Initialize Dataset
X = 10*np.random.rand(50)
y = 8*X + 1 + 2.5*np.random.randn(50)
model = LinearRegression()
model.train(X,y)
model.animate(X,y)
class LinearRegression():
# Using Gradient Descent for Linear Regression
def __init__(self, learning_rate=0.001, epochs=100):
self.learning_rate = learning_rate
self.epochs = epochs
self.a_0 = 0
self.a_1 = 0
self.w_list = []
def train(self, X, y):
n = X.shape[0]
for i in range(self.epochs):
self.w_list.append([self.a_0,self.a_1])
y_train = self.a_0 + self.a_1 * X
error = y - y_train # Whether you use y_train - y or y - y_train will make a difference
mse = np.sum(error ** 2) / n
self.a_0 -= -2/n * np.sum(error) * self.learning_rate
self.a_1 -= -2/n * np.sum(error * X) * self.learning_rate
#if i%10 == 0:
# print("MSE",str(i)+":", mse)
self.w_list = np.array(self.w_list)
def animate(self, X, y):
fig, ax = plt.subplots()
ax.scatter(X,y)
plot_range = np.array(range(int(min(X))-1,int(max(X))+3))
a_0,a_1 = self.w_list[0,]
y_plot = plot_range*a_1 + a_0
ln, = ax.plot(plot_range, y_plot, color="red", label="Best Fit")
def animator(frame):
a_0, a_1 = self.w_list[frame,]
y_plot = plot_range * a_1 + a_0
ln.set_data(plot_range,y_plot)
print("Launching Animation")
anim = animation.FuncAnimation(fig,func = animator, frames = self.epochs)
anim.show()
if __name__ == "__main__":
main()
You need to call plt.show() to open the plot window.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
def main():
# Initialize Dataset
X = 10*np.random.rand(50)
y = 8*X + 1 + 2.5*np.random.randn(50)
model = LinearRegression()
model.train(X,y)
model.animate(X,y)
class LinearRegression():
# Using Gradient Descent for Linear Regression
def __init__(self, learning_rate=0.001, epochs=100):
self.learning_rate = learning_rate
self.epochs = epochs
self.a_0 = 0
self.a_1 = 0
self.w_list = []
def train(self, X, y):
n = X.shape[0]
for i in range(self.epochs):
self.w_list.append([self.a_0,self.a_1])
y_train = self.a_0 + self.a_1 * X
error = y - y_train # Whether you use y_train - y or y - y_train will make a difference
mse = np.sum(error ** 2) / n
self.a_0 -= -2/n * np.sum(error) * self.learning_rate
self.a_1 -= -2/n * np.sum(error * X) * self.learning_rate
#if i%10 == 0:
# print("MSE",str(i)+":", mse)
self.w_list = np.array(self.w_list)
def animate(self, X, y):
fig, ax = plt.subplots()
ax.scatter(X,y)
plot_range = np.array(range(int(min(X))-1,int(max(X))+3))
a_0,a_1 = self.w_list[0,]
y_plot = plot_range*a_1 + a_0
ln, = ax.plot(plot_range, y_plot, color="red", label="Best Fit")
def animator(frame):
a_0, a_1 = self.w_list[frame,]
y_plot = plot_range * a_1 + a_0
ln.set_data(plot_range,y_plot)
print("Launching Animation")
anim = animation.FuncAnimation(fig,func = animator, frames = self.epochs)
plt.show()
if __name__ == "__main__":
main()

Animating multiple Circles in each frames in Python

I am trying to create the animation in this video using Python. But I stuck on the very first step. Till now I've created a Circle and a point rotating around its circumference. My code is given below. Now I want to plot the y values corresponding to x=np.arange(0, I*np.pi, 0.01) along the x-axis (as shown in update() function in the code). For this I have to define another function to plot these x and y and pass that function inside a new animation.FuncAnimation().
Is there any way to plot everything using only the update() function?
Note I have found a code of this animation in here. But it is written in Java!
My Code
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
W = 6.5
H = 2
radius = 1
I = 2
T = 3
N = 2
plt.style.use(['ggplot', 'dark_background'])
def create_circle(x, y, r):
circle = plt.Circle((x, y), radius=r, fill=False, alpha=0.7, color='w')
return circle
def create_animation():
fig = plt.figure()
ax = plt.axes(xlim=(-2, W + 2), ylim=(-H, H))
circle = create_circle(0, 0, radius)
ax.add_patch(circle)
line1, = ax.plot(0, 1, marker='o', markersize=3, color='pink', alpha=0.7)
def update(theta):
x = radius * np.cos(theta)
y = radius * np.sin(theta)
line1.set_data([0, x], [0, y])
return line1,
anim = []
anim.append(animation.FuncAnimation(fig, update,
frames=np.arange(0, I * np.pi, 0.01),
interval=10, repeat=True))
# anim.append(animation.FuncAnimation(fig, update_line, len(x),
# fargs=[x, y, line, line1], interval=10))
plt.grid(False)
plt.gca().set_aspect('equal')
plt.gca().spines['left'].set_visible(False)
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.gca().spines['bottom'].set_visible(False)
plt.gca().set_xticks([])
plt.gca().set_yticks([])
plt.show()
if __name__ == '__main__':
create_animation()
Edit. I've improved the task by defining a global variable pos and changing the update() function in the following manner ...The animation now looks better but still having bugs!
Improved Portion
plot, = ax.plot([], [], color='w', alpha=0.7)
level = np.arange(0, I * np.pi, 0.01)
num = []
frames = []
for key, v in enumerate(level):
num.append(key)
frames.append(v)
def update(theta):
global pos
x = radius * np.cos(theta)
y = radius * np.sin(theta)
wave.append(y)
plot.set_data(np.flip(level[:pos] + T), wave[:pos])
line1.set_data([0, x], [0, y])
pos += 1
return line1, plot,
Edit Till now I've done the following:
def update(theta):
global pos
x, y = 0, 0
for i in range(N):
prev_x = x
prev_y = y
n = 2 * i + 1
rad = radius * (4 / (n * np.pi))
x += rad * np.cos(n * theta)
y += rad * np.sin(n * theta)
wave.append(y)
circle = create_circle(prev_x, prev_y, rad)
ax.add_patch(circle)
plot.set_data(np.flip(level[:pos] + T), wave[:pos])
line2.set_data([x, T], [y, y])
line1.set_data([prev_x, x], [prev_y, y])
pos += 1
return line1, plot, line2,
Output
Please help to correct this animation. Or, is there any efficient way to do this animation?
Edit Well, now the animation is partially working. But there is a little issue: In my code (inside the definition of update()) I have to add circles centered at (prev_x, prev_y) of radius defined as rad for each frame. For this reason I try to use a for loop in the definition of update() but then all the circles remains in the figure (see the output below). But I want one circle in each frame with the centre and radius as mentioned above. Also the same problem is with the plot. I try to use ax.clear() inside the for loop but it didn't work.

Resources