AstroPy: how to transform a SkyCoord object in the GCRS frame to the topocentric frame? - coordinates

In AstroPy the get_sun() method returns the equatorial coordinates of the Sun as a SkyCoord object in the GCRS coordinate frame. How can I transform these coordinates into the topocentric frame to get topocentric equatorial coordinates of the Sun?
I've looked at the transform_to() method, but it doesnt seem to offer the topocentric frame. Somewhere in the docs I've seen this statement: "The frame classes that are built in to astropy are ICRS, FK5, FK4, FK4NoETerms, Galactic, and AltAz." Does this mean, that for the transformation to the topocentric frame I would have to define a new class as a sublass of the BaseCoordinateFrame class?
EDIT: The mathematical background can be seen e.g. here (Dieudonné's answer): https://astronomy.stackexchange.com/questions/19170/transformation-from-geocentric-coordinates-into-equatorial-coordinates
I would like to know if this is already implemented in AstroPy as a ready-to-use function?
EDIT 2: It looks to me that I should get the desired result, if I still keep the GCRS frame, but change the obsgeolocand obsgeovel parameters from the default values (0,0,0) to those of my actual position. But how do I do that?
EDIT 3: I have managed to change at least the obsgeoloc but it makes no difference, I get the same equatorial coordinates, unless I do something wrong or simply misunderstand the whole thing, or maybe the difference is not obvious, because the coordinate values are given with the precision to 8 decimals? (And I still dont know which values to take for obsgeovel, but even if I use some arbitrary values, I get again the same coordinates.)
<SkyCoord (GCRS: obstime=2018-12-01T00:00:00.000, obsgeoloc=(0., 0., 0.) m, obsgeovel=(0., 0., 0.) m / s): (ra, dec, distance) in (deg, deg, AU)
(246.72726607, -21.71360187, 0.98613747)>
<GCRS Coordinate (obstime=2018-12-01T00:00:00.000, obsgeoloc=(4659791.10339651, 1289069.45080985, 4147935.86909442) m, obsgeovel=(0., 0., 0.) m / s): (ra, dec) in deg
(246.72726607, -21.71360187)>

If i've understood your question correctly then what you want are the equatorial coordinates of the Sun (i.e axes aligned with ICRS axes), but for an observer at a given origin (topocentric).
In this case, instead of using get_sun, which always returns coordinates with a geocentric origin, you can use astropy.coordinates.get_body which allows one to specify the location of the observer using an EarthLocation object.
>>> from astropy.coordinates import EarthLocation, get_body
>>> from astropy.time import Time
>>> location = EarthLocation.of_site('lapalma')
>>> t = Time.now()
>>> sun = get_body('sun', t, location)
<SkyCoord (GCRS: obstime=2018-12-17 11:12:59.165352, obsgeoloc=(-3111752.92801233, -4649601.13172555, 3057088.20910137) m, obsgeovel=(339.04768621, -227.31606521, -0.62038673) m / s): (ra, dec, distance) in (deg, deg, AU)
(264.76635166, -23.35141062, 0.98401329)>
>>> sun = get_body('sun', t)
<SkyCoord (GCRS: obstime=2018-12-17 11:12:59.165352, obsgeoloc=(0., 0., 0.) m, obsgeovel=(0., 0., 0.) m / s): (ra, dec, distance) in (deg, deg, AU)
(264.76514306, -23.34957481, 0.98403534)>

According to my reading of this wikipedia article Topocentric is another name for the AltAz frame already implemented in Astropy.
So you can do what you want by setting up an Earth Location object and transforming to AltAz:
>>> from astropy.coordinates import EarthLocation, SkyCoord, get_sun, AltAz
>>> from astropy.time import Time
>>> import astropy.units as u
>>> Fort_Sumner = EarthLocation(lat=34.4900*u.deg, lon=-104.221800*u.deg, height=40*u.km)
>>> get_sun(Time("2018-12-01T00:00:00")).transform_to(AltAz(location=Fort_Sumner))
<SkyCoord (AltAz: obstime=2018-12-01T00:00:00.000, location=(-1301.01298069, -5133.33948381, 3614.02816719) km, pressure=0.0 hPa, temperature=0.0 deg_C, relative_humidity=0.0, obswl=1.0 micron): (az, alt, distance) in (deg, deg, km)
(245.77428805, -3.34077928, 1.47524431e+08)>

Related

How to find the local horizon longitude of the highest point of the ecliptic

I would like to use pyephem or skyfield to find the degree of the local horizon (starting from South = 0°) at which is located the highest point of the ecliptic. Note that I am not trying to find the culmination of a planet. It could well be that, when looking South, there is no planet on the ecliptic by that time/latitude, but there will still be a highest point of the ecliptic. Can anyone help?
While it’s likely that someone will soon jump in with a closed-form solution that uses spherical trigonometry — in which case it’s likely that Skyfield or PyEphem will only be used to determine the Earth orientation — here’s a quick way to get an answer within about a degree:
Generate the ecliptic as 360 points one degree apart across the sky.
Compute the altitude and azimuth of each one.
Choose the highest.
The result agrees closely to what I see if I open Stellarium, turn on the ecliptic, and choose a field star right near the point where the ecliptic reaches the highest point in the sky.
import numpy as np
from skyfield import api, framelib
from skyfield.positionlib import Apparent
𝜏 = api.tau
ts = api.load.timescale()
eph = api.load('de421.bsp')
bluffton = api.Topos('40.74 N', '84.11 W')
t = ts.utc(2021, 2, 16, 22, 52)
angle = np.arange(360) / 360.0 * 𝜏
zero = angle * 0.0
f = framelib.ecliptic_frame
d = api.Distance([np.sin(angle), np.cos(angle), zero])
v = api.Velocity([zero, zero, zero])
p = Apparent.from_time_and_frame_vectors(t, f, d, v)
p.center = bluffton
alt, az, distance = p.altaz()
i = np.argmax(alt.degrees) # Which of the 360 points has highest altitude?
print('Altitude of highest point on ecliptic:', alt.degrees[i])
print('Azimuth of highest point on ecliptic:', az.degrees[i])
The result:
Altitude of highest point on ecliptic: 67.5477569215633
Azimuth of highest point on ecliptic: 163.42529398930515
This is probably a computationally expensive enough approach that it won’t interest you once you or someone else does the spherical trigonometry to find an equation for the azimuth; but at the very least this might provide numbers to check possible formulae against.

Rotate geometry around its local axis

I tried to rotate a geometry around its local axis, but haven't found a way to do so. I know that there is ST_Rotate (see https://postgis.net/docs/ST_Rotate.html) for 2D calculations and (among others) ST_RotateX (see https://postgis.net/docs/ST_RotateX.html), but these methods rotate a geometry around the origin. I also tried to abuse ST_Affine when I tried to change (what seems to be) the origin (namely 0/0/0):
SELECT ST_Affine(
ST_GeomFromText(ST_AsText(runway_area)),
1, 0, 0, 0,
cos(rotRadians), -sin(rotRadians), 0, sin(rotRadians), cos(rotRadians),
--- use the geometry's centroid instead of 0, 0, 0
ST_X(ST_GeomFromText(ST_AsText(runway_area))), ST_Y(ST_GeomFromText(ST_AsText(runway_area))), ST_Z(ST_GeomFromText(ST_AsText(runway_area)))
)
It didn't work out - all I got was something that was way away from the intended location. Do I miss a very fundamental method by PostGIS here to rotate a geometry around one of its local axis?
Take a closer look at ST_Rotate, and note that there are three argument signatures:
geometry ST_Rotate(geometry geomA, float rotRadians);
geometry ST_Rotate(geometry geomA, float rotRadians, float x0, float y0);
geometry ST_Rotate(geometry geomA, float rotRadians, geometry pointOrigin);
The x0 and y0 or pointOrigin arguments for the last two signatures do what your questions asks: they allow the rotation to pivot around a custom defined coordinate.
And an example from the docs show how to rotate a geometry 60 degrees clockwise around the centroid:
SELECT ST_AsEWKT(ST_Rotate(geom, radians(-60.0), ST_Centroid(geom)))
FROM (SELECT 'LINESTRING (50 160, 50 50, 100 50)'::geometry AS geom) AS foo;
st_asewkt
--------------------------------------------------------------
LINESTRING(116.4225 130.6721,21.1597 75.6721,46.1597 32.3708)
(1 row)
You could combine ST_Rotate with ST_Centroid, example:
ST_Rotate(ST_GeomFromText(ST_AsText(runway_area)), -pi()/3, ST_Centroid(ST_GeomFromText(ST_AsText(runway_area)))
The last three arguments to ST_Affine do not represent the origin, but the global "shift" in the affine transformation. The documentation for ST_RotateX shows how to use this function to generate a rotation around the x-axis. All these parameters are zero since it's a rotation without translation.
If you want to employ a general axis, you would need to construct the corresponding rotation matrix and substitute its elements for the arguments a,b,c,d,e,f,g,h,i of ST_Affine and set xoff, yoff, zoff to zero.

Cartesian Coordinate System in Perspective Projection

I'm still implementing a perspective projection for my augmented reality application. I've already asked some questions about the viewport-calculation and other camera stuff, which is explained from Aldream in this thread
However, I don't get any useful value at the moment and I think this depends on my calculation of the cartesian coordinate space.
I had some different ways to transform latitude,longitude and altitude to a cartesian coordinate space, but nothing of them seems to work properly. Currently I'm using ECEF(earth centered), but I also tried different calculations like a combination of the haversine-formula and trigonometry (to calculate x and y from the distance and the bearing between two points).
So my question is:
How does the cartesian coordinate space affect my perspective projection? Where do I have to "compensate" my units?(When I'm using meter or centimeter for example)?
Lets say I'm using ECEF, than I get values in meter, so for example, my camera is at (0,0,2m height) and my point is at (10,10,0). Now I can easily use the function mentioned on wikipedia and afterwards using the conversion of dx,dy,dz explained in my other thread (mentioned above). What I still don't get: How does this projection "know" what my units in the coordinate system are? I think this is the mistake I'm currently doing. I don't handle the units of my coordinate system and therefore, cannot get any good value from my projection.
When I'm using a coordinate system with centimeter as unit, all of my values from my perspective projection are increasing. Where do I have to "resolve" this unit-problem? Do I have to "transform" my camera-width and camera-height from pixel to meter? Do I have to convert the coordinate system to pixel? Which coordinate-system should be used to handle this situation? I hope you can understand my problem.
Edit:I solved it myself.
I've changed my coordinate system from ecef to a own system (using haversine and bearing and then calculating x,y,z) and now I get good values! :)
I'll try another way to explain it here then. :)
The short answer is: the unit of your cartesian positions doesn't matter as long as you keep it homogeneous, ie as long as you apply this unit both to your scene and to your camera.
For the longer answer, let's go back to the formula you used...
With:
d the relative Cartesian coordinates
s the size of your printable surface
r the size of your "sensor" / recording surface (ie r_x and r_y the size of the sensor and r_z its focal length)
b the position on your printable surface
.. and do the pseudo dimensional analysis. We have:
[PIXEL] = (([LENGTH] x [PIXEL]) / ([LENGTH] * [LENGTH])) * [LENGTH]
Whatever you use as unit for LENGTH, it will be homogenized, ie only the proportion is kept.
Ex:
[PIXEL] = (([MilliM] x [PIXEL]) / ([MilliMeter] * [MilliMeter])) * [MilliMeter]
= (([Meter/1000] x [PIXEL]) / ([Meter/1000] * [Meter/1000])) * [Meter/1000]
= 1000 * 1000 / 1000 /1000 * (([Meter] x [PIXEL]) / ([Meter] * [Meter])) * [Meter]
= (([Meter] x [PIXEL]) / ([Meter] * [Meter])) * [Meter]
Back to my explanations on your other thread:
If we use those notations to express b_x:
b_x = (d_x * s_x) / (d_z * r_x) * r_z
= (d_x * w) / (d_z * 2 * f * tan(α)) * f
= (d_x * w) / (d_z * 2 * tan(α)) // with w in px
Wheter you use (d_x, d_y, d_z) = (X,Y,Z) or (d_x, d_y, d_z) = (1000*X,1000*Y,1000*Z), the ratio d_x / d_z won't change.
Now for the reasons behind your problem, you should maybe check if you apply the correct unit to the position of your camera / to its distance to the scene too. Check also your α or the unit of the focal length, depending on which one you use.
If think the later suggestion is the most likely. It can be easy to forget to also apply the right unit to the characteristics of your camera.

Draw Camera Range with Postgis

i am working on some camera data. I have some points which consist of azimuth, angle, distance, and of course coordinate field attributes. In postgresql postgis I want to draw shapes like this with functions.
how can i draw this pink range shape?
at first should i draw 360 degree circle then extracting out of my shape... i dont know how?
I would create a circle around the point(x,y) with your radius distance, then use the info below to create a triangle that has a larger height than the radius.
Then using those two polygons do an ST_Intersection between the two geometries.
NOTE: This method only works if the angle is less than 180 degrees.
Note, that if you extend the outer edges and meet it with a 90 degree angle from the midpoint of your arc, you have a an angle, and an adjacent side. Now you can SOH CAH TOA!
Get Points B and C
Let point A = (x,y)
To get the top point:
point B = (x + radius, y + (r * tan(angle)))
to get the bottom point:
point C = (x + radius, y - (r * tan(angle)))
Rotate your triangle to you azimouth
Now that you have the triangle, you need to rotate it to your azimuth, with a pivot point of A. This means you need point A at the origin when you do the rotation. The rotation is the trickiest part. Its used in computer graphics all the time. (Actually, if you know OpenGL you could get it to do the rotation for you.)
NOTE: This method rotates counter-clockwise through an angle (theta) around the origin. You might have to adjust your azimuth accordingly.
First step: translate your triangle so that A (your original x,y) is at 0,0. Whatever you added/subtracted to x and y, do the same for the other two points.
(You need to translate it because you need point A to be at the origin)
Second step: Then rotate points B and C using a rotation matrix. More info here, but I'll give you the formula:
Your new point is (x', y')
Do this for points B and C.
Third step: Translate them back to the original place by adding or subtracting. If you subtracted x last time, add it this time.
Finally, use points {A,B,C} to create a triangle.
And then do a ST_Intersection(geom_circle,geom_triangle);
Because this takes a lot of calculations, it would be best to write a program that does all these calculations and then populates a table.
PostGIS supports curves, so one way to achieve this that might require less math on your behalf would be to do something like:
SELECT ST_GeomFromText('COMPOUNDCURVE((0 0, 0 10), CIRCULARSTRING(0 10, 7.071 7.071, 10 0), (10 0, 0 0))')
This describes a sector with an origin at 0,0, a radius of 10 degrees (geographic coordinates), and an opening angle of 45°.
Wrapping that with additional functions to convert it from a true curve into a LINESTRING, reduce the coordinate precision, and to transform it into WKT:
SELECT ST_AsText(ST_SnapToGrid(ST_CurveToLine(ST_GeomFromText('COMPOUNDCURVE((0 0, 0 10), CIRCULARSTRING(0 10, 7.071 7.071, 10 0), (10 0, 0 0))')), 0.01))
Gives:
This requires a few pieces of pre-computed information (the position of the centre, and the two adjacent vertices, and one other point on the edge of the segment) but it has the distinct advantage of actually producing a truly curved geometry. It also works with segments with opening angles greater than 180°.
A tip: the 7.071 x and y positions used in the example can be computed like this:
x = {radius} cos {angle} = 10 cos 45 ≈ 7.0171
y = {radius} sin {angle} = 10 sin 45 ≈ 7.0171
Corner cases: at the antimeridian, and at the poles.

Point in Tilt Direction - iPhone

In my cocos2d game, I have my player sprite and I want to have him move in the direction I tilt my iPhone. I can deal with that, the hardest bit which I can't work out is:
How do I make my sprite rotate to point in the direction I am tilting? This is represented very well in the 'Tilt to Live' game on the app store. I want controls just like that.
My sprite(for those unfamiliar with cocos2d) does have a rotation value if that helps.
Thanks.
If you don't like the above, here's a simpler way to get a reasonable result!
Hold the iPad in front of you, let LR be the left / right tilt, TA the towards you / away from you tilt. So LR runs from -90 to 90, TA from -90 to 90. (TA negative is leaning towards your belly.)
Display both those numbers on your screen, and move the device around, so you are certain you have that right to begin with. You won't be able to do anything until that is working.
The solutionAngle will be like a clock hand, clockwise, with 12 distant from you.
Go through this decision chain:
If both LR and TA is zero, the machine is flat. Act appropriately.
If LR is flat (0), the answer is either 0 or 180, depending on the sign of TA.
If TA is flat (0), the answer is either 90 or 270, depending on the sign of LR.
Otherwise:
adjustmentAngle = arctan( sin(TA) / sin(LR) )
// (NB, that should run from -90 to +90)
if ( LR > 0 ) finalResult = 90 - adjustmentAngle
if ( LR < 0 ), finalResult = 270 + adjustmentAngle
I think that will do it! Hope it helps!
IMO...... be sure to smooth the result over time, for a good feel.
.
setting the angle...
"the only thing I am unsure of currently (concerning your own idea) is how do I apply it to my player? Do I merely make the player rotation value equal to the adjustmentAngle?" .. hi Josh, yes simply set the rotation to the final angle you calculate using the above! Fortunately it's that simple.
If you ever have to convert back/fore between degrees and radians, just paste in these lines of code that everyone uses:
#include <math.h>
static inline float degreestoradians (double degrees) {return degrees * M_PI/180;}
static inline float radianstodegrees (double degrees) {return degrees * 180/M_PI;}
.
where are the axes?...
PS, here's the incredibly handy diagram you may want to bookmark:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAcceleration_Class/Reference/UIAcceleration.html
.
converting from accelerometer to angles...
"the accelerometer doesn't provide the raw data in angles. How do get from the raw data"
Quite right, I forgot to mention it sorry. This is an everyday problem...
distanceFactor = square root of (x^2 + y^2 + z^2)
angle X axis = acos ( x / distanceFactor )
angle y axis = acos ( y / distanceFactor )
angle z axis = acos ( z / distanceFactor) )
You must TEST this by writing the three angles on the screen and then moving it around, in other words "physically unit test" that section you write, before you proceed!
here is one of many answers from SO: UIAccelerationValue angle
BTW as you can probably see, you can get a rough result by taking the ratio of simply the raw x by raw y value, rather than the ratio of the two sines, in the 'adjustmentAngle' expression ... but anyway don't worry about that for now.
And finally!
IMPORTANT Readers should note that the amazing new Core Motion system, handles a lot of this for you, depending on your needs. Check it out!!!!!
Hope it helps!