Last active
December 10, 2015 09:39
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
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
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 abind_param
.