defmodule GeolocationClient do @moduledoc """ Client for Google Geolocation API """ @base_url "https://www.googleapis.com/" @doc """ Set API key """ @spec set_api_key(String.t()) :: :ok def set_api_key(key) do Application.put_env(:geolocation_client, :api_key, key) end @doc """ Geolocate from a list of MAC addresses """ @spec geolocate_from_mac_addresses([GeolocationClient.Entities.MacAddress.t()], boolean()) :: GeolocationClient.Entities.LatLon.t() | {:error, any()} def geolocate_from_mac_addresses(mac_addresses, consider_ip \\ false) when is_list(mac_addresses) do # Implementation would go here url = "#{@base_url}geolocation/v1/geolocate?key=#{Application.get_env(:geolocation_client, :api_key)}" body = %{ "considerIp" => consider_ip, "wifiAccessPoints" => Enum.map(mac_addresses, fn mac -> %{ "macAddress" => mac.mac_address, "signalStrength" => mac.signal_strength } end) } case Req.post(url, json: body) do {:ok, %Req.Response{status: 200, body: %{"location" => location, "accuracy" => accuracy}}} -> %GeolocationClient.Entities.LatLon{ latitude: location["lat"], longitude: location["lng"], accuracy: accuracy } {:ok, %Req.Response{status: status, body: body}} -> {:error, %{status: status, body: body}} {:error, reason} -> {:error, reason} end end @doc """ Get altitude for given latitude and longitude """ @spec get_altitude(GeolocationClient.Entities.LatLon.t()) :: float() | {:error, any()} def get_altitude(%GeolocationClient.Entities.LatLon{latitude: lat, longitude: lon}) do # Implementation would go here url = "https://maps.googleapis.com/maps/api/elevation/json?locations=#{lat}%2C#{lon}&key=#{Application.get_env(:geolocation_client, :api_key)}" case Req.get(url) do {:ok, %Req.Response{status: 200, body: %{"results" => [%{"elevation" => elevation} | _]}}} -> elevation {:ok, %Req.Response{status: status, body: body}} -> {:error, %{status: status, body: body}} {:error, reason} -> {:error, reason} end end @doc """ Geolocate from mac addresses and get full location (LLA) """ @spec geolocate_full_location([GeolocationClient.Entities.MacAddress.t()], boolean()) :: GeolocationClient.Entities.Location.t() | {:error, any()} def geolocate_full_location(mac_addresses, consider_ip \\ false) when is_list(mac_addresses) do case geolocate_from_mac_addresses(mac_addresses, consider_ip) do %GeolocationClient.Entities.LatLon{latitude: lat, longitude: lon} = lat_lon -> case get_altitude(lat_lon) do elevation when is_float(elevation) -> %GeolocationClient.Entities.Location{ latitude: lat, longitude: lon, altitude: elevation } {:error, _} = error -> error end {:error, _} = error -> error end end end