Current section
Files
Jump to
Current section
Files
lib/location.ex
defmodule ViaUtils.Location do
@moduledoc """
Documentation for `UtilsLocation`.
"""
@doc """
Hello world.
## Examples
iex> UtilsLocation.hello()
:world
"""
require Logger
require ViaUtils.Constants, as: VC
require ViaUtils.Shared.ValueNames, as: SVN
defstruct [SVN.latitude_rad(), SVN.longitude_rad(), SVN.altitude_m()]
@spec new(number(), number(), number()) :: struct()
def new(lat, lon, alt \\ 0) do
%ViaUtils.Location{
SVN.latitude_rad() => lat,
SVN.longitude_rad() => lon,
SVN.altitude_m() => alt
}
end
@spec new_degrees(number(), number(), number()) :: struct()
def new_degrees(lat, lon, alt \\ 0) do
new(ViaUtils.Math.deg2rad(lat), ViaUtils.Math.deg2rad(lon), alt)
end
@spec to_map(struct()) :: map()
def to_map(location) do
Map.from_struct(location)
end
@spec to_string(map()) :: binary()
def to_string(
%{SVN.latitude_rad() => lat, SVN.longitude_rad() => lon, SVN.altitude_m() => alt} =
_location
) do
lat_str = ViaUtils.Format.eftb(ViaUtils.Math.rad2deg(lat), 5)
lon_str = ViaUtils.Format.eftb(ViaUtils.Math.rad2deg(lon), 5)
alt_str = ViaUtils.Format.eftb(alt, 1)
"lat/lon/alt: #{lat_str}/#{lon_str}/#{alt_str}"
end
@spec dx_dy_between_points(struct(), struct()) :: tuple()
def dx_dy_between_points(
%{SVN.latitude_rad() => lat1, SVN.longitude_rad() => lon1} = _wp1,
%{SVN.latitude_rad() => lat2, SVN.longitude_rad() => lon2} = _wp2
) do
dx_dy_between_points(lat1, lon1, lat2, lon2)
end
@spec dx_dy_between_points(float(), float(), float(), float()) :: tuple()
def dx_dy_between_points(latitude1_rad, longitude1_rad, latitude2_rad, longitude2_rad) do
dpsi =
:math.log(
:math.tan(VC.pi_4() + latitude2_rad / 2) / :math.tan(VC.pi_4() + latitude1_rad / 2)
)
dlat = latitude2_rad - latitude1_rad
dlon = longitude2_rad - longitude1_rad
q =
if abs(dpsi) > 0.0000001 do
dlat / dpsi
else
:math.cos(latitude1_rad)
end
{dlat * VC.earth_radius_m(), q * dlon * VC.earth_radius_m()}
end
@spec angle_between_points(struct(), struct()) :: float()
def angle_between_points(location1, location2) do
{dx, dy} = dx_dy_between_points(location1, location2)
ViaUtils.Math.constrain_angle_to_compass(:math.atan2(dy, dx))
end
@spec location_from_point_with_dx_dy(struct(), float(), float()) :: struct()
def location_from_point_with_dx_dy(
%{SVN.latitude_rad() => lat1, SVN.longitude_rad() => lon1, SVN.altitude_m() => alt1} =
_starting_point,
dx,
dy
) do
dlat = dx / VC.earth_radius_m()
lat2 = lat1 + dlat
dpsi = :math.log(:math.tan(VC.pi_4() + lat2 / 2) / :math.tan(VC.pi_4() + lat1 / 2))
q =
if abs(dpsi) > 0.0000001 do
dlat / dpsi
else
:math.cos(lat1)
end
dlon = dy / VC.earth_radius_m() / q
lon2 = lon1 + dlon
# {lat2, lon2}
new(lat2, lon2, alt1)
end
@spec location_from_point_with_distance_bearing(struct(), float(), float()) :: struct()
def location_from_point_with_distance_bearing(starting_point, distance, bearing) do
dx = distance * :math.cos(bearing)
dy = distance * :math.sin(bearing)
# Logger.debug("dx/dy: #{dx}/#{dy}")
location_from_point_with_dx_dy(starting_point, dx, dy)
end
end