Remapping XYZ spherical coordinates to Lat/Long coordinates - coordinate-transformation

I have a surface of points in XYZ (all Z are 0), and I map these points onto a Sphere.
So I have an XYZ coordinate for each point on the sphere.
What I want to achieve is the convert this XYZ sphere coordinate to its Lat/Long parallel, in terms of the Geographic location. So for example if I have the point (0,0,0) I want to see how to represent it as the South Pole at Lat/Long (90.0000° S, 45.0000° E).
I searched online and there are endless webpages that explain parts of this, but a lot of them talk about converting Lat/Long to XYZ and not the other way around.
I'm not sure if there is a straightforward formula to convert one to another.
Hopefully someone can point me in the right direction.
Thanks

There is a so-called local ENU (east north up) coordinate system that complies with the one you call XYZ. ENU can be converted to earth-centered XeYeZe. From that XeYeZe, you can convert to (lat,long,H).
In Python you can use pymap3d to do all your required computation. Here is a runnable code that you can try.
import pymap3d
ell_wgs84 = pymap3d.Ellipsoid('wgs84')
# Your ENU system needs origin definition (lat0, lon0, h0) +
# and also needs a reference ellipsoid: let's use `ell_wgs84` defined above
lat0, lon0, h0 = -90, 45, 0 # origin of ENU, (h is height above ellipsoid)
# Test ENU coordinates: (e1, n1, u1) by `enu2geodetic()`
e1, n1, u1 = 0.0, 0.0, 0.0 # just the origin of this ENU system
lat1, lon1, h1 = pymap3d.enu2geodetic(e1, n1, u1, \
lat0, lon0, h0, \
ell=ell_wgs84, deg=True) # use wgs86 ellisoid
# this should agree with: (lat0, lon0, h0)
print(lat1, lon1, h1) # -90.0 44.99999999999999 1.313839409243646e-12 OK!
# Inversion check by `geodetic2enu()`
# input values to convert: lat1, lon1, h1
e1k, n1k, u1k = pymap3d.geodetic2enu(lat1, lon1, h1, lat0, lon0, h0, ell=ell_wgs84, deg=True)
print(e1k, n1k, u1k) # 0,0,0 OK
# Now arbitrary ENU to lat/long and reverse
lat112, lon112, h112 = pymap3d.enu2geodetic(1120, 100, 10, \
lat0, lon0, h0, \
ell=ell_wgs84, deg=True)
print(lat112, lon112, h112)
# Check
e112k, n112k, u112k = pymap3d.geodetic2enu(lat112, lon112, h112, lat0, lon0, h0, ell=ell_wgs84, deg=True)
print(e112k, n112k, u112k) # 1120, 100, 10 OK

Related

Ground longitude/latitude under a satellite (cartesian coordinates) at a specfic epoch

The script I'm wanting to develop uses the cartesian coordinates (XYZ) from a satellite, and in conjunction with the range, elevation and azimuth from a location, I then take a satellite’s orbital information and get the ground longitude/latitude under that satellite at a given time.
One step further from this: imagne the signal from a satellite piercing the atmosphere at exactly 300km above sea level. At this particular point when altitude is 300km, I need to calculate the ground longitude/latitude.
In the pyemph module there appears to be already a method (ephem.readtle) that can achieve this, but for TLE (two line element) data only. I'd like to use a satellite's cartesian coordinates to develop this. Is there such a method already out there? Or perhaps somebody with experience in this
domain can point me in the right direction.
A similar question already exists referring to ECEF from Azimuth, Elevation, Range and Observer Lat,Lon,Alt, but it's not the same problem.
Here's what I have developed already:
- satellite cartesian coordinates, XYZ
- azimuth, elevation and range of satellite from ground station
- ground station coordinates in lat, long, height above sea level
Here's what I need:
- ground longitude/latitude under a satellite at a specific epoch, and in particular where the piercing point in the atmosphere (the point which the signal from the satellite pierces the atmosphere) is 300km altitude.
I found what I was looking for via this:
def ionospheric_pierce_point(self, dphi, dlambda, ele, azi):
Re = 6378136.3 # Earth ellipsoid in meters
h = cs.SHELL_HEIGHT * 10**3 # Height of pierce point meters, and where maximum electron density is assumed
coeff = Re / (Re + h)
lat_rx = dphi
long_rx = dlambda
# Degrees to radians conversions
ele_rad = np.deg2rad(ele)
azi_rad = np.deg2rad(azi)
lat_rx_rad = np.deg2rad(lat_rx)
long_rx_rad = np.deg2rad(long_rx)
psi_pp = (np.pi / 2) - ele_rad - np.arcsin(coeff * np.cos(ele_rad)) # Earth central angle between user and the Eart projection of the pierce point, in radians
psi_pp_deg = np.rad2deg(psi_pp)
lat_pp = np.arcsin(np.sin(lat_rx_rad)*np.cos(psi_pp) +
np.cos(lat_rx_rad)*np.sin(psi_pp)*np.cos(azi_rad)) # in radians
if (lat_rx > 70 and ((np.tan(psi_pp)*np.cos(azi_rad)) > np.tan((np.pi/2) - lat_rx_rad))) or (lat_rx < -70 and ((np.tan(psi_pp)*np.cos(azi_rad + np.pi)) > np.tan((np.pi/2) + lat_rx_rad))):
long_pp = long_rx_rad + np.pi - np.arcsin((np.sin(psi_pp)*np.sin(azi_rad)) / np.cos(lat_pp))
else:
long_pp = long_rx_rad + np.arcsin((np.sin(psi_pp)*np.sin(azi_rad)) / np.cos(lat_pp))
lat_pp_deg = np.rad2deg(lat_pp)
long_pp_deg = np.rad2deg(long_pp)
return lat_pp_deg, long_pp_deg

orientation of normal surface/vertex vectors

Given a convex 3d polygon (convex hull) How can I determine the correct direction for normal surface/vertex vectors? As the polygon is convex, by correct I mean outward facing (away from the centroid).
def surface_normal(centroid, p1, p2, p3):
a = p2-p1
b = p3-p1
n = np.cross(a,b)
if **test including centroid?** :
return n
else:
return -n # change direction
I actually need the normal vertex vectors as I am exporting as a .obj file, but I am assuming that I would need to calculate the surface vectors before hand and combine them.
This solution should work under the assumption of a convex hull in 3d. You calculate the normal as shown in the question. You can normalize the normal vector with
n /= np.linalg.norm(n) # which should be sqrt(n[0]**2 + n[1]**2 + n[2]**2)
You can then calculate the center point of your input triangle:
pmid = (p1 + p2 + p3) / 3
After that you calculate the distance of the triangle-center to your surface centroid. This is
dist_centroid = np.linalg.norm(pmid - centroid)
The you can calculate the distance of your triangle_center + your normal with the length of the distance to the centroid.
dist_with_normal = np.linalg.norm(pmid + n * dist_centroid - centroid)
If this distance is larger than dist_centroid, then your normal is facing outwards. If it is smaller, it is pointing inwards. If you have a perfect sphere and point towards the centroid, it should almost be zero. This may not be the case for your general surface, but the convexity of the surface should make sure, that this is enough to check for its direction.
if(dist_centroid < dist_with_normal):
n *= -1
Another, nicer option is to use a scalar product.
pmid = (p1 + p2 + p3) / 3
if(np.dot(pmid - centroid, n) < 0):
n *= -1
This checks if your normal and the vector from the mid of your triangle to the centroid have the same direction. If that is not so, change the direction.

Determine angle of a straight line in 3D space

I have a straight line in space with an start and end point (x,y,z) and I am attempting to get the angle between this vector and the plane defined by z=0. I am using VB.NET
Here is a picture of the line in my 3d environment (the line I'm intersted in is circled in red) :
It is set to an angle of 70 degrees right now.
You need 2 rays to define an angle.
If you want the angle between a vector and a plane, it is defined for any vector in that plane. However, there is only one minimal value for that, which is the angle between a vector and its projection onto said plane.
Therefore, that minimal value is the one we take when we speak of the angle between a vector and a plane.
This value is also π/2 - the angle between your vector and the the vector that is normal to the plane.You can read more about it all on this site.
With v your vector (thus v.x = end.x - start.x and idem for y and z), n the normal to the plane and a the angle you are looking for, we know from the definition of a scalar product that:
<v,n> = ||v|| * ||n|| * cos(π/2 - a)
We know cos(π/2 - a) = sin(a), and the normal to the z=0 plane is simply the vector n = (0, 0, 1). Thus both the scalar product, v.x * n.x + v.y * n.y + v.z * n.z, and the norm of n, ||n|| = 1, can be simplified a lot. We get the following expression:
sin(a) = v.z / ||v||
Thus finally, the formula by taking the reciprocical of the sine and expliciting the norm of v:
a = Asin(v.z / sqrt( v.x*v.x + v.y*v.y + v.z*v.z ))
According to this documentation the Asin function exists in your System.Math class. It does, however, return the value in radians:
Return Value
Type: System.Double
An angle, θ, measured in radians, such that -π/2 ≤ θ ≤ π/2
-or-
NaN if d < -1 or d > 1 or d equals NaN.
Luckily the same System.Math class contains the value of π so that you can do the conversion:
a *= 180 / Math.PI

Stata - Multiple rotated plots on graph (including distributions on sides of axes)

I would like to produce a single graph containing both: (1) a scatter plot (2) either histograms or kernel density functions of the Y and X variables to the left of the Y axis and below the X axis.
I found a graph that does this in MATLAB -- I would just like to produce something similar in Stata:
That graph was produced using the following MATLAB code:
n = 1000;
rho = .7;
Z = mvnrnd([0 0], [1 rho; rho 1], n);
U = normcdf(Z);
X = [gaminv(U(:,1),2,1) tinv(U(:,2),5)];
[n1,ctr1] = hist(X(:,1),20);
[n2,ctr2] = hist(X(:,2),20);
subplot(2,2,2); plot(X(:,1),X(:,2),'.'); axis([0 12 -8 8]); h1 = gca;
title('1000 Simulated Dependent t and Gamma Values');
xlabel('X1 ~ Gamma(2,1)'); ylabel('X2 ~ t(5)');
subplot(2,2,4); bar(ctr1,-n1,1); axis([0 12 -max(n1)*1.1 0]); axis('off'); h2 = gca;
subplot(2,2,1); barh(ctr2,-n2,1); axis([-max(n2)*1.1 0 -8 8]); axis('off'); h3 = gca;
set(h1,'Position',[0.35 0.35 0.55 0.55]);
set(h2,'Position',[.35 .1 .55 .15]);
set(h3,'Position',[.1 .35 .15 .55]);
colormap([.8 .8 1]);
UPDATE: The Stata13 manual entry for "graph combine" has precisely this example (http://www.stata.com/manuals13/g-2graphcombine.pdf). Here is the code:
use http://www.stata-press.com/data/r13/lifeexp, clear
generate loggnp = log10(gnppc)
label var loggnp "Log base 10 of GNP per capita"
scatter lexp loggnp, ysca(alt) xsca(alt) xlabel(, grid gmax) fysize(25) saving(yx)
twoway histogram lexp, fraction xsca(alt reverse) horiz fxsize(25) saving(hy)
twoway histogram loggnp, fraction ysca(alt reverse) ylabel(,nogrid) xlabel(,grid gmax) saving(hx)
graph combine hy.gph yx.gph hx.gph, hole(3) imargin(0 0 0 0) graphregion(margin(l=22 r=22)) title("Life expectancy at birth vs. GNP per capita") note("Source: 1998 data from The World Bank Group")
There's probably a better a way to do it, but this is my quick attempt to take up the challenge.
sysuse auto,clear
set obs 1000
twoway scatter mpg price, saving(sct,replace) ///
xsc(r(0(5000)20000) off ) ysc(r(10(10)50) off) ///
xti("") yti("") xlab(,nolab) ylab(,nolab)
kdensity mpg, n(1000) k(gauss) gen(x0 d0)
line x0 d0, xsc(rev off) ysc(alt) xlab(,nolab) xtick(,notick) saving(hist0, replace)
kdensity price, n(1000) k(gauss) gen(x1 d1)
line d1 x1, xsc(alt) ysc(rev off) ylab(,nolab) ytick(,notick) saving(hist1, replace)
graph combine hist0.gph sct.gph hist1.gph, cols(2) holes(3)
I'd also like to know if there are ways to improve on it. The codes are not very neat, and I had trouble properly aligning the line plot and the scatter plot without removing the ticks and labels of the scatter plot (xcommon and ycommon did not really do the job for me).
credit to this post on the Statalist.

Plotting point perpendicular to line half way through cocos2d

So I have a line that is plotted between two points. Lets say A and B, I can grab the mid point of the line in Cocos2d really easily and I also can calculate the vector and the perpendicular vector to this line quite easily. However my math skills are very rusty and I have no idea how to do the following.
Lets say the distance between A and B is 50, so the midpoint is 25. I would like to plot a point that is perpendicular to this line with a distance of 10 away from it.
C
/ \
/ \
/ \
/ \
/ \
A------------B
Sorry for the terrible example, but I'm not sure how to do this. Also the AB line is always at some angle, it's never straight like it is here.
Given the midpoint m and the perpendicular vector v, you need to normalize v and then move in the direction of v from m. So something like this:
Vector2d nv = v / v.length(); // Assuming Vector2d is your vector class and length gives the length of v
Point2d newPoint = m + (nv * 10.0); // Assumes you can multiply a vector by a scalar
If you aren't working in C++, you may have to write it manually like this:
Vector2d nv;
nv.x = v.x / v.length();
nv.y = v.y / v.length();
newPoint.x = m.x + nv.x * 10.0;
newPoint.y = m.y + nv.y * 10.0;