Skip to content

Instantly share code, notes, and snippets.

@kubo
Last active December 10, 2015 09:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kubo/4416118 to your computer and use it in GitHub Desktop.
Save kubo/4416118 to your computer and use it in GitHub Desktop.
This is sample code to bind GeoRuby objects as SDO_GEOMETRY datatype. See: https://github.com/rsim/oracle-enhanced/pull/268
require 'oci8'
require 'geo_ruby'
module OCI8::Object::Mdsys
class SdoGeometry < OCI8::Object::Base
set_typename('MDSYS.SDO_GEOMETRY')
end
end
class OCI8::BindType::GeoRuby < OCI8::BindType::Object
def self.create(con, val, param, max_array_size)
self.new(con, val, con.get_tdo_by_class(OCI8::Object::Mdsys::SdoGeometry), max_array_size)
end
def set(val)
super(val && georuby_to_sdo_geometry(val))
end
def get()
val = super()
return nil if val.nil?
elem_info = val.sdo_elem_info
ordinates = val.sdo_ordinates
sdo_geometry_to_georuby(val.sdo_gtype, val.sdo_srid, val.sdo_point, elem_info && elem_info.to_ary, ordinates && ordinates.to_ary)
end
private
def georuby_to_sdo_geometry(val)
case val
when GeoRuby::SimpleFeatures::Point
# 2D or 3D Point-Only Geometry
# http://docs.oracle.com/cd/E11882_01/appdev.112/e11830/sdo_objrelschema.htm#BGHIFACF
# http://georuby.rubyforge.org/georuby-doc/classes/GeoRuby/SimpleFeatures/Point.html
# I don't know how to handle the M coordinate.
{
:sdo_gtype => val.z ? 3001 : 2001,
:sdo_srid => val.srid,
:sdo_point => {
:x => val.x,
:y => val.y,
:z => val.z
},
:sdo_elem_info => nil,
:sdo_ordinates => nil
}
when GeoRuby::SimpleFeatures::LineString
# as https://github.com/daqo/oracle-enhanced/blob/61385aafba140e4b72a0d8eeac1f58e9561a0cb3/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb#L759 does.
{
:sdo_gtype => 2002,
:sdo_srid => val.srid,
:sdo_point => nil,
:sdo_elem_info => [1, 2, 1],
:sdo_ordinates => val.points.inject([]) do |ary, point|
case point
when GeoRuby::SimpleFeatures::Point
ary << point.x
ary << point.y
when Array
ary << point[0]
ary << point[1]
else
raise "Unsupported type #{point.class}"
end
ary
end
}
when GeoRuby::SimpleFeatures::Polygon
# as https://github.com/daqo/oracle-enhanced/blob/61385aafba140e4b72a0d8eeac1f58e9561a0cb3/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb#L782 does.
{
:sdo_gtype => 2003,
:sdo_srid => val.srid,
:sdo_point => nil,
:sdo_elem_info => [1, 3, 1],
:sdo_ordinates => val.rings.inject([]) do |ary, ring|
ring.points.inject(ary) do |ary, point|
case point
when GeoRuby::SimpleFeatures::Point
ary << point.x
ary << point.y
when Array
ary << point[0]
ary << point[1]
else
raise "Unsupported type #{point.class}"
end
ary
end
ary
end
}
else
raise "unsupported type #{val.class}"
end
end
def sdo_geometry_to_georuby(gtype, srid, point, elem_info, ordinates)
case gtype
when 2001
return GeoRuby::SimpleFeatures::Point.from_x_y(point.x, point.y, val.sdo_srid) if point
when 3001
return GeoRuby::SimpleFeatures::Point.from_x_y_z(point.x, point.y, point.z, val.sdo_srid) if point
when 2002
coordinates = []
while not ordinates.empty?
coordinates << [ordinates.shift, ordinates.shift]
end
return GeoRuby::SimpleFeatures::LineString.from_coordinates(coordinates, val.sdo_srid)
end
raise "Could not convert {:gtype => #{gtype}, :point => #{point}, :elem_info => #{elem_info}, :ordinates => #{ordinates}}, to GeoRuby"
end
end
# for OCI8::Cursor#define
OCI8::BindType::Mapping[:geo_ruby] = OCI8::BindType::GeoRuby
# for OCI8::Cursor#bind_param
OCI8::BindType::Mapping[GeoRuby::SimpleFeatures::Point] = OCI8::BindType::GeoRuby
OCI8::BindType::Mapping[GeoRuby::SimpleFeatures::LineString] = OCI8::BindType::GeoRuby
OCI8::BindType::Mapping[GeoRuby::SimpleFeatures::Polygon] = OCI8::BindType::GeoRuby
if defined? ActiveRecord::ConnectionAdapters::SpatialOracleColumn
OCI8::BindType::Mapping[ActiveRecord::ConnectionAdapters::SpatialOracleColumn] = OCI8::BindType::GeoRuby
end
if $0 == __FILE__
# -- You need to create test_sdo_geometry in advance.
# CREATE TABLE test_sdo_geometry (id varchar2(20), geo sdo_geometry)
conn = OCI8.new('ruby/oci8@//localhost/XE')
cursor = conn.parse('insert into test_sdo_geometry values(:1, :2)')
point = GeoRuby::SimpleFeatures::Point.from_x_y(10,20,123)
line = GeoRuby::SimpleFeatures::LineString.from_coordinates([[1.5,45.2],[-54.12312,-0.012]],256)
cursor.bind_param(1, 'point')
cursor.bind_param(2, point)
cursor.exec
cursor.bind_param(1, 'line')
cursor.bind_param(2, line)
cursor.exec
cursor = conn.parse("select * from test_sdo_geometry where id in ('point', 'line')")
# OCI8::Cursor#define(pos, :geo_ruby) is required to fetch a SDO_GEOMETRY column as a GeoRuby object for now.
# Ruby-oci8 may be changed to work fine without this.
cursor.define(2, :geo_ruby)
cursor.exec do |row|
case row[0]
when 'point'
raise "#{point} != #{row[1]}" if point != row[1]
when 'line'
raise "#{line} != #{row[1]}" if line != row[1]
end
end
end
@nathanvda
Copy link

I created an issue rsim/oracle-enhanced#310 on oracle-enhanced repo.

I am trying to hook this into my code, I need primarily to be able to set a MDSYS.SDO_GEOMETRY to NULL without errors. I have the impression it is not connected correctly. Where is the ActiveRecord::ConnectionAdapters::SpatialOracleColumn defined? It is imho not defined in oracle-enhanced.

This code is now in a initializer, but a MDSYS.SDO_GEOMETRY column is still not recognised when doing a bind_param.

@kubo
Copy link
Author

kubo commented Jun 25, 2013

ActiveRecord::ConnectionAdapters::SpatialOracleColumn is in https://github.com/ps2/spatial_adapter/blob/master/lib/spatial_adapter/oracle_enhanced.rb.

I had tested this code without rails. I posted a pull request to fix one issue with rails. But one issue remains. See rsim/oracle-enhanced#311.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment