Skip to content

Commit

Permalink
Geocode source (#29)
Browse files Browse the repository at this point in the history
* Geocode source

* Typo
  • Loading branch information
Cristine Guadelupe authored Nov 23, 2022
1 parent ec25959 commit 9645c15
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 6 deletions.
64 changes: 58 additions & 6 deletions lib/maplibre.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ defmodule MapLibre do

@to_kebab Utils.kebab_case_properties()
@geometries [Geo.Point, Geo.LineString, Geo.Polygon, Geo.GeometryCollection]
@query_base "https://nominatim.openstreetmap.org/search?format=geojson&limit=1&polygon_geojson=1"

defstruct spec: %{}

Expand Down Expand Up @@ -163,8 +164,7 @@ defmodule MapLibre do
def add_source(ml, source, opts) do
validate_source!(opts)
source = %{source => opts_to_ml_props(opts)}
sources = if ml.spec["sources"], do: Map.merge(ml.spec["sources"], source), else: source
update_in(ml.spec, fn spec -> Map.put(spec, "sources", sources) end)
put_source(ml, source)
end

@doc """
Expand All @@ -187,8 +187,7 @@ defmodule MapLibre do
data = Geo.JSON.encode!(geom, feature: true)
source_props = opts_to_ml_props(opts) |> Map.merge(%{"type" => "geojson", "data" => data})
source = %{source => source_props}
sources = if ml.spec["sources"], do: Map.merge(ml.spec["sources"], source), else: source
update_in(ml.spec, fn spec -> Map.put(spec, "sources", sources) end)
put_source(ml, source)
end

@doc """
Expand Down Expand Up @@ -233,8 +232,45 @@ defmodule MapLibre do
|> Map.merge(%{"type" => "geojson", "data" => data})

source = %{source => source_props}
sources = if ml.spec["sources"], do: Map.merge(ml.spec["sources"], source), else: source
update_in(ml.spec, fn spec -> Map.put(spec, "sources", sources) end)
put_source(ml, source)
end

@doc """
Adds a data source by a given geocode to the sources in the specification.
For the `:geojson` type, provides integration with [Nominatim](https://nominatim.org).
Any valid geocode is support in a free-form query string.
Ml.new()
|> Ml.add_geocode_source("brazil", "brazil")
|> Ml.add_geocode_source("pilkington-avenue", "pilkington avenue, birmingham")
"""
@spec add_geocode_source(t(), String.t(), String.t()) :: t()
def add_geocode_source(ml, source, query) do
query = String.replace(query, " ", "+")
data = "#{@query_base}&q=#{query}"
source = %{source => %{"type" => "geojson", "data" => data}}
put_source(ml, source)
end

@doc """
Same as `add_geocode_source/3` but for structured queries.
The last argument is an atom for the geocode type.
Supported types: `:street`, `:city`, `:county`, `:state`, `:country` and `:postalcode`
Ml.new()
|> Ml.add_geocode_source("new-york", "new york", :city)
|> Ml.add_geocode_source("ny", "new york", :state)
"""
@spec add_geocode_source(t(), String.t(), String.t(), atom()) :: t()
def add_geocode_source(ml, source, query, type) do
validate_geocode_type!(type)
data = "#{@query_base}&#{type}=#{query}"
source = %{source => %{"type" => "geojson", "data" => data}}
put_source(ml, source)
end

defp validate_source!(opts) do
Expand Down Expand Up @@ -286,6 +322,17 @@ defmodule MapLibre do
end
end

defp validate_geocode_type!(type) do
geocode_types = [:street, :city, :county, :state, :country, :postalcode]

if type not in geocode_types do
types = geocode_types |> Enum.map_join(", ", &inspect/1)

raise ArgumentError,
"unknown geocode type, expected one of #{types}, got: #{inspect(type)}"
end
end

@doc """
Adds a layer to the layers list in the specification.
Expand Down Expand Up @@ -688,4 +735,9 @@ defmodule MapLibre do
_ -> nil
end
end

defp put_source(ml, source) do
sources = if ml.spec["sources"], do: Map.merge(ml.spec["sources"], source), else: source
update_in(ml.spec, fn spec -> Map.put(spec, "sources", sources) end)
end
end
60 changes: 60 additions & 0 deletions test/maplibre_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,66 @@ defmodule MapLibreTest do
end
end

describe "add_geocode_source/3" do
test "adds a new source to the map by a given name" do
ml =
Ml.new()
|> Ml.add_geocode_source("brazil", "brazil")

source = ml.spec["sources"]["brazil"]
assert source["type"] == "geojson"

assert source["data"] ==
"https://nominatim.openstreetmap.org/search?format=geojson&limit=1&polygon_geojson=1&q=brazil"
end

test "adds a new source to the map by a given address" do
ml =
Ml.new()
|> Ml.add_geocode_source("pilkington-avenue", "pilkington avenue, birmingham")

source = ml.spec["sources"]["pilkington-avenue"]
assert source["type"] == "geojson"

assert source["data"] ==
"https://nominatim.openstreetmap.org/search?format=geojson&limit=1&polygon_geojson=1&q=pilkington+avenue,+birmingham"
end
end

describe "add_geocode_source/4" do
test "raises an error when an invalid geocode type is given" do
assert_raise ArgumentError,
"unknown geocode type, expected one of :street, :city, :county, :state, :country, :postalcode, got: :invalid",
fn ->
Ml.new() |> Ml.add_geocode_source("invalid", "invalid", :invalid)
end
end

test "adds a new source to the map by a given state" do
ml =
Ml.new()
|> Ml.add_geocode_source("ny", "new york", :state)

source = ml.spec["sources"]["ny"]
assert source["type"] == "geojson"

assert source["data"] ==
"https://nominatim.openstreetmap.org/search?format=geojson&limit=1&polygon_geojson=1&state=new york"
end

test "adds a new source to the map by a given city" do
ml =
Ml.new()
|> Ml.add_geocode_source("new-york", "new york", :city)

source = ml.spec["sources"]["new-york"]
assert source["type"] == "geojson"

assert source["data"] ==
"https://nominatim.openstreetmap.org/search?format=geojson&limit=1&polygon_geojson=1&city=new york"
end
end

describe "add_layer/2" do
test "raises an error when no layer id is given" do
assert_raise ArgumentError, "layer id is required", fn ->
Expand Down

0 comments on commit 9645c15

Please sign in to comment.