From f7097a53c4b5b6fb0013cb254295584b0543b8f4 Mon Sep 17 00:00:00 2001 From: Amaury Trujillo <2842742+amauryt@users.noreply.github.com> Date: Fri, 24 May 2024 21:58:54 +0200 Subject: [PATCH] Add DB module --- README.md | 20 ++++++++++++++++++-- src/wkb/db.cr | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/wkb/db.cr diff --git a/README.md b/README.md index cc4d8e5..8cf1671 100644 --- a/README.md +++ b/README.md @@ -383,13 +383,16 @@ Given that the extension is optional, you could also implement your own (Geo)JSO ## Geometries on a Database -This library can be used to communicate with any system that implements WKB, including many database engines. Below is a full example of using it to receive and transmit geometry data from and to PostGIS using [crystal-pg](https://github.com/will/crystal-pg), the Postgres driver for [crystal-db](https://github.com/crystal-lang/crystal-db). +This library can be used to communicate with any system that implements WKB, including many database engines. In that case, you can either work at the bytes level doing the conversion yourself, or use converters for `WKB::Object` and all its descendants for easier reading with `DB::Serializable` from `crystal-db`. + +Below is a full example of using it to receive and transmit geometry data from and to PostGIS using [crystal-pg](https://github.com/will/crystal-pg), the Postgres driver for [crystal-db](https://github.com/crystal-lang/crystal-db). ```crystal require "db" require "pg" require "wkb" -require "wkb/geojson" # Needed for GeoJSON support +require "wkb/geojson" # For GeoJSON support +require "wkb/db" # For `DB::Serializable` support. Must be loaded after "db"! decoder = WKB::BinDecoder.new encoder = WKB::BinEncoder.new(WKB::Flavor::Ext) # or `ExtSRID` if you use SRID @@ -409,6 +412,15 @@ polygon_json = <<-JSON } JSON +class Place + include DB::Serializable + + property name : String + + @[DB::Field(converter: WKB::DB::PointConverter)] + property location : WKB::Point +end + DB.open("postgres://user:password@host:port/db_name") do |db| point_bytes = db.scalar("SELECT 'POINT(1 2 3)'::GEOMETRY;").as(Bytes) point = decoder.decode(point_bytes).as(WKB::Point) @@ -424,6 +436,10 @@ DB.open("postgres://user:password@host:port/db_name") do |db| polygon_bytes = encoder.encode(polygon) polygon_perimeter = db.scalar("SELECT ST_Perimeter($1::GEOMETRY)", polygon_bytes).as(Float64) polygon_perimeter # => 114.35571426165451 + + query_str = "SELECT 'Neverland' AS name, 'POINT(1 2)'::GEOMETRY AS location" + places = Place.from_rs(db.query(query_str)) + places.first.location.x # => 1.0 end ``` diff --git a/src/wkb/db.cr b/src/wkb/db.cr new file mode 100644 index 0000000..24e2326 --- /dev/null +++ b/src/wkb/db.cr @@ -0,0 +1,25 @@ +require "./bin_decoder" + +module WKB + module DB + {% for kind in %w[ + Object + Geometry + Point + LineString + Polygon + MultiPoint + MultiLineString + MultiPolygon + GeometryCollection + ] %} + module {{kind.id}}Converter + @@decoder = WKB::BinDecoder.new + + def self.from_rs(rs : ::DB::ResultSet) + @@decoder.decode(rs.read(Bytes)).as(WKB::{{kind.id}}) + end + end + {% end %} + end +end