pyephem FixedObject() for given RA/Dec - python

I'm looking to determine the alt/az of (un-famous) stars at given RA/Dec at specific times from Mauna Kea. I'm trying to compute these parameters using pyephem, but the resulting alt/az don't agree with other sources. Here's the calculation for HAT-P-32 from Keck:
import ephem
telescope = ephem.Observer()
telescope.lat = '19.8210'
telescope.long = '-155.4683'
telescope.elevation = 4154
telescope.date = '2013/1/18 10:04:14'
star = ephem.FixedBody()
star._ra = ephem.degrees('02:04:10.278')
star._dec = ephem.degrees('+46:41:16.21')
star.compute(telescope)
print star.alt, star.az
which returns -28:43:54.0 73:22:55.3, though according to Stellarium, the proper alt/az should be: 62:26:03 349:15:13. What am I doing wrong?
EDIT: Corrected latitude and longitude, which were formerly reversed.

First, you've got long and latitude backwards; second, you need to provide the strings in hexadecimal form; and third, you need to provide the RA as hours, not degrees:
import ephem
telescope = ephem.Observer()
# Reversed longitude and latitude for Mauna Kea
telescope.lat = '19:49:28' # from Wikipedia
telescope.long = '-155:28:24'
telescope.elevation = 4154.
telescope.date = '2013/1/18 00:04:14'
star = ephem.FixedBody()
star._ra = ephem.hours('02:04:10.278') # in hours for RA
star._dec = ephem.degrees('+46:41:16.21')
star.compute(telescope)
This way, you get:
>>> print star.alt, star.az
29:11:57.2 46:43:19.6

PyEphem always uses UTC for time, so that programs operate the same and give the same output wherever they are run. You simply need to convert the date you are using to UTC, instead of using your local time zone, and the results agree fairly closely with Stellarium; use:
telescope.date = '2013/1/18 05:04:14'
The result is this alt/az:
62:27:19.0 349:26:19.4
To know where the small remaining difference comes from, I would have to look into how the two programs handle each step of their computation; but does this get you close enough?

Related

Convert UK grid coordinates (X,Y) to latitude and longitude

I have a list of position coordinates given in UK grid reference format (X,Y) and I would like to convert them into latitude and longitude.
I am using OSGridConverter (python library) but is not converting it correctly. For example, one input for one location is X = 517393.6563 and Y = 194035.5469.
from OSGridConverter import grid2latlong
l=grid2latlong('TG 517393.6563 194035.5469')
The above, gives me an error: OSGridConverter error: Invalid grid reference
Therefore, which is wrong, I try:
>>>l=grid2latlong('TG 517393 194035')
>>>(l.latitude,l.longitude)
(52.71367793063314, 1.7297510074170983)
Which ends with a location out in UK, which is not correct. Most probably is due data formats, but I am not sure about how to solve it.
You should probably use something like pyproj:
import pyproj
crs_british = pyproj.Proj(init='EPSG:27700')
crs_wgs84 = pyproj.Proj(init='EPSG:4326')
long, lat = pyproj.transform(crs_british, crs_wgs84, 517393.6563, 194035.5469)
print(lat, long)
In your case this will give: 51.63289090467179, -0.3052119183057834
Firstly, a definition:
OS Grid Grid references (OSGGRs) refer to grid references prepended with the two letter grid identifiers, as explained rather well here.
As you found OSGGRs arn't floating point numbers. See the link above.
Moving on, the numbers for OSGGRs have their origin at the south westerly point of the grid - which is very different from the origin of the OSGB BNG projection (coords are easting and northings). See above link for great explanation.
You seem to be taking the BGGRs numeric portion as an Easting/Northing, which is incorrect.
So, armed with this knowledge, we can use OSGridConverter to convert from OSGGRs to eastings and northings using a conversion to lat long as an intermediary. But note the authors comments on errors at the bottom of the PyPI package page
import OSGridConverter
cvt_wgs84 = OSGridConverter.grid2latlong('SN5117013189')
print(cvt_wgs84.latitude, cvt_wgs84.longitude)
51.79749210128498 -4.160451113839529
cvt_EN = OSGridConverter.latlong2grid(cvt_wgs84.latitude, cvt_wgs84.longitude)
print(cvt_EN.E,cvt_EN.N)
251116 213191
# Back to OSGGR, however, note the error of tens of meters.
str(cvt_EN)
'SN 51116 13191'
I did a quick 'by-eye' check by-map on the agreement between the OSGGR coords and the lat/long, and the error is in meters. May be acceptable to some.

How to take a datetime.time object in UT and convert to local time given a longitude

I need to convert a time in UT combined with a longitude into local time. I am working on analyzing a future Earth observing satellite mission and need to do this conversion to continue.
I found a general solution to this problem here: T(lt) = T(ut) + lon/(360/24), however implementing it is driving me absolutely bonkers.
My data are datetime time objects:
In[9]: sattime[0]
Out[9]: datetime.time(18, 0)
and longitude coordinates from 0 to 360 degrees.
I need to take this object and use the above equation to convert to a local time. I ONLY care about the time relative to midnight and NOT about the date (in fact the rest of my code is currently using the datetime.time object only and preferably the local time output would be the same type of object).
I have tried the following from here but am stuck getting the local variable back into a time object.
test = 4
for (i,j) in zip(sattime, satloncor):
td = datetime.datetime.combine(datetime.datetime.min, i) - datetime.datetime.min
seconds = td // datetime.timedelta(milliseconds=1)
local = (seconds + (j/(360/86400)))/1000
print (local)
if test<0:
break
test-=1
The test part of the code just makes sure I am not wasting time doing the conversion for all ~400,000 data points.
So to summarize, I want to take a datetime.time object in UT coupled with a corresponding longitude, and convert it to local solar time as a datetime.time object.
This all seems super convoluted and seems like there should be an easier way. Any help is greatly appreciated! Thanks!
I figured it out but it is not pretty.
localtimes = []
for (i,j) in zip(sattime, satloncor):
td = dt.datetime.combine(dt.datetime.min, i) - dt.datetime.min
seconds = td // dt.timedelta(seconds=1)
local = (seconds + (j/(360/86400)))/3600
if local>24:
local-=24
strip = [math.modf(local)[1],math.modf(local)[0]*60 ]
if strip[0]==24:
localtimes.append(dt.time(0, int(strip[1]),0))
else:
localtimes.append(dt.time(int(strip[0]), int(strip[1]),0))
Sorry, but it is super convoluted because it's a complicated subject. For just one example, did you know that every few years there is a day with 86401 seconds in it?
A couple of good web sites to start with:
http://www.ephemeris.com/books.html
I can recommend the "Practical Astronomy with your Calculator" book.
https://www.timeanddate.com/
Hope this helps.

Working out which points lat/lon coordinates are closest to

I currently have a list of coordinates
[(52.14847612092221, 0.33689512047881015),
(52.14847612092221, 0.33689512047881015),
(52.95756796776235, 0.38027099942700493),
(51.78723479900971, -1.4214854900618064)
...]
I would like to split this list into 3 separate lists/datafames corresponding to which city they are closest to (in this case the coordinates are all in the UK and the 3 cities are Manchester, Cardiff and London)
So at the end result I would like the current single list of coordinates to be split into either separate lists ideally or it could be a dataframe with 3 columns would be fine eg:
leeds cardiff london
(51.78723479900971, (51.78723479900971, (51.78723479900971,
-1.4214854900618064) -1.4214854900618064) -1.4214854900618064)
(those are obiously not correct coordinates!)
-Hope that makes sense. It doesn't have to be overly accurate (don't need to take into consideration the curvature of the earth or anything like that!)
I'm really not sure where to start with this - I'm very new to python and would appreciate any help!
Thanks in advance
This will get you started:
from geopy.geocoders import Nominatim
geolocator = Nominatim()
places = ['london','cardiff','leeds']
coordinates = {}
for i in places:
coordinates[i] = ((geolocator.geocode(i).latitude, geolocator.geocode(i).longitude))
>>>print coordinates
{'cardiff': (51.4816546, -3.1791933), 'leeds': (53.7974185, -1.543794), 'london': (51.5073219, -0.1276473)}
You can now hook up the architecture for putting this in a pandas dataframe, calculating the distance metric between your coordinates and the above.
Ok so now we want to do distances between what is a very small array (the coordinates).
Here's some code:
import numpy as np
single_point = [3, 4] # A coordinate
points = np.arange(20).reshape((10,2)) # Lots of other coordinates
dist = (points - single_point)**2
dist = np.sum(dist, axis=1)
dist = np.sqrt(dist)
From here there is any number of things you can do. You can sort it using numpy, or you can place it in a pandas dataframe and sort it there (though that's really just a wrapper for the numpy function I believe). Whichever you're more comfortable with.
This is a pretty brute force approach, and not too adaptable. However, that can be the easiest to understand and might be plenty efficient for the problem at hand. It also uses only pure python, which may help you to understand some of python's conventions.
points = [(52.14847612092221, 0.33689512047881015), (52.14847612092221, 0.33689512047881015), (52.95756796776235, 0.38027099942700493), (51.78723479900971, -1.4214854900618064), ...]
cardiff = (51.4816546, -3.1791933)
leeds = (53.7974185, -1.543794)
london = (51.5073219, -0.1276473)
def distance(pt, city):
return ((pt[0] - city[0])**2 + (pt[1] - city[1])**2)**0.5
cardiff_pts = []
leeds_pts = []
london_pts = []
undefined_pts = [] # for points equidistant between two/three cities
for pt in points:
d_cardiff = distance(pt, cardiff)
d_leeds = distance(pt, leeds)
d_london = distance(pt, london)
if (d_cardiff < d_leeds) and (d_cardiff < d_london):
cardiff_pts.append(pt)
elif (d_leeds < d_cardiff) and (d_leeds < d_london):
leeds_pts.append(pt)
elif (d_london < d_cardiff) and (d_london < d_leeds):
london_pts.append(pt)
else:
undefined_pts.append(pt)
Note that this solution assumes the values are on a cartesian reference frame, which latitude longitude pairs are not.

How do I get a PyEphem observer's meridian in "epoch of date" coordinates?

I'm trying to determine the apparent right ascension of the an observer's meridian in the context of right ascension ephemerides that I'm getting for the observer using PyEpehm's Observer class. I understand that the latter will be provided in "epoch of date" coordinates, but don't know how to get the meridian's right ascension for the observer in those coordinates.
I assume that sidereal_time will give me what I'm looking for
import ephem
import numpy
zone1 = ephem.city('London')
zone1.date = '2014/08/04 11:00:00'
meridian = zone1.sidereal_time()
but am not sure what epoch the resulting coordinates are in.
How do I get a PyEphem observer's meridian in "epoch of date" coordinates?
Per its definition (at least as I understand it!), the sidereal time is necessarily a relationship between two epoch-of-date coordinates: the equinox point of that date, and the point where the observe is located on the Earth’s surface.
We can check this by choosing a point on the meridian — say, the point at 0° latitude where it crosses the celestial equator — and ask PyEphem, using its slightly awkward interface, what the RA of that point is for the moment that you are asking about:
import ephem
zone1 = ephem.city('London')
zone1.date = '2014/08/04 11:00:00'
zone1.pressure = 0.0
meridian = zone1.sidereal_time()
print 'Hour angle:', meridian
print
overhead = ephem.degrees('90:00')
south = ephem.degrees('180:00')
zone1.epoch = zone1.date
ra, dec = zone1.radec_of(az=south, alt=overhead - zone1.lat)
print 'Epoch-of-date:'
print 'RA:', ra
print 'dec:', dec
print
zone1.epoch = '2000/1/1'
ra, dec = zone1.radec_of(az=south, alt=overhead - zone1.lat)
print 'Epoch J2000:'
print 'RA:', ra
print 'dec:', dec
print
The output, I think, confirms that the sidereal time is the same as the epoch-of-date right ascension of the point we asked about — at least I think that the tiny, less-than-an-arcsecond discrepancy is due to rounding errors inside of the libastro that PyEphem relies upon, and not to some additional theoretical complication that is escaping us:
Sidereal time: 7:51:14.43
Epoch-of-date:
RA: 7:51:15.24
dec: 0:00:03.5
Epoch J2000:
RA: 7:50:30.36
dec: 0:02:19.5
Because I find PyEphem a bit awkward for these kinds of questions, note that I have started a new Skyfield library that tries to make computation a bit simpler, if you ever feel like trying out an alternative. Either way: enjoy!
Local Sidereal Time is by definition the RA of the local meridian. To be more precise, Local Apparent Sidereal Time is defined as the hour angle of the vernal equinox at that locality: it has the same value as the right ascension of any celestial body that is crossing the local meridian at that same moment, which is exactly what you want.
Your use of pyephem's sideral_time() function would produce the apparent RA ("epoch-of-date" RA is also probably the same thing under this definition but doesn't mean the same thing).
>>> GST = ephem.Observer()
>>> GST.lat = '0'
>>> GST.lon = '0'
>>> GST.elevation = '0'
>>> GST.elevation = 0
>>> GST.date = '2014/08/04 11:00:00'
>>> GST.sidereal_time()
7:51:44.73
Notice that London and Greenwich are not the same thing either.
Now let's place a star at this RA and see what we get--this won't work out exact, but it will be close enough to show the relationship.
>>> star._ra = '7:51:44.73'
>>> star._dec = '45'
>>> star._epoch = '2014/08/04 11:00:00'
>>> GST.pressure = 0
>>> star.compute(GST)
>>> star.az, star.alt
(359:59:38.7, 45:00:08.2)
Here we can see that our fictitious star is on our meridian (well as close and pyephem is going to allow us to calculated it), where the meridian is defined as azimuth angles of 0 or 180 degrees. In pyephem, it has always behaved a bit oddly when you try to use it basically backwards as in this demonstration. Let's try another in the forward direction.
This time we'll choose a known star and adjust the time at the site until it the LST matches the apparent RA of the star.
>>> vega = ephem.star('Vega') #retrieve vega from star catalog
>>> ephem.now() #get the current time
2015/7/9 01:42:49
>>> GST.date = '2015/7/9 01:42:49' #set time at site
>>> vega.compute(GST) #compute vega over the site for this time
>>> vega.a_ra #retrieve the astronomical RA for vega epoch J2000
18:36:56.47
>>> vega.ra #retrieve apparent RA for vega epoch now
18:37:29.64
Notice that they are a bit different owing to precession of the earth about its axis and nutation of the axis owing to earth-moon interactions
>>> GST.sidereal_time() #get the sidereal time for now
20:49:34.12
Note that the sidereal time is a bit off from the apparent RA of vega, so let's adjust the time until they are exactly the same
>>> GST.date = '2015/7/8 23:31:06.16'
>>> GST.sidereal_time()
18:37:29.64
Now they are identical. Now compute vega for the site again.
>>> vega.compute(GST)
>>> vega.ra, vega.dec
(18:37:29.64, 38:48:06.5)
>>> vega.az, vega.alt
(0:00:00.0, 51:11:53.5)
As you can see here that the azimuth angle is precisely on the meridian as expected. This forward demonstration shows that LST is equivalent to the apparent RA for the local meridian.

lat/lon to utm to lat/lon is extremely flawed, how come?

I've tried the following,
input: lat/lon data
then I'll calculate a box around it by, let's say 50 m, so +/- 50 m on easting/northing value.
Now I reconvert it to lat/lon and with a script:
http://robotics.ai.uiuc.edu/~hyoon24/LatLongUTMconversion.py I get a result that just can't be, lon before is around 7, afterwards around 2.
zone, easting, northing = LLtoUTM(23, location.get_lat(), location.get_lon())
topUTM = northing + error
bottomUTM = northing - error
leftUTM = easting - error
rightUTM = easting + error
left, top = UTMtoLL(23, leftUTM, topUTM, zone)
Is the error in my code, or might be the script flawed?
So I've tried to use pyproj, just lat/lon to utm to lat/lon to see what happens
>>> p = pyproj.Proj(proj='utm', zone=32, ellps='WGS84')
>>> p
<pyproj.Proj object at 0x7ff9b8487dd0>
>>> x,y = p(47.9941214, 7.8509671)
>>> print x,y
5159550.36822 1114087.43925
>>> print p(x,y,inverse=True)
(47.971558538495991, 7.8546573140162605)
And here it's not as extremely far off as with the script from above, but it still seems strongly enough incorrect as to not be able to use it. How come? What can I do to get more exact results?
EDIT:
I ran test() and it all tests passed.
in epsg file there is no such thing. The closest I've found was this:
<32632> +proj=utm +zone=32 +ellps=WGS84 +datum=WGS84 +units=m +no_defs <>
no tmerc. Also What would I need to pass the towgs84 as parameters? The ones above?
I've created a small UTM conversion library for Python last week and uploaded it to the Python Package Index: http://pypi.python.org/pypi/utm
I have compared it to using pyproj and it is faster and more accurate. Given your sample data, this is the result:
>>> import utm
>>> u = utm.from_latlon(47.9941214, 7.8509671)
>>> print u
(414278, 5316285, 32, 'T')
>>> print utm.to_latlon(*u)
(47.994157948891505, 7.850963967574302)
UPDATE: Richards answer below describes the real solution for this issue.
The error is in your code.
First off, the PyProj issue listed in one of the other answers is real. You should check your epsg file and make sure it includes the line
<2392> +proj=tmerc +lat_0=0 +lon_0=24 +k=1.000000 +x_0=2500000 +y_0=0 +ellps=intl +towgs84=-90.7,-106.1,-119.2,4.09,0.218,-1.05,1.37 +units=m +no_defs no_defs <>
Note the towgs84 parameter.
Your problem with PyProj stems from mis-using the projection command.
If we take 47.9941214N, 7.8509671E and convert to UTM we get Zone 32, 414278 Easting, 5316286 Northing.
You perform the following PyProj operations:
p = pyproj.Proj(proj='utm', zone=32, ellps='WGS84')
>>> x,y = p(47.9941214, 7.8509671)
>>> print x,y
5159550.36822 1114087.43925
>>> print p(x,y,inverse=True)
(47.971558538495991, 7.8546573140162605)
But, if we consult the PyProj documentation, we see the following:
Calling a Proj class instance with the arguments lon, lat will convert
lon/lat (in degrees) to x/y native map projection coordinates (in
meters).
Let's try running the OP's PyProj operations again, but switch the order of the lon/lat arguments:
p = pyproj.Proj(proj='utm', zone=32, ellps='WGS84')
>>> x,y = p(7.8509671, 47.9941214)
>>> print x,y
414278.16731 5316285.59492
>>> print p(x,y,inverse=True)
(7.850967099999812, 47.994121399999784)
The operation inverts itself (pretty much) perfectly!
To answer the first part of your question, if you look in http://robotics.ai.uiuc.edu/~hyoon24/LatLongUTMconversion.py at the definition of UTMtoLL, you find the following:
UTMtoLL(ReferenceEllipsoid, northing, easting, zone)
Yet you use UTMtoLL(23, leftUTM, topUTM, zone) where leftUTM is an Easting and topUTM is a Northing.
Therefore, in the case of both your first script and PyProj, you've used the wrong order of arguments.
It's a good reminder to always double- (or triple-) check your work before suggesting that someone else's is wrong. That said, Python's documentation is not the greatest and PyProj's documentation in this instance is cryptic at best. A nice web-based explanation of this command and accompanied by examples of its usage would have probably prevented angst on your part.
I have no problem with pyproj, try the following code
from pyproj import Proj
Lat = 52.063098675
Lon = -114.132980348 #Calgary
ZoneNo = "11" #Manually input, or calcuated from Lat Lon
myProj = Proj("+proj=utm +zone="+ZoneNo+",\
+north +ellps=WGS84 +datum=WGS84 +units=m +no_defs") #north for north hemisphere
UTMx, UTMy = myProj(Lon, Lat)
########################################
#UTM ==> Lat Lon:
ZoneNo = "11" #Manually input or from other sources
myProj = Proj("+proj=utm +zone="+\
ZoneNo+", +north +ellps=WGS84 +datum=WGS84 +units=m +no_defs")
Lon2, Lat2 = myProj(UTMx, UTMy,inverse=True)
print Lat2
print Lon2
Your issue with pyProj sounds just like the one described here:
http://code.google.com/p/pyproj/issues/detail?id=3
which is resolved:
solved! in epsg file there must be
<2392> +proj=tmerc +lat_0=0 +lon_0=24 +k=1.000000 +x_0=2500000 +y_0=0 +ellps=intl
+towgs84=-90.7,-106.1,-119.2,4.09,0.218,-1.05,1.37 +units=m +no_defs no_defs <>
note the towgs84 parameter!
Check that thread out if you want to continue to use pyproj.
Also, does the test() function of the module work? Have you tried any of the scripts that come with it in the test directory?

Categories

Resources