Converting xy coordinates in relation to pixels to latitude and longitude - python

Here is my situation:
The company I work for are using some third party software that plots the location of devices on a wifi network. The software requires you to upload a floor plan of the area (this is just a 2000 x 1187 px jpeg). The software spits out xy coordinates for every device location which is visualised over the floor plan.
I have been asked to take these xy coordinates and display them using the Google maps API, so therefore need to somehow convert them to latitude and longitude.
The third party software uses the pixel dimensions of the jpeg as reference to calculating the xy coordinate, according to the limited documentation.
I have attempted to use Python and the pyproj module to make the conversion (there are over half a million coordinates to convert), but the results just seem to plot the lat long in roughly the same position every time.
Here is what I have so far:
projection = Proj("+proj=lcc +lat_1=53.466082 +lat_2=53.464971 +lat_0=53.465482 +lon_0=-2.338944 +x_0=0 +y_0=0 +no_defs +a=6378137 +rf=298.257222101 +to_meter=1 +k_0=200")
wgs84 = Proj(init='epsg:4326')
lat = xCoordinate
lon = yCoordinate
xx, yy = transform(projection, wgs84, lat, lon)
latLong = {'lat':xx, 'long':yy}
return latLong
I admittedly have very little experience with mapping etc and have very little time to get my head around the topic.
I hope this is enough information to go on. If anyone can help in pointing me in the right direction then I would be eternally grateful.
Cheers.

Related

What's the fastest way of calculating if satellite is in camera FOV

I need to find fastest way of checking if satelllite (given its TLE) is visible from camera (given its position on Earth - longitude and latitude - and its field of view). Field of view of camera is defined as a rectangle with 15deg width, 60deg height and center in some known altitude and azimuth.
TLDR:
My first attempt was to use PyEphem (https://rhodesmill.org/pyephem/) to calculate satellites ALT and AZ relative to observer and to check if it is not eclipsed. To check if this satellite is in FOV I used astropy Regions (https://astropy-regions.readthedocs.io/en/stable/) to construct Rectangular Sky Region. It was working fine but I have 5400 satellites in TLE and 24 diffrently positioned cameras and this metod turned to be too slow.
In more details:
I need to simulate satellite observations with widefield cameras (15deg width, 60deg height) placed all around the globe. I tried to define each camera as "station" object with known position and FOV defined as RectangularSkyRegion from astropy regions:
region = RectangleSkyRegion(center = SkyCoord(az_mid, alt_mid, unit = "deg"), width = 15 *u.deg, height = 60 *u.deg)
then using PyEphem I was iterating over time:
time = ephem.Date("2021/09/09 19:30:00.00")
for j in range(12*60*60*24*30):
date = ephem.Date(time + 5*j*ephem.second)
and now I don't really know what to do, because I need to check for few things. First I need to check if there is night where camera is, I do this with help from PyEphem. I just simply check if time of previous sunset is bigger than time of previous sunrise. I also need to check if a satellite is above horizon realtive to observer and if it is eclipsed. I can do that quite simply but first I need to compute every satellite position realtive to observer which is slow (5400 diffrent satellites and 24 diffrent observers). There is a way in PyEphem to compute satellite position knowing only time of observation, but this will give me geocentric latitude and longitude angles of subpoint and elevation above sea level or right ascension and declination which I would have to convert to ALT and AZ to be of any use for my problem.
Ideally I would want to compute satellite position only once per time iteration and then check if it falls in any of my FOV, unfortunately I do not know a way to efficently convert geocentric subpoints and elevation to ALT AZ or to define FOV diffrently.

Convert Latitude, Longitude and Altitude to point in a flat 3D space

I have a camera and I want to identify specific points in the world with it. I want it to be the more precise as it can be. I tried to convert to (x, y, z) using the answer in this: Lat Long to X Y Z position in JS .. not working. But the thing is, I don't know how to affect the camera angles (yaw, pitch, roll) with the curvature of the earth, so it gets kind of weird. So I was thinking of converting it to a flat 3D representation and convert altitude, latitude, and altitude without the curvature of the earth to (x, y, z) so that I can see all the points in front.
Is this a good idea? If not, how can I affect the angles of the camera depending on the position in the world so that I always have the right perspective from the camera?
You can use this python library https://pypi.python.org/pypi/nvector, which also adheres to the WGS84 standard. It will convert lat long to x,y,z coordinates.
wgs84 = nv.FrameE(name='WGS84')
wgs84.GeoPoint(latitude=latitude, longitude=longitude, z=1, degrees=True)

Why are Basemap south polar stereographic map projection coordinates not agreeing with those of data sets in the same projection?

Some satellite based earth observation products provide latitude/longitude information while others provide the X/Y coordinates within a given grid projection (and there are also some having both, see example).
My approach in the second case is to set up a Basemap map which has the same parameters (projection, ellipsoid, origin of map) as given by the data provider in a way that the given X/Y values equal the Basemap coordinates. However if I do so the geolocation does not agree with other data sets including the Basemap coastline.
I have experienced this with three different data sets from different trustworthy sources. For the minimal example I use Landsat data provided by the U.S. Geological Survey which includes both, X/Y coordinates of a South Polar Stereographic grid and the corresponding lat/lon coordinates for all four corners of the image.
From a Landsat metafile we get (ID: LC82171052016079LGN00):
CORNER_UL_LAT_PRODUCT = -66.61490 CORNER_UL_LON_PRODUCT = -61.31816
CORNER_UR_LAT_PRODUCT = -68.74325 CORNER_UR_LON_PRODUCT = -58.04533
CORNER_LL_LAT_PRODUCT = -67.68721 CORNER_LL_LON_PRODUCT = -67.01109
CORNER_LR_LAT_PRODUCT = -69.94052 CORNER_LR_LON_PRODUCT = -64.18581
CORNER_UL_PROJECTION_X_PRODUCT = -2259300.000
CORNER_UL_PROJECTION_Y_PRODUCT = 1236000.000
CORNER_UR_PROJECTION_X_PRODUCT = -1981500.000
CORNER_UR_PROJECTION_Y_PRODUCT = 1236000.000
CORNER_LL_PROJECTION_X_PRODUCT = -2259300.000
CORNER_LL_PROJECTION_Y_PRODUCT = 958500.000
CORNER_LR_PROJECTION_X_PRODUCT = -1981500.000
CORNER_LR_PROJECTION_Y_PRODUCT = 958500.000
...
GROUP = PROJECTION_PARAMETERS MAP_PROJECTION = "PS" DATUM = "WGS84"
ELLIPSOID = "WGS84" VERTICAL_LON_FROM_POLE = 0.00000 TRUE_SCALE_LAT =
-71.00000 FALSE_EASTING = 0 FALSE_NORTHING = 0 GRID_CELL_SIZE_PANCHROMATIC = 15.00 GRID_CELL_SIZE_REFLECTIVE = 30.00
GRID_CELL_SIZE_THERMAL = 30.00 ORIENTATION = "NORTH_UP"
RESAMPLING_OPTION = "CUBIC_CONVOLUTION" END_GROUP =
PROJECTION_PARAMETERS
By using Basemap with the right map projection we should be able to derive the corner lat/lon values from the X/Y values:
import numpy as np
from mpl_toolkits.basemap import Basemap
m=Basemap(resolution='h',projection='spstere', ellps='WGS84', boundinglat=-60,lon_0=180, lat_ts=-71)
x_crn=np.array([-2259300,-1981500,-2259300,-1981500])# upper left, upper right, lower left, lower right
y_crn=np.array([1236000, 1236000, 958500, 958500])# upper left, upper right, lower left, lower right
x0, y0= m(0, -90)
#Basemap coordinates at the south pole
#note that (0,0) of the Basemap is in a corner of the map,
#while other data sets use the south pole.
#This is easy to take into account:
lon_crn, lat_crn = m(x0-x_crn, y0-y_crn, inverse=True)
print 'lon_crn: '+str(lon_crn)
print 'lat_crn: '+str(lat_crn)
Which returns:
lon_crn: [-61.31816102 -58.04532791 -67.01108782 -64.1858106 ]
lat_crn: [-67.23548626 -69.3099076 -68.28071626 -70.47651326]
As you can see the longitudes agree to the given precision with those from the metafile, but the latitudes are to low.
I can approximate the latitudes by:
lat_crn=(lat_crn+90.)*1.0275-90.
But this is really not satisfying.
This is how the image is located if using the X/Y corner coordinates from the metafile (in red the Basemap drawcoastlines()):
and this is how it looks like using the corner lat/lon:
In this case I can simply use the lat/lon coordinates, but as mentioned before there are datasets (like this) which is provided by X/Y coordinates only, which makes it very important to rely on the Basemap projection. I know that there are other modules to re-project the data as a potential workaround, but it should work without other modules and a re-projection could introduce errors itself.
As this problem appears with different data sets I like to believe that it is a bug in the Basemap module, but I might also make the same mistake again and again or have wrong expectations.
I did some experimentation and it seems like changing lat_ts has no effect with projection='spstere'. In fact, it seems as if the projection latitude is implicitly assumed to be lat_ts=-90. regardless of what value you assign.
I had more success using projection='stere' instead, so that you would construct the Basemap in your example as follows:
m=Basemap(width=5400000., height=5400000., projection='stere',
ellps='WGS84', lon_0=180., lat_0=-90., lat_ts=-71.)
You may prefer to set the latitude and longitude of the corners instead of the width and height of the plot for your application.

Getting J2000 XYZ coordinates for a location on earth in Python

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

Obtain Latitude and Longitude from a GeoTIFF File

Using GDAL in Python, how do you get the latitude and longitude of a GeoTIFF file?
GeoTIFF's do not appear to store any coordinate information. Instead, they store the XY Origin coordinates. However, the XY coordinates do not provide the latitude and longitude of the top left corner and bottom left corner.
It appears I will need to do some math to solve this problem, but I don't have a clue on where to start.
What procedure is required to have this performed?
I know that the GetGeoTransform() method is important for this, however, I don't know what to do with it from there.
To get the coordinates of the corners of your geotiff do the following:
from osgeo import gdal
ds = gdal.Open('path/to/file')
width = ds.RasterXSize
height = ds.RasterYSize
gt = ds.GetGeoTransform()
minx = gt[0]
miny = gt[3] + width*gt[4] + height*gt[5]
maxx = gt[0] + width*gt[1] + height*gt[2]
maxy = gt[3]
However, these might not be in latitude/longitude format. As Justin noted, your geotiff will be stored with some kind of coordinate system. If you don't know what coordinate system it is, you can find out by running gdalinfo:
gdalinfo ~/somedir/somefile.tif
Which outputs:
Driver: GTiff/GeoTIFF
Size is 512, 512
Coordinate System is:
PROJCS["NAD27 / UTM zone 11N",
GEOGCS["NAD27",
DATUM["North_American_Datum_1927",
SPHEROID["Clarke 1866",6378206.4,294.978698213901]],
PRIMEM["Greenwich",0],
UNIT["degree",0.0174532925199433]],
PROJECTION["Transverse_Mercator"],
PARAMETER["latitude_of_origin",0],
PARAMETER["central_meridian",-117],
PARAMETER["scale_factor",0.9996],
PARAMETER["false_easting",500000],
PARAMETER["false_northing",0],
UNIT["metre",1]]
Origin = (440720.000000,3751320.000000)
Pixel Size = (60.000000,-60.000000)
Corner Coordinates:
Upper Left ( 440720.000, 3751320.000) (117d38'28.21"W, 33d54'8.47"N)
Lower Left ( 440720.000, 3720600.000) (117d38'20.79"W, 33d37'31.04"N)
Upper Right ( 471440.000, 3751320.000) (117d18'32.07"W, 33d54'13.08"N)
Lower Right ( 471440.000, 3720600.000) (117d18'28.50"W, 33d37'35.61"N)
Center ( 456080.000, 3735960.000) (117d28'27.39"W, 33d45'52.46"N)
Band 1 Block=512x16 Type=Byte, ColorInterp=Gray
This output may be all you need. If you want to do this programmaticly in python however, this is how you get the same info.
If the coordinate system is a PROJCS like the example above you are dealing with a projected coordinate system. A projected coordiante system is a representation of the spheroidal earth's surface, but flattened and distorted onto a plane. If you want the latitude and longitude, you need to convert the coordinates to the geographic coordinate system that you want.
Sadly, not all latitude/longitude pairs are created equal, being based upon different spheroidal models of the earth. In this example, I am converting to WGS84, the geographic coordinate system favoured in GPSs and used by all the popular web mapping sites. The coordinate system is defined by a well defined string. A catalogue of them is available from spatial ref, see for example WGS84.
from osgeo import osr, gdal
# get the existing coordinate system
ds = gdal.Open('path/to/file')
old_cs= osr.SpatialReference()
old_cs.ImportFromWkt(ds.GetProjectionRef())
# create the new coordinate system
wgs84_wkt = """
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84",6378137,298.257223563,
AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],
UNIT["degree",0.01745329251994328,
AUTHORITY["EPSG","9122"]],
AUTHORITY["EPSG","4326"]]"""
new_cs = osr.SpatialReference()
new_cs .ImportFromWkt(wgs84_wkt)
# create a transform object to convert between coordinate systems
transform = osr.CoordinateTransformation(old_cs,new_cs)
#get the point to transform, pixel (0,0) in this case
width = ds.RasterXSize
height = ds.RasterYSize
gt = ds.GetGeoTransform()
minx = gt[0]
miny = gt[3] + width*gt[4] + height*gt[5]
#get the coordinates in lat long
latlong = transform.TransformPoint(minx,miny)
Hopefully this will do what you want.
I don't know if this is a full answer, but this site says:
The x/y map dimensions are called easting and northing. For datasets in a geographic coordinate system these would hold the longitude and latitude. For projected coordinate systems they would normally be the easting and northing in the projected coordinate system. For ungeoreferenced images the easting and northing would just be the pixel/line offsets of each pixel (as implied by a unity geotransform).
so they may actually be longitude and latitude.

Categories

Resources