diff --git a/lib/geo/json/decoder.ex b/lib/geo/json/decoder.ex index 7bb183e..f13b57e 100644 --- a/lib/geo/json/decoder.ex +++ b/lib/geo/json/decoder.ex @@ -5,9 +5,11 @@ defmodule Geo.JSON.Decoder do Point, PointZ, LineString, + LineStringM, LineStringZ, Polygon, MultiPoint, + MultiPointM, MultiLineString, MultiPolygon, MultiPolygonZ, @@ -116,6 +118,12 @@ defmodule Geo.JSON.Decoder do %LineString{coordinates: coordinates, srid: get_srid(crs), properties: properties} end + defp do_decode("LineStringM", coordinates, properties, crs) do + coordinates = Enum.map(coordinates, &List.to_tuple(&1)) + + %LineStringM{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)) @@ -137,6 +145,12 @@ defmodule Geo.JSON.Decoder do %MultiPoint{coordinates: coordinates, srid: get_srid(crs), properties: properties} end + defp do_decode("MultiPointM", coordinates, properties, crs) do + coordinates = Enum.map(coordinates, &List.to_tuple(&1)) + + %MultiPointM{coordinates: coordinates, srid: get_srid(crs), properties: properties} + end + defp do_decode("MultiLineString", coordinates, properties, crs) do coordinates = Enum.map(coordinates, fn sub_coordinates -> diff --git a/lib/geo/json/encoder.ex b/lib/geo/json/encoder.ex index 1407e99..33fa45a 100644 --- a/lib/geo/json/encoder.ex +++ b/lib/geo/json/encoder.ex @@ -5,10 +5,12 @@ defmodule Geo.JSON.Encoder do Point, PointZ, LineString, + LineStringM, LineStringZ, Polygon, PolygonZ, MultiPoint, + MultiPointM, MultiPointZ, MultiLineString, MultiLineStringZ, @@ -136,6 +138,12 @@ defmodule Geo.JSON.Encoder do %{"type" => "LineString", "coordinates" => coordinates} end + defp do_encode(%LineStringM{coordinates: coordinates}) do + coordinates = Enum.map(coordinates, &Tuple.to_list(&1)) + + %{"type" => "LineStringM", "coordinates" => coordinates} + end + defp do_encode(%LineStringZ{coordinates: coordinates}) do coordinates = Enum.map(coordinates, &Tuple.to_list(&1)) @@ -166,6 +174,12 @@ defmodule Geo.JSON.Encoder do %{"type" => "MultiPoint", "coordinates" => coordinates} end + defp do_encode(%MultiPointM{coordinates: coordinates}) do + coordinates = Enum.map(coordinates, &Tuple.to_list(&1)) + + %{"type" => "MultiPointM", "coordinates" => coordinates} + end + defp do_encode(%MultiPointZ{coordinates: coordinates}) do coordinates = Enum.map(coordinates, &Tuple.to_list(&1)) diff --git a/lib/geo/line_stringm.ex b/lib/geo/line_stringm.ex new file mode 100644 index 0000000..e23336c --- /dev/null +++ b/lib/geo/line_stringm.ex @@ -0,0 +1,8 @@ +defmodule Geo.LineStringM do + @moduledoc """ + Defines the LineStringZ struct. + """ + + @type t :: %__MODULE__{coordinates: [{number, number, number}], srid: integer | nil, properties: map} + defstruct coordinates: [], srid: nil, properties: %{} +end diff --git a/lib/geo/multi_pointm.ex b/lib/geo/multi_pointm.ex new file mode 100644 index 0000000..26fe097 --- /dev/null +++ b/lib/geo/multi_pointm.ex @@ -0,0 +1,8 @@ +defmodule Geo.MultiPointM do + @moduledoc """ + Defines the MultiPointZ struct. + """ + + @type t :: %__MODULE__{coordinates: [{number, number, number}], srid: integer | nil, properties: map} + defstruct coordinates: [], srid: nil, properties: %{} +end diff --git a/lib/geo/wkb/decoder.ex b/lib/geo/wkb/decoder.ex index a7400a9..8a989ab 100644 --- a/lib/geo/wkb/decoder.ex +++ b/lib/geo/wkb/decoder.ex @@ -6,10 +6,12 @@ defmodule Geo.WKB.Decoder do @point_z 0x80_00_00_01 @point_zm 0xC0_00_00_01 @line_string 0x00_00_00_02 + @line_string_m 0x40_00_00_02 @line_string_z 0x80_00_00_02 @polygon 0x00_00_00_03 @polygon_z 0x80_00_00_03 @multi_point 0x00_00_00_04 + @multi_point_m 0x40_00_00_04 @multi_point_z 0x80_00_00_04 @multi_line_string 0x00_00_00_05 @multi_line_string_z 0x80_00_00_05 @@ -27,11 +29,13 @@ defmodule Geo.WKB.Decoder do PointM, PointZM, LineString, + LineStringM, LineStringZ, Polygon, PolygonZ, GeometryCollection, MultiPoint, + MultiPointM, MultiPointZ, MultiLineString, MultiLineStringZ, @@ -135,6 +139,32 @@ defmodule Geo.WKB.Decoder do {%LineString{coordinates: coordinates, srid: srid}, rest} end + defp do_decode( + @line_string_m, + <>, + srid, + unquote(endian) + ) do + {coordinates, rest} = + Enum.map_reduce(1..count, rest, fn _, + <> -> + {%PointM{coordinates: coordinates}, _rest} = + do_decode( + @point_m, + <>, + nil, + unquote(endian) + ) + + {coordinates, rest} + end) + + {%LineStringM{coordinates: coordinates, srid: srid}, rest} + end + defp do_decode( @line_string_z, <>, @@ -221,6 +251,22 @@ defmodule Geo.WKB.Decoder do {%MultiPoint{coordinates: coordinates, srid: srid}, rest} end + defp do_decode( + @multi_point_m, + <>, + srid, + unquote(endian) + ) do + {coordinates, rest} = + Enum.map_reduce(List.duplicate(1, count), rest, fn _, <> -> + {%PointM{coordinates: coordinates}, rest} = decode(rest) + + {coordinates, rest} + end) + + {%MultiPointM{coordinates: coordinates, srid: srid}, rest} + end + defp do_decode( @multi_point_z, <>, diff --git a/lib/geo/wkb/encoder.ex b/lib/geo/wkb/encoder.ex index c25fd29..e597734 100644 --- a/lib/geo/wkb/encoder.ex +++ b/lib/geo/wkb/encoder.ex @@ -6,10 +6,12 @@ defmodule Geo.WKB.Encoder do @point_z 0x80_00_00_01 @point_zm 0xC0_00_00_01 @line_string 0x00_00_00_02 + @line_string_m 0x40_00_00_02 @line_string_z 0x80_00_00_02 @polygon 0x00_00_00_03 @polygon_z 0x80_00_00_03 @multi_point 0x00_00_00_04 + @multi_point_m 0x40_00_00_04 @multi_point_z 0x80_00_00_04 @multi_line_string 0x00_00_00_05 @multi_line_string_z 0x80_00_00_05 @@ -27,10 +29,12 @@ defmodule Geo.WKB.Encoder do PointM, PointZM, LineString, + LineStringM, LineStringZ, Polygon, PolygonZ, MultiPoint, + MultiPointM, MultiPointZ, MultiLineString, MultiLineStringZ, @@ -98,6 +102,19 @@ defmodule Geo.WKB.Encoder do {@line_string, [<> | coordinates]} end + def do_encode(%LineStringM{coordinates: coordinates}, unquote(endian_atom)) do + {coordinates, count} = + Enum.map_reduce(coordinates, 0, fn {x, y, z}, acc -> + {[ + <>, + <>, + <> + ], acc + 1} + end) + + {@line_string_m, [<> | coordinates]} + end + def do_encode(%LineStringZ{coordinates: coordinates}, unquote(endian_atom)) do {coordinates, count} = Enum.map_reduce(coordinates, 0, fn {x, y, z}, acc -> @@ -141,6 +158,16 @@ defmodule Geo.WKB.Encoder do {@multi_point, [<> | coordinates]} end + def do_encode(%MultiPointM{coordinates: coordinates}, unquote(endian_atom)) do + {coordinates, count} = + Enum.map_reduce(coordinates, 0, fn coordinate, acc -> + point = encode!(%PointM{coordinates: coordinate}, unquote(endian_atom)) + {point, acc + 1} + end) + + {@multi_point_m, [<> | coordinates]} + end + def do_encode(%MultiPointZ{coordinates: coordinates}, unquote(endian_atom)) do {coordinates, count} = Enum.map_reduce(coordinates, 0, fn coordinate, acc -> diff --git a/lib/geo/wkt/decoder.ex b/lib/geo/wkt/decoder.ex index 8c910fd..21183a8 100644 --- a/lib/geo/wkt/decoder.ex +++ b/lib/geo/wkt/decoder.ex @@ -7,10 +7,12 @@ defmodule Geo.WKT.Decoder do PointM, PointZM, LineString, + LineStringM, LineStringZ, Polygon, PolygonZ, MultiPoint, + MultiPointM, MultiPointZ, MultiLineString, MultiPolygon, @@ -63,6 +65,10 @@ defmodule Geo.WKT.Decoder do %Point{coordinates: create_point(coordinates), srid: srid} end + defp do_decode("LINESTRINGM" <> coordinates, srid) do + %LineStringM{coordinates: create_line_string(coordinates), srid: srid} + end + defp do_decode("LINESTRINGZ" <> coordinates, srid) do %LineStringZ{coordinates: create_line_string(coordinates), srid: srid} end @@ -79,6 +85,10 @@ defmodule Geo.WKT.Decoder do %Polygon{coordinates: create_polygon(coordinates), srid: srid} end + defp do_decode("MULTIPOINTM" <> coordinates, srid) do + %MultiPointM{coordinates: create_line_string(coordinates), srid: srid} + end + defp do_decode("MULTIPOINTZ" <> coordinates, srid) do %MultiPointZ{coordinates: create_line_string(coordinates), srid: srid} end diff --git a/lib/geo/wkt/encoder.ex b/lib/geo/wkt/encoder.ex index a451cbe..88b8e8b 100644 --- a/lib/geo/wkt/encoder.ex +++ b/lib/geo/wkt/encoder.ex @@ -7,10 +7,12 @@ defmodule Geo.WKT.Encoder do PointM, PointZM, LineString, + LineStringM, LineStringZ, Polygon, PolygonZ, MultiPoint, + MultiPointM, MultiPointZ, MultiLineString, MultiLineStringZ, @@ -60,6 +62,12 @@ defmodule Geo.WKT.Encoder do "LINESTRING#{coordinate_string}" end + defp do_encode(%LineStringM{coordinates: coordinates}) do + coordinate_string = create_line_string_str(coordinates) + + "LINESTRINGM#{coordinate_string}" + end + defp do_encode(%LineStringZ{coordinates: coordinates}) do coordinate_string = create_line_string_str(coordinates) @@ -84,6 +92,12 @@ defmodule Geo.WKT.Encoder do "MULTIPOINT#{coordinate_string}" end + defp do_encode(%MultiPointM{coordinates: coordinates}) do + coordinate_string = create_line_string_str(coordinates) + + "MULTIPOINTM#{coordinate_string}" + end + defp do_encode(%MultiPointZ{coordinates: coordinates}) do coordinate_string = create_line_string_str(coordinates) diff --git a/test/geo/json_test.exs b/test/geo/json_test.exs index fe51a9e..b84ebe7 100644 --- a/test/geo/json_test.exs +++ b/test/geo/json_test.exs @@ -81,6 +81,16 @@ defmodule Geo.JSON.Test do assert(exjson == new_exjson) end + test "GeoJson to LineStringM and back" do + json = "{ \"type\": \"LineStringM\", \"coordinates\": [ [100.0, 0.0, 50.0], [101.0, 1.0, 20.0] ]}" + exjson = Jason.decode!(json) + geom = Jason.decode!(json) |> Geo.JSON.decode!() + + assert(geom.coordinates == [{100.0, 0.0, 50.0}, {101.0, 1.0, 20.0}]) + new_exjson = Geo.JSON.encode!(geom) + assert(exjson == new_exjson) + end + test "GeoJson to LineStringZ and back" do json = "{ \"type\": \"LineStringZ\", \"coordinates\": [ [100.0, 0.0, 50.0], [101.0, 1.0, 20.0] ]}" exjson = Jason.decode!(json) @@ -127,6 +137,16 @@ defmodule Geo.JSON.Test do assert(exjson == new_exjson) end + test "GeoJson to MultiPointM and back" do + json = "{ \"type\": \"MultiPointM\", \"coordinates\": [ [100.0, 0.0, 5], [101.0, 1.0, 50] ]}" + exjson = Jason.decode!(json) + geom = Jason.decode!(json) |> Geo.JSON.decode!() + + assert(geom.coordinates == [{100.0, 0.0, 5}, {101.0, 1.0, 50}]) + new_exjson = Geo.JSON.encode!(geom) + assert(exjson == new_exjson) + end + test "GeoJson to MultiLineString and back" do json = "{ \"type\": \"MultiLineString\", \"coordinates\": [[ [100.0, 0.0], [101.0, 1.0] ],[ [102.0, 2.0], [103.0, 3.0] ]]}" diff --git a/test/geo/wkb_test.exs b/test/geo/wkb_test.exs index 4ba839c..90bc57b 100644 --- a/test/geo/wkb_test.exs +++ b/test/geo/wkb_test.exs @@ -158,6 +158,33 @@ defmodule Geo.WKB.Test do ) end + test "Decode WKB to LineStringM" do + point = + Geo.WKB.decode!( + "004000000200000003403E000000000000402400000000000040080000000000004024000000000000403E0000000000004056800000000000404400000000000040440000000000004044000000000000" + ) + + assert(point.coordinates == [{30, 10, 3}, {10, 30, 90}, {40, 40, 40}]) + end + + test "Encode LineStringM to EWKB" do + geom = %Geo.LineStringM{coordinates: [{30, 10, 3}, {10, 30, 90}, {40, 40, 40}], srid: 4326} + + assert( + Geo.WKB.encode!(geom, :ndr) == + "0102000060E6100000030000000000000000003E400000000000002440000000000000084000000000000024400000000000003E400000000000805640000000000000444000000000000044400000000000004440" + ) + end + + test "Encode LineStringM to WKB" do + geom = %Geo.LineStringM{coordinates: [{30, 10, 3}, {10, 30, 90}, {40, 40, 40}]} + + assert( + Geo.WKB.encode!(geom) == + "004000000200000003403E000000000000402400000000000040080000000000004024000000000000403E0000000000004056800000000000404400000000000040440000000000004044000000000000" + ) + end + test "Decode WKB to LineStringZ" do point = Geo.WKB.decode!( @@ -389,6 +416,25 @@ defmodule Geo.WKB.Test do ) end + test "Encode MultiPointM to WKB" do + geom = %Geo.MultiPointM{coordinates: [{13.1668, 52.45695, 120}], srid: 4326} + + assert( + Geo.WKB.encode!(geom, :ndr) == + "0104000060E610000001000000010100004013F241CF66552A401FF46C567D3A4A400000000000005E40" + ) + end + + test "Decode EWKB to MultiPointM" do + point = + Geo.WKB.decode!( + "0104000060E610000001000000010100004013F241CF66552A401FF46C567D3A4A400000000000005E40" + ) + + assert(point.coordinates == [{13.1668, 52.45695, 120}]) + assert(point.srid == 4326) + end + test "Decode EWKB to MultiLineString" do point = Geo.WKB.decode!( diff --git a/test/geo/wkt_test.exs b/test/geo/wkt_test.exs index b462af6..1102cfb 100644 --- a/test/geo/wkt_test.exs +++ b/test/geo/wkt_test.exs @@ -130,6 +130,22 @@ defmodule Geo.WKT.Test do assert(geom.srid == 4326) end + test "Encode MultiPointM to WKT" do + geom = %Geo.MultiPointM{coordinates: [{0, 0, 100}, {20, 20, 200}]} + assert(Geo.WKT.encode!(geom) == "MULTIPOINTM(0 0 100,20 20 200)") + end + + test "Decode WKT to MultiPointM" do + geom = Geo.WKT.decode!("MULTIPOINTM(0 0 100,20 20 200)") + assert(geom.coordinates == [{0, 0, 100}, {20, 20, 200}]) + end + + test "Decode EWKT to MultiPointM" do + geom = Geo.WKT.decode!("SRID=4326;MULTIPOINTM(0 0 100,20 20 200)") + assert(geom.coordinates == [{0, 0, 100}, {20, 20, 200}]) + assert(geom.srid == 4326) + end + test "Encode MultiLineString to WKT" do geom = %Geo.MultiLineString{ coordinates: [[{10, 10}, {20, 20}, {10, 40}], [{40, 40}, {30, 30}, {40, 20}, {30, 10}]]