Almost everything I do has coordinates in it: projected, geographic, arbitrary – and for as long as I’ve been coding in Python I’ve been slowly storing my snippets of code and building on them as I reuse them. One of the tasks I usually have to do when starting a new Python project is copy over my math code. Once I’ve built on it, I then have to decide whether or not I’m going to retrospectively fit the new code into my old projects – you can see how this would just be a headache to consider let alone implement!

This week I have started the process of releasing my code as Open Source under an MIT license and wrapping it all up in a clean module to be uploaded to the Python Package Index library so anyone can use it.
History Lesson Please 🙂

Sometime ago – when I was young (18), dumb (very), and dopey at math (and life in general) – I was training to be an Artillery Surveyor, in the corps of Royal Australian Artillery, in the Australian Regular Army. We had to load various programs on our Hewlett Packard HP41 CV Calculators using a magnetic card strip reader. Now, to be fair – these calculators were already ancient when I joined the Army! I didn’t know what was on the magnetic strips or how they worked, only that they made my life as an Artillery Surveyor much easier. Later in life as I slowly improved my maths, then programming, then kicked ass at both – I would use these mystery programs as a bench mark for my own learning.
As my career in the spatial world progressed I realised these programs are invaluable, sure they’re not sexy, however when working in CAD or GIS, or basic setout computations – these snippets of code are literally everywhere – problem is, you usually have to have CAD, or GIS, or something that will do your basic setout computations – to determine a bearing from one point to another, or to create coordinates using starting coordinates and a bearing and distance – it ain’t easy – So every time I learnt a new language; HTML, Java, JavaScript, Python, Ruby, heck even Excel – I would create something that required these programs so I could re-code them to perfection – without ever seeing the original code. I’d have to imagine the maths, the loops, the variables, the constants – and not just how they worked but what purpose they could serve beyond Artillery Surveying. I’ve created games, a game engine, programmes for work, programmes not for work – heaps of cool stuff – all because of my love for this calculator and it’s magnetic cards – which I admit – I didn’t immediately have a thing for!
That’s great – code please!
The code I have currently committed is quite basic, it doesn’t take into account scale factors or curvature of the earth (suits the flat-earthers no doubt) but can be used for 3d distances, differences in elevations etc. Most of the basic functions that could be performed on an Artillery Surveyor HP41Cv programmed calculator are existent but not yet complete – ie CBD, BDC, DMS, TRAV and so on.
I won’t go into the full breakdown of the module because I’ve already released that on GitHub (wait a few weeks before it’s completed) here’s some code snippets.
def get_deltas_from_coordinates(self, from_coordinates, to_coordinates): # difference between both sets of coordinates. delta_e = from_coordinates["e"] - to_coordinates["e"] delta_n = from_coordinates["n"] - to_coordinates["n"] delta_el = from_coordinates["el"] - to_coordinates["el"] return delta_e, delta_n, delta_el
def get_deltas_from_bearing_distance(self, bearing, distance_2d): # Creates deltas from bearing and distance delta_e = distance_2d * (math.sin(math.radians(bearing))) delta_n = distance_2d * (math.cos(math.radians(bearing))) delta_el = 0.0 # TODO requires vertical bearing and distance for delta_el. return delta_e, delta_n, delta_el
def get_coordinates_from_deltas(self, from_coordinates, deltas): # Return new coords from deltas. return [from_coordinates[0] + deltas[0], from_coordinates[1] + deltas[1], from_coordinates[2] + deltas[2]]
def get_bearing_from_deltas(self, deltas): # A great big switch statement to determine the correct direction. delta_e = deltas[0] delta_n = deltas[1] delta_el = deltas[2] bearing = 0.0 if delta_e == 0 and delta_n > 0: bearing = 0.0 elif delta_e == 0 and delta_n < 0: bearing = 180.0 elif delta_e > 0 and delta_n == 0: bearing = 90.0 elif delta_e < 0 and delta_n == 0: bearing = 270.0 # Check for 45 degree variations. elif abs(delta_e) == abs(delta_n): if delta_e > 0 and delta_n > 0: bearing = 45.0 elif delta_e > 0 > delta_n: bearing = 135.0 elif delta_e < 0 and delta_n < 0: bearing = 225.0 elif delta_n > 0 > delta_e: bearing = 315.0 # Compute it out. elif delta_e > 0: bearing = math.degrees(math.atan(delta_e / delta_n)) elif delta_e < 0 and delta_n < 0: bearing = math.degrees(math.atan(delta_e / delta_n)) + 180 elif delta_n > 0 > delta_e: bearing = math.degrees(math.atan(delta_e / delta_n)) + 360 # The final piece. if bearing < 0: bearing += 180 return bearing
Practicality?
The big question, who would use this and where? A couple of basic examples of how this code can be used are;
- Web based calculations – if you’ve a Django site this will fit right in.
- Games – yep, already built a game engine that allows for autonomous robots to run around and shoot each other using this very code.
- General calculations – my previously detailed Bore Calculator used this code to calculate 3D points before spitting out the setout file and the 3D model.
Literally any calculation suitable enough to be done on a flat plane can utilise this library of mathematical goodness! There’s a bit of work to be done before it’s ready to be used as a plugin, expect plenty of updates on this subject though as I deploy and use this pretty handy library.