Packages
geo
3.5.1
4.1.0
4.0.1
4.0.0
3.6.0
3.5.1
3.5.0
3.4.3
3.4.2
3.4.1
3.4.0
3.3.8
3.3.7
3.3.5
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.0
3.1.0
3.0.0
2.1.0
2.0.0
1.5.0
1.4.1
1.4.0
1.3.1
1.3.0
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.18.0
0.17.0
0.16.1
0.16.0
0.15.2
0.15.1
0.15.0
0.14.0
0.13.0
0.12.0
0.11.2
0.11.1
0.11.0
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.2
0.7.1
0.6.1
0.5.1
Encodes and decodes WKB, WKT, and GeoJSON formats.
Current section
Files
Jump to
Current section
Files
lib/geo/json/decoder.ex
defmodule Geo.JSON.Decoder do
@moduledoc false
alias Geo.{
Point,
PointZ,
LineString,
LineStringZ,
Polygon,
MultiPoint,
MultiLineString,
MultiPolygon,
MultiPolygonZ,
GeometryCollection
}
defmodule DecodeError do
@type t :: %__MODULE__{message: String.t(), value: any}
defexception [:message, :value]
def message(%{message: nil, value: value}) do
"unable to decode value: #{inspect(value)}"
end
def message(%{message: message}) do
message
end
end
@doc """
Takes a map representing GeoJSON and returns a Geometry.
Feature objects with null geometries will be stripped from a FeatureCollection,
and a standalone Feature object with null geometry will be decoded to nil.
"""
@spec decode!(map()) :: Geo.geometry() | nil
def decode!(geo_json) do
cond do
Map.has_key?(geo_json, "geometries") ->
crs = Map.get(geo_json, "crs")
geometries =
Enum.map(Map.get(geo_json, "geometries"), fn x ->
do_decode(
Map.get(x, "type"),
Map.get(x, "coordinates"),
Map.get(x, "properties", %{}),
crs
)
end)
%GeometryCollection{
geometries: geometries,
properties: Map.get(geo_json, "properties", %{})
}
Map.has_key?(geo_json, "coordinates") ->
crs = Map.get(geo_json, "crs")
do_decode(
Map.get(geo_json, "type"),
Map.get(geo_json, "coordinates"),
Map.get(geo_json, "properties", %{}),
crs
)
Map.get(geo_json, "type") == "Feature" ->
do_decode(
"Feature",
Map.get(geo_json, "geometry"),
Map.get(geo_json, "properties", %{}),
Map.get(geo_json, "id", "")
)
Map.get(geo_json, "type") == "FeatureCollection" ->
geometries =
Map.get(geo_json, "features")
|> Enum.map(fn x ->
do_decode(
Map.get(x, "type"),
Map.get(x, "geometry"),
Map.get(x, "properties", %{}),
Map.get(x, "id", "")
)
end)
|> Enum.reject(&is_nil/1)
%GeometryCollection{
geometries: geometries,
properties: %{}
}
true ->
raise DecodeError, value: geo_json
end
end
@doc """
Takes a map representing GeoJSON and returns a Geometry.
"""
@spec decode(map()) :: {:ok, Geo.geometry() | nil} | {:error, DecodeError.t()}
def decode(geo_json) do
{:ok, decode!(geo_json)}
rescue
exception in [DecodeError] ->
{:error, exception}
end
defp do_decode("Point", [x, y, z], properties, crs) do
%PointZ{coordinates: {x, y, z}, srid: get_srid(crs), properties: properties}
end
defp do_decode("Point", [x, y], properties, crs) do
%Point{coordinates: {x, y}, srid: get_srid(crs), properties: properties}
end
defp do_decode("LineString", coordinates, properties, crs) do
coordinates = Enum.map(coordinates, &list_to_tuple(&1))
%LineString{coordinates: coordinates, srid: get_srid(crs), properties: properties}
end
defp do_decode("LineStringZ", coordinates, properties, crs) do
coordinates = Enum.map(coordinates, &List.to_tuple(&1))
%LineStringZ{coordinates: coordinates, srid: get_srid(crs), properties: properties}
end
defp do_decode("Polygon", coordinates, properties, crs) do
coordinates =
Enum.map(coordinates, fn sub_coordinates ->
Enum.map(sub_coordinates, &list_to_tuple(&1))
end)
%Polygon{coordinates: coordinates, srid: get_srid(crs), properties: properties}
end
defp do_decode("MultiPoint", coordinates, properties, crs) do
coordinates = Enum.map(coordinates, &list_to_tuple(&1))
%MultiPoint{coordinates: coordinates, srid: get_srid(crs), properties: properties}
end
defp do_decode("MultiLineString", coordinates, properties, crs) do
coordinates =
Enum.map(coordinates, fn sub_coordinates ->
Enum.map(sub_coordinates, &list_to_tuple(&1))
end)
%MultiLineString{coordinates: coordinates, srid: get_srid(crs), properties: properties}
end
defp do_decode("MultiPolygon", coordinates, properties, crs) do
coordinates =
Enum.map(coordinates, fn sub_coordinates ->
Enum.map(sub_coordinates, fn third_sub_coordinates ->
Enum.map(third_sub_coordinates, &list_to_tuple(&1))
end)
end)
%MultiPolygon{coordinates: coordinates, srid: get_srid(crs), properties: properties}
end
defp do_decode("MultiPolygonZ", coordinates, properties, crs) do
coordinates =
Enum.map(coordinates, fn sub_coordinates ->
Enum.map(sub_coordinates, fn third_sub_coordinates ->
Enum.map(third_sub_coordinates, &list_to_tuple(&1))
end)
end)
%MultiPolygonZ{coordinates: coordinates, srid: get_srid(crs), properties: properties}
end
defp do_decode("Feature", nil, _properties, _id), do: nil
defp do_decode("Feature", geometry, properties, _id) do
do_decode(Map.get(geometry, "type"), Map.get(geometry, "coordinates"), properties, nil)
end
defp do_decode(type, [x, y, _z], properties, crs) do
do_decode(type, [x, y], properties, crs)
end
defp do_decode(type, _, _, _) do
raise DecodeError, message: "#{type} is not a valid type"
end
defp list_to_tuple([x, y | _]), do: {x, y}
defp get_srid(%{"type" => "name", "properties" => %{"name" => "EPSG:" <> srid}}) do
{srid, _} = Integer.parse(srid)
srid
end
defp get_srid(%{"type" => "name", "properties" => %{"name" => srid}}) do
srid
end
defp get_srid(nil) do
nil
end
end