Current section
Files
Jump to
Current section
Files
lib/geocalc.ex
defmodule Geocalc do
@earth_radius 6_371_000
@pi :math.pi
@moduledoc """
Calculate distance, bearing and more between Latitude/Longitude points.
"""
@doc """
Calculates distance between 2 points.
Return distance in meters.
## Example
iex> berlin = [52.5075419, 13.4251364]
iex> paris = [48.8588589, 2.3475569]
iex> Geocalc.distance_between(berlin, paris)
878327.4291149472
iex> Geocalc.distance_between(paris, berlin)
878327.4291149472
"""
def distance_between([point_1_lat, point_1_lng], [point_2_lat, point_2_lng]) do
point_1_lat_rad = degrees_to_radians(point_1_lat)
point_2_lat_rad = degrees_to_radians(point_2_lat)
diff_lat = degrees_to_radians(point_2_lat - point_1_lat)
diff_lng = degrees_to_radians(point_2_lng - point_1_lng)
a = :math.sin(diff_lat / 2) * :math.sin(diff_lat / 2) + :math.cos(point_1_lat_rad) * :math.cos(point_2_lat_rad) * :math.sin(diff_lng / 2) * :math.sin(diff_lng / 2)
c = 2 * :math.atan2(:math.sqrt(a), :math.sqrt(1 - a))
@earth_radius * c
end
@doc """
Calculates bearing.
Return radians.
## Example
iex> berlin = [52.5075419, 13.4251364]
iex> paris = [48.8588589, 2.3475569]
iex> Geocalc.bearing(berlin, paris)
-1.9739245359361486
iex> Geocalc.bearing(paris, berlin)
1.0178267866082613
"""
def bearing([point_1_lat, point_1_lng], [point_2_lat, point_2_lng]) do
y = :math.sin(degrees_to_radians(point_2_lng) - degrees_to_radians(point_1_lng)) * :math.cos(degrees_to_radians(point_2_lat))
x = :math.cos(degrees_to_radians(point_1_lat)) * :math.sin(degrees_to_radians(point_2_lat)) - :math.sin(degrees_to_radians(point_1_lat)) * :math.cos(degrees_to_radians(point_2_lat)) * :math.cos(degrees_to_radians(point_2_lng) - degrees_to_radians(point_1_lng))
:math.atan2(y, x)
end
@doc """
Finds point between start and end points in direction to end point with given distance.
Return array with latitude and longitude.
## Example
iex> berlin = [52.5075419, 13.4251364]
iex> paris = [48.8588589, 2.3475569]
iex> distance = 500_000
iex> Geocalc.destination_point_given_distance_from_start_and_end_point(berlin, paris, distance)
[50.5582900851695, 6.90714527103055]
"""
def destination_point_given_distance_from_start_and_end_point([point_1_lat, point_1_lng], [point_2_lat, point_2_lng], distance) do
brng = bearing([point_1_lat, point_1_lng], [point_2_lat, point_2_lng])
destination_point_given_distance_and_bearing_from_start_point([point_1_lat, point_1_lng], brng, distance)
end
@doc """
Finds point from start point with given distance and bearing.
Return array with latitude and longitude.
## Example
iex> berlin = [52.5075419, 13.4251364]
iex> bearing = -1.9739245359361486
iex> distance = 100_000
iex> Geocalc.destination_point_given_distance_and_bearing_from_start_point(berlin, bearing, distance)
[52.147030316318904, 12.076990111001148]
"""
def destination_point_given_distance_and_bearing_from_start_point([point_1_lat, point_1_lng], bearing, distance) do
rad_lat = :math.asin(:math.sin(degrees_to_radians(point_1_lat)) * :math.cos(distance / @earth_radius) + :math.cos(degrees_to_radians(point_1_lat)) * :math.sin(distance / @earth_radius) * :math.cos(bearing))
rad_lng = degrees_to_radians(point_1_lng) + :math.atan2(:math.sin(bearing) * :math.sin(distance / @earth_radius) * :math.cos(degrees_to_radians(point_1_lat)), :math.cos(distance / @earth_radius) - :math.sin(degrees_to_radians(point_1_lat)) * :math.sin(rad_lat))
[radians_to_degrees(rad_lat), radians_to_degrees(rad_lng)]
end
@doc """
Converts degrees to radians.
Return radians.
"""
def degrees_to_radians(degrees) do
normalize_degrees(degrees) * :math.pi / 180
end
defp normalize_degrees(degrees) when degrees > 180 do
normalize_degrees(degrees - 2 * 180)
end
defp normalize_degrees(degrees) do
degrees
end
@doc """
Converts radians to degrees.
Return degrees.
"""
def radians_to_degrees(radians) do
normalize_radians(radians) * 180 / :math.pi
end
defp normalize_radians(radians) when radians > @pi do
normalize_radians(radians - 2 * :math.pi)
end
defp normalize_radians(radians) do
radians
end
end