# Class representing both cartesian and spherical coordinates

I have had little experience with OO programming, being a typical engineer I tend to hack together functional code. However, I'm trying to change that and I see value in implementing OO when modelling the physical world. I've written my first ever class for a position vector. I want an instance of the class to be able to store both the cartesian [x, y, z] and polar [z, theta, phi] coordinates of a position. Updating one attribute automatically updates the others. This class will be later be reused in other classes via composition.

I want an instance of my class to do the following:

>> p = PositionVector([15,5,5])
>> p.cartesian
[15,5,5]
>> p.polar
[16.583123951777, 0.0, 1.2645189576252271]
>> p.x = 3
>> p.cartesian
[3,5,5]
>> p.polar
[7.681145747868608, 0.7853981633974483, 0.8619682853367363]
>> p.theta = 0.5
>> p.cartesian
[5.117141702137862, 2.7955072527614058, 5.0]
>> p.polar
[7.681145747868608, 0.5, 0.8619682853367363]


The class I've written so far is below and while it works as expected, my limited understanding of Python classes and the property function tells me there's a better way to do this. I don't know if the methods for x y z r theta and phi need to be fully written since they are simply elements within each list/array but can't figure out how to get around this. I also don't know if this is the safest and most pythonic way of structuring a class object:

class PositionVector(object):
def __init__(self, vector, system='cartesian'):
if system == 'cartesian':
self.cartesian = vector
self.polar = self.position_vector_polar(self.cartesian)
if system == 'polar':
self.polar = vector
self.cartesian = self.position_vector_cartesian(self.polar)

@property
def cartesian(self):
return self._cartesian
@cartesian.setter
def cartesian(self, value):
self._cartesian = value
self._polar = self.position_vector_polar(self._cartesian)
self._x, self._y, self._z = self._cartesian
self._r, self._theta, self._phi = self._polar

@property
def polar(self):
return self._polar
@polar.setter
def polar(self, value):
self._polar = value
self._cartesian  = self.position_vector_cartesian(self._polar)
self._x, self._y, self._z = self.cartesian
self._r, self._theta, self._phi = self.polar

@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
self._cartesian[0]  = self._x
self._polar = self.position_vector_polar(self._cartesian)

# repeat above for y and z variables

@property
def r(self):
return self._r
@r.setter
def r(self, value):
self._r = value
self._polar[0]  = self._r
self._cartesian = self.position_vector_cartesian(self._polar)

# repeat above for thets and phi variables

def position_vector_cartesian(self, polar):
r, theta, phi = polar
x = r * math.cos(theta) * math.sin(phi)
y = r * math.sin(theta) * math.sin(phi)
z = r * math.cos(phi)
return [x, y, z]

def position_vector_polar(self, cartesian):
x, y, z = cartesian
r = math.sqrt(x ** 2 + y ** 2 + z ** 2)
theta = math.atan(y/x)
phi = math.acos(z/r)
return [r, theta, phi]


Note that the lists will be be replaced with NumPy arrays so I can perform algebra on the vectors, but for now I just want to get the class structure correct.

As far as I understand the code, a PositionVector object holds both cartesian and polar coordinates. This is surely redundant: multiple representations of the same position hold no more information than a single one (note that there are many more coordinate systems, and you cannot possibly account for all of them). I recommend to keep the position in a preferred coordinate system, and provide methods to convert it to and from other representations.

Edit: addressing concerns in the comment:

What about if I want to set a new radius, r? What would be the most appropriate way to update [x, y, z]?

This is a very important question I didn't elaborate on in the answer. In the algebraic world we want to reason in terms of objects, as opposed to coordinates. Coordinates are secondary - they are just instrumental for calculations.

In other words, you do not want to set new radius. You want to apply a transformation (translate, rotate, scale, etc).

I wrote the class with the ability to apply vector algebra in mind, if I follow your suggestion, would vector algebra be straight forward, regardless of which ordinates I am working with?

I seriously hope so.

• you are absolutely right. Though I only need cartesian and polar/spherical coordinates at this stage. So, I should just store a single cartesian array [x,y,z] and then if I want x, y, z, r, theta, phi or [r, theta, phi] I should just use methods to extract/convert them from [x, y, z]? What about if I want to set a new radius, r? What would be the most appropriate way to update [x, y, z]? I wrote the class with the ability to apply vector algebra in mind, if I follow your suggestion, would vector algebra be straight forward, regardless of which ordinates I am working with? – user162390 Dec 7 '14 at 0:11
• @user162390 Please see update – vnp Dec 7 '14 at 0:37
• Thanks @vnp, I think I understand. I'll try and build or at least redesign it to later allow for methods for handling transformations. I guess I was holding off on this step until I had the basic class working and had a better idea of how to implement it, however, I should really have transformations in mind from the beginning. I'll try and post amendments tonight or tomorrow. – user162390 Dec 7 '14 at 3:22