Skip to content

Instantly share code, notes, and snippets.

@jmeyers314
Created April 8, 2018 17:05
Show Gist options
  • Save jmeyers314/eaf91a050081a0f699c1d91115933e00 to your computer and use it in GitHub Desktop.
Save jmeyers314/eaf91a050081a0f699c1d91115933e00 to your computer and use it in GitHub Desktop.
Great circles
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2018-04-08T17:04:37.142633Z",
"start_time": "2018-04-08T17:04:37.119296Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Using matplotlib backend: MacOSX\n"
]
}
],
"source": [
"import matplotlib as mpl\n",
"from mpl_toolkits.mplot3d import Axes3D, proj3d\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib auto"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"ExecuteTime": {
"end_time": "2018-04-08T17:04:38.420881Z",
"start_time": "2018-04-08T17:04:38.126231Z"
}
},
"outputs": [],
"source": [
"def circle(p0, p1, ax, **kwargs):\n",
" \"\"\"Draw circle passing through p0 and p1\"\"\"\n",
" v = np.cross(p0, p1)\n",
" u = np.cross(p0, v)\n",
" p0 = np.array(p0)/np.sqrt(np.dot(p0, p0))\n",
" u = u/np.sqrt(np.dot(u, u))\n",
" theta = np.linspace(0, 2*np.pi, 100)\n",
" r = np.outer(p0, np.cos(theta)) + np.outer(u, np.sin(theta))\n",
" ax.plot(r[0], r[1], r[2], **kwargs)\n",
"\n",
"def circle_pole(p0, ax, **kwargs):\n",
" \"\"\"Draw great circle with pole at p0\"\"\"\n",
" rand = np.random.uniform(size=3)\n",
" rand /= np.sqrt(np.dot(rand, rand))\n",
" u = np.cross(p0, rand)\n",
" v = np.cross(p0, u)\n",
" u /= np.sqrt(np.dot(u, u))\n",
" v /= np.sqrt(np.dot(v, v))\n",
" theta = np.linspace(0, 2*np.pi, 100)\n",
" r = np.outer(u, np.cos(theta)) + np.outer(v, np.sin(theta))\n",
" ax.plot(r[0], r[1], r[2], **kwargs)\n",
"\n",
"def surface_arc(p0, radius, ax, start=0, stop=2*np.pi, label=None, **kwargs):\n",
" \"\"\"Draw an arc around `p0` with `radius` from angle `start` to `stop`, where\n",
" start and stop are measured CCW from +Alt when viewing from outside sphere.\"\"\"\n",
" # Almost the same as circle_pole.\n",
" # First find a vector perp to vertical and to p0\n",
" u = np.cross(p0, [0,0,1])\n",
" u /= np.sqrt(np.dot(u, u))\n",
" # Next find a vector perp to u and p0\n",
" v = np.cross(u, p0)\n",
" v /= np.sqrt(np.dot(v, v))\n",
" \n",
" theta = np.linspace(start, stop, 100)\n",
" # Tricky bit is center of circle is not (0,0,0), but point, and radius != 1\n",
" r = np.outer(radius*v, np.cos(theta)) + np.outer(radius*u, np.sin(theta))\n",
" r += np.array(p0)[:,None]\n",
" ax.plot(r[0], r[1], r[2], **kwargs)\n",
"\n",
" # return label at midway point along arc\n",
" if label:\n",
" theta = 0.5*(start+stop)\n",
" r = radius*v*np.cos(theta) + radius*u*np.sin(theta)\n",
" r += np.array(p0)\n",
" return r, ax.annotate(label, xy=(0, 0))\n",
"\n",
"def solve_triangle(alt, az, lat):\n",
" # correct for lat>0 and HA<0\n",
" c90malt = np.cos(np.pi/2-alt)\n",
" c90mlat = np.cos(np.pi/2-lat)\n",
" s90malt = np.sin(np.pi/2-alt)\n",
" s90mlat = np.sin(np.pi/2-lat)\n",
" caz = np.cos(az)\n",
" saz = np.sin(az)\n",
" c90mdec = c90malt*c90mlat + s90malt*s90mlat*caz\n",
" dec = np.pi/2 - np.arccos(c90mdec) # This arccos is unambiguous\n",
"\n",
" # arcsin, arccos would be ambiguous here, so use arctan2\n",
" s90mdec = np.sin(np.pi/2-dec)\n",
" smq = saz/s90mdec*s90mlat\n",
" cmq = (c90mlat-c90malt*c90mdec)/s90malt*s90mdec\n",
" q = -np.arctan2(smq, cmq)\n",
"\n",
" # arcsin here is ambiguous, use arctan2\n",
" smha = saz/s90mdec*s90malt\n",
" cmha = (c90malt-c90mlat*c90mdec)/(s90mlat*s90mdec)\n",
" ha = -np.arctan2(smha, cmha)\n",
"\n",
" print(\"alt = \", alt*180/np.pi)\n",
" print(\"az = \", az*180/np.pi)\n",
" print(\"lat = \", lat*180/np.pi)\n",
" print(\"ha = \", ha*180/np.pi)\n",
" print(\"dec = \", dec*180/np.pi)\n",
" print(\"q = \", q*180/np.pi)\n",
"\n",
" return dict(ha=ha, dec=dec, q=q)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"ExecuteTime": {
"end_time": "2018-04-08T17:04:39.318415Z",
"start_time": "2018-04-08T17:04:38.797974Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"alt = 65.6\n",
"az = 231.36\n",
"lat = 19.800000000000004\n",
"ha = 18.8668475078\n",
"dec = 3.77165715688\n",
"q = 47.5581097468\n"
]
}
],
"source": [
"# constants\n",
"zenith = [0,0,1]\n",
"nadir = [0,0,-1]\n",
"horizon_north = [0,1,0]\n",
"horizon_east = [1,0,0]\n",
"\n",
"# inputs and derived\n",
"lat = 19.8*np.pi/180\n",
"NCP = [0,np.cos(lat),np.sin(lat)]\n",
"SCP = [0,-np.cos(lat),-np.sin(lat)]\n",
"alt = 65.6*np.pi/180\n",
"az = 231.36*np.pi/180\n",
"point = [np.cos(alt)*np.sin(az),\n",
" np.cos(alt)*np.cos(az),\n",
" np.sin(alt)]\n",
"triangle = solve_triangle(alt, az, lat)\n",
"q = triangle['q']\n",
"ha = triangle['ha']\n",
"dec = triangle['dec']\n",
"\n",
"fig = plt.figure(figsize=(8, 7))\n",
"ax = fig.gca(projection='3d')\n",
"ax.view_init(10,0)\n",
"\n",
"# plot unit sphere\n",
"phi, theta = np.mgrid[0.0:np.pi:20j, 0.0:2.0*np.pi:20j]\n",
"r = 1\n",
"x = r*np.sin(phi)*np.cos(theta)\n",
"y = r*np.sin(phi)*np.sin(theta)\n",
"z = r*np.cos(phi)\n",
"ax.plot_surface(x, y, z,\n",
" rstride=1, cstride=1, \n",
" color='c', alpha=0.1, linewidth=0)\n",
"\n",
"# Plot hour angle=0 circle\n",
"circle(zenith, horizon_north, ax, c='k')\n",
"ax.set_xlabel(\"x\")\n",
"ax.set_ylabel(\"y\")\n",
"ax.set_zlabel(\"z\")\n",
"\n",
"# Plot Zenith/nadir point\n",
"ax.scatter(*zenith, c='r')\n",
"ax.scatter(*nadir, c='r')\n",
"\n",
"# Plot NCP/SCP point\n",
"ax.scatter(*NCP, c='b')\n",
"ax.scatter(*SCP, c='b')\n",
"\n",
"# Plot horizon\n",
"circle(horizon_north, horizon_east, ax, c='r')\n",
"\n",
"# Plot *\n",
"ax.scatter(*point, c='k')\n",
"\n",
"# Plot meridian. Goes through zenith and point\n",
"circle(zenith, point, ax, c='r')\n",
"\n",
"# Plot hour circle through NCP and point\n",
"circle(NCP, point, ax, c='b')\n",
"\n",
"# Plot equator.\n",
"circle_pole(NCP, ax, c='b')\n",
"\n",
"# Label the hour angle\n",
"if ha > 0:\n",
" halabel = surface_arc(NCP, 0.15, ax, start=0, stop=-ha, label=\"HA\")\n",
"else:\n",
" halabel = surface_arc(NCP, 0.15, ax, start=0, stop=-ha, label=\"-HA\")\n",
"\n",
"# Label the parallactic angle\n",
"if q > 0:\n",
" qlabel = surface_arc(point, 0.15, ax, start=0, stop=q, label=\"q\")\n",
"else:\n",
" qlabel = surface_arc(point, 0.15, ax, start=0, stop=q, label=\"-q\")\n",
"\n",
"# Add labels\n",
"labels = []\n",
"labels.append((NCP, ax.annotate(\"NCP\", xy=(0,0))))\n",
"labels.append((SCP, ax.annotate(\"SCP\", xy=(0,0))))\n",
"labels.append((zenith, ax.annotate(\"zenith\", xy=(0,0))))\n",
"labels.append((nadir, ax.annotate(\"nadir\", xy=(0,0))))\n",
"labels.append((point, ax.annotate(\"*\", xy=(0,0))))\n",
"labels.append(halabel)\n",
"labels.append(qlabel)\n",
"\n",
"def update_position(labels, fig, ax):\n",
" for p, label in labels:\n",
" x, y, _ = proj3d.proj_transform(p[0], p[1], p[2], ax.get_proj())\n",
" label.set_x(x)\n",
" label.set_y(y)\n",
" fig.canvas.draw()\n",
"update_position(labels, fig, ax)\n",
"fig.canvas.mpl_connect('motion_notify_event', lambda event: update_position(labels, fig, ax))\n",
"pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.2"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment