diff --git a/lib/geo/wkt/decoder.ex b/lib/geo/wkt/decoder.ex index a98c148..70e1110 100644 --- a/lib/geo/wkt/decoder.ex +++ b/lib/geo/wkt/decoder.ex @@ -53,75 +53,84 @@ defmodule Geo.WKT.Decoder do end defp do_decode("POINT ZM" <> coordinates, srid) do - %PointZM{coordinates: create_point(coordinates), srid: srid} + %PointZM{coordinates: create_coordinates(coordinates, &create_point/1), srid: srid} end defp do_decode("POINT Z" <> coordinates, srid) do - %PointZ{coordinates: create_point(coordinates), srid: srid} + %PointZ{coordinates: create_coordinates(coordinates, &create_point/1), srid: srid} end defp do_decode("POINT M" <> coordinates, srid) do - %PointM{coordinates: create_point(coordinates), srid: srid} + %PointM{coordinates: create_coordinates(coordinates, &create_point/1), srid: srid} end defp do_decode("POINT" <> coordinates, srid) do - %Point{coordinates: create_point(coordinates), srid: srid} + %Point{coordinates: create_coordinates(coordinates, &create_point/1), srid: srid} end defp do_decode("LINESTRINGM" <> coordinates, srid) do - %LineStringM{coordinates: create_line_string(coordinates), srid: srid} + %LineStringM{coordinates: create_coordinates(coordinates, &create_line_string/1), srid: srid} end defp do_decode("LINESTRINGZM" <> coordinates, srid) do - %LineStringZM{coordinates: create_line_string(coordinates), srid: srid} + %LineStringZM{coordinates: create_coordinates(coordinates, &create_line_string/1), srid: srid} end defp do_decode("LINESTRINGZ" <> coordinates, srid) do - %LineStringZ{coordinates: create_line_string(coordinates), srid: srid} + %LineStringZ{coordinates: create_coordinates(coordinates, &create_line_string/1), srid: srid} end defp do_decode("LINESTRING" <> coordinates, srid) do - %LineString{coordinates: create_line_string(coordinates), srid: srid} + %LineString{coordinates: create_coordinates(coordinates, &create_line_string/1), srid: srid} end defp do_decode("POLYGONZ" <> coordinates, srid) do - %PolygonZ{coordinates: create_polygon(coordinates), srid: srid} + %PolygonZ{coordinates: create_coordinates(coordinates, &create_polygon/1), srid: srid} end defp do_decode("POLYGON" <> coordinates, srid) do - %Polygon{coordinates: create_polygon(coordinates), srid: srid} + %Polygon{coordinates: create_coordinates(coordinates, &create_polygon/1), srid: srid} end defp do_decode("MULTIPOINTM" <> coordinates, srid) do - %MultiPointM{coordinates: create_line_string(coordinates), srid: srid} + %MultiPointM{coordinates: create_coordinates(coordinates, &create_line_string/1), srid: srid} end defp do_decode("MULTIPOINTZ" <> coordinates, srid) do - %MultiPointZ{coordinates: create_line_string(coordinates), srid: srid} + %MultiPointZ{coordinates: create_coordinates(coordinates, &create_line_string/1), srid: srid} end defp do_decode("MULTIPOINT" <> coordinates, srid) do - %MultiPoint{coordinates: create_line_string(coordinates), srid: srid} + %MultiPoint{coordinates: create_coordinates(coordinates, &create_line_string/1), srid: srid} end defp do_decode("MULTILINESTRINGZM" <> coordinates, srid) do - %MultiLineStringZM{coordinates: create_polygon(coordinates), srid: srid} + %MultiLineStringZM{ + coordinates: create_coordinates(coordinates, &create_polygon/1), + srid: srid + } end defp do_decode("MULTILINESTRINGZ" <> coordinates, srid) do - %MultiLineStringZ{coordinates: create_polygon(coordinates), srid: srid} + %MultiLineStringZ{coordinates: create_coordinates(coordinates, &create_polygon/1), srid: srid} end defp do_decode("MULTILINESTRING" <> coordinates, srid) do - %MultiLineString{coordinates: create_polygon(coordinates), srid: srid} + %MultiLineString{coordinates: create_coordinates(coordinates, &create_polygon/1), srid: srid} end defp do_decode("MULTIPOLYGONZ" <> coordinates, srid) do - %MultiPolygonZ{coordinates: create_multi_polygon(coordinates), srid: srid} + %MultiPolygonZ{ + coordinates: create_coordinates(coordinates, &create_multi_polygon/1), + srid: srid + } end defp do_decode("MULTIPOLYGON" <> coordinates, srid) do - %MultiPolygon{coordinates: create_multi_polygon(coordinates), srid: srid} + %MultiPolygon{ + coordinates: create_coordinates(coordinates, &create_multi_polygon/1), + srid: srid + } end defp do_decode("GEOMETRYCOLLECTION" <> coordinates, srid) do @@ -141,10 +150,21 @@ defmodule Geo.WKT.Decoder do %GeometryCollection{geometries: geometries, srid: srid} end + defp create_coordinates(coordinates, cont) do + bare = + coordinates + |> String.trim() + |> remove_outer_parenthesis() + + case bare do + "" -> nil + "EMPTY" -> nil + coordinates -> cont.(coordinates) + end + end + defp create_point(coordinates) do coordinates - |> String.trim() - |> remove_outer_parenthesis |> String.split() |> Enum.map(fn x -> binary_to_number(x) end) |> List.to_tuple() @@ -152,28 +172,22 @@ defmodule Geo.WKT.Decoder do defp create_line_string(coordinates) do coordinates - |> String.trim() - |> remove_outer_parenthesis |> String.split(",") - |> Enum.map(&create_point(&1)) + |> Enum.map(fn c -> create_coordinates(c, &create_point/1) end) end defp create_polygon(coordinates) do coordinates - |> String.trim() - |> remove_outer_parenthesis |> String.split(~r{\), *\(}) |> Enum.map(&repair_str(&1, "(", ")")) - |> Enum.map(&create_line_string(&1)) + |> Enum.map(fn c -> create_coordinates(c, &create_line_string/1) end) end defp create_multi_polygon(coordinates) do coordinates - |> String.trim() - |> remove_outer_parenthesis |> String.split(~r{\)\), *\(\(}) |> Enum.map(&repair_str(&1, "((", "))")) - |> Enum.map(&create_polygon(&1)) + |> Enum.map(fn c -> create_coordinates(c, &create_polygon/1) end) end defp binary_to_number(binary) do diff --git a/lib/geo/wkt/encoder.ex b/lib/geo/wkt/encoder.ex index f9921a1..01e410e 100644 --- a/lib/geo/wkt/encoder.ex +++ b/lib/geo/wkt/encoder.ex @@ -42,6 +42,20 @@ defmodule Geo.WKT.Encoder do get_srid_binary(geom.srid) <> do_encode(geom) end + defp do_encode_empty(%x{}) do + typename = + x + |> Module.split() + |> Enum.at(-1) + |> to_string() + |> String.upcase() + + "#{typename} EMPTY" + end + + defp do_encode(%{coordinates: nil} = geometry), do: do_encode_empty(geometry) + defp do_encode(%{coordinates: []} = geometry), do: do_encode_empty(geometry) + defp do_encode(%Point{coordinates: {x, y}}) do "POINT(#{x} #{y})" end diff --git a/test/geo/wkt_test.exs b/test/geo/wkt_test.exs index 58e6132..46b08e3 100644 --- a/test/geo/wkt_test.exs +++ b/test/geo/wkt_test.exs @@ -2,6 +2,20 @@ defmodule Geo.WKT.Test do use ExUnit.Case, async: false use ExUnitProperties + test "Encodes nil coordinate geometries as EMPTY" do + geom = %Geo.Point{coordinates: nil} + assert(Geo.WKT.encode!(geom) == "POINT EMPTY") + + geom = %Geo.Polygon{coordinates: nil} + assert(Geo.WKT.encode!(geom) == "POLYGON EMPTY") + + geom = %Geo.Polygon{coordinates: []} + assert(Geo.WKT.encode!(geom) == "POLYGON EMPTY") + + geom = %Geo.LineStringZM{coordinates: nil} + assert(Geo.WKT.encode!(geom) == "LINESTRINGZM EMPTY") + end + test "Encode Point to WKT" do geom = %Geo.Point{coordinates: {30, -90}} assert(Geo.WKT.encode!(geom) == "POINT(30 -90)") @@ -53,6 +67,14 @@ defmodule Geo.WKT.Test do assert(Geo.WKT.encode!(geom) == "LINESTRING(30 10,10 30,40 40)") end + test "Decode WKT to empty Linestring" do + linestring = Geo.WKT.decode!("LINESTRING EMPTY") + assert(linestring.coordinates == nil) + + point = Geo.WKT.decode!("POINT EMPTY") + assert(point.coordinates == nil) + end + test "Decode WKT to Linestring" do geom = Geo.WKT.decode!("LINESTRING(30 10,10 30,40 40)") assert(geom.coordinates == [{30, 10}, {10, 30}, {40, 40}])