felt / geo Goto Github PK
View Code? Open in Web Editor NEWA collection of GIS functions for Elixir
License: MIT License
A collection of GIS functions for Elixir
License: MIT License
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?
Test written. Just implement
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.
Document the public API.
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
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.
I am trying to print an object inside a view template with:
<%= IO.inspect Geo.JSON.encode(@policies) %>
but I am getting the error:
no function clause matching in Geo.JSON.do_encode/1
What is going on here? Thanks.
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
Tests already written. Just have to implement
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
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
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.
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
https://github.com/bryanjos/geo/blob/master/test/geo/ecto_test.exs#L164
lat should come before long in the geo struct
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()
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
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.
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 oid
17031(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.
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?
Test written. Just implement
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 :)
To allow searching distance in meters
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?
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
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.
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?
Pretty much the reason I wrote this library. Created an encoder, decoder, and formatter for use with Postgrex in the Geo.Postgrex module. It works as I initially tested them in a fork of Postgrex, but I haven't wrote tests for them in this project yet.
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 :)
Might as well get the latest version of poison?
No worries if you know of reasons not to do this...
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!
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)
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
WKT
Test written. Just implement
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....).
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);")
I don't like having postgrex as a dependency and I'm wondering if I should make that part a separate library.
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
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?
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!
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
?
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
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!
I'll be happy to submit a PR in the near future ๐
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])
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.
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)
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?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.