I have problems with shifting coordinates by 100 meters horizontaly/verticaly in Python. I found that If your displacements aren't too great (less than a few kilometers) and you're not right at the poles, use the quick and dirty estimate that 111,111 meters (111.111 km) in the y direction is 1 degree (of latitude) and 111,111 * cos(latitude) meters in the x direction is 1 degree (of longitude). here: https://gis.stackexchange.com/questions/2951/algorithm-for-offsetting-a-latitude-longitude-by-some-amount-of-meters
I wrote two functions in Python. This function works, that is it computes the shift in latitude:
def vertikalne(shift):
return shift/111_111
Check that distance between (49.550586, 18.859254) and (49.550586 + vertikalne(100), 18.859254) is really 100 meters.
But this function does not work and I don`t know what is the problem:
import math
def horizontalne(latitude, shift):
return (shift/111_111) * math.cos(latitude*math.pi/180) # UPDATE: I just converted to radians
Distance between (49.550586, 18.859254) and (49.550586 , 18.859254 + horizontalne(49.550586 , 100)) is 49 which is nonsense.
Can you help me please?
Correct solutions is:
import math
def horizontalne(latitude, shift):
return shift/(111_111 * math.cos(math.radians(latitude)))
Change math.cos(latitude) to math.cos(math.pi/180*latitude) as the math.cos() expects angles in radians and not in degrees.
math.cos() takes radians as input not degrees. You need to convert.
def horizontalne(latitude, shift):
return (shift/111_111) * math.cos(2*math.pi()*latitude/360)
something like this.
And if you want to calculate distances you need to apply Pythagoras' theorem. Maybe even take the average of the latitudes for the cos.
--
Mine is wrong. #vojtam is correct. (parentheses...)
It is rather unclear what the function is supposed to do. Maybe better to give your params more meaningful names. e.g. shift_distance, shift_long, shift_lat etc.
I think it is supposed to do the following:
The functions both take a distance as input and as output it gives the fraction of 1 degree of the arc of the intended circle around the earth. (for vertikalne it is the meridian, for horizontalne it is the parallel at the given latitude. (https://www.britannica.com/science/latitude )
111_111 meters is indeed more or less the distance over the earth of 1 degree. (at least along a meridian, the equator is slightly larger....)
So your evaluation should be different:
A = (49.550586, 18.859254) # lat,long
B = (49.550586, 18.859254 + horizontalne(49.550586 , 100)) # B is 100m east of A along the parallel at lat 49.550586
(B[0] - A[0], (B[1] - A[1]) )
gives:
Out[42]: (0.0, 0.0005838993787818936)
Which means: moving 100 meters horizontally along the earth's parallel at latitude 49.55... means changing the longitude angle for 0.0005838993787818936 of a degree. Makes sense to me.
Related
I am new in python.
I have two vectors in 3d space, and I want to know the angle between two
I tried:
vec1=[x1,y1,z1]
vec2=[x2,y2,z2]
angle=np.arccos(np.dot(vec1,vec2)/(np.linalg.norm(vec1)*np.linalg.norm(vec2)))
but when change the order, vec2,vec1 obtain the same angle and no higher.
I want to give me a greater angle when the order of the vectors changes.
Use a function to help you choose which angle do you want. In the beggining of your code, write:
def angle(v1, v2, acute):
# v1 is your firsr vector
# v2 is your second vector
angle = np.arccos(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))
if (acute == True):
return angle
else:
return 2 * np.pi - angle
Then, when you want to calculate an angle (in radians) in your program just write
angle(vec1, vec2, 'True')
for acute angles, and
angle(vec2, vec1, 'False')
for obtuse angles.
For example:
vec1 = [1, -1, 0]
vec2 = [1, 1, 0]
#I am explicitly converting from radian to degree
print(180* angle(vec1, vec2, True)/np.pi) #90 degrees
print(180* angle(vec2, vec1, False)/np.pi) #270 degrees
If you're working with 3D vectors, you can do this concisely using the toolbelt vg. It's a light layer on top of numpy.
import numpy as np
import vg
vec1 = np.array([x1, y1, z1])
vec2 = np.array([x2, y2, z2])
vg.angle(vec1, vec2)
You can also specify a viewing angle to compute the angle via projection:
vg.angle(vec1, vec2, look=vg.basis.z)
Or compute the signed angle via projection:
vg.signed_angle(vec1, vec2, look=vg.basis.z)
I created the library at my last startup, where it was motivated by uses like this: simple ideas which are verbose or opaque in NumPy.
What you are asking is impossible as the plane that contains the angle can be oriented two ways and nothing in the input data gives a clue about it.
All you can do is to compute the smallest angle between the vectors (or its complement to 360°), and swapping the vectors can't have an effect.
The dot product isn't guilty here, this is a geometric dead-end.
The dot product is commutative, so you'll have to use a different metric. It doesn't care about the order.
Since the dot product is commutative, simply reversing the order you put the variables into the function will not work.
If your objective is to find the obtuse(larger) angle rather than the acute(smaller) one, subtract the value returned by your function from 360 degrees. Since you seem to have a criteria for when you want to switch the variables around, you should use that same criteria to determine when to subtract your found value from 360. This will give you the value you are looking for in these cases.
I have some points that are located in the same place, with WGS84 latlngs, and I want to 'jitter' them randomly so that they don't overlap.
Right now I'm using this crude method, which jitters them within a square:
r['latitude'] = float(r['latitude']) + random.uniform(-0.0005, 0.0005)
r['longitude'] = float(r['longitude']) + random.uniform(-0.0005, 0.0005)
How could I adapt this to jitter them randomly within a circle?
I guess I want a product x*y = 0.001 where x and y are random values. But I have absolutely no idea how to generate this!
(I realise that really I should use something like this to account for the curvature of the earth's surface, but in practice a simple circle is probably fine :) )
One simple way to generate random samples within a circle is to just generate square samples as you are, and then reject the ones that fall outside the circle.
The basic idea is, you generate a vector with x = radius of circle y = 0.
You then rotate the vector by a random angle between 0 and 360, or 0 to 2 pi radians.
You then apply this displacement vector and you have your random jitter in a circle.
An example from one of my scripts:
def get_randrad(pos, radius):
radius = random() * radius
angle = random() * 2 * pi
return (int(pos[0] + radius * cos(angle)),
int(pos[1] + radius * sin(angle)))
pos beeing the target location and radius beeing the "jitter" range.
As pjs pointed out, add
radius *= math.sqrt(random())
for uniform distribution
Merely culling results that fall outside your circle will be sufficient.
If you don't want to throw out some percentage of random results, you could choose a random angle and distance, to ensure all your values fall within the radius of your circle. It's important to note, with this solution, that the precision of the methods you use to extrapolate an angle into a vector will skew your distribution to be more concentrated in the center.
If you make a vector out of your x,y values, and then do something like randomize the length of said vector to fall within your circle, your distribution will no longer be uniform, so I would steer clear of that approach, if uniformity is your biggest concern.
The culling approach is the most evenly distributed, of the three I mentioned, although the random angle/length approach is usually fine, except in cases involving very fine precision and granularity.
I would like to calculate a point based on direction and distance using GeoDjango or GeoPy.
For example, If I have a point that is (-24680.1613, 6708860.65389) I would like to find out a point 1KM North, 1KM East, 1KM Sourh and 1KM west using Vincenty distance formula.
I closest thing I can find is a "destination" function in distance.py (https://code.google.com/p/geopy/source/browse/trunk/geopy/distance.py?r=105). Although I cannot find this documented anywhere and I'm yet to figure out how to use it.
Any help is much appreciated.
Edit 2
Okay, there is an out-of-the-box solution with geopy, it is just not well-documented:
import geopy
import geopy.distance
# Define starting point.
start = geopy.Point(48.853, 2.349)
# Define a general distance object, initialized with a distance of 1 km.
d = geopy.distance.VincentyDistance(kilometers = 1)
# Use the `destination` method with a bearing of 0 degrees (which is north)
# in order to go from point `start` 1 km to north.
print d.destination(point=start, bearing=0)
The output is 48 52m 0.0s N, 2 21m 0.0s E (or Point(48.861992239749355, 2.349, 0.0)).
A bearing of 90 degrees corresponds to East, 180 degrees is South, and so on.
Older answers:
A simple solution would be:
def get_new_point():
# After going 1 km North, 1 km East, 1 km South and 1 km West
# we are back where we were before.
return (-24680.1613, 6708860.65389)
However, I am not sure if that serves your purposes in all generality.
Okay, seriously, you can get started using geopy. First of all, you need to define your starting point in a coordinate system known to geopy. At the first glance, it seems that you cannot just "add" a certain distance into a certain direction. The reason, I think, is that calculation of the distance is a problem without simple inverse solution. Or how would we invert the measure function defined in https://code.google.com/p/geopy/source/browse/trunk/geopy/distance.py#217?
Hence, you might want to take an iterative approach.
As stated here: https://stackoverflow.com/a/9078861/145400 you can calculate the distance between two given points like that:
pt1 = geopy.Point(48.853, 2.349)
pt2 = geopy.Point(52.516, 13.378)
# distance.distance() is the VincentyDistance by default.
dist = geopy.distance.distance(pt1, pt2).km
For going north by one kilometer you would iteratively change the latitude into a positive direction and check against the distance. You can automate this approach using a simple iterative solver from e.g. SciPy: just find the root of geopy.distance.distance().km - 1 via one of the optimizers listed in http://docs.scipy.org/doc/scipy/reference/optimize.html#root-finding.
I think it is clear that you go south by changing the latitude into a negative direction, and west and east by changing the longitude.
I have no experience with such geo calculations, this iterative approach only makes sense if there is no simple direct way for "going north" by a certain distance.
Edit: an example implementation of my proposal:
import geopy
import geopy.distance
import scipy.optimize
def north(startpoint, distance_km):
"""Return target function whose argument is a positive latitude
change (in degrees) relative to `startpoint`, and that has a root
for a latitude offset that corresponds to a point that is
`distance_km` kilometers away from the start point.
"""
def target(latitude_positive_offset):
return geopy.distance.distance(
startpoint, geopy.Point(
latitude=startpoint.latitude + latitude_positive_offset,
longitude=startpoint.longitude)
).km - distance_km
return target
start = geopy.Point(48.853, 2.349)
print "Start: %s" % start
# Find the root of the target function, vary the positve latitude offset between
# 0 and 2 degrees (which is for sure enough for finding a 1 km distance, but must
# be adjusted for larger distances).
latitude_positive_offset = scipy.optimize.bisect(north(start, 1), 0, 2)
# Build Point object for identified point in space.
end = geopy.Point(
latitude=start.latitude + latitude_positive_offset,
longitude=start.longitude
)
print "1 km north: %s" % end
# Make the control.
print "Control distance between both points: %.4f km." % (
geopy.distance.distance(start, end).km)
Output:
$ python test.py
Start: 48 51m 0.0s N, 2 21m 0.0s E
1 km north: 48 52m 0.0s N, 2 21m 0.0s E
Control distance between both points: 1.0000 km.
A 2020 update for this question, based on Dr. Jan-Philip Gehrcke's answer.
VincentyDistance is officially deprecated, and was never fully precise and sometimes inaccurate.
This snippet shows how to use with the latest (and future versions of GeoPy - Vincenty will be deprecated in 2.0)
import geopy
import geopy.distance
# Define starting point.
start = geopy.Point(48.853, 2.349)
# Define a general distance object, initialized with a distance of 1 km.
d = geopy.distance.distance(kilometers=1)
# Use the `destination` method with a bearing of 0 degrees (which is north)
# in order to go from point `start` 1 km to north.
final = d.destination(point=start, bearing=0)
final is a new Point object, which when printed, returns 48 51m 43.1721s N, 2 20m 56.4s E
Which as you can see is more accurate than Vincenty, and should maintain better accuracy near the poles.
Hope it helps!
I had to deal with adding meters to longitude and latitude.
Here's what I did, inspired by this source :
import math
from geopy.distance import vincenty
initial_location = '50.966086,5.502027'
lat, lon = (float(i) for i in location.split(','))
r_earth = 6378000
lat_const = 180 / math.pi
lon_const = lat_const / math.cos(lat * math.pi / 180)
# dx = distance in meters on x axes (longitude)
dx = 1000
new_longitude = lon + (dx / r_earth) * lon_const
new_longitude = round(new_longitude, 6)
new_latitude = lat + (dy / r_earth) * lat_const
new_latitude = round(new_latitude, 6)
# dy = distance on y axes (latitude)
new_latitude = lat + (dy / r_earth) * lat_const
new_latitude = round(new_latitude, 6)
new_location = ','.join([str(y_lat), str(x_lon)])
dist_to_location = vincenty(location, new_location).meters
my previous question (Integral of Intensity function in python)
you can see the diffraction model in image below:
I want to calculate integral of intensity in each pixel (square), so I can't use R and Theta as variable. How can I do this in X-Y coordinate.
Our function:
instead of sin(theta) we can use:
sintheta= (np.sqrt((x)**2 + (y)**2)/(np.sqrt((x)**2 + (y)**2 + d**2)))
Other constants:
lamb=550*10**(-9)
k=2.0*np.pi/lamb
a=5.5*2.54*10**(-2)
d=2.8
when you plot function, the result is something like this:(The image above is a view from top)
the method in previous topic:
calculate integrate of function in (0.0, dist) and after that * (2*np.pix) which x = ka*np.sin(theta), But now I want integrate in each pixel. which the previous method doesn't work, because this is X-Y coordinate , not polar.
Actually, the integration in Cartesian coordinates is rather straightforward. Now that you have the intensity function, you have to express the radius r by coordinates x and y. A trivial thing which you have actually done in your question.
So, the function to be integrated (without some constants) is:
from scipy import special as sp
# Fraunhofer intensity function (spherical aperture)
def f(x,y):
r = np.sqrt(x**2 + y**2)
return (sp.j1(r)/r)**2
Or by using the fact that 2 J1(x)/x = J0(x) + J2(x) [thanks, Jaime!]:
def f(x,y):
r = np.sqrt(x**2 + y**2)
return (sp.j0(r) + sp.jn(2,r))**2
This form is better in the sense that it does not have a singularity anywhere.
Now, I do not use any constant factors. You may add them if you want, but I find it easier to normalize with the integration result over infinite area. Otherwise it is too easy to just forget some constant (I do, usually).
The integration can be carried out with scipy.integrate.nquad. It accepts a multi-dimensional function to integrate. So, in this case:
import scipy.integrate
integral = scipy.integrate.nquad(f, ([-d/2, d/2], [-d/2, d/2]))[0]
However, as your function is very clearly symmetric, you might consider integrating over one quadrant only and then multiply by four:
4. * scipy.integrate.nquad(f, ([0, d/2], [0, d/2]))[0]
By using these the full intensity is:
>>> 4. * scipy.integrate.nquad(f, [[0,inf],[0,inf]])[0]
12.565472446489999
(Which looks very much like 4 pi, BTW.) Of course, you can also use the polar coordinates to calculate the full value, as the function has circular symmetry (as outlined in Integral of Intensity function in python). The different values are due to different scaling (2 pi omitted in the polar integration, 2 because I am using the sum form of the bessel functions here).
For example for a square area from -1..1 on both directions the normalized (divided by the above full power value) power over the square area is:
>>> 4*scipy.integrate.nquad(f, [[0,1],[0,1]])[0] / 12.565472446489999
0.27011854108867
So, approximately 27 % of the incoming light shines onto the square photodetector.
When it comes to your constants, it seems that something (at least units) is missing. My guess:
wavelength: 550 nm
circular aperture diameter: 0.0055" = 0.14 mm
distance from the aperture to the sensor: 2.8 mm
square sensor size 5.4 um x 5.4 um
The last one I just guessed from the image. As the sensor size is very much smaller than the distance, sin(ϴ) is very close to y/d, where d is distance and y displacement from the optical axis. By using these numbers x = ka sin(ϴ) = kay / d ≈ 1.54. For that number the intensity integral gives approximately 0.52 (or 52 %).
If you are comparing this to some experimental value, remember that there are numerous error sources. The image on the image plane is a fourier transform of the aperture. If there are small imperfections in the aperture edge, they may change the resulting spot. Airy rings are seldom as beautiful as astronomers think...
Just for fun:
EDIT: Just found out that I need to convert latitude, longitude and elevation of a location on earth to J2000 coordinates and nothing to do with ra/dec or the moon. Sorry for this. Your answers did give me a lot of insights. Please see the edited question below.
Question: how do i convert latitude, longitude and elevation to J2000 coordinates (XYZ). Is there a conversion present in ephem? I checked the docs but I couldnt find something I need (or mightve overlooked something due to my lack of knowledge in this field). Thanks
***************** OLD (Disregard) ******************
I have the moon position in Right Ascension (RA) and Declination (Dec) and I want to convert them into X Y Z coordinates. Is there a built-in PyEphem function for this? Also, what is the math behind it? Thanks.
EDIT: I am using the J2000 coordinate system (which is equatorial i think, this is my first time working with astronomy). I have the distance to moon available. The ra/dec values are already in J2000 (equatorial) coordinates.
X points North
Y points West
Z points towards the sky
Best answer:
It has just come to my attention that, in June 2011, the Naval Observatory released a Python interface to the powerful NOVAS reference software with which the highest-precision astronomical computations are performed:
http://aa.usno.navy.mil/software/novas/novas_py/novaspy_intro.php
With this library you can get the answer you are seeking, at far higher precision than PyEphem has ever offered:
from novas import compat as novas
jd_tt = novas.julian_date(2012, 9, 8, 12.00)
delta_t = 66.603 # from http://maia.usno.navy.mil/ser7/deltat.preds
lat = 42.3583 # positive is north
lon = -71.0603 # negative is west
observer = novas.make_observer_on_surface(lat, lon, 0, 0, 0)
print novas.geo_posvel(jd_tt, delta_t, observer)
On my machine this gives the answer:
((-3.5081406460494928e-06, 3.135277713623258e-05, 2.858239912567112e-05),
(-0.00019753847060164693, -2.2329942271278055e-05, 2.4885824275734915e-07))
Try it yourself and see if this gives you the kind of results that you need!
Newer answer:
It appears that the answer is “no” — PyEphem, to my surprise, gives no easy way to get the answer to the question "where, in x, y, z coordinates, is (say) Boston at time t ?”
This is a surprise because “libastro”, the library behind PyEphem, of course has to compute this internally in order to figure out where other objects are relative to an observer. It seems to do so in two places. In parallax.c it defines ta_par() which talks only about angles on the outside, but on its inside you can see that it temporarily computes the x, y, z of the observer. You can even see the important constant 298.257 hidden inside there, which measures how flat the earth is, since it is not a perfect sphere.
The other place is in earthsat.c which looks like a completely different code base from the rest of “libastro”, and so it duplicates some of the logic. Its EarthFlat constant of 298.25 is a bit less precise, but is doing the same job. And its function, GetSitPosition(), actually exposes x,y,z coordinates instead of keeping them hidden. But it is declared static so there is no way to call in to this useful function from outside!
So for the moment, PyEphem gives you no way to compute your x,y,z directly. But it does provide one important piece of information: the current sidereal time, which you will (I think) be able to use to figure how far around the earth Boston (or wherever) has traveled by time t, which will be important in figuring out your coordinates.
I will see if I can work up a quick solution in Python that combines the hour angle from PyEphem with some explicit trigonometry to get you an answer. But, for the moment, no: PyEphem does not expose this information directly, sadly enough; I will put it on the list of things for a future version!
Older answer, from when the question was about the x,y,z position of the Moon:
PyEphem does not, alas, have built-in functions for converting from the polar coordinates used in amateur astronomy to the x/y/z coordinates which will let you map out how objects are distributed in space around the Earth. But the conversion is easy to do yourself:
import ephem
import math
m = ephem.Mars('2012/8/1')
print m.ra, m.dec
x = math.cos(m.dec) * math.cos(m.ra)
y = math.cos(m.dec) * math.sin(m.ra)
z = math.sin(m.dec)
print x, y, z
print 'sanity check: vector length =', math.sqrt(x*x + y*y + z*z)
The output of this script is:
12:58:51.20 -6:24:05.6
-0.961178016954 -0.252399543786 -0.111495695074
sanity check: vector length = 1.0
The position of Mars for the random date that I used here are quite reasonable values: an RA that is almost one hour more than halfway around the great circle (since 12h would be exactly halfway), and a declination that pushes the position a bit south. Thus the x, y, and z that we get out: the z is a slightly negative number since -6° is indeed south of the equator, and x and y are both negative since going 13h around a 24h circle puts you down in the negative/negative quadrant of a normal unit circle.
Note that although J2000 has a north and south — so that we can truthfully say that the slightly negative z is a southward direction — it does not have an east and west, since the earth turning below it is constantly swinging east and west in all directions. Instead, RA measures from “the first point of Ares” which is the direction in which the sun lies during the spring Equinox. So x and y are not east or west; they are coordinates pointing out into the solar system on a fixed axis defined by the direction that the Earth sits in every Spring.
This x y z vector I have created is a “unit vector” — a little vector that has the magnitude 1.0, as I verified in the script to make sure I had the formulae correct. If you were computing x y and z coordinates for objects whose distance from the earth you knew, then you could get a real vector — whose magnitude were distances, instead of fractions of 1 — by multiplying each of the three x y and z by the distance to the object.
Does that help you out? From your description — and your question about east and west — I could not tell if you wanted RA and dec turned into x y z or whether you are actually wanting the azimuth and altitude converted (but the math is the same either way). That would look something like:
x = math.cos(m.alt) * math.cos(m.az)
y = math.cos(m.alt) * math.sin(m.az)
z = math.sin(m.alt)
What are you trying to accomplish with these coordinates? That could help us make sure that we are giving them to you in a useful format.
I suggest you look at the PyEphem documentation for coordinate conversion.
Basically, PyEphem only deals with three coordinate systems - equatorial, ecliptic, and galactic - each defined by two angles and an epoch (adjustment offset for polar progression).
Depending on what your coordinate scheme looks like, you should be able to use trigonometry to convert to it if you also have the object's distance.
Edit: your "X-Y-Z" coordinates seem to be lefthanded ecliptic coordinates.
from ephem import Equatorial, Ecliptic, degree
def convert_equatorial_to_XYZ(ra, dec, dist=1.0, epoch='2000'):
"""
Given
ra right ascension (in hours)
dec declination (in degrees)
dist distance (optional, defaults to 1.0)
epoch epoch (optional, assumes J2000)
Return
degrees North, degrees West, distance
"""
eq = Equatorial(ra, dec, epoch=epoch)
ec = Ecliptic(eq)
return ec.lat/degree, 360.0 - ec.lon/degree, dist