Skip to content

Make geo optional #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,13 @@ point, polygon, ... %Geo.Point{coordinates: {0.0, 1.0}}, ... *****
\*\*\*\* MySQL added a native JSON type in version 5.7.8, if you're using earlier versions,
remember to use TEXT column for your JSON field.

\*\*\*\*\* Encoding/decoding between `Geo.*` structs and the OpenGIS WKB binary format is
done using the [Geo](https://github.com/bryanjos/geo) package. If you're using MyXQL geometry
types with Ecto and need to for example accept a WKT format as user input, consider implementing an
[custom Ecto type](https://hexdocs.pm/ecto/Ecto.Type.html). Note, some structs like `%Geo.PointZ{}`
does not have equivalent on the MySQL server side and thus shouldn't be used.
\*\*\*\*\* See "Geometry support" section below

## JSON support

MyXQL comes with JSON support out of the box via the [Jason](https://github.com/michalmuskala/jason) library. To use it, add `:jason` to your dependencies:
MyXQL comes with JSON support via the [Jason](https://github.com/michalmuskala/jason) library.

To use it, add `:jason` to your dependencies:

```elixir
{:jason, "~> 1.0"}
Expand All @@ -133,6 +131,22 @@ You can customize it to use another library via the `:json_library` configuratio
config :myxql, :json_library, SomeJSONModule
```

## Geometry support

MyXQL comes with Geometry types support via the [Geo](https://github.com/bryanjos/geo) package.

To use it, add `:geo` to your dependencies:

```elixir
{:geo, "~> 3.3"}
```

Note, some structs like `%Geo.PointZ{}` does not have equivalent on the MySQL server side and thus
shouldn't be used.

If you're using MyXQL geometry types with Ecto and need to for example accept a WKT format as user
input, consider implementing an [custom Ecto type](https://hexdocs.pm/ecto/Ecto.Type.html).

## Contributing

Run tests:
Expand Down
46 changes: 31 additions & 15 deletions lib/myxql/protocol/values.ex
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,15 @@ defmodule MyXQL.Protocol.Values do
{:mysql_type_tiny, <<0>>}
end

def encode_binary_value(%Geo.Point{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.MultiPoint{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.LineString{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.MultiLineString{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.Polygon{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.MultiPolygon{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.GeometryCollection{} = geo), do: encode_geometry(geo)
if Code.ensure_loaded?(Geo) do
def encode_binary_value(%Geo.Point{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.MultiPoint{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.LineString{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.MultiLineString{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.Polygon{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.MultiPolygon{} = geo), do: encode_geometry(geo)
def encode_binary_value(%Geo.GeometryCollection{} = geo), do: encode_geometry(geo)
end

def encode_binary_value(term) when is_list(term) or is_map(term) do
string = json_library().encode!(term)
Expand All @@ -240,10 +242,12 @@ defmodule MyXQL.Protocol.Values do
raise ArgumentError, "query has invalid parameter #{inspect(other)}"
end

defp encode_geometry(geo) do
srid = geo.srid || 0
binary = %{geo | srid: nil} |> Geo.WKB.encode!(:ndr) |> Base.decode16!()
{:mysql_type_geometry, encode_string_lenenc(<<srid::uint4, binary::binary>>)}
if Code.ensure_loaded?(Geo) do
defp encode_geometry(geo) do
srid = geo.srid || 0
binary = %{geo | srid: nil} |> Geo.WKB.encode!(:ndr) |> Base.decode16!()
{:mysql_type_geometry, encode_string_lenenc(<<srid::uint4, binary::binary>>)}
end
end

## Time/DateTime
Expand Down Expand Up @@ -389,10 +393,22 @@ defmodule MyXQL.Protocol.Values do
Enum.reverse(acc)
end

# https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html#gis-internal-format
defp decode_geometry(<<srid::uint4, r::bits>>) do
srid = if srid == 0, do: nil, else: srid
r |> Base.encode16() |> Geo.WKB.decode!() |> Map.put(:srid, srid)
if Code.ensure_loaded?(Geo) do
# https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html#gis-internal-format
defp decode_geometry(<<srid::uint4, r::bits>>) do
srid = if srid == 0, do: nil, else: srid
r |> Base.encode16() |> Geo.WKB.decode!() |> Map.put(:srid, srid)
end
else
defp decode_geometry(_) do
raise """
encoding/decoding geometry types requires :geo package, add:

{:geo, "~> 3.3"}

to your mix.exs and run `mix deps.compile --force myxql`.
"""
end
end

defp decode_int1(<<v::int1, r::bits>>, null_bitmap, t, acc),
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ defmodule MyXQL.MixProject do
{:db_connection, "~> 2.0", db_connection_opts()},
{:decimal, "~> 1.6"},
{:jason, "~> 1.0", optional: true},
{:geo, "~> 3.3"},
{:geo, "~> 3.3", optional: true},
{:binpp, ">= 0.0.0", only: [:dev, :test]},
{:dialyxir, "~> 1.0-rc", only: :dev, runtime: false},
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false},
Expand Down