defmodule Geonames do
@moduledoc """
Geonames-Elixir is a simple wrapper around the API provided
by geonames.org. All interaction with the API is provied by
this module via easy to use functions.
Each API endpoint maps to a single function, all of which
requiring a map containing the parameters of the request.
If no arguments are required, then the hash can be omited
as it will default to %{}
## Examples
Below you will find a few examples on how to query the
geonames API.
###### General search
Geonames.search %{ q: "London" }
Geonames.search %{ q: "London, United Kingdom" }
###### Find cities in a bounding box
Geonames.cities %{ north: 44.1, south: -9.9, east: -22.4, west: 55.2 }
###### Find earthquakes in a bounding box
Geonames.earthquakes %{ north: 44.1, south: -9.9, east: -22.4, west: 55.2, date: "2015-05-30" }
As you can see, the interface is very simple to use. All
functions will return a map in the exact format returned
by the API. Currently, Geonames-Elixir will make no attempt
to format this response in any way.
"""
alias Geonames.Endpoints, as: EP
alias Geonames.Helpers
endpoints = [
EP.Astergdem,
EP.Children,
EP.Cities,
EP.Contains,
EP.CountryCode,
EP.CountryInfo,
EP.CountrySubdivision,
EP.Earthquakes,
EP.FindNearby,
EP.FindNearbyPlaceName,
EP.FindNearbyPostalCodes,
EP.FindNearbyStreets,
EP.FindNearbyStreetsOSM,
EP.FindNearbyWeather,
EP.FindNearbyWikipedia,
EP.FindNearestAddress,
EP.FindNearestIntersection,
EP.FindNearestIntersectionOSM,
EP.FindNearbyPOIsOSM,
EP.Get,
EP.GTOPO30,
EP.Hierarchy,
EP.Neighbourhood,
EP.Neighbours,
EP.Ocean,
EP.PostalCodeCountryInfo,
EP.PostalCodeLookup,
EP.PostalCodeSearch,
EP.Search,
EP.Siblings,
EP.SRTM1,
EP.SRTM3,
EP.Timezone,
EP.Weather,
EP.WeatherICAO,
EP.WikipediaBoundingBox,
EP.WikipediaSearch
]
for endpoint <- endpoints do
@doc """
Makes a request to the GeoNames endpoint `/#{endpoint.endpoint}`
The arguments map may contain the following keys:
#{Enum.map(endpoint.available_url_parameters, fn e -> "- #{e}\n" end)}
Each request parameter should be supplied in a map. For example,
Geonames.#{endpoint.function_name}(%{
#{
Enum.join(
Enum.map(endpoint.available_url_parameters, fn e -> "#{to_string(e)}: \"val\"" end),
",\n "
)
}
})
"""
@spec unquote(endpoint.function_name)(map() | keyword()) :: map()
def unquote(endpoint.function_name)(args \\ %{}) do
url_params = unquote(endpoint).url_arguments(Enum.into(args, %{}))
unless Helpers.required_parameters_provided?(
unquote(endpoint).required_url_parameters,
url_params
),
do: raise(ArgumentError, message: "Not all required parameters were supplied")
unquote(endpoint).endpoint
|> Helpers.build_url_string(url_params)
|> perform_geonames_request()
|> case do
{:ok, json_response} -> json_response
{:error, error_message} -> raise RuntimeError, message: error_message
end
end
end
@doc """
Performs a simple get request to the specified URL.
This is not specific to GeoNames and could be used
for any basic GET request, but it assumes the response
is in JSON.
This function will return one of two values
{ :ok, %{ ... }}
{ :error, "Reason" }
A successful request will return a parsed Map of the
response. If an error occurs, an :error tuple will
be returned with a string describing the error.
"""
@spec perform_geonames_request(String.t()) :: {:ok, map()} | {:error, String.t()}
def perform_geonames_request(url) do
case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, Geonames.JsonDecoder.decode!(body)}
{:ok, %HTTPoison.Response{status_code: status_code, body: _body}} ->
{:error, "An unexpected #{status_code} response was received"}
{:error, %HTTPoison.Error{reason: reason}} ->
{:error, "Request failed with the following error: #{to_string(reason)}"}
end
end
end