Rotate points around normal to sphere surface - python

I would like to rotate an array about the axis that is normal to a given location on the surface of a sphere, but unsure how to go about it.
As an example, the below code creates a series of points (a dipole field line), shifts it away from the centre of a sphere and rotates to some angle in the xy plane.
At this location, I would like to rotate the field line to any angle around the axis that is normal to the sphere surface.
import numpy as np
import matplotlib.pyplot as plt
field=np.linspace(-np.pi/2,np.pi/2,100)
circle=np.linspace(0,2*np.pi,100)
theta=60*np.pi/180
r_shift=0.9
r=1.5*np.sin(field+np.pi/2)**2
x0=r*np.cos(field)+r_shift
y0=r*np.sin(field)
# rotate around y-axis
x=x0*np.cos(theta)
y=y0
fig,ax=plt.subplots()
ax.set_box_aspect(1)
ax.plot(np.cos(circle),np.sin(circle),color='k')
ax.plot(x,y)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
plt.show()
I think it could be done with Euler angles or Rodriguez' formula, but I'm not familiar enough to implement this.

Related

Matplotlib: plot arbitrary vectors on polar axes

There are previous questions about quiver plots on polar axes in matplotlib, however they concern vector fields. I'm interested in drawing arbitrary vectors on polar axes. If there is a genuine duplicate, please link it.
I'm writing some software which concerns a circular world. I'm plotting an agent's trajectory from the centre of a circular arena to the edge. This is visualised by drawing a vector from the centre of the circle to the edge. I'm trying to use matplotlib's quiver plot to plot vectors on a set of polar axes. Here's a minimum working example:
import matplotlib.pyplot as plt
import numpy as np
if __name__ == '__main__':
fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')
# Plot origin (agent's start point)
ax.plot(0, 0, color='black', marker='o', markersize=5)
# Plot agent's path
ax.quiver((0, 0), (0, 1), color='black')
# Example of where (0, 1) should be
ax.plot(0, 1, color='black', marker='o', markersize=5)
# Plot configuration
ax.set_rticks([])
ax.set_rmin(0)
ax.set_rmax(1)
ax.set_thetalim(-np.pi, np.pi)
ax.set_xticks(np.linspace(np.pi, -np.pi, 4, endpoint=False))
ax.grid(False)
ax.set_theta_direction(-1)
ax.set_theta_zero_location("N")
plt.show()
If you run the code, you get this plot
The plot shows the origin plotted correctly, an example point at (0, 1) to show where the vector should end, then the vector itself which appears far too short (though the direction is correct). From the docs, I understand that quiver takes cartesian coordinates (x,y) denoting the start point of the vector and (u,v) denoting the vector's direction. In my previous experience with quiver (u,v) essentially denotes where the vector's tip will be, so in this case we'd expect the vector to be drawn from (0,0) to (0,1) which isn't the case and I don't know why.
In short, I want to be able to draw arbitrary vectors on a set of polar axes and quiver isn't working as I expected. Three questions:
Is my code actually sensible given my goal? I want to draw a unit vector from the origin to the edge of the polar plot.
Am I completely misunderstanding how to use quiver?
How can I draw arbitrary vectors on polar axes in matplotlib? I know about arrow and I'm going to give that a try though initial attempts were unsuccessful.
Short of using a standard plot and just defining my own polar system within it I'm completely stumped.
You did not specify u and v in ax.quiver(x,y,u,v). To make sure the arrow is 1 unit long you will need to set the scale und units as well.
ax.quiver(0,0,0,1, color='black', angles="xy", scale_units='xy', scale=1.)

Galactic and equatorial projected grids with matplotlib

I'm attempting to draw a scatter plot of astronomic sources projected on a full sky map, using two coordinate grids: galactic and equatorial. Generating a single grid plot (using one of the pre-defined projections) is easy:
import matplotlib.pyplot as plt
import numpy as np
# Random galactic coordinates
l_rad = np.random.uniform(-np.pi, np.pi, 100)
b_rad = np.random.uniform(-np.pi / 2., np.pi / 2., 100)
plt.figure()
plt.subplot(111, projection="aitoff")
plt.grid(True)
plt.plot(ra_rad, dec_rad, 'o', markersize=2, alpha=0.3)
plt.show()
What I need though is a map with two grids superimposed: one for galactic coordinates (which is already there) and another one for equatorial. That is, a second grid like the one in the image below (notice the hour convention instead of decimal degrees for the right ascension) should be superimposed onto the first grid:
I think the Kapteyn package can do something similar (image below) but I'd prefer to avoid it if possible (I find it rather complicated to use).
I haven't used it, but I noticed Astropy.WCSAxes package has an example of drawing a map with overlaid galactic and equatorial grids: https://docs.astropy.org/en/stable/visualization/wcsaxes/overlaying_coordinate_systems.html

Fine-tuning of pcolor() polar plot

I have a 64x360 Matrix of values belonging to radial and azimuthal coordinates. I want to visualize them in two plots: a cartesian and a polar plot.
I visualized the heatmap in cartesian coordinates using imshow():
import numpy as np
import matplotlib.pyplot as plt
P=np.loadtxt('Pdata.csv')
print np.shape(P)
plt.imshow(P)
plt.xlabel('radius')
plt.ylabel('theta')
plt.show()
This gives me the desired plot:
The same plot in polar coordinates was also pretty straigh forward using pcolor():
r=np.arange(0,np.shape(P)[1],1)
t=np.arange(0,np.shape(P)[0],1)
R,T = np.meshgrid(r,t)
fig = plt.figure()
ax = fig.add_subplot(111, polar = True)
ax.pcolor(T,R,P)
plt.show()
However, I am not really satisfied with the result:
The resolution of the plot seems to be pretty limited so that it's not possible to distinguish between angles with higher intensity and lower intensity, as it is in the cartesian plot. The whole solid angle seems to be divided into six or seven "cake wedges" only. Is there an easy and pythonic way to enhance the angular resolution?
Ok, I found out something. It works with:
t = np.radians(np.linspace(0, np.shape(P)[0],np.shape(P)[0]))
r = np.arange(0, np.shape(P)[1], 1)
Just as seen here: Polar contour plot in matplotlib - best (modern) way to do it?

Plotting the surface of a sphere onto a circle

I am trying to create an animation of a sphere rotating but only 2π of the surface of this object can be seen, so I require the ability to plot regions (separation into longitude and latitude) onto a circle and to have them defined by parameters such that they can be altered for rotation using two angles (theta and phi from spherical polar coordinates).
I have tried a few things, but the only time a made anything worthwhile was using tricontour:
import matplotlib.pyplot as plt
import matplotlib.tri as tri
import numpy as np
from math import *
r=1.0
N=360
circlexy = np.asarray([[r*cos(radians(i)), r*sin(radians(i))]
for i in np.arange(0,N,0.1)])
circlez = [r*(cos(radians(j)))*sin(radians(j))
for j in np.arange(0,N,0.1)]
x1 = circlexy[:,0]
y1 = circlexy[:,1]
z1=circlez
plt.figure()
plt.gca().set_aspect('equal')
plt.tricontourf(x1, y1, z1, 3)
plt.show()
However, this plot is not very smooth and even if it were to be it is not very helpful as z1 is required to be the same length as x1 and y1 and so I cannot see a way to define two angles (this plot only rotates in one plane).
I could well have made errors above as my experience in coding is not particularly broad. What would be best to use to make such a plot? Thank you for your time.

plot circle on unequal axes with pyplot

I would like to plot a circle on an auto-scaled pyplot-generated graphic. When I run
ax.get_aspect()
hoping for a value with which I could manipulate the axes of a ellipse, pyplot returns:
auto
which is less than useful. What methods would you suggest for plotting a circle on a pyplot plot with unequal axes?
This question is more than one year old, but I too just had this question. I needed to add circles to a matplotlib plot and I wanted to be able to specify the circle's location in the plot using data coordinates, and I didn't want the circle radius to change with panning/zooming (or worse the circle turning into an ellipse).
The best and most simple solution that I've found is simply plot a curve with a single point and include a circle marker:
ax.plot(center_x,center_y,'bo',fillstyle='none',markersize=5)
which gives a nice, fixed-size blue circle with no fill!
It really does depend what you want it for.
The problem with defining a circle in data coordinates when aspect ratio is auto, is that you will be able to resize the figure (or its window), and the data scales will stretch nicely. Unfortunately, this would also mean that your circle is no longer a circle, but an ellipse.
There are several ways of addressing this. Firstly, and most simply, you could fix your aspect ratio and then put a circle on the plot in data coordinates:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = plt.axes()
ax.set_aspect(1)
theta = np.linspace(-np.pi, np.pi, 200)
plt.plot(np.sin(theta), np.cos(theta))
plt.show()
With this, you will be able to zoom and pan around as per usual, but the shape will always be a circle.
If you just want to put a circle on a figure, independent of the data coordinates, such that panning and zooming of an axes did not effect the position and zoom on the circle, then you could do something like:
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = plt.axes()
patch = mpatches.Circle((325, 245), 180, alpha=0.5, transform=None)
fig.artists.append(patch)
plt.show()
This is fairly advanced mpl, but even so, I think it is fairly readable.
HTH,
Building on #user3208430, if you want the circle to always appear at the same place in the axes (regardless of data ranges), you can position it using axes coordinates via transform:
ax.plot(.94, .94, 'ro', fillstyle='full', markersize=5, transform=ax.transAxes)
Where x and y are between [0 and 1]. This example places the marker in the upper right-hand corner of the axes.

Categories

Resources