Converting Polar to Cartesian coordinates (0-360 degrees) - python

I am currently using the following code to convert from Cartesian (x, y) coordinates to an angle (0-360 degrees):
def anti_clockwise(x,y):
alpha = degrees(atan2(y,x))
return (alpha + 360) % 360
I am now trying to go back by specifying a distance (eg, 100) and angle (result from above code) to return to some x, y coordinates.
I have been able to get this to work using a simple trigonometry function, but this is limited to 0-90 degrees. Is there any way of getting the x, y coordinates for the full 0-360 degrees range?
Following is what I'm using but realized I didn't convert back to radians!
def get_coord(magnitude, degrees):
angle = radians(degrees)
x = magnitude * cos(angle)
y = magnitude * sin(angle)
return x, y

The following has been tested ans is working:
def get_coord(magnitude, degrees):
angle = radians(degrees)
x = magnitude * cos(angle)
y = magnitude * sin(angle)
return x, y
The problem was not converting to radians during the angle calculation.

Related

Translation and rotation of vector field

I have a vector field of shape (width, height, 2) where the third axis holds the magnitude and angle of each vector.
I want to perform a transformation (rotation and translation) around a given pivot point. I would expect that either numpy or scipy have a function for calculating the transformation matrix, but have so far found nothing.
This is what I've tride so far btw. Just iterating through each coordinate and calculating the new magnitude. The vectors are in polar coordinates.
pivot = (magnitude.shape[1]/2, magnitude.shape[0])
for c in range(magnitude.shape[1]):
for r in range(magnitude.shape[0]):
magnitude[r, c] -= dist + 2*np.linalg.norm((pivot[0]-r, pivot[1]-c))*np.sin(np.abs(angle/2))
From your description I am assuming that
field_x = field[:,:,0] * np.sin(field[:,:,1])
field_y = field[:,:,1] * np.cos(field[:,:,1])
If you want to rotate around the origin with no translation it is just add the rotation angle to field[:,:,1].
Rotation matrix is useful when you want to apply the same transformation to all the points that are in an affine form. (you have a polar representation).
It is not clear to me how exactly you want to do the transformation, so I will give you a function that rotates the point around a given pivot and then translate.
To translate around a given pivot you have to first translate the center of rotation to the origin, apply the rotation and, then you translate the center of rotation back to the original position.
def translate(x, y, tx, ty):
return (x + tx, y + ty)
def rotate(x, y, angle):
c = np.cos(angle)
s = np.sin(angle)
return x * c - y * s, x * s + y * c
def polar2cartesian(field):
c = np.sin(field[:,:,1])
s = np.cos(field[:,:,1])
r = field[:,:,0]
return r*c, r*s
def cartesian2polar(x, y):
result = np.empty(x.shape + (2,))
result[:,:,0] = np.sqrt(x**2 + y**2)
result[:,:,1] = np.arctan2(y, x);
return result;
def transform(field1, pivot, angle, translation):
x,y = polar2cartesian(field1)
px,py = polar2cartesian(pivot)
tx,ty = polar2cartesian(translation)
# move the center of rotation to the origin
x, y = translate(x, y, -px, -py);
x, y = rotate(x, y, angle)
x, y = translate(x, y, tx, ty)
return cartesian2polar(x, y)
To illustrate the use I will use some 1000 x 1 field with constant radius and constant rotation.
r = np.linspace(0, 2*np.pi, 1000)
field1 = np.zeros((1000, 1, 2))
field2 = np.zeros((1000, 1, 2))
field3 = np.zeros((1000, 1, 2))
plt.figure(figsize=(5, 5))
R = 10;
field1[:,0,0] = 1
field1[:,0,1] = 0
field2[:,0,0] = R+1
field2[:,0,1] = r
field3[:,0,0] = 0
field3[:,0,1] = R*r;
field4[:,0,0] = R;
field4[:,0,1] = r
plt.plot(*polar2cartesian(transform(field1, field3, field3[:,:,1], field4)))
field4[:,0,0] = R+2
field3[:,0,1] = -(R+2)*r + np.pi
plt.plot(*polar2cartesian(transform(field1, field3, field3[:,:,1], field4)))
plt.plot(*polar2cartesian(field2))
Interpretation The first plot is a circle of radius R+1 the second is the trajectory of a point in a circle of radius one rolling inside the circle of radius R+1, and the third plot is the trajectory of a point in a circle of radius one rolling outside the circle of radius R+1

How to convert Euler angles to Quaternions and get the same Euler angles back from Quaternions?

I am rotating n 3D shape using Euler angles in the order of XYZ meaning that the object is first rotated along the X axis, then Y and then Z. I want to convert the Euler angle to Quaternion and then get the same Euler angles back from the Quaternion using some [preferably] Python code or just some pseudocode or algorithm. Below, I have some code that converts Euler angle to Quaternion and then converts the Quaternion to get Euler angles. However, this does not give me the same Euler angles.
I think the problem is I don't know how to associate yaw, pitch and roll to X, Y an Z axes. Also, I don't know how to change order of conversions in the code to correctly convert the Euler angles to Quaternion and then convert the Quaternion to Euler angle so that I am able to get the same Euler angle back. Can someone help me with this?
And here's the code I used:
This function converts Euler angles to Quaternions:
def euler_to_quaternion(yaw, pitch, roll):
qx = np.sin(roll/2) * np.cos(pitch/2) * np.cos(yaw/2) - np.cos(roll/2) * np.sin(pitch/2) * np.sin(yaw/2)
qy = np.cos(roll/2) * np.sin(pitch/2) * np.cos(yaw/2) + np.sin(roll/2) * np.cos(pitch/2) * np.sin(yaw/2)
qz = np.cos(roll/2) * np.cos(pitch/2) * np.sin(yaw/2) - np.sin(roll/2) * np.sin(pitch/2) * np.cos(yaw/2)
qw = np.cos(roll/2) * np.cos(pitch/2) * np.cos(yaw/2) + np.sin(roll/2) * np.sin(pitch/2) * np.sin(yaw/2)
return [qx, qy, qz, qw]
And this converts Quaternions to Euler angles:
def quaternion_to_euler(x, y, z, w):
import math
t0 = +2.0 * (w * x + y * z)
t1 = +1.0 - 2.0 * (x * x + y * y)
X = math.degrees(math.atan2(t0, t1))
t2 = +2.0 * (w * y - z * x)
t2 = +1.0 if t2 > +1.0 else t2
t2 = -1.0 if t2 < -1.0 else t2
Y = math.degrees(math.asin(t2))
t3 = +2.0 * (w * z + x * y)
t4 = +1.0 - 2.0 * (y * y + z * z)
Z = math.degrees(math.atan2(t3, t4))
return X, Y, Z
And I use them as follow:
import numpy as np
euler_Original = np.random.random(3) * 360).tolist() # Generate random rotation angles for XYZ within the range [0, 360)
quat = euler_to_quaternion(euler_Original[0], euler_Original[1], euler_Original[2]) # Convert to Quaternion
newEulerRot = quaternion_to_euler(quat[0], quat[1], quat[2], quat[3]) #Convert the Quaternion to Euler angles
print (euler_Original)
print (newEulerRot)
The print statements print different numbers for euler_Original and newEulerRot which I don't want to be the case. For example if euler_original contains numbers like (0.2, 1.12, 2.31) in radians I get this Quaternion --> [0.749, 0.290, -0.449, 0.389] and converting the Quaternion to Euler angles gives me this --> (132.35, 64.17, 11.45) which is pretty wrong. I wonder how I can fix this?
Although I'm interested in getting the above code to work by making changes to it but, I would rather learn how to set up the equations correctly. This way I would know how I can get the correct Quaternions even if the order of rotations (XYZ --> YZX etc) for applying Euler angles is changed.
We can use Rotation from scipy.spatial.transform.
from scipy.spatial.transform import Rotation
# Create a rotation object from Euler angles specifying axes of rotation
rot = Rotation.from_euler('xyz', [90, 45, 30], degrees=True)
# Convert to quaternions and print
rot_quat = rot.as_quat()
print(rot_quat)
The result would be:
[ 0.56098553 0.43045933 -0.09229596 0.70105738]
Then, you can also get it back in Euler angles:
print(rot.as_euler('xyz', degrees=True))
Which results in:
[90. 45. 30.]
As a final check, create a rotation object from the quaternions calculated above and get it as Euler angles:
rot = Rotation.from_quat(rot_quat)
# Convert the rotation to Euler angles given the axes of rotation
print(rot.as_euler('xyz', degrees=True))
Which results in:
[90. 45. 30.]
Major problem:
The input order of euler_to_quaternion is different to the output order of quaternion_to_euler
The former takes angles in the order Z, Y, X (yaw, pitch, roll), and the latter returns X, Y, Z. Fix:
def euler_to_quaternion(roll, pitch, yaw):
# or
euler_to_quaternion(euler_Original[2], euler_Original[1], euler_Original[0])
Minor problem
euler_to_quaternion takes radians whereas quaternion_to_euler returns degrees.
Not really a problem per se, but it's always better to keep angles in radians as most library functions use them.
X = math.atan2(t0, t1)
Y = math.asin(t2)
Z = math.atan2(t3, t4)

extract (ordered) pixel values on circle given midpoint and radius using python

I have an image and want to pull out intensities of pixels that belong to a circle of radius r around a midpoint (Mx,My).
The critical part is building the list of coordinates (x,y) which define the perimeter of the circle for lookup. I adapted a version of the midpoint circle algorithm from rosetta but the points on the circle aren't ordered. Is there a smart way to obtain a list of coordinates that I can use for lookup of the intensity values in an image? Ideally avoiding numpy as I have to implement it without it (but I can use openCV). Any hints that would speed up the value lookup from the image once I have the coordinates would also be appreciated.
This is my code for the unordered list:
def circle(x0, y0, radius):
f = 1 - radius
ddf_x = 1
ddf_y = -2 * radius
x = 0
y = radius
clist=[]
while x < y:
if f >= 0:
y -= 1
ddf_y += 2
f += ddf_y
x += 1
ddf_x += 2
f += ddf_x
clist.append([x0 + x, y0 + y])
clist.append([x0 - x, y0 + y])
clist.append([x0 + x, y0 - y])
clist.append([x0 - x, y0 - y])
clist.append([x0 + y, y0 + x])
clist.append([x0 - y, y0 + x])
clist.append([x0 + y, y0 - x])
clist.append([x0 - y, y0 - x])
return clist
c=circle(10,10,5)
for i in range(len(c)):
plt.plot(c[i][0],c[i][1],'o',color=[i/50.0,1-i/50.0,1])
I am sure there are built-in functions for that in opencv as mentioned in the comments.
To get a simple ordered list of coordinates on a circle just use trigonometry.
x_perimeter = x_center + sin(angle) * radius
y_perimeter = y_center + cos(angle) * radius
Do that for reasonable angle increments and you get the desired number of points in whatever order you like.
It's for sure not the most efficient way but its simple and intuitive. All you need is a function that gives you subpixel intensities which most image processing libraries provide. Otherwise write one your own or round the coordinate values.

How can I generate a random point (x, y) 10 steps apart from y0(a, b) in xy-plane?

I have generated a random point named y0=(a,b) in xy-plane , How can I generate another random point (x,y) 10 steps apart from y0?
note: by 10 steps apart from the firt point I don't mean the Euclidean distance. I mean the number of steps on lattice between the two point (a,b) and (x,y) which is given by |x-a|+|y-b|=10
My attempt(sometimes gives wrong result).
import random
y0=(random.randint(0,50),random.randint(0,50))# here I generated the first point.
y=random.randint(0,50)
# I used the formula |x-a|+|y-b|=10.
x=(10 -abs(y-y0[1]))+y0[0] or x=-(10 -abs(y-y0[1]))+y0[0]
x0=(x,y)
Let's say you have a point (x, y)
create another random point anywhere on the plane: (x1, y2) = (random(), random())
take the vector from your point to the new point: (vx, vy) = (x1-x, y1-y)
get the length l of the vector: l = sqrt(vx * vx + vy * vy)
use l to normalise the vector (so it has a length of 1): (vx, vy) = (vx / l, vy / l)
make the vector 10 steps long: (vx, vy) = (vx * 10, vy * 10)
add it to your original point to get to the desired point: (x1, y2) = (x + vx, y + vy)
voilá :)
from random import random
from math import sqrt
# Deviation
dev = 50
# Required distance between points
l = 10
if __name__ == '__main__':
# First random point
x0, y0 = dev*random(), dev*random()
# Second point
x1 = dev*random()
y1 = y0 + sqrt(l**2 - (x1 - x0)**2)
# Output
print "First point (%s, %s)" % (x0, y0)
print "Second point (%s, %s)" % (x1, y1)
print "Distance: %s" % (sqrt((x1 - x0)**2 + (y1 - y0)**2))
Let's say that your new point (x, y) is on a cercle of radius 10 and center (x0, y0). The random component is the angle.
import math as m
# radius of the circle
r = 10
# create random angle and compute coordinates of the new point
theta = 2*m.pi*random.random()
x = x0 + r*m.cos(theta)
y = y0 + r*m.sin(theta)
# test if the point created is in the domain [[0,50], [0, 50]] (see comments of PM2Ring)
while not ( 0<=x<=50 and 0<=y<=50 ) :
# update theta: add pi/2 until the new point is in the domain (see HumanCatfood's comment)
theta += 0.5*m.pi
x = x0 + r*m.cos(theta)
y = y0 + r*m.sin(theta)
So, you got the formula d=d1+d2=|x-x0|+|y-y0| , for d=10
Let's examine what's going on with this formula:
Let's say we generate a random point P at (0,0)
Let's say we generate y=random.randint(0,50) and let's imagine the value is 50.
What does this mean?
d1=|x-p[0]|=50 and your original formula is d=d1+d2=|x-x0|+|y-y0|, so
that means d2=|y-y0|=10-50 and d2=|y-y0|=-40. Is this possible? Absolutely not! An absolute value |y-y0| will always be positive, that's why your formula won't work for certain random points, you need to make sure (d-d1)>0, otherwise your equation won't have solution.
If you wanted to consider Euclidean distance you just need to generate random points in a circle where your original point will be the center, something like this will do:
import random
import math
def random_point(p, r=10):
theta = 2 * math.pi * random.random()
return (p[0] + r * math.cos(theta), p[1] + r * math.sin(theta))
If you draw a few random points you'll see more and more how the circle shape is created, let's try with N=10, N=50, N=1000:
Now, it seems you need the generated circle to be constrained at certain area region. One possible choice (not the most optimal though) would be generating random points till they meet those constraints, something like this would do:
def random_constrained_point(p, r=10, x_limit=50, y_limit=50):
i = 0
MAX_ITERATIONS = 100
while True:
x0, y0 = random_point(p, r)
if (0 <= x0 <= x_limit and 0 <= y0 <= y_limit):
return (x0, y0)
if i == MAX_ITERATIONS:
return p
i += 1
Once you got this, it's interesting to check what shape is created when you increase more and more the circle radius (10,20,50):
As you can see, your generated random constrained points will form a well_defined subarc.
this code generate a random point xy-plane named y0 then generate another point x0 10 steps apart from y0 in taxi distance .
------- begining of the code--------
import random
y0=(random.randint(0,50),random.randint(0,50))
while True:
y=random.randint(0,50)
x=(10 -abs(y-y0[1]))+y0[0]
if (abs(x-y0[0])+abs(y-y0[1]))==10:
x0=(x,y)
break
abs(x)+abs(y)=10 defines a square, so all you need to do is pick a random value along the perimeter of the square (40 units long), and map that random distance back to your x,y coordinate pair.
Something like (untested):
x = random.randint(-10,9)
y = 10 - abs(x)
if (random.randint(0,1) == 0):
x = -x
y = -y
x = x + y0[0]
y = y + y0[1]
x0=(x,y)
Clipping the x range that way ensures that all points are picked uniformly. Otherwise you can end up with (-10,0) and (10,0) having twice the chance of being picked compared to any other coordinate.

Python Work out area of a polygon on a spherical surface

I have a series of points, of right ascension and declination values.
These points correspond to the vertices of a polygon on the surface of a sphere.
What would be the best way to calculate the area enclosed by these points? I would assume that converting the points with an equal-area projection, and then carrying out typical polygonal area calculating on a flat surface would be an appropriate solution.
note: I cannot use custom python libraries. eg pyproj or shapely
Example code (works for latitude longitude, what modifications would be required to enure this works with sky coordinates?)
def reproject(latitude, longitude):
"""Returns the x & y coordinates in metres using a sinusoidal projection"""
from math import pi, cos, radians
earth_radius = 6371009
lat_dist = pi * earth_radius / 180.0
y = [lat * lat_dist for lat in latitude]
x = [long * lat_dist * cos(radians(lat))
for lat, long in zip(latitude, longitude)]
return x, y
def area_of_polygon(x, y):
"""Calculates the area of an arbitrary polygon given its vertices"""
area = 0.0
for i in xrange(-1, len(x)-1):
area += x[i] * (y[i+1] - y[i-1])
return abs(area) / 2.0
dec = [-15.,89.,89.,-15.,-15.]
ra = [105.,105.,285.,285.,105.]
x,y = reproject(dec, ra)
print area_of_polygon(x,y)
One of the ways is to perform a line integral based on Green's Theorem. See below an implementation, and this question for more details.
def polygon_area(lats, lons, algorithm = 0, radius = 6378137):
"""
Computes area of spherical polygon, assuming spherical Earth.
Returns result in ratio of the sphere's area if the radius is specified.
Otherwise, in the units of provided radius.
lats and lons are in degrees.
"""
from numpy import arctan2, cos, sin, sqrt, pi, power, append, diff, deg2rad
lats = np.deg2rad(lats)
lons = np.deg2rad(lons)
# Line integral based on Green's Theorem, assumes spherical Earth
#close polygon
if lats[0]!=lats[-1]:
lats = append(lats, lats[0])
lons = append(lons, lons[0])
#colatitudes relative to (0,0)
a = sin(lats/2)**2 + cos(lats)* sin(lons/2)**2
colat = 2*arctan2( sqrt(a), sqrt(1-a) )
#azimuths relative to (0,0)
az = arctan2(cos(lats) * sin(lons), sin(lats)) % (2*pi)
# Calculate diffs
# daz = diff(az) % (2*pi)
daz = diff(az)
daz = (daz + pi) % (2 * pi) - pi
deltas=diff(colat)/2
colat=colat[0:-1]+deltas
# Perform integral
integrands = (1-cos(colat)) * daz
# Integrate
area = abs(sum(integrands))/(4*pi)
area = min(area,1-area)
if radius is not None: #return in units of radius
return area * 4*pi*radius**2
else: #return in ratio of sphere total area
return area
Please find a somewhat more explicit version (and with many more references and TODOs...) here.
Looks like I can treat ra and dec like lat and long, work out the area on the Earth's surface in m^2, and use this value to convert into an area in sq degrees.
Please let me know if the solution I propose below is flawed:
def reproject(latitude, longitude):
"""Returns the x & y coordinates in metres using a sinusoidal projection"""
from math import pi, cos, radians
earth_radius = 6371009
lat_dist = pi * earth_radius / 180.0
y = [lat * lat_dist for lat in latitude]
x = [long * lat_dist * cos(radians(lat))
for lat, long in zip(latitude, longitude)]
return x, y
def area_of_polygon(x, y):
"""Calculates the area of an arbitrary polygon given its vertices"""
area = 0.0
for i in xrange(-1, len(x)-1):
area += x[i] * (y[i+1] - y[i-1])
return ((abs(area) / 2.0)/5.10100E14) * 41253
dec = [-15.,89.,89.,-15.,-15.]
ra = [105.,105.,285.,285.,105.]
x,y = reproject(dec, ra)
print area_of_polygon(x,y)

Categories

Resources