Packages
geo
2.0.0
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/wkt.ex
defmodule Geo.WKT do
alias Geo.Point
alias Geo.PointZ
alias Geo.PointM
alias Geo.PointZM
alias Geo.LineString
alias Geo.Polygon
alias Geo.MultiPoint
alias Geo.MultiLineString
alias Geo.MultiPolygon
alias Geo.GeometryCollection
@moduledoc """
Converts to and from WKT and EWKT
point = Geo.WKT.decode("POINT(30 -90)")
Geo.Point[coordinates: {30, -90}, srid: nil]
Geo.WKT.encode(point)
"POINT(30 -90)"
point = Geo.WKT.decode("SRID=4326;POINT(30 -90)")
Geo.Point[coordinates: {30, -90}, srid: 4326]
"""
@doc """
Takes a Geometry and returns a WKT string
"""
@spec encode(Geo.geometry) :: binary
def encode(geom) do
get_srid_binary(geom.srid) <> do_encode(geom)
end
defp do_encode(%Point{ coordinates: {x, y}}) do
"POINT(#{x} #{y})"
end
defp do_encode(%PointZ{ coordinates: {x, y, z}}) do
"POINT Z(#{x} #{y} #{z})"
end
defp do_encode(%PointM{ coordinates: {x, y, m}}) do
"POINT M(#{x} #{y} #{m})"
end
defp do_encode(%PointZM{ coordinates: {x, y, z, m}}) do
"POINT ZM(#{x} #{y} #{z} #{m})"
end
defp do_encode(%LineString{ coordinates: coordinates}) do
coordinate_string = create_line_string_str(coordinates)
"LINESTRING#{coordinate_string}"
end
defp do_encode(%Polygon{ coordinates: coordinates}) do
coordinate_string = create_polygon_str(coordinates)
"POLYGON#{coordinate_string}"
end
defp do_encode(%MultiPoint{ coordinates: coordinates}) do
coordinate_string = create_line_string_str(coordinates)
"MULTIPOINT#{coordinate_string}"
end
defp do_encode(%MultiLineString{ coordinates: coordinates}) do
coordinate_string = create_polygon_str(coordinates)
"MULTILINESTRING#{coordinate_string}"
end
defp do_encode(%MultiPolygon{ coordinates: coordinates}) do
coordinate_string = create_multi_polygon_str(coordinates)
"MULTIPOLYGON#{coordinate_string}"
end
defp do_encode(%GeometryCollection{ geometries: geometries}) do
geom_str = Enum.map(geometries, &do_encode(&1)) |> Enum.join(",")
"GEOMETRYCOLLECTION(#{geom_str})"
end
@doc """
Takes a WKT string and returns a Geo.Geometry struct or list of Geo.Geometry
"""
@spec decode(binary) :: Geo.geometry
def decode(wkt) do
wkt_split = String.split(wkt, ";")
{srid, actual_wkt} =
if length(wkt_split) == 2 do
{hd(wkt_split) |> String.replace("SRID=","") |> String.to_integer, List.last(wkt_split)}
else
{nil, wkt}
end
do_decode(actual_wkt, srid)
end
defp do_decode("POINT ZM" <> coordinates, srid) do
%PointZM{ coordinates: create_point(coordinates), srid: srid }
end
defp do_decode("POINT Z" <> coordinates, srid) do
%PointZ{ coordinates: create_point(coordinates), srid: srid }
end
defp do_decode("POINT M" <> coordinates, srid) do
%PointM{ coordinates: create_point(coordinates), srid: srid }
end
defp do_decode("POINT" <> coordinates, srid) do
%Point{ coordinates: create_point(coordinates), srid: srid }
end
defp do_decode("LINESTRING" <> coordinates, srid) do
%LineString{ coordinates: create_line_string(coordinates), srid: srid }
end
defp do_decode("POLYGON" <> coordinates, srid) do
%Polygon{ coordinates: create_polygon(coordinates), srid: srid }
end
defp do_decode("MULTIPOINT" <> coordinates, srid) do
%MultiPoint{ coordinates: create_line_string(coordinates), srid: srid }
end
defp do_decode("MULTILINESTRING" <> coordinates, srid) do
%MultiLineString{ coordinates: create_polygon(coordinates), srid: srid }
end
defp do_decode("MULTIPOLYGON" <> coordinates, srid) do
%MultiPolygon{ coordinates: create_multi_polygon(coordinates), srid: srid }
end
defp do_decode("GEOMETRYCOLLECTION" <> coordinates, srid) do
geometries = String.slice(coordinates, 0..(String.length(coordinates)-2))
|> String.split(",", [parts: 2])
|> Enum.map(fn(x) ->
case x do
"(" <> wkt ->
do_decode(wkt, srid)
_ ->
do_decode(x, srid)
end
end)
%GeometryCollection{ geometries: geometries, srid: srid }
end
defp create_point(coordinates) do
coordinates
|> String.strip
|> remove_outer_parenthesis
|> String.split
|> Enum.map(fn(x) -> binary_to_number(x) end)
|> List.to_tuple
end
defp create_line_string(coordinates) do
coordinates
|> String.strip
|> remove_outer_parenthesis
|> String.split(",")
|> Enum.map(&create_point(&1))
end
defp create_polygon(coordinates) do
coordinates
|> String.strip
|> remove_outer_parenthesis
|> String.split(~r{\), *\(})
|> Enum.map(&repair_str(&1, "(", ")"))
|> Enum.map(&create_line_string(&1))
end
defp create_multi_polygon(coordinates) do
coordinates
|> String.strip
|> remove_outer_parenthesis
|> String.split(~r{\)\), *\(\(})
|> Enum.map(&repair_str(&1, "((", "))"))
|> Enum.map(&create_polygon(&1))
end
defp binary_to_number(binary) do
if String.contains?(binary,"."), do: String.to_float(binary), else: String.to_integer(binary)
end
defp remove_outer_parenthesis(coordinates) do
coordinates
|> String.replace("(", "", global: false)
|> String.reverse
|> String.replace(")", "", global: false)
|> String.reverse
end
defp create_pair_str({x, y}) do
"#{x} #{y}"
end
defp create_line_string_str(coordinates) do
coordinate_str = coordinates
|> Enum.map(&create_pair_str(&1))
|> Enum.join(",")
"(#{coordinate_str})"
end
defp create_polygon_str(coordinates) do
coordinate_str = coordinates
|> Enum.map(&create_line_string_str(&1))
|> Enum.join(",")
"(#{coordinate_str})"
end
defp create_multi_polygon_str(coordinates) do
coordinate_str = coordinates
|> Enum.map(&create_polygon_str(&1))
|> Enum.join(",")
"(#{coordinate_str})"
end
defp get_srid_binary(nil) do
""
end
defp get_srid_binary(0) do
""
end
defp get_srid_binary(srid) do
"SRID=#{srid};"
end
defp repair_str(str, starts, ends) do
str = if !String.starts_with?(str, starts), do: starts <> str, else: str
str = if !String.ends_with?(str, ends), do: str <> ends, else: str
str
end
end