Convert Latitude longitude to meters / feet in ( haversine in python ) [duplicate] - python

I want to compute the distance between two lon / lat points by using Geod class from pyproj library.
from pyproj import Geod
g = Geod(ellps='WGS84')
lonlat1 = 10.65583081724002, -7.313341167341917
lonlat2 = 10.655830383300781, -7.313340663909912
_, _, dist = g.inv(lonlat1[0], lonlat1[1], lonlat2[0], lonlat2[1])
I get the following error :
ValueError Traceback (most recent call last)
<ipython-input-5-8ba490aa5fcc> in <module>()
----> 1 _, _, dist = g.inv(lonlat1[0], lonlat1[1], lonlat2[0], lonlat2[1])
/usr/lib/python2.7/dist-packages/pyproj/__init__.pyc in inv(self, lons1, lats1, lons2, lats2, radians)
558 ind, disfloat, dislist, distuple = _copytobuffer(lats2)
559 # call geod_inv function. inputs modified in place.
--> 560 _Geod._inv(self, inx, iny, inz, ind, radians=radians)
561 # if inputs were lists, tuples or floats, convert back.
562 outx = _convertback(xisfloat,xislist,xistuple,inx)
_geod.pyx in _geod.Geod._inv (_geod.c:1883)()
ValueError: undefined inverse geodesic (may be an antipodal point)
Where does this error message come from ?

Those two points are only a few centimetres apart. It looks like pyproj / Geod doesn't cope well with points which are that close together. That's a bit strange, since simple plane geometry is more than adequate at such distances. Also, that error message is a bit suspicious, since it's suggesting that the two points are antipodal, i.e., diametrically opposite, which is clearly not the case! OTOH, maybe the antipodal point it mentions is some intermediate point that arises somehow in the calculation... Still, I'd be rather hesitant in using a library that behaves like this.
Given this defect, I suspect that pyproj has other flaws. In particular, it probably uses the old Vincenty's formulae for its ellipsoid geodesic calculations, which is known to be unstable when dealing with near-antipodal points, and not particularly accurate over large distances. I recommend using the modern algorithms of C. F. F. Karney.
Dr Karney is a major contributor to the Wikipedia articles on geodesics, in particular Geodesics on an ellipsoid, and his geographiclib is available on PyPi, so you can easily install it using pip. See his SourceForge site for further information, and geographiclib binding in other languages.
FWIW, here's a short demo of using geographiclib to compute the distance in your question.
from geographiclib.geodesic import Geodesic
Geo = Geodesic.WGS84
lat1, lon1 = -7.313341167341917, 10.65583081724002
lat2, lon2 = -7.313340663909912, 10.655830383300781
d = Geo.Inverse(lat1, lon1, lat2, lon2)
print(d['s12'])
output
0.07345528623159624
That figure is in metres, so those two points are a little over 73mm apart.
If you'd like to see geographiclib being used to solve a complex geodesic problem, please see this math.stackexchange answer I wrote last year, with Python 2 / 3 source code on gist.
Hopefully, this is no longer an issue, since pyproj now uses code from geographiclib.

Related

Get New GPS with Known start GPS, bearing and distance in feet

I am brand new to Python in general and have a limited but aged knowledge of Javascript. I am trying to accomplish a small project using Python, but I have gotten stuck with my own comprehension.
I have a GPS coordinate in decimal degrees as a starting point (30.456025341663068, -86.41408883615411), a distance in feet (86 feet) from the start point to the expected endpoint and I have a bearing in degrees (0 to 360) from start point to endpoint. Given these values, I am attempting to simply return the GPS coordinate in decimal degrees of the endpoint.
I found one StackExchange post that appears to give me a viable option using GeoPy, but I'm simply getting lost in trying to implement it. Can someone assist with how to accomplish this? GeoPy is not a requirement for me, I just imported it based on the answer in the other StackExchange question:
calculating a gps coordinate given a point, bearing and distance
Perhaps you could do something like this:
import geopy
import geopy.distance
lat = 30.456025341663068
lon = -86.41408883615411
distance_ft = 86
bearing = 0
start_point = geopy.Point(lat, lon)
end_point = geopy.distance.geodesic(feet=distance_ft).destination(start_point, bearing)
print(end_point.latitude, end_point.longitude)
This should output something like:
30.456261790886277 -86.41408883615411
You can then also use the geodesic method to calculate the distance between the points:
print(geopy.distance.geodesic(start_point, end_point).feet)
And get something like:
86.0000000020017
Alternative answer which is not using geopy
from pyproj import Geod
g = Geod(ellps="WGS84")
lat = 30.456025341663068
long = -86.41408883615411
feet_in_meters = .3048
distance_in_feet = 86
distance_in_meters = distance_in_feet * feet_in_meters
long_destination, lat_destination, azi_back = g.fwd(lons=long, lats=lat, az=0, dist=distance_in_meters)
which print(lat_destination, long_destination)
30.456261790886273 -86.41408883615411

What is wrong with my geopy great circle distance computation?

I want to compute the distance (in km) using geopy library between two points defined by their respective (lat, lon) coordinates.
My code
from geopy.distance import great_circle
# lat, lon
p1 = (45.8864, -7.2305)
p2 = (46.2045, -7.2305)
# distance in km
great_circle(p1, p2).km
>>> 35.371156132664765
To check above results, I used the tool available here: https://www.movable-type.co.uk/scripts/latlong.html but the two outputs do not match.
The output of my code is 35.371156132664765 though the above tool returns a distance of 15.41 km.
How come the results are different ?
Your calculation for the points p1 and p2 is wrong, you need to convert the minutes and seconds into degree correctly. Otherwise the code works prefectly.

Alternative of ST_DWithin of PostGIS in python shapely

I've two linestrings Line1,Line2.
line1 = "LINESTRING(72.863221 18.782499,72.863736 18.770147,72.882275 18.756169,72.881417 18.750805,72.878842 18.736987,72.874379 18.709512,72.860989 18.679593,72.864422 18.653897)"
line2 = "LINESTRING(72.883133 18.780793,72.882103 18.760314,72.862534 18.716422,72.860474 18.683577)"
I'm trying to perform the following query of POSTGIS in shapely. As of now I haven't been able to find the alternative of ST_DWithin command.
road2 = "ST_GeographyFromText('SRID=4326;%s')"%line1
road4 = "ST_GeographyFromText('SRID=4326;%s')"%line2
cur.execute("SELECT ST_AsText(road1) from %s as road1,%s as road2
where ST_DWithin(road1,road2,500)"%(road2,road4))
res = cur.fetchall()
print res
Does anyone knows what is the alternative of ST_DWithin in shapely ?
As far as I know, shapely only supports operations in planar coordinates (no geography types). However, for LineStrings which are not too large so that the curvature of the globe can be neglected, one could partially "circumvent" this by:
working in some planar projection (for example directly in the lat/lon or lon/lat coordinates)
following the second note in the documentation of ST_DWithin and the first note in the documentation of ST_Expand, i.e.:
checking if the bounding box of the second LineString intersects with the expanded bounding box of the first LineString
if yes, checking if the minimum distance is indeed below the prescribed threshold
For example:
from shapely.wkt import dumps, loads
from shapely.geometry.geo import box
spec = [
"LINESTRING(72.863221 18.782499,72.863736 18.770147,72.882275 18.756169,72.881417 18.750805,72.878842 18.736987,72.874379 18.709512,72.860989 18.679593,72.864422 18.653897)",
"LINESTRING(72.883133 18.780793,72.882103 18.760314,72.862534 18.716422,72.860474 18.683577)"
]
lines = list(map(loads, spec))
eta = 0.005
b1 = box(*lines[0].bounds).buffer(eta)
b2 = box(*lines[1].bounds)
flag = b2.intersects(b1) and (lines[0].distance(lines[1]) < eta)
print(eta, flag)
Alternatively, if you would like to check if the entire second LineString is within prescribed threshold from the first LineString, you could also use the buffer method as:
lines[0].buffer(eta).contains(lines[1])
The threshold supplied here to the buffer method is expressed in the same coordinate system in which the LineStrings are defined. Within the lon/lat system, this would represent the "central angle" - the issue then consists in the fact that the great circle distance corresponding to a fixed eta not only depends on the particular values of latitude and longitude but also on the direction of the displacement. However, if the LineStrings are not too large and the required precision is not too high, it probably won't matter that much.

Geopy: calculating GPS heading / bearing

First time poster here.
I am doing some data analyses on collected GPS data for a bridge inspection ROV octorotor. We have the octorotor running on ROS using a 3D scanning LIDAR, stereo vision, INS, and some other neat tech. I'm currently using a ublox LEA-6T in a similar setup as Doug Weibel's setup to collect raw GPS data like carrier phase, doppler shift, and satellite ephemeris. Then I use an opensource project RTKLIB to do some DGPS post processing with local NOAA CORS stations to obtain cm accuracy for better pose estimation when reconstructing the 3D point cloud of the bridge.
Anyhow, I'm using most of scipy to statistically verify my test results.
Specifically for this portion though, I'm just using:
python-3.3
numpy
geopy
I've been studding my positional covariance with respect to offset from my measured ground truth using geopy's handy distance function. With little massaging the arguments, I can find the distance respect to each direction depicted by each standard deviation element in the matrix; North, East, Up and the three directions between.
However, these distances are absolute and do not describe direction.
Say: positive, negative would correlate to northward or southward respectively.
I could simply use the latitude and longitude to detect polarity of direction,
But I'd like to be able to find the precise point to point bearing of the distance described instead,
As I believe a value of global heading could be useful for further applications other than my current one.
I've found someone else pose a similar question
But its seem to be assuming a great circle approximation
Where I would prefer using at least the WGS-84 ellipsoidal model, or any of the same models that can be used in geopy:
Jump to Calculating distances
Any suggestion appreciated,
-ruffsl
Sources if interested:
python-3.3: http:// www.python.org/download/releases/3.3.0/
numpy: http:// www.numpy.org/
geopy: https:// code.google.com/p/geopy/
scipy: http:// www.scipy.org/
ublox LEA-6T: http:// www.u-blox.com/en/gps-modules/u-blox-6-timing-module/lea-6t.html
Doug Weibel's: http:// diydrones.com/profiles/blogs/proof-of-concept-test-extremely-accurate-3d-velocity-measurement
RTKLIB: http:// www.rtklib.com/
NOAA CORS: http:// geodesy.noaa.gov/CORS/
ROS: http:// www.ros.org/wiki/
Use the geographiclib package for python. This computes distances and bearings on the ellipsoid and much more. (You can interpolate paths, measure areas, etc.) For example, after
pip install geographiclib
you can do
>>> from geographiclib.geodesic import Geodesic
>>> Geodesic.WGS84.Inverse(-41.32, 174.81, 40.96, -5.50)
{'lat1': -41.32, 'a12': 179.6197069334283, 's12': 19959679.26735382, 'lat2': 40.96, 'azi2': 18.825195123248392, 'azi1': 161.06766998615882, 'lon1': 174.81, 'lon2': -5.5}
This computes the geodesic from Wellington, New Zealand (41.32S 174.81E) to Salamanca, Spain (40.96N 5.50W). The distance is given by s12 (19959679 meters) and the initial azimuth (bearing) is given by azi1 (161.067... degrees clockwise from north).
Bearing between two lat/long coordinates: (lat1, lon1), (lat2, lon2)
In the code below lat1,lon1,lat2,lon2 are asumend to be in radians.
Convert before from degrees to radians.
dLon = lon2 - lon1;
y = Math.sin(dLon) * Math.cos(lat2);
x = Math.cos(lat1)*Math.sin(lat2) -
Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
brng = Math.atan2(y, x).toDeg();
Bearing is now in range -180/180.
to normalize to compass bearing (0-360)
if brng < 0: brng+= 360
#AlexWien's answer in Python
import math, numpy as np
def get_bearing(lat1,lon1,lat2,lon2):
dLon = lon2 - lon1;
y = math.sin(dLon) * math.cos(lat2);
x = math.cos(lat1)*math.sin(lat2) - math.sin(lat1)*math.cos(lat2)*math.cos(dLon);
brng = np.rad2deg(math.atan2(y, x));
if brng < 0: brng+= 360
return brng

Get n points on a line

If I have a line defined by a start and end coordinates, how do I get n equally spaced points on that line, taking the curvature of the earth into account?
I'm looking for an algorithm, and/or a python library that implements this.
Using geographiclib, a python implementation of GeographicLib, I was able to do this:
from geographiclib.geodesic import Geodesic
number_points = 10
gd = Geodesic.WGS84.Inverse(35, 0, 35, 90)
line = Geodesic.WGS84.Line(gd['lat1'], gd['lon1'], gd['azi1'])
for i in range(number_points + 1):
point = line.Position(gd['s12'] / number_points * i)
print((point['lat2'], point['lon2']))
output:
(35.0, -7.40353472481637e-21)
(38.29044006500327, 7.8252809205988445)
(41.01134777655358, 16.322054184499173)
(43.056180665524245, 25.451710440063902)
(44.328942450747135, 35.08494460239694)
(44.76147256654079, 45.00000000000001)
(44.328942450747135, 54.91505539760305)
(43.05618066552424, 64.54828955993611)
(41.01134777655356, 73.67794581550085)
(38.290440065003274, 82.17471907940114)
(34.99999999999999, 90.0
You can use the npts method from pyproj's Geod class.
See
https://jswhit.github.io/pyproj/pyproj.Geod-class.html
Given a single initial point and terminus point (specified by python
floats lon1,lat1 and lon2,lat2), returns a list of longitude/latitude
pairs describing npts equally spaced intermediate points along the
geodesic between the initial and terminus points.
Emphasis mine because I missed that at first.
First you create an Geod instance, specifying the ellipsoid you want it to use. Then you can call the method.
from pyproj import Geod
geod = Geod("+ellps=WGS84")
points = geod.npts(lon1=-89.6627,
lat1=39.7658,
lon2=147.2800,
lat2=-42.8500,
npts=100
)
points is now a list of tuples on the geodesic line between your start and end point:
[(-91.27649937899028, 39.21278457275204),
(-92.86468478264302, 38.6377120347621),
(-94.42723159402209, 38.04136774269571),
(-95.96421169120758, 37.42453136174509),
(-97.47578514283185, 36.78797425216882),
...

Categories

Resources