Giter Club home page Giter Club logo

geo's Introduction

Geo

Build Status Module Version Hex Docs Total Download License Last Updated

A collection of GIS functions. Handles conversions to and from well-known text (WKT), well-known binary (WKB), and GeoJSON for the following geometries:

  • Point
  • PointZ
  • PointM
  • PointZM
  • LineString
  • LineStringZ
  • LineStringZM
  • Polygon
  • PolygonZ
  • MultiPoint
  • MultiPointZ
  • MultiLineString
  • MultiLineStringZ
  • MultiPolygon
  • MultiPolygonZ
  • GeometryCollection

Note: If you are looking for the Postgrex PostGIS extension, check out geo_postgis.

Note: If you are looking to do geospatial calculations in memory with Geo's structs, check out topo.

defp deps do
  [
    {:geo, "~> 3.6"}
  ]
end

Examples

Encode and decode WKT and EWKT:

iex(1)> {:ok, point} = Geo.WKT.decode("POINT(30 -90)")
%Geo.Point{ coordinates: {30, -90}, srid: nil}

iex(2)> Geo.WKT.encode!(point)
"POINT(30 -90)"

iex(3)> point = Geo.WKT.decode!("SRID=4326;POINT(30 -90)")
%Geo.Point{coordinates: {30, -90}, srid: 4326}

Encode and decode WKB and EWKB:

iex(1)> {:ok, point} = Geo.WKB.decode("0101000000000000000000F03F000000000000F03F")
%Geo.Point{ coordinates: {1.0, 1.0}, srid: nil }

iex(2)> Geo.WKB.encode!(point)
"00000000013FF00000000000003FF0000000000000"

iex(3)> point = Geo.WKB.decode!("0101000020E61000009EFB613A637B4240CF2C0950D3735EC0")
%Geo.Point{ coordinates: {36.9639657, -121.8097725}, srid: 4326 }

iex(4)> Geo.WKB.encode!(point)
"0020000001000010E640427B633A61FB9EC05E73D350092CCF"

Encode and decode GeoJSON:

Geo only encodes and decodes maps shaped as GeoJSON. JSON encoding and decoding must be done before and after.

# Examples using Jason as the JSON parser

iex(1)> Geo.JSON.encode(point)
{:ok, %{ "type" => "Point", "coordinates" => [100.0, 0.0] }}

iex(2)> point = Jason.decode!("{\"type\": \"Point\", \"coordinates\": [100.0, 0.0] }") |> Geo.JSON.decode
%Geo.Point{ coordinates: {100.0, 0.0}, srid: nil }

iex(3)> Geo.JSON.encode!(point) |> Jason.encode!
"{\"coordinates\":[100.0,0.0],\"type\":\"Point\"}"

Copyright and License

Copyright (c) 2014 Bryan Joseph

Released under the MIT License, which can be found in the repository in LICENSE.

geo's People

Contributors

aseigo avatar axelson avatar babzich avatar bcksl avatar bolek avatar bryanjos avatar caspg avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar ilyashuma avatar jdenen avatar joekain avatar joseluistorres avatar josevalim avatar kabie avatar kenichi avatar kianmeng avatar milafrerichs avatar murb avatar nhunzaker avatar philipgiuliani avatar pragtob avatar s3cur3 avatar sespindola avatar swr avatar tcitworld avatar wojtekmach avatar yordis avatar zmoshansky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

geo's Issues

Postgrex.Error: ERROR (undefined_object): type "geometry" does not exist

Hi there, I'm new to Elixir world :)

I'm trying to play with Phoenix framework, Ecto and Postgres. I needed geolocation and I've found this package. I've added dependencies to mix.exs:

  defp deps do
    [{:phoenix, "~> 0.13"},
     {:phoenix_ecto, "~> 0.4"},
     {:geo, "~> 0.12.0"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_html, "~> 1.0"},
     {:phoenix_live_reload, "~> 0.4", only: :dev},
     {:cowboy, "~> 1.0"}]
  end

configured database:

config :baho_api, BahoApi.Repo,
  database: "baho_api_dev",
  adapter: Ecto.Adapters.Postgres,
  extensions: [{Geo.PostGIS, library: Geo}],
  username: "USER",
  password: "SECRET",
  size: 10 # The amount of database connections in the pool

then I've created the migration migration:

  def change do
    create table(:mytable) do
      add :place, :geometry
      timestamps
    end
  end

but when I run mix ecto.migrate I get this error:

** (Postgrex.Error) ERROR (undefined_object): type "geometry" does not exist
    (ecto) lib/ecto/adapters/sql/worker.ex:26: Ecto.Adapters.SQL.Worker.query!/4
    (ecto) lib/ecto/adapters/sql.ex:187: Ecto.Adapters.SQL.use_worker/3
    (ecto) lib/ecto/adapters/postgres.ex:59: Ecto.Adapters.Postgres.execute_ddl/3
    (stdlib) timer.erl:194: :timer.tc/3
    (ecto) lib/ecto/migration/runner.ex:22: Ecto.Migration.Runner.run/6
    (ecto) lib/ecto/migrator.ex:113: Ecto.Migrator.attempt/6
    (ecto) lib/ecto/migrator.ex:63: anonymous fn/4 in Ecto.Migrator.do_up/4
    (ecto) lib/ecto/adapters/sql.ex:442: Ecto.Adapters.SQL.transaction/3

I'm almost sure I'm missing something right away, can someone help me with this problem?

thanks in advance :)

Missing LICENSE

Could you add a LICENSE file to this repo?

I noticed some of your other code is MIT Licensed, is there any reason to believe this code would be any different?

geo does not compile

I am working on OS X Yosemite, Version 10.10.4. Elixir version 1.0.5 I am trying to compile geo and it hangs.

mix.exs is as follows

defmodule Geo1.Mixfile do
use Mix.Project

def project do
[app: :geo1,
version: "0.0.1",
elixir: "~> 1.0",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps]
end

def application do
[applications: [:logger]]
end

defp deps do
[{:geo, "~> 0.15.0"}]
end
end

When i compile, it just hangs after lib/geo.ex. Am i missing anything?

$ mix compile
==> geo
Compiled lib/geo/multi_point.ex
Compiled lib/geo/point.ex
Compiled lib/geo/line_string.ex
Compiled lib/geo/geometry_collection.ex
Compiled lib/geo/multi_line_string.ex
Compiled lib/geo/multi_polygon.ex
Compiled lib/geo/wkb/writer.ex
Compiled lib/geo/wkb/reader.ex
Compiled lib/geo/polygon.ex
Compiled lib/geo/utils.ex
Compiled lib/geo/geo_json.ex
Compiled lib/geo/wkb.ex
Compiled lib/geo/wkt.ex
Compiled lib/geo/postgis.ex
Compiled lib/geo.ex

Improve WKB and WKT encoder/decoder

We should try to follow the convention of the JSON parser where we have the ! functions that raise an error and the regular one that give a tuple back

WKB

  • Add encode!
  • Add encode
  • Add decode
  • Add decode!

WKT

  • Add encode!
  • Add encode
  • Add decode
  • Add decode!

`srid` in GeoJSON output

Hi! This isn't so much an issue, but rather, a question:

I see that GeoJSON output can contain an srid attribute:

%Geo.Point{ coordinates: {30, -90}, srid: nil}

This attribute isn't part of the official GeoJSON spec. How did you come to the decision to add it? I'm just asking because I'm working on a very similar library for Python. I didn't initially support SRIDs/EWKB/EWKT, but I've built a draft of the feature. In a discussion of the feature, one of the GeoJSON creators suggested the I put the srid right at the root of the GeoJSON object:

{"type": "Point", "coordinates": [30, 90], "srid": "4326"}

My alternative suggestion was to place it in a metadata object. Like so:

{"type": "Point", "coordinates": [30, 90], "metadata": {"srid": "4326"}}

My thought was that this would be more extensible. What are your thoughts? I'm curious about your thought process on your design.

Great library, by the way. The code looks really simple and elegant.

Postgrex expected a binary, got %Geo.Point

This query is working:

INSERT INTO "bookmarks" ("address","bookmark_type","description","is_active","is_delete","latlng","tag","uid","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) RETURNING "id" ["adi", "private", "testing", true, false, %Geo.Point{coordinates: {26.7885576, 75.8270401}, srid: 4326}, "test1 test2 test3", "2ffce5f2e79a4f8c917fc58591260cdb80011f6d", {{2017, 6, 10}, {17, 30, 46, 127816}}, {{2017, 6, 10}, {17, 30, 46, 134318}}]

while this one returns error:

SELECT b0."id", b0."uid", b0."tag", b0."description", b0."address", b0."bookmark_type", b0."is_active", b0."is_delete", b0."latlng", b0."inserted_at", b0."updated_at" FROM "bookmarks" AS b0 WHERE (b0."bookmark_type" = 'public') AND (ST_Within(b0."latlng",ST_Buffer($1, $2, 8))) AND ((b0."is_active" = TRUE) AND (b0."is_delete" != TRUE)) ORDER BY b0."updated_at" DESC LIMIT $3 OFFSET $4 [%Geo.Point{coordinates: {26.7885576, 75.8270401}, srid: 4326}, 1.0, 10, 0]

So, I'm totally confused. It seems to me that potgrex in both cases should either work or not work - because either cast or not cast to binary.

May be I'm mising something in the configuration, or you can navigate me where should I dig deeper to see where is the problem?

Does not correctly decode WKB

Here is an example WKB (this came from a MySQL Geometry type column)

"000000000101000000000000000000F03F000000000000F03F"

This online tool correctly decodes it as a point. But Geo throws an error:

Geo.WKB.decode("000000000101000000000000000000F03F000000000000F03F")
** (ArgumentError) argument error
     (stdlib) :binary.part("00F03F", 0, 8)
        (geo) lib/geo/wkb/reader.ex:16: Geo.WKB.Reader.read/2
        (geo) lib/geo/wkb.ex:202: Geo.WKB.decode/2
     (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6

Add support for Geometry Collections

Implement as a list of geometries. For the example of GeoJson,

 { "type": "GeometryCollection",
    "geometries": [
      { "type": "Point",
        "coordinates": [100.0, 0.0]
        },
      { "type": "LineString",
        "coordinates": [ [101.0, 0.0], [102.0, 1.0] ]
        }
    ]
  }

Would become

[
Geo.Geometry[type: :point, coordinates: [100.0, 0.0], srid: nil],
Geo.Geometry[type: :line_string, coordinates: [ [101.0, 0.0], [102.0, 1.0] ], srid: nil]
]

Same for decoding of WKT and WKB

No extension found

Hi.

Tried to play around with geographic location using your library. In development mode everything is ok, but once release was build in prod mode using exrm I started receiving following error:
Request: POST /api/reviews ** (exit) an exception was raised: ** (ArgumentError) no extension found for oid17031(postgrex) lib/postgrex/types.ex:298: Postgrex.Types.fetch!/2 (postgrex) lib/postgrex/types.ex:215: Postgrex.Types.encoder/2 (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2 (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2 (postgrex) lib/postgrex/query.ex:72: DBConnection.Query.Postgrex.Query.encoders/2 (postgrex) lib/postgrex/query.ex:40: DBConnection.Query.Postgrex.Query.describe/2 (db_connection) lib/db_connection.ex:884: DBConnection.describe_execute/5 (db_connection) lib/db_connection.ex:1009: DBConnection.run_begin/3

I tried different variations - but still can not find out what could be the root cause of this issue.
Regarding setup - I followed guide in ReadMe - so I have a :geography column in database, postgis enabled and added extensions in config files for Ecto.

Confused about how to use st_within

Hi, I have a User model that has a field :loc, :geometry and I have the earthdistance, cube and postgis extensions working on my PG database. I'm confused on how to actually use the st_distance.

I want to be able to return all users within X miles(or km) of a particular user, given that they have inputted their address and I converted them to lat/long via Google's geocoder library.

I tried to run the "example_query" in iex as a test and I got an error saying from/2 was not a function:

' from location in Location, limit: 5, select: st_distance(location.geom, ^geom) '

I then used the exact examples that are in the README, i.e., but I still wasn't able to do a distance query

Embedding encoded GeoJSON in another JSON object

I am forming a response as such:

  %{
      id: park.id,
      name: park.name,
      geojson: Geo.JSON.encode(park.geom)
    }

The resulting response has the GeoJSON object wrapped in double quotes, which causes all the double quotes within the object to be escaped, since the whole thing is being re-encoded by Poison on the way out. I did a quick test and added an additional function to geo_json.ex:

def shallow_encode(geom) do
    do_encode(geom)
    |> add_crs(geom.srid)
end

which solved the problem.

Instead of the hack, what would be the recommended way to do this?

Allow properties to be added to Geo types

Is there a PostGIS (or other) limitation that would prevent us from adding properties to the Geo structs?

For example it would be very nice to be able to do the following:

%Geo.Point{coordinates: {1, 1}, properties: %{time: "12:01"}}
|> Geo.JSON.encode()

Support/Add Docs about Postgis GIS Objects in Migrations

First, thank you very much for this library!

AFAIK, Geo doesn't migrations with advanced types like geometry(Point,4326) etc. If that's correct, some documentation on how to use/drawing attention to the abilities of Postgis GIS Objects with Geo would be very helpful (for newcomers like myself....).

Postgis Migrations Example

To add a basic geometry type in a migration:

  def change do
    create table(:address) do
       ....
       add :geom, :geometry
    end

Or, if you would like to use more advanced types see Postgis GIS Objects. These types are useful for enforcing constraints on {Lng,Lat} (order matters), or ensuring that a particular projection/representation is used, etc.

  def change do
    create table(:address) do
       ....
    end
    execute("SELECT AddGeometryColumn ('address','lng_lat_point',4326,'POINT',2);")

Unable to add support for ST_ClosestPoint

Hi @bryanjos, thanks for the awesome library. I am trying to add a macro to lib/geo/postgis.ex to support ST_ClosestPoint

defmacro st_closestpoint(geometryA, geometryB) do
      quote do: fragment("ST_ClosestPoint(?,?)", unquote(geometryA), unquote(geometryB))
end

I receive the following error while trying to use it with my queries

iex > alias StoreRoom.{Repo, BusStop, Route}
iex > import Ecto.Query
iex > import Geo.PostGIS
iex > home = %Geo.Point{coordinates: {****, ****}, srid: 4326}
iex > query = from r in Route, order_by: st_distance(r.line, ^home), distinct: r.code, limit: 5, select: r
iex > (from r in subquery(query), select: %{pickup: st_closestpoint(r.line, ^home)}) |> Repo.all()
** (Postgrex.Error) ERROR 42883 (undefined_function): function st_closestpoint(geography, unknown) does not exist
[debug] QUERY ERROR db=1.9ms
SELECT ST_ClosestPoint(s0."line",$1) FROM (SELECT DISTINCT ON (r0."code") r0."id" AS "id", r0."org" AS "org", r0."code" AS "code", r0."name" AS "name", r0."line" AS "line", r0."inserted_at" AS "inserted_at", r0."updated_at" AS "updated_at" FROM "routes" AS r0 ORDER BY r0."code", ST_Distance(r0."line",$2) LIMIT 5) AS s0 [%Geo.Point{coordinates: {78.3762, 17.4474}, srid: 4326}, %Geo.Point{coordinates: {78.3762, 17.4474}, srid: 4326}]
    (ecto) lib/ecto/adapters/sql.ex:436: Ecto.Adapters.SQL.execute_and_cache/7
    (ecto) lib/ecto/repo/queryable.ex:130: Ecto.Repo.Queryable.execute/5
    (ecto) lib/ecto/repo/queryable.ex:35: Ecto.Repo.Queryable.all/4

I am fairly new to elixir and postgis. Forgive me if this is a trivial mistake. Any guidance would of great help

Set Operations on Geometries

I've been thinking about this one for a while. Adding set operations such as contains, intersects, etc. Some of the math can get complicated.

Elixir 1.3 Warnings

Hey!

Just upgraded to Elixir 1.3, and there's a fair number of warnings, largely about imperative if cases. Happy to look into doing a PR, just thought I'd note it down here first.

Thanks!

  • Ben

Geo.*.cast functions crash instead of returning :error

Example:

Geo.Point.cast(%{"type" => "Pint", "coordinates" => [0, 1]})

crashes with

** (FunctionClauseError) no function clause matching in Geo.JSON.do_decode/3

    The following arguments were given to Geo.JSON.do_decode/3:

        # 1
        "Pint"

        # 2
        [0, 1]

        # 3
        nil

    Attempted function clauses (showing 6 out of 6):

        defp do_decode("Point", [x, y], crs)
        defp do_decode("LineString", coordinates, crs)
        defp do_decode("Polygon", coordinates, crs)
        defp do_decode("MultiPoint", coordinates, crs)
        defp do_decode("MultiLineString", coordinates, crs)
        defp do_decode("MultiPolygon", coordinates, crs)

    lib/geo/geo_json.ex:65: Geo.JSON.do_decode/3
    lib/geo/point.ex:24: Geo.Point.cast/1

Instead, they should just return :error.

Also I noticed that it will even decode JSON first, however it still uses the decode function with a bang instead of catching the error. I know this is a bit more to code, but goes a long way because one can then return meaningful errors in APIs when the user supplies invalid JSON or GeoJSON.

Problem with encoding/decoding GeometryCollection

iex(1)> geom = %Geo.GeometryCollection{geometries: [%Geo.Point{ coordinates: {54.1745659, 15.5398456}, srid: 4326 }], srid: 4326 }

%Geo.GeometryCollection{geometries: [%Geo.Point{coordinates: {54.1745659,
    15.5398456}, srid: 4326}], srid: 4326}

iex(2)> Geo.WKB.encode(geom)
"0020000007000010E6000000010000000001404B16582CE7BF97402F1466A479C76C"

iex(3)> geodata = Geo.WKB.decode("0020000007000010E6000000010000000001404B16582CE7BF97402F1466A479C76C")

** (Protocol.UndefinedError) protocol Enumerable not implemented for %Geo.Point{coordinates: {54.1745659, 15.5398456}, srid: nil}
    (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
    (elixir) lib/enum.ex:116: Enumerable.reduce/3
    (elixir) lib/enum.ex:1627: Enum.reduce/3
    (elixir) lib/enum.ex:1188: Enum.map/2
       (geo) lib/geo/wkb.ex:171: Geo.WKB.decode/2

I got "srid: nill", what i'm doing wrong when encoding GeometryCollection and why Geo can encode but can not decode?

Using geometry types with sqlite_ecto2

I'm currently using this library together with a PostgreSQL database (using PostGIS). There I have a schema with a field using Geo.Point.

defmodule City do
  use Ecto.Schema
  schema "locality" do
    field(:name, :string)
    field(:population, :integer)
    field(:coordinate, Geo.Point)
  end
end

This works perfectly.

Now I need to provide support for a sqlite database. When I only change the adapter I get the following issue (I stored the coordinate as GeoJSON and also tried WKB):

** (ArgumentError) cannot load `"{\"type\":\"Point\",\"coordinates\":[13.41053,52.52437]}"` as type Geo.Point for field `coordinate` in schema City
    (ecto) lib/ecto/schema.ex:1533: Ecto.Schema.load!/5
    (ecto) lib/ecto/schema.ex:1474: Ecto.Schema.safe_load_zip/4
    (ecto) lib/ecto/schema.ex:1475: Ecto.Schema.safe_load_zip/4
    (ecto) lib/ecto/schema.ex:1460: Ecto.Schema.__safe_load__/6
    (ecto) lib/ecto/repo/queryable.ex:307: Ecto.Repo.Queryable.process_source/6
    (ecto) lib/ecto/repo/queryable.ex:178: Ecto.Repo.Queryable.preprocess/5
    (elixir) lib/enum.ex:1294: Enum."-map/2-lists^map/1-0-"/2
    (sqlite_ecto2) lib/sqlite_db_connection/query.ex:43: DBConnection.Query.Sqlite.DbConnection.Query.decode/3

I played around a bit and could solve the issue by adding the following function head to Geo.Point:

    if Code.ensure_loaded?(Poison) do
      def load(point) when is_binary(point), do: { :ok, Poison.decode!(point) |> Geo.JSON.decode }
    end

If this is the right direction and a PR is welcome I would be happy to do the changes.

Add the ability to cast values

I'd like to be able to do:

pa = %Geo.Point{coordinates: {40.7127, 74.0059}, srid: 4326}
pb = %Geo.Point{coordinates: {41.8369, 87.6847}, srid: 4326}

query = st_distance({pa, :geography}, {pb, :geography})
# Generates ST_Distance($1::geography, $2::geography).........

I'm probably going to have some free time next week to start working on it

Cannot JSON encode a point

I am trying to return a model that has a Geo.Point attribute, and am getting the following Poison error:

Poison.EncodeError at PUT /api/driver/update
unable to encode value: {37.7758055, -59.5867633}

What is the proper way to JSON encode a Geo.Point?

Trying to run in dev- error: type geometry can not be handled

Trying to run this package in dev, but I'm not sure how to get it configured properly:

- created migration

  def up do
    execute "CREATE EXTENSION IF NOT EXISTS postgis"
  end

  def down do
    execute "DROP EXTENSION IF EXISTS postgis"
  end

- created point on new table column

      add :state, :string
      add :postal, :string
      add :geom, :geometry
      add :radius, :integer

- had to update poison for phoenix
{:poison, "~> 3.0", override: true},

- created postgres_types.ex file

Postgrex.Types.define(MyApp.PostgresTypes,
                      [Geo.PostGIS.Extension] ++ Ecto.Adapters.Postgres.extensions(),
                      json: Poison)

- test.ex added types
types: LocalistApi.PostgresTypes

- get error ->
type geometry can not be handled by the configured extensions

- if I add types: LocalistApi.PostgresTypes to my dev.exs file i get this error
function MyApp.PostgresTypes.encode_params/2 is undefined (module MyApp.PostgresTypes is not available)

can you please help me understand what I am missing/doing incorrectly? Thanks!

(ArgumentError) no extension found for oid '8661175'

I recently started working on a Phoenix app that shares a Postgres DB with an existing Rails app. The Rails app has a table that I am trying to also access in the Phoenix app called drivers, which has a geo_location column that is a PostGIS point.

Rails schema.rb:

create_table "drivers", force: true do |t|
  ...
  t.spatial  "geo_location", limit: {:srid=>4326, :type=>"point", :geographic=>true}
  ...
end

The Rails app has been working fine with this in development, the geo_location correctly gets stored and the PostGIS extension is definitely enabled on the db.

When I try to connect to the same table via my Phoenix app, I get (ArgumentError) no extension found for oid '8661175'

mix.exs

  ...
  defp deps do
    [{:phoenix, "~> 0.14"},
     {:phoenix_ecto, "~> 0.5"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_html, "~> 1.1"},
     {:phoenix_live_reload, "~> 0.4", only: :dev},
     {:comeonin, "~> 1.0"},
     {:geo, "~> 0.13.0"},
     {:cowboy, "~> 1.0"}]
  end
  ...

config/dev.exs

config :my_app, MyApp.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "postgres",
  database: "my_db",
  extensions: [{Geo.PostGIS.Extension, library: Geo}],
  size: 10 # The amount of database connections in the pool

web/models/driver.ex

defmodule MyApp.Driver do
  use MyApp.Web, :model

  schema "drivers" do
    field :geo_location, Geo.Point

    field :created_at, Ecto.DateTime
    field :updated_at, Ecto.DateTime
  end
end

My attempt to connect via iex:

iex(4)> MyApp.Repo.all(MyApp.Driver)
** (ArgumentError) no extension found for oid `8661175`
    (postgrex) lib/postgrex/types.ex:282: Postgrex.Types.fetch!/2
    (postgrex) lib/postgrex/types.ex:215: Postgrex.Types.format/2
      (elixir) lib/enum.ex:977: anonymous fn/3 in Enum.map/2
      (elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3
      (elixir) lib/enum.ex:977: Enum.map/2
    (postgrex) lib/postgrex/protocol.ex:124: Postgrex.Protocol.message/3
    (postgrex) lib/postgrex/connection.ex:408: Postgrex.Connection.new_data/2
    (postgrex) lib/postgrex/connection.ex:309: Postgrex.Connection.handle_info/2

Any guidance is greatly appreciated

How to build custom type over Geo.Point?

Is this the correct way of building a custom type with underlying %Geo.Point{}?

defmodule Coordinates do
  @behaviour Ecto.Type

  defstruct [:lat, :lng]

  def type, do: :geography

  def load(%Geo.Point{coordinates: {lat, lng}}),
    do: {:ok, %__MODULE__{lat: lat, lng: lng}}
  def load(_),
    do: :error

  def dump(%__MODULE__{lat: lat, lng: lng}),
    do: {:ok, %Geo.Point{coordinates: {lat, lng}}}
  def dump(_),
    do: :error

  defmacrop are_coordinates(lat, lng) do
    quote do
      trunc(unquote(lat)) in -90..90 and trunc(unquote(lng)) in -180..180
    end
  end

  def cast(%{lat: lat, lng: lng}) when are_coordinates(lat, lng),
    do: {:ok, %__MODULE__{lat: lat, lng: lng}}
  def cast(%{"lat" => lat, "lng" => lng}) when are_coordinates(lat, lng),
    do: {:ok, %__MODULE__{lat: lat, lng: lng}}
  def cast(_),
    do: :error
end

and then using it as:

defmodule Location do
  schema "locations" do
    field :address,     :string
    field :postal_code, :string
    field :city,        :string
    field :country,     :string
    field :coordinates, Coordinates
  end
end

Test specific configuration not documented?

Hi @bryanjos , thanks for the awesome work on this library! It's been a smooth process using it so far, though admittedly I haven't done much yet.

I was trying to add acceptance tests to my prototype, snapshot in this branch: https://github.com/MasbiaSoupKitchenNetwork/open_pantry/tree/erroring_gis_specs

and though the geometry type is working fine in the app when running normally, it errors in the tests saying:

15:33:56.754 [error] Postgrex.Protocol (#PID<0.577.0>) disconnected: ** (RuntimeError) type `geometry` can not be handled by the configured extensions
15:33:56.811 [error] #PID<0.716.0> running OpenPantry.Endpoint terminated
Server: localhost:4001 (http)
Request: GET /en/food_selections
** (exit) an exception was raised:
    ** (RuntimeError) type `geometry` can not be handled by the configured extensions
        (ecto) lib/ecto/adapters/postgres/connection.ex:86: Ecto.Adapters.Postgres.Connection.prepare_execute/5
        (ecto) lib/ecto/adapters/sql.ex:243: Ecto.Adapters.SQL.sql_call/6
        (ecto) lib/ecto/adapters/sql.ex:431: Ecto.Adapters.SQL.execute_and_cache/7
        (ecto) lib/ecto/repo/queryable.ex:130: Ecto.Repo.Queryable.execute/5
        (ecto) lib/ecto/repo/queryable.ex:35: Ecto.Repo.Queryable.all/4
        (elixir) lib/enum.ex:1229: Enum."-map/2-lists^map/1-0-"/2
        (open_pantry) web/controllers/food_selection_controller.ex:8: OpenPantry.FoodSelectionController.index/2
        (open_pantry) web/controllers/food_selection_controller.ex:1: OpenPantry.FoodSelectionController.action/2
        (open_pantry) web/controllers/food_selection_controller.ex:1: OpenPantry.FoodSelectionController.phoenix_controller_pipeline/2
        (open_pantry) lib/open_pantry/endpoint.ex:1: OpenPantry.Endpoint.instrument/4
        (open_pantry) lib/phoenix/router.ex:261: OpenPantry.Router.dispatch/2
        (open_pantry) web/router.ex:1: OpenPantry.Router.do_call/2
        (open_pantry) lib/open_pantry/endpoint.ex:1: OpenPantry.Endpoint.phoenix_pipeline/1
        (open_pantry) lib/open_pantry/endpoint.ex:1: OpenPantry.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4

I thought maybe my Postgrex.Types definition was not being loaded somehow, so tried adding that to test_helper, but it warns that's being redefined and wasn't helping, and I poked around but couldn't think of what else could be the issue. For now I may just comment out that type from the schema as it's not really being actively used/relied upon for current funtionality, and is mostly a future-support feature/fun opportunity to play with PostGIS and your Geo library, but if anyone has any guidance on what issue might be, it would be appreciated!

Module Ecto.Adapters.Postgres is not available

I was using 1.2.1 with elixir 1.2 and postgresx 0.12.
Now I am trying to update to 1.3.1 elixir 1.4 and postgresx 0.13

When I am configuring the types I am getting Module Ecto.Adapters.Postgres is not available before I was using the extension option, any clues?

Postgrex.Types.define(Stef.PostgresTypes,
              [Geo.PostGIS.Extension] ++ Ecto.Adapters.Postgres.extensions(),
              json: Poison)

Implement Poison.Decoder

Many times, it is a lot easier to return the record fetched from db in a controller like this:

def show(conn, params)
  address = Repo.get!(Address, params["id"])
  json conn, address
end

However, the address has a Geo.Point field, and poison does not know how to decode it. It would be great if an implementation for Poison.Decoder could be added.

(ArgumentError) no extension found for oid `31600`

Hey! I think, this issue should be placed here.

I'm trying to store geometry object to database. Unfortunately, I meet this kind of error.

** (ArgumentError) no extension found for oid `38120`: %Postgrex.TypeInfo{array_elem: 0, base_type: 0, comp_elems: [], input: "geometry_in", oid: 38120, output: "geometry_out", receive: "geometry_recv", send: "geometry_send", type: "geometry"}
     stacktrace:
       (postgrex) lib/postgrex/types.ex:272: Postgrex.Types.fetch!/2
       (postgrex) lib/postgrex/types.ex:191: Postgrex.Types.encoder/2
       (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2
       (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2
       (postgrex) lib/postgrex/query.ex:101: DBConnection.Query.Postgrex.Query.encoders/2
       (postgrex) lib/postgrex/query.ex:48: DBConnection.Query.Postgrex.Query.describe/2
       (db_connection) lib/db_connection.ex:785: DBConnection.describe_execute/4
       (db_connection) lib/db_connection.ex:882: DBConnection.run_begin/3
       (db_connection) lib/db_connection.ex:830: DBConnection.run_meter/3
       (db_connection) lib/db_connection.ex:449: DBConnection.prepare_execute/4
       (postgrex) lib/postgrex.ex:117: Postgrex.query/4
       (ecto) lib/ecto/adapters/postgres/connection.ex:31: Ecto.Adapters.Postgres.Connection.query/4
       (stdlib) timer.erl:197: :timer.tc/3
       (ecto) lib/ecto/adapters/sql.ex:262: Ecto.Adapters.SQL.query/7
       (ecto) lib/ecto/pool.ex:149: Ecto.Pool.run/4
       (ecto) lib/ecto/adapters/sql.ex:247: Ecto.Adapters.SQL.query/6
       (ecto) lib/ecto/adapters/sql.ex:225: Ecto.Adapters.SQL.query/5
       (ecto) lib/ecto/adapters/sql.ex:487: Ecto.Adapters.SQL.model/6
       (ecto) lib/ecto/repo/schema.ex:297: Ecto.Repo.Schema.apply/5
       (ecto) lib/ecto/repo/schema.ex:81: anonymous fn/11 in Ecto.Repo.Schema.do_insert/4

I have a geo 1.0.4. What's going wrong?

Separate Postgrex extension into it's own library

It is beneficial to some to have the WKB, WKT, and GeoJSON parts of Geo without the PostGIS parts. This is a proposal to make the PostGIS parts into it's own library. This would include the Postgrex extension along with the PostGIS macros. This would be a breaking change so Geo would be bumped to 2.0.0

Move Ecto specific code to geo_ecto

Many of the modules depends of Ecto just for the types,

from the Core perspective, Geo doesn't need to implement something related to Ecto. Most of the Geo packages could become leaner so it will become more easier to maintain the core of the package.

Also,

Moving this to independent package is welcoming to more advance code for Ecto related stuff, for example.

https://github.com/bryanjos/geo/blob/ac5990e5c09f56528ca47d0deae96bb9d5d69a8e/lib/geo/point.ex#L26

This is assuming that everyone will be using GeoJSON but right now I am interested in use the other packages formatting. So we would need to configure which formatter I will be using on my platform (maybe we will end up doing Geo.Ecto.GeoJSON.Point or Geo.Ecto.WKT.Point because we do not want to configure globally the formatter being used)

Investigate using more than one geometry type for a column

This is an issue to check if it is possible to insert more than one type (point, line, etc) into a column when using geo with an Ecto schema. If it is currently possible, document how to. If it is not currently possible, come up with a plan to make it so.

decoding MultiPolygon

Hi there, i am having errors decoding MultiPolygon, either with ecto

iex(11)> Transfere.Repo.all(query)                                                                                                                              [debug] SELECT l0."id", l0."freguesia", l0."municipio", l0."distrito", l0."geom" FROM "locations" AS l0 LIMIT 5 (1.1ms)
** (ArgumentError) cannot load `"0106000020E6100000010000000103000000010000000F00000091A1EF7505D521C0F4AD6182E481424072B3CE92FED421C01D483CDAE281424085184FAEF7D421C0CB159111E1814240E1EBD7FBF8D421C0D421F7C8DF814240AD111315FFD421C0FE1F21C0DE81424082A0669908D521C050071118DE814240813C5E700FD521C0954EEF97DE814240DC889FA815D521C0B3382182E08142400148A81817D521C0E620D22BE2814240F1E95BDE19D521C08BD53852E3814240F81699E217D521C05B35D7DCE4814240B287C8D715D521C0336338FEE481424085882FB90FD521C0FEF65484E5814240A53E1E460AD521C09A0EA286E581424091A1EF7505D521C0F4AD6182E4814240"` as type Geo.MultiPolygon
             lib/ecto/type.ex:315: Ecto.Type.load!/2
             lib/ecto/schema.ex:553: anonymous fn/3 in Ecto.Schema.do_load/4
    (elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3
             lib/ecto/schema.ex:551: Ecto.Schema.do_load/4
             lib/ecto/schema.ex:545: Ecto.Schema.__load__/4
             lib/ecto/adapters/sql.ex:403: anonymous fn/3 in Ecto.Adapters.SQL.process_row/2
    (elixir) lib/enum.ex:1036: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
             lib/ecto/adapters/sql.ex:396: Ecto.Adapters.SQL.process_row/2

and with Geo.WKB.decode


iex(11)> Geo.WKB.decode("0106000020E6100000010000000103000000010000000F00000091A1EF7505D521C0F4AD6182E481424072B3CE92FED421C01D483CDAE281424085184FAEF7D421C0CB159111E1814240E1EBD7FBF8D421C0D421F7C8DF814240AD111315FFD421C0FE1F21C0DE81424082A0669908D521C050071118DE814240813C5E700FD521C0954EEF97DE814240DC889FA815D521C0B3382182E08142400148A81817D521C0E620D22BE2814240F1E95BDE19D521C08BD53852E3814240F81699E217D521C05B35D7DCE4814240B287C8D715D521C0336338FEE481424085882FB90FD521C0FEF65484E5814240A53E1E460AD521C09A0EA286E581424091A1EF7505D521C0F4AD6182E4814240")
** (Protocol.UndefinedError) protocol Enumerable not implemented for %Geo.Polygon{coordinates: [[{-8.91605728674111, 37.014786050505705}, {-8.916004741413165, 37.01473548835222}, {-8.915952155261275, 37.014681049196575}, {-8.915962095363229, 37.014641876859656}, {-8.916008623674168, 37.01461030595236}, {-8.916081231858929, 37.01459027129624}, {-8.91613341474863, 37.01460551438246}, {-8.91618086764759, 37.0146639501776}, {-8.916191835920474, 37.01471469650441}, {-8.91621298667903, 37.01474979186158}, {-8.916197854221068, 37.01479683407043}, {-8.916182273129241, 37.01480081322952}, {-8.916135584881212, 37.01481680058167}, {-8.916094008628827, 37.01481707489911}, {-8.91605728674111, 37.014786050505705}]], srid: nil}
    (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
    (elixir) lib/enum.ex:112: Enumerable.reduce/3
    (elixir) lib/enum.ex:981: Enum.map/2
             lib/geo/wkb.ex:239: Geo.WKB.decode_coordinates/2
             lib/geo/wkb.ex:171: Geo.WKB.decode/2

thanks for the lib :)

Not working with ecto 2.1

I have defined a file in my phoenix project in lib/postgres_types.ex

Postgrex.Types.define(Publit.PostgresTypes,
                      [Geo.PostGIS.Extension] ++ Ecto.Adapters.Postgres.extensions(),
                      [json: Poison])

and also set the config test.exs

...
config :publit, Publit.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "postgres",
  database: "publit_test",
  hostname: "localhost",
  pool: Ecto.Adapters.SQL.Sandbox,
  types: Publit.PostgresTypes

and generates this error
Compiling 1 file (.ex)

== Compilation error on file lib/postgres_types.ex ==
** (UndefinedFunctionError) function Geo.PostGIS.Extension.init/1 is undefined or private. Did you mean one of:

  * init/2

Geo.PostGIS.Extension.init([])
lib/postgrex/type_module.ex:704: Postgrex.TypeModule.configure/1
(elixir) lib/enum.ex:1184: Enum."-map/2-lists^map/1-0-"/2
lib/postgrex/type_module.ex:11: Postgrex.TypeModule.define/3
(elixir) lib/kernel/parallel_compiler.ex:117: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1

If I remove the extension (Geo.PostGIS.Extension) from lib/postgres_types.ex it compiles

Postgrex.Types.define(Publit.PostgresTypes,
                      Ecto.Adapters.Postgres.extensions(),
                      [json: Poison])

Postgrex expected %Postgrex.Point{}, got %Geo.Point

I'm a bit new to Phoenix so bear with me. At the moment I'm just trying to manually enter some numbers into latitude and longitude form fields and have it add the location to the database.

I'm getting this error:

Postgrex expected %Postgrex.Point{}, got %Geo.Point{coordinates: {4343, 2342342}, srid: 4326}. Please make sure the value you are passing matches the definition in your table or in your query or convert the value accordingly.

I've got a simple table with one field

defmodule Test.Repo.Migrations.CreateLocations do
  use Ecto.Migration

  def change do
    create table(:locations) do
      add :location, :point

      timestamps()
    end

  end
end

and the schema is as follows

  schema "locations" do
    field :location, Geo.Point
    field :lat, :float, virtual: true
    field :long, :float, virtual: true

    timestamps()
  end

And my 'create' method in the controller is:

def create(conn, %{"location" => location_params}) do
    IO.inspect location_params
    lat_string = Map.get(location_params, "lat")
    long_string = Map.get(location_params, "long")
    point = "SRID=4326;POINT(#{lat_string} #{long_string})"
    geopoint = Geo.WKT.decode(point)
    IO.inspect geopoint

    case MapView.create_location(%{"location" => geopoint}) do
      {:ok, location} ->
        conn
        |> put_flash(:info, "Location created successfully.")
        |> redirect(to: location_path(conn, :show, location))
      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end

It's obviously quite naive but I'm still not really sure the best way to add a point to the database -- the first step is this I suppose. Is there something obviously wrong here?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.