From 35516c77d9755b600eff22ca3671520c94be0622 Mon Sep 17 00:00:00 2001 From: Denis Baranov Date: Thu, 9 Jun 2022 12:28:57 +0200 Subject: [PATCH 1/2] added MultiPointM --- lib/geo/json/decoder.ex | 7 +++++++ lib/geo/json/encoder.ex | 7 +++++++ lib/geo/multi_pointm.ex | 8 ++++++++ lib/geo/wkb/decoder.ex | 18 ++++++++++++++++++ lib/geo/wkb/encoder.ex | 12 ++++++++++++ lib/geo/wkt/decoder.ex | 5 +++++ lib/geo/wkt/encoder.ex | 7 +++++++ test/geo/json_test.exs | 10 ++++++++++ test/geo/wkb_test.exs | 19 +++++++++++++++++++ test/geo/wkt_test.exs | 16 ++++++++++++++++ 10 files changed, 109 insertions(+) create mode 100644 lib/geo/multi_pointm.ex diff --git a/lib/geo/json/decoder.ex b/lib/geo/json/decoder.ex index 7bb183e..16af6fb 100644 --- a/lib/geo/json/decoder.ex +++ b/lib/geo/json/decoder.ex @@ -8,6 +8,7 @@ defmodule Geo.JSON.Decoder do LineStringZ, Polygon, MultiPoint, + MultiPointM, MultiLineString, MultiPolygon, MultiPolygonZ, @@ -137,6 +138,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..a9b03b0 100644 --- a/lib/geo/json/encoder.ex +++ b/lib/geo/json/encoder.ex @@ -9,6 +9,7 @@ defmodule Geo.JSON.Encoder do Polygon, PolygonZ, MultiPoint, + MultiPointM, MultiPointZ, MultiLineString, MultiLineStringZ, @@ -166,6 +167,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/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..9f6019c 100644 --- a/lib/geo/wkb/decoder.ex +++ b/lib/geo/wkb/decoder.ex @@ -10,6 +10,7 @@ defmodule Geo.WKB.Decoder do @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 @@ -32,6 +33,7 @@ defmodule Geo.WKB.Decoder do PolygonZ, GeometryCollection, MultiPoint, + MultiPointM, MultiPointZ, MultiLineString, MultiLineStringZ, @@ -221,6 +223,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..fe85517 100644 --- a/lib/geo/wkb/encoder.ex +++ b/lib/geo/wkb/encoder.ex @@ -10,6 +10,7 @@ defmodule Geo.WKB.Encoder do @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 @@ -31,6 +32,7 @@ defmodule Geo.WKB.Encoder do Polygon, PolygonZ, MultiPoint, + MultiPointM, MultiPointZ, MultiLineString, MultiLineStringZ, @@ -141,6 +143,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..bd3e52e 100644 --- a/lib/geo/wkt/decoder.ex +++ b/lib/geo/wkt/decoder.ex @@ -11,6 +11,7 @@ defmodule Geo.WKT.Decoder do Polygon, PolygonZ, MultiPoint, + MultiPointM, MultiPointZ, MultiLineString, MultiPolygon, @@ -79,6 +80,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..8913f62 100644 --- a/lib/geo/wkt/encoder.ex +++ b/lib/geo/wkt/encoder.ex @@ -11,6 +11,7 @@ defmodule Geo.WKT.Encoder do Polygon, PolygonZ, MultiPoint, + MultiPointM, MultiPointZ, MultiLineString, MultiLineStringZ, @@ -84,6 +85,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..5a1888c 100644 --- a/test/geo/json_test.exs +++ b/test/geo/json_test.exs @@ -127,6 +127,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..d7c819c 100644 --- a/test/geo/wkb_test.exs +++ b/test/geo/wkb_test.exs @@ -389,6 +389,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}]] From ff9c39ee3ba1cb6dbc482105ff8927b9e336e5ca Mon Sep 17 00:00:00 2001 From: Denis Baranov Date: Thu, 9 Jun 2022 12:40:10 +0200 Subject: [PATCH 2/2] added LinestringM --- lib/geo/json/decoder.ex | 7 +++++++ lib/geo/json/encoder.ex | 7 +++++++ lib/geo/line_stringm.ex | 8 ++++++++ lib/geo/wkb/decoder.ex | 28 ++++++++++++++++++++++++++++ lib/geo/wkb/encoder.ex | 15 +++++++++++++++ lib/geo/wkt/decoder.ex | 5 +++++ lib/geo/wkt/encoder.ex | 7 +++++++ test/geo/json_test.exs | 10 ++++++++++ test/geo/wkb_test.exs | 27 +++++++++++++++++++++++++++ 9 files changed, 114 insertions(+) create mode 100644 lib/geo/line_stringm.ex diff --git a/lib/geo/json/decoder.ex b/lib/geo/json/decoder.ex index 16af6fb..f13b57e 100644 --- a/lib/geo/json/decoder.ex +++ b/lib/geo/json/decoder.ex @@ -5,6 +5,7 @@ defmodule Geo.JSON.Decoder do Point, PointZ, LineString, + LineStringM, LineStringZ, Polygon, MultiPoint, @@ -117,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)) diff --git a/lib/geo/json/encoder.ex b/lib/geo/json/encoder.ex index a9b03b0..33fa45a 100644 --- a/lib/geo/json/encoder.ex +++ b/lib/geo/json/encoder.ex @@ -5,6 +5,7 @@ defmodule Geo.JSON.Encoder do Point, PointZ, LineString, + LineStringM, LineStringZ, Polygon, PolygonZ, @@ -137,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)) 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/wkb/decoder.ex b/lib/geo/wkb/decoder.ex index 9f6019c..8a989ab 100644 --- a/lib/geo/wkb/decoder.ex +++ b/lib/geo/wkb/decoder.ex @@ -6,6 +6,7 @@ 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 @@ -28,6 +29,7 @@ defmodule Geo.WKB.Decoder do PointM, PointZM, LineString, + LineStringM, LineStringZ, Polygon, PolygonZ, @@ -137,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, <>, diff --git a/lib/geo/wkb/encoder.ex b/lib/geo/wkb/encoder.ex index fe85517..e597734 100644 --- a/lib/geo/wkb/encoder.ex +++ b/lib/geo/wkb/encoder.ex @@ -6,6 +6,7 @@ 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 @@ -28,6 +29,7 @@ defmodule Geo.WKB.Encoder do PointM, PointZM, LineString, + LineStringM, LineStringZ, Polygon, PolygonZ, @@ -100,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 -> diff --git a/lib/geo/wkt/decoder.ex b/lib/geo/wkt/decoder.ex index bd3e52e..21183a8 100644 --- a/lib/geo/wkt/decoder.ex +++ b/lib/geo/wkt/decoder.ex @@ -7,6 +7,7 @@ defmodule Geo.WKT.Decoder do PointM, PointZM, LineString, + LineStringM, LineStringZ, Polygon, PolygonZ, @@ -64,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 diff --git a/lib/geo/wkt/encoder.ex b/lib/geo/wkt/encoder.ex index 8913f62..88b8e8b 100644 --- a/lib/geo/wkt/encoder.ex +++ b/lib/geo/wkt/encoder.ex @@ -7,6 +7,7 @@ defmodule Geo.WKT.Encoder do PointM, PointZM, LineString, + LineStringM, LineStringZ, Polygon, PolygonZ, @@ -61,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) diff --git a/test/geo/json_test.exs b/test/geo/json_test.exs index 5a1888c..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) diff --git a/test/geo/wkb_test.exs b/test/geo/wkb_test.exs index d7c819c..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!(