Python finding closest location - python

I am currently working on a project whereby I am plotting longitude & latitude on a map. Upon plotting I am then entering a location and I would like my python model to identify the 3 closest locations to the user.
I have looked at geopandas but have had no luck. How can I make the model output the 3 closest longitudes/latitudes?

Try BallTree from sklearn
import pandas as pd
import numpy as np
from sklearn.neighbors import BallTree, DistanceMetric
# Dataset
N = 10
df = pd.DataFrame({'place': 'place' + pd.RangeIndex(1, N+1).astype(str),
'lat': np.random.uniform(30, 35, N),
'lon': np.random.uniform(-150, -145, N)})
coords = np.radians(df[['lat', 'lon']])
dist = DistanceMetric.get_metric('haversine')
tree = BallTree(coords, metric=dist)
# User coordinate
user = [32.109643, -148.167012]
coords = np.radians(user)
# Query the database
distances, indices = tree.query([coords], k=3)
out = df.iloc[indices[0]]
Output:
# Nearest places around [32.109643, -148.167012]
>>> out
place lat lon
9 place10 32.267336 -148.225587
5 place6 31.269928 -148.293427
7 place8 31.667689 -149.197724

Step 1: Build a distance formula
from math import radians, cos, sin, sqrt, atan2
def dms(degrees, minutes, seconds):
""" Convert degrees minutes and seconds to radians """
return radians(degrees + minutes / 60 + seconds / 3600)
def dist(p, q, R=6_371):
""" Compute distance between two points on a sphere.
Haversine formula:
a = sin²(Δφ/2) + cos φ1 ⋅ cos φ2 ⋅ sin²(Δλ/2)
c = 2 ⋅ atan2( √a, √(1−a) )
d = R ⋅ c
where:
φ is latitude
λ is longitude
R is earth’s radius (mean radius = 6,371km)
Example:
>>> p = dms(50, 3, 59), dms(5, 42, 53) # 50° 03′ 59″
>>> q = dms(58, 38, 38), dms(3, 4, 12) # 58° 38′ 38″
>>> round(dist(p, q), 1)
968.9
"""
# https://www.movable-type.co.uk/scripts/philamg.html
phi_p, lam_p = p
phi_q, lam_q = q
delta_phi = phi_q - phi_p
delta_lam = lam_q - lam_p
a = sin(delta_phi / 2)**2 + cos(phi_p) * cos(phi_q) * sin(delta_lam / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
return R * c
Step 2: Compute the three closest points
from heapq import nsmallest
from functools import partial
answer = nsmallest(3, locations, key=partial(dist, user_location))

Related

How to solve first order ODE equations of motion with given init

I have converted an equation to the first order ODE and now i would like to solve the motion equations for multiple periods with given conditions.
The equations shall be solved with the following 0 values:
x(0), y(0), vx(0), vy(0) = 3, 1, 2, 1.3 × π
This is the first order ODE, derived from motion equation of Newton's gravitational law for cellestial bodies:
How can I solve this in python? Could I use Runge Kutta or Keplers methods? I feel like I'm doing something wrong
import math
import matplotlib.pyplot as plt
import numpy as np
g = 6.674 * 10**(-11)
M = 4*(math.pi**2)/g
x0 = 3 #x-position of the center or h
y0 = 1 #y-position of the center or k
vx0 = 2
vy0 = 1.3* math.pi
#Trying second order
def RK2(y0, f, tlist):
t = [tlist[0]]
tf = tlist[1]
dt = tlist[2]
y = [y0]
while t[-1] < tf:
k1 = dt * f(y[-1],t[-1])
k2 = dt * f(y[-1]+0.5*k1 , t[-1]+0.5*dt) ##Take half step
y.append( y[-1] + k2 )
t.append(t[-1] + dt)
return(np.array(y), np.array(t) )

pint: convert geographic CRS degrees to nautical miles

I would like to use pint to convert degrees (distance in a geographic CRS) into nautical miles.
https://geopandas.org/docs/reference/api/geopandas.GeoDataFrame.sjoin_nearest.html outputs distance in degree for epsg:4326.
Given distance (in nm) varies from equator to pole i'm not sure if this is possible.
I could use a rule of thumb of 1 deg ~= 111 km ~= 60 nm.
Perhaps it can be calculated using the starting point and distance using something like: https://github.com/anitagraser/movingpandas/blob/master/movingpandas/geometry_utils.py#L38
This code is also useful: https://geopy.readthedocs.io/en/stable/#module-geopy.distance
Here's some code to test:
import pandas as pd
import geopandas as gpd
df = pd.DataFrame({"lon": [0], "lat": [0]})
gdf_pt = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df["lon"], df["lat"]), crs="epsg:4326")
df2 = pd.DataFrame({"lon": [1, 2], "lat": [0, 0]})
gdf_pts = gpd.GeoDataFrame(df2, geometry=gpd.points_from_xy(df2["lon"], df2["lat"]), crs="epsg:4326")
value = gdf_pt.sjoin_nearest(gdf_pts, distance_col="distances")["distances"].values[0]
import pint
l = value * ureg.arcdegree
Probably best to throw it to Mercator and use that if you can
import pint_pandas
gdf = gdf_pt.to_crs("EPSG:3395").sjoin_nearest(gdf_pts.to_crs("EPSG:3395"), distance_col="distances")
gdf["distance"] = gdf["distance"].astype("pint[meter]").pint.to("nautical_mile")
This function, which I've pulled from existing code, computes the distance in meters between two lat/long sets. "rlat" and "rlong" are expressed in radians; you'll have to do the conversion from degrees. To get nm instead of meters, just set R to 3440.
from math import *
# Radius of the earth, in meters.
R = 6371000
# Return distance between two lat/longs.
def distance( pt1, pt2 ):
rlat1 = pt1.rlat
rlat2 = pt2.rlat
dlat = pt2.rlat - pt1.rlat
dlong = pt2.rlong - pt1.rlong
a = sin(dlat/2) * sin(dlat/2) + cos(rlat1) * cos(rlat2) * sin(dlong/2) * sin(dlong/2)
c = 2 * atan2(sqrt(a), sqrt(1-a))
return R * c

Calculating distances in TSPLIB

Hello i have a problem with calculating distances between cities from tsp library: http://www.math.uwaterloo.ca/tsp/world/countries.html. I have this set of data (cities in djibouti): http://www.math.uwaterloo.ca/tsp/world/dj38.tsp. I used this function to calculate distaces in this QaA here: http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/TSPFAQ.html. i programed this in python and now it looks like this, here is my code:
cityCoords = {
1:(11003.611100,42102.500000),
2:(11108.611100,42373.888900),
3:(11133.333300,42885.833300),
4:(11155.833300,42712.500000),
5:(11183.333300,42933.333300),
6:(11297.500000,42853.333300),
7:(11310.277800,42929.444400),
8:(11416.666700,42983.333300),
9:(11423.888900,43000.277800),
10:(11438.333300,42057.222200),
11:(11461.111100,43252.777800),
12:(11485.555600,43187.222200),
13:(11503.055600,42855.277800),
14:(11511.388900,42106.388900),
15:(11522.222200,42841.944400),
16:(11569.444400,43136.666700),
17:(11583.333300,43150.000000),
18:(11595.000000,43148.055600),
19:(11600.000000,43150.000000),
20:(11690.555600,42686.666700),
21:(11715.833300,41836.111100),
22:(11751.111100,42814.444400),
23:(11770.277800,42651.944400),
24:(11785.277800,42884.444400),
25:(11822.777800,42673.611100),
26:(11846.944400,42660.555600),
27:(11963.055600,43290.555600),
28:(11973.055600,43026.111100),
29:(12058.333300,42195.555600),
30:(12149.444400,42477.500000),
31:(12286.944400,43355.555600),
32:(12300.000000,42433.333300),
33:(12355.833300,43156.388900),
34:(12363.333300,43189.166700),
35:(12372.777800,42711.388900),
36:(12386.666700,43334.722200),
37:(12421.666700,42895.555600),
38:(12645.000000,42973.333300)
}
def calcCityDistances(coordDict):
cities = list(coordDict.keys())
n = len(cities)
distances = {}
latitude = []
longitude = []
RRR = 6378.388;
PI = 3.141592;
for i in range(1,n+1):
cityA = cities[i-1]
latA, longA = coordDict[cityA]
deg = int(latA)
Min = latA - deg
latitude.append(PI * (deg + 5 * Min / 3) / 180)
deg = int(longA);
Min = longA - deg;
longitude.append(PI * (deg + 5 * Min / 3) / 180)
for i in range(1,n+1):
for j in range(i + 1, n + 1):
q1 = cos(longitude[i-1] - longitude[j-1]);
q2 = cos(latitude[i-1] - latitude[j-1]);
q3 = cos(latitude[i-1] + latitude[j-1]);
key = frozenset((i, j))
distances[key] = {}
dist = RRR * acos(0.5 * ((1.0 + q1) * q2 - (1.0 - q1) * q3)) + 1.0
distances[key]['dist'] = dist
distances[key]['pher'] = init_fer
distances[key]['vis'] = 0
return distances
distances = calcCityDistances(cityCoords)
My problem is that the distances calculated in this algorithm are off mark in huge scale. average lenght of one route between cities is 10 000 km and the problem is that the optimal TSP route is 6635. you can imagine that when i apply this to my Ant Colony System algorithm the result is around 110 000 km. this is really different from 6 thousand. Can someone explain what am i doing wrong please ?
I'm not familiar with the distance calculation listed in the TSP FAQ. Here's the resource I've used in the past: http://www.movable-type.co.uk/scripts/latlong.html
He gives two great circle distance calculation methods. Neither one looks like the one TSP provided. But, they both produced a distance that seemed to match reality (that Diksa and Dikhil are about 31k apart).
The input data is in 1000ths of a degree, and I'm not sure if the conversion to radians given takes that into account.
Here's an implementation that might give you better results: note I updated the input data to degrees:
import cmath
import math
cityCoords = {
1:(11.0036111,42.1025),
2:(11.1086111,42.3738889)
}
def spherical_cosines(coordDict):
R = 6371; # kilometers
cities = list(coordDict.keys())
n = len(cities)
for i in range(1,n+1):
for j in range(i + 1, n + 1):
cityA = cities[i-1]
lat1, lon1 = coordDict[cityA]
cityB = cities[j-1]
lat2, lon2 = coordDict[cityB]
lat1_radians = math.radians(lat1)
lat2_radians = math.radians(lat2)
lon1_radians = math.radians(lon1)
lon2_radians = math.radians(lon2)
print('A={},{} B={},{}'.format(lat1_radians, lon1_radians, lat2_radians, lon2_radians))
delta_lon_radians = math.radians(lon2-lon1)
distance = cmath.acos(cmath.sin(lat1_radians) * cmath.sin(lat2_radians) + cmath.cos(lat1_radians) *
math.cos(lat2_radians) * cmath.cos(delta_lon_radians)) * R;
print('spherical_cosines distance={}'.format(distance))
spherical_cosines(cityCoords)
update:
The code you posted is not producing the correct distance values. Here's the first two cities using calcCityDistances and sperical cosines:
input loc=11003.6111, 42102.5
input loc=11108.6111, 42373.8889
radians A = 192.05631381917777,734.8329132074075
B=193.88890915251113,739.5740671363777
calcCityDistances distance = 8078.816781077703
input degrees A=11.0036111,42.1025 B=11.1086111,42.3738889
radians A=0.19204924330399503,0.7348272483209126
B=0.19388183901858905,0.7395638781792782
spherical_cosines> distance=(31.835225475974934+0j)
Units is kilometers. Spherical cosines produces approximately the right value. Is the code you're using the same as what you posted? Notice the radians conversion doesn't seem to take into account that the input is thousandths of a degree

Pairwise calculations of distances between two sets of points

I'm facing some troubles with doing a pairwise calculation in Python.
I have two sets of nodes (e.g. suppliers and customers).
Set 1: SupplierCO = (Xco, Yco) for multiple suppliers
Set 2: Customer CO = (Xco, Yco) for multiple customers
I want to calculate the distances between a customer and all the suppliers, and save the shortest distance. This should be looped for all customers.
I realize I will have to work with two for loops, and an if function. But I don't understand how to select the coordinates from the correct points while looping.
Thanks for the responses!
Some more information:
- Haversine distance
- Each point in set 1 has to be compared to all the points of set 2
- This is what I've so far
import urllib.parse
from openpyxl import load_workbook, Workbook
import requests
from math import radians, cos, sin, asin, sqrt
"""load datafile"""
workbook = load_workbook('Macro.xlsm')
Companysheet = workbook.get_sheet_by_name("Customersheet")
Networksheet = workbook.get_sheet_by_name("Suppliersheet")
"""search for column with latitude/longitude - customers"""
numberlatC = -1
i = 0
for col in Customersheet.iter_cols():
if col[2].value == "Latitude" :
numberlatC = i
i+=1
numberlongC = -1
j = 0
for col in Customersheet.iter_cols():
if col[2].value == "Longitude" :
numberlongC = j
j+=1
latC = [row[numberlatC].value for row in Companysheet.iter_rows() ]
longC = [row[numberlongC].value for row in Companysheet.iter_rows()]
# haversine formula
dlon = lonC - lonS
dlat = latC - latS
a = sin(dlat/2)**2 + cos(latC) * cos(latS) * sin(dlon/2)**2
c = 2 * asin(sqrt(a))
r = 6371 # Radius of earth in kilometers. Use 3956 for miles
distance = c*r
distances.append([distance])
return distances
customers = [latC, longC]
Thanks!
This should give you the general idea. In the following example I've just used regular coordinates, however, you should be able to convert this to your need.
supplier = [(1,3),(2,4),(8,7),(15,14)]
customer = [(0,2),(8,8)]
def CoordinatesDistance(A, B):
import math
x1, x2 = A
y1, y2 = B
return math.sqrt(math.exp((x2-x1)+(y2-y1)))
def shortest_distance_pair(Cust, Sup):
pairs = []
for X in Cust:
shortest_distance = 999999
for Y in Sup:
distance = CoordinatesDistance(X,Y)
#customer_distance.append(distance)
if distance < shortest_distance:
shortest_distance = distance
sdp = (X,Y)
pairs.append(sdp)
return pairs
print(shortest_distance_pair(customer,supplier))
print(shortest_distance_pair(customer,supplier))
[((0, 2), (8, 7)), ((8, 8), (8, 7))]
Now if you create two lists, 1. Customer coordinates, and 2. Supplier coordinates; you should be able to utilize the above.

Python Work out area of a polygon on a spherical surface

I have a series of points, of right ascension and declination values.
These points correspond to the vertices of a polygon on the surface of a sphere.
What would be the best way to calculate the area enclosed by these points? I would assume that converting the points with an equal-area projection, and then carrying out typical polygonal area calculating on a flat surface would be an appropriate solution.
note: I cannot use custom python libraries. eg pyproj or shapely
Example code (works for latitude longitude, what modifications would be required to enure this works with sky coordinates?)
def reproject(latitude, longitude):
"""Returns the x & y coordinates in metres using a sinusoidal projection"""
from math import pi, cos, radians
earth_radius = 6371009
lat_dist = pi * earth_radius / 180.0
y = [lat * lat_dist for lat in latitude]
x = [long * lat_dist * cos(radians(lat))
for lat, long in zip(latitude, longitude)]
return x, y
def area_of_polygon(x, y):
"""Calculates the area of an arbitrary polygon given its vertices"""
area = 0.0
for i in xrange(-1, len(x)-1):
area += x[i] * (y[i+1] - y[i-1])
return abs(area) / 2.0
dec = [-15.,89.,89.,-15.,-15.]
ra = [105.,105.,285.,285.,105.]
x,y = reproject(dec, ra)
print area_of_polygon(x,y)
One of the ways is to perform a line integral based on Green's Theorem. See below an implementation, and this question for more details.
def polygon_area(lats, lons, algorithm = 0, radius = 6378137):
"""
Computes area of spherical polygon, assuming spherical Earth.
Returns result in ratio of the sphere's area if the radius is specified.
Otherwise, in the units of provided radius.
lats and lons are in degrees.
"""
from numpy import arctan2, cos, sin, sqrt, pi, power, append, diff, deg2rad
lats = np.deg2rad(lats)
lons = np.deg2rad(lons)
# Line integral based on Green's Theorem, assumes spherical Earth
#close polygon
if lats[0]!=lats[-1]:
lats = append(lats, lats[0])
lons = append(lons, lons[0])
#colatitudes relative to (0,0)
a = sin(lats/2)**2 + cos(lats)* sin(lons/2)**2
colat = 2*arctan2( sqrt(a), sqrt(1-a) )
#azimuths relative to (0,0)
az = arctan2(cos(lats) * sin(lons), sin(lats)) % (2*pi)
# Calculate diffs
# daz = diff(az) % (2*pi)
daz = diff(az)
daz = (daz + pi) % (2 * pi) - pi
deltas=diff(colat)/2
colat=colat[0:-1]+deltas
# Perform integral
integrands = (1-cos(colat)) * daz
# Integrate
area = abs(sum(integrands))/(4*pi)
area = min(area,1-area)
if radius is not None: #return in units of radius
return area * 4*pi*radius**2
else: #return in ratio of sphere total area
return area
Please find a somewhat more explicit version (and with many more references and TODOs...) here.
Looks like I can treat ra and dec like lat and long, work out the area on the Earth's surface in m^2, and use this value to convert into an area in sq degrees.
Please let me know if the solution I propose below is flawed:
def reproject(latitude, longitude):
"""Returns the x & y coordinates in metres using a sinusoidal projection"""
from math import pi, cos, radians
earth_radius = 6371009
lat_dist = pi * earth_radius / 180.0
y = [lat * lat_dist for lat in latitude]
x = [long * lat_dist * cos(radians(lat))
for lat, long in zip(latitude, longitude)]
return x, y
def area_of_polygon(x, y):
"""Calculates the area of an arbitrary polygon given its vertices"""
area = 0.0
for i in xrange(-1, len(x)-1):
area += x[i] * (y[i+1] - y[i-1])
return ((abs(area) / 2.0)/5.10100E14) * 41253
dec = [-15.,89.,89.,-15.,-15.]
ra = [105.,105.,285.,285.,105.]
x,y = reproject(dec, ra)
print area_of_polygon(x,y)

Categories

Resources