Current section
Files
Jump to
Current section
Files
lib/open_street_map.ex
defmodule OpenStreetMap do
@moduledoc """
Functions for requests to OpenStreetMap API
"""
@doc """
Search objects by query
"""
@spec search(keyword()) :: tuple()
def search(args) do
call("search", args)
end
@doc """
Reverse geocoding generates an address from a latitude and longitude
"""
@spec reverse(keyword()) :: tuple()
def reverse(args) do
call("reverse", args)
end
defp call(type, args) do
type
|> generate_url(args)
|> prepare_url(type)
|> fetch(args)
|> parse(args)
end
defp generate_url(type, args) do
Enum.filter(args, fn({key, _value}) -> Enum.member?(valid_args(type), key) end)
|> List.foldl("", fn({key, value}, acc) -> acc <> add_to_options(key, to_string(value), acc) end)
end
defp valid_args(type) do
case type do
"search" -> [:q, :format, :addressdetails, :extratags, :namedetails, :viewbox, :bounded, :exclude_place_ids, :limit, :accept_language, :email]
"reverse" -> [:format, :lat, :lon, :zoom, :addressdetails, :extratags, :namedetails, :accept_language, :email]
_ -> []
end
end
defp add_to_options(key, value, "") do
"?" <> key_value_param(key, modify_search(value))
end
defp add_to_options(key, value, _acc) do
"&" <> key_value_param(key, modify_search(value))
end
defp modify_search(value) do
String.replace(value, ~r/\s+/, "+")
end
defp key_value_param(:accept_language, value) do
"accept-language=" <> value
end
defp key_value_param(key, value) do
"#{key}=#{value}"
end
defp prepare_url(url, type) do
type <> url
end
defp fetch(url, args) do
base_url = args[:hostname] || "https://nominatim.openstreetmap.org/"
headers = ["Accept": "Application/json; Charset=utf-8"]
options = [ssl: [{:versions, [:'tlsv1.2']}], recv_timeout: 500]
case HTTPoison.get(base_url <> url, headers, options) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} -> {:ok, body}
{:ok, %HTTPoison.Response{status_code: 404}} -> {:error, "Page not found"}
{:error, %HTTPoison.Error{reason: reason}} -> {:error, reason}
end
end
defp parse({result, response}, args) do
if result == :ok && args[:format] != 'xml' do
{:ok, Poison.Parser.parse!(response)}
end
end
end