Geofence calculating new longitude, latitude from distance and bearing (Python - Library) - python

I need to create a set of Lat/Long to define a geofence zone, I have a central point (LatX, LongX) and I want to provide a distance (Miles) and distance (Degrees) and then get the new position (LatY, LongY).
I have some examples but there do not take into account potential N/S and E/W , is there a python library that can be used to to provide these new positions based on a distance and angle?

Yes there is! Try:
https://geopy.readthedocs.io/en/stable/#module-geopy.distance
pip install geopy
Example code:
from geopy import distance, location
london = location.Point(51.5074, 0.1278)
paris = location.Point(48.8566, -2.3522)
brighton = distance.geodesic().destination(
point=london,
bearing=180,
distance=distance.Distance(kilometers=76))
print("london-paris:", distance.distance(london, paris).kilometers)
print("brighton-paris:", distance.distance(brighton, paris).kilometers)
prints
london-paris: 343.9231200909896
brighton-paris: 282.31635799139883

Related

How to get earth vector data from jpl horizons in python?

I am trying to get the vector data for Earth using Astroquery's Horizons Class. I have the following code:
from astroquery.jplhorizons import Horizons
import numpy as np
earth = Horizons(id=399, epochs = {'start':'2005-06-20', 'stop':'2005-06-21','step':'1d'})
earthVectors = earth.vectors()
earthX = earthVectors['x'].data # X is in AU
au2km = 149_597_870.7
earthXkm = earthX * au2km # X is in km
which returns earthXkm = [-3429775.6506088143 -899299.0538429054] in kilometers.
Getting this information directly from JPL Hoizons gives [-2793030.0, -2627770.0] kilometers.
There is a large discrepancy here and this is the same for all the values in the astropy table. I would also not expect the data to vary as much in one day as that from the astroquery result.
Is there an error in my code, or does the horizons vectors() method not work as intended?
You could just use astropy's get_body_barycentric instead (Note that Horizons currently uses the DE441 ephemeris, so the code below will download the file for this ephemeris which is 3.3 Gb):
from astropy.coordinates import get_body_barycentric, solar_system_ephemeris
from astropy.time import Time
# set the ephemeris to use DE441
solar_system_ephemeris.set("ftp://ssd.jpl.nasa.gov/pub/eph/planets/bsp/de441.bsp")
t = Time("2005-06-20", scale="tdb")
pos = get_body_barycentric("earth", t)
print(pos.x)
<Quantity -2793031.73765342 km>
This is identical (to within a micron [probably just the numerical truncation]) to what I get from the Horizons web interface (outputting value in km rather than AU).
After further analysis I have found the cause of the discrepancy.
Astroquery's Horizon Class uses a default coordinate system centered at the center of the sun for vectors. The Horizon's app; however, using the Solar System barycenter as the default coordinate origin. Using the location attribute set to the solar system barycenter fixes the issue.
location='#ssb' or location='500#0'

Which multipolygon does the point of longtitude and latitude belong to in Python?

I have the longitude and latitude and the expected result is that whichever multipolygon the point is in, I get the name or ID of the multipolygon.
import geopandas as gpd
world = gpd.read_file('/Control_Areas.shp')
world.plot()
Output
0 MULTIPOLYGON (((-9837042.000 6137048.000, -983...
1 MULTIPOLYGON (((-11583146.000 5695095.000, -11...
2 MULTIPOLYGON (((-8542840.287 4154568.013, -854...
3 MULTIPOLYGON (((-10822667.912 2996855.452, -10...
4 MULTIPOLYGON (((-13050304.061 3865631.027, -13.
Previous attempts:
I have tried fiona, shapely and geopandas to get that done but I have struggled horribly to make progress on this. The closest I have gotten is the within and contains function, but the area of work I have struggled is the transformation of multipolygon to polygon successfully as well and then utilising the power of within and contains to get the desired output.
The shapefile has been downloaded from here.
world.crs gives {'init': 'epsg:3857'} (Web Mercator projection) so you should first reproject your GeoDataFrame in the WGS84 projection if you want to keep the latitude-longitude coordinate system of your point.
world = world.to_crs("EPSG:4326")
Then you can use the intersects method of GeoPandas to find the indexes of the Polygons that contain your point.
For example for the city of New York:
from shapely.geometry import Point
NY_pnt = Point(40.712784, -74.005941)
world[["ID","NAME"]][world.intersects(NY_pnt)]
which results in:
ID NAME
20 13501 NEW YORK INDEPENDENT SYSTEM OPERATOR
you can check the result with shapely within method:
NY_pnt.within(world["geometry"][20])
If you have multiple points, you can create a GeoDataFrame and use the sjoin method:
NY_pnt = Point(40.712784, -74.005941)
LA_pnt = Point(34.052235, -118.243683)
points_df = gpd.GeoDataFrame({'geometry': [NY_pnt, LA_pnt]}, crs='EPSG:4326')
results = gpd.sjoin(points_df, world, op='within')
results[['ID', 'NAME']]
Output:
ID NAME
0 13501 NEW YORK INDEPENDENT SYSTEM OPERATOR
1 11208 LOS ANGELES DEPARTMENT OF WATER AND POWER

How to find out whether a point coordinate exists on a path between other two point coordinates?

I've created an application that scrapes data on house listings and calculates the distance to the nearest shop and train station. I've got the lat,lon coordinates for all the shops, listings and train stations and I currently use geopy.distance
import geopy.distance
distanceToShop = geopy.distance.distance(shopCoordinate, listingCoordinate).km
distanceToStation = geopy.distance.distance(stationCoordinate, listingCoordinate).km
These yield a value in kilometers between the two points. Then I filter out locations that are not close enough to a shop and trainstation. For train stations I tolerate a longer distance than for shops. Sometimes the shop is too far to be filtered in, but on the way to the station. In situations like this I would tolerate a longer distance to the shop, since it's on the way to the station.
In this case the shop is between the house and the stations. Station is at coordinate (40.651100, -73.949626). House is at coordinate (40.651401, -73.937987). Distance between house and station is 0.98 kilometers.
In the below scenario shop is at coordinate (40.650913, -73.948301) and distance from house to shop is 870 meters.
Acceptable distance from house to shop is 650 meters. However, if shop is on the way from the station a longer distance is acceptable. If I naively drop out houses because the closest shop is too far (870m > 650m) then this house would not qualify, but in this case the shop is on the way from the station (on the road home), so it should qualify.
Representation of problem on a map
A more robust solution would calculate an area between the two points (e.g. a square), and find out whether the shop is located within the square. This would allow for a more realistic result.
Representation of square drawn on map
How can I find out programatically if this is the case? Are there libraries that I could study?
EDIT: SOLVED - found out a way to calculate the bearing (degree angle between two coordinates) which can be manipulated and used to calculate other coordinates. By calculating the bearing I am able to offset it by +/- 90 degrees to create corners for both the target and destination coordinates. Then using another import (shapely) I drew a polygon from the coordinates and checked whether the shop existed in that matrix.
EDIT 2: Neater solution using geographiclib.geodesic with fewer imports:
import geopy.distance
from geographiclib.geodesic import Geodesic
from shapely.geometry import Point
from shapely.geometry.polygon import Polygon
def getAreaCorners(origin, target, distanceInMeters, angle):
geod = Geodesic.WGS84
bearing = geod.Inverse(origin[0], origin[1], target[0], target[1])['azi1']
rightBearing = bearing + angle
if rightBearing < 0: rightBearing += 360
leftBearing = bearing - angle
if leftBearing < 0: leftBearing += 360
og1 = geod.Direct(origin[0], origin[1], rightBearing, distanceInMeters)
og2 = geod.Direct(origin[0], origin[1], leftBearing, distanceInMeters)
og1 = (og1['lat2'], og1['lon2'])
og2 = (og2['lat2'], og2['lon2'])
tg1 = geod.Direct(target[0], target[1], rightBearing, distanceInMeters)
tg2 = geod.Direct(target[0], target[1], leftBearing, distanceInMeters)
tg1 = (tg1['lat2'], tg1['lon2'])
tg2 = (tg2['lat2'], tg2['lon2'])
return [og1, og2, tg2, tg1]
def isShopOnPath(areaCoordinateList, shopCoordinate):
polygon = Polygon(areaCoordinateList)
shopCoordinatePoint = Point(shopCoordinate)
return polygon.contains(shopCoordinatePoint)
origin = (40.650809, -73.949583)
target = (40.644661, -73.943108)
shopCoordinate = (40.650467, -73.948553)
distanceInMeters = 100
angle = 90
distanceToShop = round(geopy.distance.distance(shopCoordinate, target).km,2)
distanceToStation = round(geopy.distance.distance(origin, target).km,2)
print('House is {} km from station. Shop is {} km from house.'.format(distanceToStation, distanceToShop))
if distanceToShop >= 0.65:
if isShopOnPath(getAreaCorners(origin, target, distanceInMeters, angle), shopCoordinate):
print('Shop is too far, but on the way from the station.')
else:
print('Shop is too far and not on the way from the station')
else:
print('Shop is less than 650m from the house.')

How to extract LineString data from GeoDataFrame and match with Polygon?

Dear StackOverflow community,
Main question:
I have a GeoDataFrame containing the StreetNetwork of NYC - Manhattan (obtained through the osmnx package), where I would like to extract the coordinates (lon/lat data) from all streets which are stored as LineStrings under geometry, like so:
*
field_1 0
access
bridge
geometry LINESTRING (-73.9975944 40.7140611, -73.997492...
highway residential
junction
key 0
lanes
length 11.237
maxspeed 25 mph
name Catherine Street
oneway True
osmid 5670536
ref
service
tunnel
u 1773060097
v 42437559
width
geometry LINESTRING (-73.9975944 40.7140611, -73.997492...
Name: 0, dtype: object
*
What I am trying to do is to extract the geometry information for each line-item:
df.iloc[x][3]
The issue is that the output format is as str:
[1]: LINESTRING (-73.9975944 40.7140611, -73.9974922 40.7139962)
[2]: print(type(...))
[2]: <class 'str'>
This makes it hard to automate and process the output data. Does anyone know how to extract it so that it is already in LineString (or any other useable list/array) format?
Further question:
My overall goal is to map this street information with a shapefile for taxi zones (zones in the format of polygons) to identify which streets are in which zone, and which lon/lat areas are covered with streets within one zone (polygon). Is there any straight forward way to do this leveraging shapely, geopandas or osmnx packages (i.e. something like "polygon.contains(Point)" but in the sense of "polygon.contains(LineString)"?
Thanks a lot for your support!
For extraction coordinates of geometry you need to use the following code:
df.geometry.apply(lambda geom: geom.coords, axis=1)

Determining if a place (given coordinates) is on land or on ocean

I have some coordinates from the ISS (International Space Station) and I'd like to know whether when the coordinates were recorded the ISS was over land or ocean and I should do this offline, but I'm not sure what approach to use.
A part from python standard library, I'm restricted to only using these libraries:
numpy
scipy
tensorflow
pandas
opencv-python
opencv-contrib-python
evdev
matplotlib
logzero
pyephem
scikit-image
scikit-learn
reverse-geocoder
If you know how to do this using some other libraries though, it'd be good anyway.
With this code I get the coordinates and write them to a file:
import logging
import logzero
from logzero import logger
import os
import ephem
import time
dir_path = os.path.dirname(os.path.realpath(__file__))
logzero.logfile(dir_path+"/coordinates.csv")
# Set a custom formatter
formatter = logging.Formatter('%(name)s - %(asctime)-15s - %(levelname)s: %(message)s');
logzero.formatter(formatter)
name = "ISS (ZARYA)"
line1 = "1 25544U 98067A 18282.18499736 .00001222 00000-0 25998-4 0 9992"
line2 = "2 25544 51.6418 170.6260 0003545 261.4423 234.4561 15.53790940136242"
iss = ephem.readtle(name, line1, line2)
iss.compute()
latitude = iss.sublat
longitude = iss.sublong
# Save the data to the file
logger.info("%s,%s", latitude, longitude )
Do you guys have any idea?
Thanks in advance.
global-land-mask from Karin Todd is extremely easy to use and efficient:
from global_land_mask import globe
print(globe.is_land(49.22, -2.23))
# → True
print(globe.is_land(49.22, -2.25))
# → False
It is available via pip, and its only dependency is numpy.
mpl_toolkits.basemap may be able to help.
from mpl_toolkits.basemap import Basemap
bm = Basemap() # default: projection='cyl'
print(bm.is_land(99.0, 13.0)) #True
print(bm.is_land(0.0, 0.0)) # False
Docs: here and relevant method below:
is_land(xpt, ypt)
Returns True if the given x,y point (in projection coordinates) is over land, False otherwise. The definition of land is based upon the GSHHS coastline polygons associated with the class instance. Points over lakes inside land regions are not counted as land points.
Note: you may need to be careful with the projection that is being used with the Basemap object.
In the end, I was able to solve my problem only using those libraries.
I used this website geoplaner to get a rough outline of the oceans shape (it was really really rough, since I did it by hand, but it worked fine for my purposes, I think online there should be some more accurate polygons but I was lost on how to use them).
I did so for each of the oceans and got this (note that the coordinates I used didn't cover the oceans entirely, for example I avoided the Southern Ocean):
atlanticOcean = [(-24.6,68.5), (25.3,69.8), (5.7,61.4), (4.6,52.2), (-6.3,48.4),
(-9.45,43.5), (-9.63,37.6), (-6.3,35.5), (-10.5,31.1), (-10.5,28.4),
(-16.1,24.5), (-17.2,14.7), (-8.2,4.1), (6.3,3.6), (9.9,3.4),
(9,-1.7), (13.8,-12.6), (11.7,-16.5), (14.5,-22.3), (16.1,-28.67),
(18.9,-34.5), (18.9,-55.7), (-66,-55.7), (-68.5,-50.4), (-58.6,-39.3), (-48.1,-28.2),
(-48.1,-25.7), (-41.6,-22.7), (-38.7,-17.4), (-39.5,-13.7), (-36.9,-12.5),
(-34.9,-10.4), (-35.0,-5.5), (-50,-0.1), (-53,5.5), (-57.2,6.1),
(-62.8,10.9), (-67.8,10.9), (-74.2,10.8), (-76.9,8.5), (-81.6,9.4),
(-82.7,14), (-87.4,16.1), (-86.3,21.6), (-90.2,21.7), (-91.2,19.2),
(-95.7,18.8), (-97.1,25.5), (-91.0,28.9), (-84,29.7), (-82.9,27.3),
(-80.9,24.9), (-79.3,26.7), (-81.1,31.3), (-75.4,35.2), (-73.8,40.3),
(-69.6,41.4), (-65.1,43.5), (-60,45.8), (-52.2,47.1), (-54.9,52.9),
(-44.5,60.1), (-38.8,65.1)]
indianOcean = [(21.40,-34.15), (27.37,-33.71), (40.03,-15.61), (39.68,-3.50), (51.80,10.16),
(58.84,22.26), (65.69,25.18), (71.32,19.83), (77.47,6.86), (80.24,12.53),
(80.90,15.85), (89.05,22.12), (91.38,22.08), (94.54,17.74), (94.02,16.02),
(97.00,16.82), (98.19,8.33), (100.78,3.18), (94.98,6.29), (105.0,-6.52),
(118.16,-9.26), (123.52,-11.25), (129.93,-11.08), (128.62,-14.51), (125.89,-3.57),
(118.51,-20.37), (113.06,-22.18), (115.26,-34.44), (123.52,-34.88), (130.99,-32.09),
(137.23,-36.59), (137.50,-66.47), (102.26,-65.79), (85.65,-66.22), (75.01,-69.50),
(69.04,-67.67), (54.18,-65.76), (37.48,-68.65)]
Now, the Pacific Ocean was more complicated, because it stretches over two sides of a map and you can have two successive points with longitude of -179 and 179 which lead to this polygon to be badly represented in a xy plane. What I did was to divide it in two and so I got this:
pacificEast = [(149.9,-37.8),(153.9,-28.5),(143.2,-11.5),(152.1,-0.9),(127.9,5.7),
(122.9,23.8),(123.4,31),(128.9,33.7),(129.8,29.4),(141.6,35),
(142.8,41),(148,43.3),(144.6,45.5),(146.2,49.3),(144.9,54.2),
(136.8,55.2),(143.1,59.1),(153.7,59.2),(159.4,61.6),(160.3,60.5),
(161.4,60.3),(155.4,57),(156.6,50.3),(160.8,52.8),(164.1,55.8),
(163.8,58.1),(167.3,60.1),(170.7,59.8), (179.9,-77.1),
(166.4,-77.1), (173.8,-71.8), (142.9,-66.8), (146.9,-44.8)]
pacificWest = [(-179.9,62.2),(-179.7,64.7),
(-177.3,65.3),(-173.6,63.4),(-166,62.2),(-165.8,60.9),(-168.4,60.4),
(-166.6,58.9),(-158.5,57.8),(-153.1,57),(-144.8,59.9),(-136.1,56.9),
(-131.7,51.9),(-125.2,48.4),(-124.5,44.6),(-124.4,40.7),(-117.6,32.7),
(-110.7,23.2),(-105.8,19.7),(-96.1,15.3),(-87.9,12.4),(-83.7,7.3),
(-78.7,6.1),(-80.2,0.9),(-82.2,-0.6),(-81.2,-6.3),(-76.7,-14.4),
(-70.4,-18.9),(-73.7,-36.7),(-76,-46.2),(-75.1,-53),(-73.4,-55.1),
(-66.6,-56.3),(-64.6,-55),(-59.6,-63.4),(-68.4,-65.7),(-75.8,-72.2),
(-98.6,-71.8),(-126.8,-73.2),(-146.8,-75.7),(-162.6,-78.4),(-179.9,-77.1)]
As I understand, using matplotlib you can create the polygons from the verteces (the lists of coordinates) with path and then you can use the contains_point() function to check whether the point is in either of the polygons (therefore it's in 'ocean') or not (in 'land'):
p1 = path.Path(atlanticOcean)
p2 = path.Path(indianOcean)
p3 = path.Path(pacificEast)
p4 = path.Path(pacificWest)
target = [(lon, lat)]
result1 = p1.contains_points(target)
result2 = p2.contains_points(target)
result3 = p3.contains_points(target)
result4 = p4.contains_points(target)
# if target is in one of the polygons, it is in ocean
if result1==True or result2==True or result3==True or result4==True:
print("In Ocean")
else:
print("Land")
the lon and lat variables for me where the ones of the ISS which I calculated with the program in my question.

Categories