rgeo 0.1.22 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/History.rdoc +31 -0
  2. data/README.rdoc +53 -59
  3. data/Spatial_Programming_With_RGeo.rdoc +120 -67
  4. data/Version +1 -1
  5. data/ext/proj4_c_impl/extconf.rb +1 -1
  6. data/lib/rgeo.rb +80 -87
  7. data/lib/rgeo/cartesian.rb +0 -10
  8. data/lib/rgeo/coord_sys.rb +0 -7
  9. data/lib/rgeo/coord_sys/proj4.rb +2 -2
  10. data/lib/rgeo/error.rb +2 -6
  11. data/lib/rgeo/feature.rb +0 -7
  12. data/lib/rgeo/feature/curve.rb +5 -5
  13. data/lib/rgeo/feature/factory.rb +45 -5
  14. data/lib/rgeo/feature/factory_generator.rb +2 -4
  15. data/lib/rgeo/feature/geometry.rb +97 -27
  16. data/lib/rgeo/feature/geometry_collection.rb +3 -3
  17. data/lib/rgeo/feature/line_string.rb +3 -3
  18. data/lib/rgeo/feature/multi_curve.rb +2 -2
  19. data/lib/rgeo/feature/multi_surface.rb +3 -3
  20. data/lib/rgeo/feature/point.rb +6 -6
  21. data/lib/rgeo/feature/polygon.rb +4 -4
  22. data/lib/rgeo/feature/surface.rb +3 -3
  23. data/lib/rgeo/geographic.rb +0 -11
  24. data/lib/rgeo/geographic/factory.rb +5 -2
  25. data/lib/rgeo/geographic/interface.rb +1 -1
  26. data/lib/rgeo/geos.rb +0 -7
  27. data/lib/rgeo/geos/factory.rb +1 -1
  28. data/lib/rgeo/impl_helper.rb +0 -7
  29. data/lib/rgeo/wkrep.rb +0 -7
  30. data/lib/rgeo/wkrep/wkb_parser.rb +2 -2
  31. data/test/tc_oneoff.rb +0 -1
  32. metadata +7 -155
  33. data/lib/active_record/connection_adapters/mysql2spatial_adapter.rb +0 -124
  34. data/lib/active_record/connection_adapters/mysqlspatial_adapter.rb +0 -136
  35. data/lib/active_record/connection_adapters/postgis_adapter.rb +0 -426
  36. data/lib/active_record/connection_adapters/spatialite_adapter.rb +0 -488
  37. data/lib/rgeo/active_record/arel_modifications.rb +0 -78
  38. data/lib/rgeo/active_record/base_modifications.rb +0 -124
  39. data/lib/rgeo/active_record/common.rb +0 -166
  40. data/lib/rgeo/active_record/mysql_common.rb +0 -150
  41. data/lib/rgeo/all.rb +0 -49
  42. data/lib/rgeo/geo_json.rb +0 -60
  43. data/lib/rgeo/geo_json/coder.rb +0 -401
  44. data/lib/rgeo/geo_json/entities.rb +0 -285
  45. data/lib/rgeo/geo_json/interface.rb +0 -129
  46. data/lib/rgeo/shapefile.rb +0 -60
  47. data/lib/rgeo/shapefile/reader.rb +0 -898
  48. data/test/active_record/common_setup_methods.rb +0 -129
  49. data/test/active_record/readme.txt +0 -43
  50. data/test/active_record/tc_mysqlspatial.rb +0 -170
  51. data/test/active_record/tc_postgis.rb +0 -282
  52. data/test/active_record/tc_spatialite.rb +0 -198
  53. data/test/shapefile/shapelib_testcases/readme.txt +0 -11
  54. data/test/shapefile/shapelib_testcases/test.dbf +0 -0
  55. data/test/shapefile/shapelib_testcases/test.shp +0 -0
  56. data/test/shapefile/shapelib_testcases/test.shx +0 -0
  57. data/test/shapefile/shapelib_testcases/test0.shp +0 -0
  58. data/test/shapefile/shapelib_testcases/test0.shx +0 -0
  59. data/test/shapefile/shapelib_testcases/test1.shp +0 -0
  60. data/test/shapefile/shapelib_testcases/test1.shx +0 -0
  61. data/test/shapefile/shapelib_testcases/test10.shp +0 -0
  62. data/test/shapefile/shapelib_testcases/test10.shx +0 -0
  63. data/test/shapefile/shapelib_testcases/test11.shp +0 -0
  64. data/test/shapefile/shapelib_testcases/test11.shx +0 -0
  65. data/test/shapefile/shapelib_testcases/test12.shp +0 -0
  66. data/test/shapefile/shapelib_testcases/test12.shx +0 -0
  67. data/test/shapefile/shapelib_testcases/test13.shp +0 -0
  68. data/test/shapefile/shapelib_testcases/test13.shx +0 -0
  69. data/test/shapefile/shapelib_testcases/test2.shp +0 -0
  70. data/test/shapefile/shapelib_testcases/test2.shx +0 -0
  71. data/test/shapefile/shapelib_testcases/test3.shp +0 -0
  72. data/test/shapefile/shapelib_testcases/test3.shx +0 -0
  73. data/test/shapefile/shapelib_testcases/test4.shp +0 -0
  74. data/test/shapefile/shapelib_testcases/test4.shx +0 -0
  75. data/test/shapefile/shapelib_testcases/test5.shp +0 -0
  76. data/test/shapefile/shapelib_testcases/test5.shx +0 -0
  77. data/test/shapefile/shapelib_testcases/test6.shp +0 -0
  78. data/test/shapefile/shapelib_testcases/test6.shx +0 -0
  79. data/test/shapefile/shapelib_testcases/test7.shp +0 -0
  80. data/test/shapefile/shapelib_testcases/test7.shx +0 -0
  81. data/test/shapefile/shapelib_testcases/test8.shp +0 -0
  82. data/test/shapefile/shapelib_testcases/test8.shx +0 -0
  83. data/test/shapefile/shapelib_testcases/test9.shp +0 -0
  84. data/test/shapefile/shapelib_testcases/test9.shx +0 -0
  85. data/test/shapefile/tc_shapelib_tests.rb +0 -527
  86. data/test/tc_geojson.rb +0 -279
@@ -1,124 +0,0 @@
1
- # -----------------------------------------------------------------------------
2
- #
3
- # MysqlSpatial adapter for ActiveRecord
4
- #
5
- # -----------------------------------------------------------------------------
6
- # Copyright 2010 Daniel Azuma
7
- #
8
- # All rights reserved.
9
- #
10
- # Redistribution and use in source and binary forms, with or without
11
- # modification, are permitted provided that the following conditions are met:
12
- #
13
- # * Redistributions of source code must retain the above copyright notice,
14
- # this list of conditions and the following disclaimer.
15
- # * Redistributions in binary form must reproduce the above copyright notice,
16
- # this list of conditions and the following disclaimer in the documentation
17
- # and/or other materials provided with the distribution.
18
- # * Neither the name of the copyright holder, nor the names of any other
19
- # contributors to this software, may be used to endorse or promote products
20
- # derived from this software without specific prior written permission.
21
- #
22
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
- # POSSIBILITY OF SUCH DAMAGE.
33
- # -----------------------------------------------------------------------------
34
- ;
35
-
36
-
37
- require 'rgeo/active_record/mysql_common'
38
- require 'active_record/connection_adapters/mysql2_adapter'
39
-
40
-
41
- module ActiveRecord
42
-
43
- class Base
44
-
45
-
46
- # Create a mysql2spatial connection adapter
47
-
48
- def self.mysql2spatial_connection(config_)
49
- config_[:username] = 'root' if config_[:username].nil?
50
- if ::Mysql2::Client.const_defined?(:FOUND_ROWS)
51
- config_[:flags] = ::Mysql2::Client::FOUND_ROWS
52
- end
53
- client_ = ::Mysql2::Client.new(config_.symbolize_keys)
54
- options_ = [config_[:host], config_[:username], config_[:password], config_[:database], config_[:port], config_[:socket], 0]
55
- ConnectionAdapters::Mysql2SpatialAdapter.new(client_, logger, options_, config_)
56
- end
57
-
58
-
59
- end
60
-
61
-
62
- module ConnectionAdapters # :nodoc:
63
-
64
- class Mysql2SpatialAdapter < Mysql2Adapter # :nodoc:
65
-
66
-
67
- class SpatialColumn < ConnectionAdapters::Mysql2Column # :nodoc:
68
-
69
- include ::RGeo::ActiveRecord::MysqlCommon::ColumnMethods
70
-
71
- end
72
-
73
-
74
- include ::RGeo::ActiveRecord::MysqlCommon::AdapterMethods
75
-
76
-
77
- ADAPTER_NAME = 'Mysql2Spatial'.freeze
78
-
79
- NATIVE_DATABASE_TYPES = Mysql2Adapter::NATIVE_DATABASE_TYPES.merge(:geometry => {:name => "geometry"}, :point => {:name => "point"}, :line_string => {:name => "linestring"}, :polygon => {:name => "polygon"}, :geometry_collection => {:name => "geometrycollection"}, :multi_point => {:name => "multipoint"}, :multi_line_string => {:name => "multilinestring"}, :multi_polygon => {:name => "multipolygon"})
80
-
81
-
82
- def native_database_types
83
- NATIVE_DATABASE_TYPES
84
- end
85
-
86
-
87
- def adapter_name
88
- ADAPTER_NAME
89
- end
90
-
91
-
92
- def columns(table_name_, name_=nil)
93
- result_ = execute("SHOW FIELDS FROM #{quote_table_name(table_name_)}", :skip_logging)
94
- columns_ = []
95
- result_.each(:symbolize_keys => true, :as => :hash) do |field_|
96
- columns_ << SpatialColumn.new(field_[:Field], field_[:Default], field_[:Type], field_[:Null] == "YES")
97
- end
98
- columns_
99
- end
100
-
101
-
102
- def indexes(table_name_, name_=nil)
103
- indexes_ = []
104
- current_index_ = nil
105
- result_ = execute("SHOW KEYS FROM #{quote_table_name(table_name_)}", name_)
106
- result_.each(:symbolize_keys => true, :as => :hash) do |row_|
107
- if current_index_ != row_[:Key_name]
108
- next if row_[:Key_name] == 'PRIMARY' # skip the primary key
109
- current_index_ = row_[:Key_name]
110
- indexes_ << ::RGeo::ActiveRecord::Common::IndexDefinition.new(row_[:Table], row_[:Key_name], row_[:Non_unique] == 0, [], [], row_[:Index_type] == 'SPATIAL')
111
- end
112
- indexes_.last.columns << row_[:Column_name]
113
- indexes_.last.lengths << row_[:Sub_part]
114
- end
115
- indexes_
116
- end
117
-
118
-
119
- end
120
-
121
- end
122
-
123
-
124
- end
@@ -1,136 +0,0 @@
1
- # -----------------------------------------------------------------------------
2
- #
3
- # MysqlSpatial adapter for ActiveRecord
4
- #
5
- # -----------------------------------------------------------------------------
6
- # Copyright 2010 Daniel Azuma
7
- #
8
- # All rights reserved.
9
- #
10
- # Redistribution and use in source and binary forms, with or without
11
- # modification, are permitted provided that the following conditions are met:
12
- #
13
- # * Redistributions of source code must retain the above copyright notice,
14
- # this list of conditions and the following disclaimer.
15
- # * Redistributions in binary form must reproduce the above copyright notice,
16
- # this list of conditions and the following disclaimer in the documentation
17
- # and/or other materials provided with the distribution.
18
- # * Neither the name of the copyright holder, nor the names of any other
19
- # contributors to this software, may be used to endorse or promote products
20
- # derived from this software without specific prior written permission.
21
- #
22
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
- # POSSIBILITY OF SUCH DAMAGE.
33
- # -----------------------------------------------------------------------------
34
- ;
35
-
36
-
37
- require 'rgeo/active_record/mysql_common'
38
- require 'active_record/connection_adapters/mysql_adapter'
39
-
40
-
41
- module ActiveRecord
42
-
43
- class Base
44
-
45
-
46
- # Create a mysqlspatial connection adapter
47
-
48
- def self.mysqlspatial_connection(config_)
49
- unless defined?(::Mysql)
50
- begin
51
- require 'mysql'
52
- rescue ::LoadError
53
- raise "!!! Missing the mysql gem. Add it to your Gemfile: gem 'mysql'"
54
- end
55
- unless defined?(::Mysql::Result) && ::Mysql::Result.method_defined?(:each_hash)
56
- raise "!!! Outdated mysql gem. Upgrade to 2.8.1 or later. In your Gemfile: gem 'mysql', '2.8.1'. Or use gem 'mysql2'"
57
- end
58
- end
59
- config_ = config_.symbolize_keys
60
- mysql_ = ::Mysql.init
61
- mysql_.ssl_set(config_[:sslkey], config_[:sslcert], config_[:sslca], config_[:sslcapath], config_[:sslcipher]) if config_[:sslca] || config_[:sslkey]
62
- default_flags_ = ::Mysql.const_defined?(:CLIENT_MULTI_RESULTS) ? ::Mysql::CLIENT_MULTI_RESULTS : 0
63
- default_flags_ |= ::Mysql::CLIENT_FOUND_ROWS if ::Mysql.const_defined?(:CLIENT_FOUND_ROWS)
64
- options_ = [config_[:host], config_[:username] ? config_[:username].to_s : 'root', config_[:password].to_s, config_[:database], config_[:port], config_[:socket], default_flags_]
65
- ConnectionAdapters::MysqlSpatialAdapter.new(mysql_, logger, options_, config_)
66
- end
67
-
68
-
69
- end
70
-
71
-
72
- module ConnectionAdapters # :nodoc:
73
-
74
- class MysqlSpatialAdapter < MysqlAdapter # :nodoc:
75
-
76
-
77
- class SpatialColumn < ConnectionAdapters::MysqlColumn # :nodoc:
78
-
79
- include ::RGeo::ActiveRecord::MysqlCommon::ColumnMethods
80
-
81
- end
82
-
83
-
84
- include ::RGeo::ActiveRecord::MysqlCommon::AdapterMethods
85
-
86
-
87
- ADAPTER_NAME = 'MysqlSpatial'.freeze
88
-
89
- NATIVE_DATABASE_TYPES = MysqlAdapter::NATIVE_DATABASE_TYPES.merge(:geometry => {:name => "geometry"}, :point => {:name => "point"}, :line_string => {:name => "linestring"}, :polygon => {:name => "polygon"}, :geometry_collection => {:name => "geometrycollection"}, :multi_point => {:name => "multipoint"}, :multi_line_string => {:name => "multilinestring"}, :multi_polygon => {:name => "multipolygon"})
90
-
91
-
92
- def native_database_types
93
- NATIVE_DATABASE_TYPES
94
- end
95
-
96
-
97
- def adapter_name
98
- ADAPTER_NAME
99
- end
100
-
101
-
102
- def columns(table_name_, name_=nil)
103
- result_ = execute("SHOW FIELDS FROM #{quote_table_name(table_name_)}", :skip_logging)
104
- columns_ = []
105
- result_.each do |field_|
106
- columns_ << SpatialColumn.new(field_[0], field_[4], field_[1], field_[2] == "YES")
107
- end
108
- result_.free
109
- columns_
110
- end
111
-
112
-
113
- def indexes(table_name_, name_=nil)
114
- indexes_ = []
115
- current_index_ = nil
116
- result_ = execute("SHOW KEYS FROM #{quote_table_name(table_name_)}", name_)
117
- result_.each do |row_|
118
- if current_index_ != row_[2]
119
- next if row_[2] == "PRIMARY" # skip the primary key
120
- current_index_ = row_[2]
121
- indexes_ << ::RGeo::ActiveRecord::Common::IndexDefinition.new(row_[0], row_[2], row_[1] == "0", [], [], row_[10] == 'SPATIAL')
122
- end
123
- indexes_.last.columns << row_[4]
124
- indexes_.last.lengths << row_[7]
125
- end
126
- result_.free
127
- indexes_
128
- end
129
-
130
-
131
- end
132
-
133
- end
134
-
135
-
136
- end
@@ -1,426 +0,0 @@
1
- # -----------------------------------------------------------------------------
2
- #
3
- # PostGIS adapter for ActiveRecord
4
- #
5
- # -----------------------------------------------------------------------------
6
- # Copyright 2010 Daniel Azuma
7
- #
8
- # All rights reserved.
9
- #
10
- # Redistribution and use in source and binary forms, with or without
11
- # modification, are permitted provided that the following conditions are met:
12
- #
13
- # * Redistributions of source code must retain the above copyright notice,
14
- # this list of conditions and the following disclaimer.
15
- # * Redistributions in binary form must reproduce the above copyright notice,
16
- # this list of conditions and the following disclaimer in the documentation
17
- # and/or other materials provided with the distribution.
18
- # * Neither the name of the copyright holder, nor the names of any other
19
- # contributors to this software, may be used to endorse or promote products
20
- # derived from this software without specific prior written permission.
21
- #
22
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
- # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
- # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
- # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
- # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
- # POSSIBILITY OF SUCH DAMAGE.
33
- # -----------------------------------------------------------------------------
34
- ;
35
-
36
-
37
- require 'rgeo/active_record/common'
38
- require 'active_record/connection_adapters/postgresql_adapter'
39
-
40
-
41
- module ActiveRecord
42
-
43
- class Base
44
-
45
-
46
- # Create a postgis connection adapter.
47
-
48
-
49
- def self.postgis_connection(config_)
50
- require 'pg'
51
-
52
- config_ = config_.symbolize_keys
53
- host_ = config_[:host]
54
- port_ = config_[:port] || 5432
55
- username_ = config_[:username].to_s if config_[:username]
56
- password_ = config_[:password].to_s if config_[:password]
57
- if config_.has_key?(:database)
58
- database_ = config_[:database]
59
- else
60
- raise ::ArgumentError, "No database specified. Missing argument: database."
61
- end
62
-
63
- # The postgres drivers don't allow the creation of an unconnected PGconn object,
64
- # so just pass a nil connection object for the time being.
65
- ConnectionAdapters::PostGisAdapter.new(nil, logger, [host_, port_, nil, nil, database_, username_, password_], config_)
66
- end
67
-
68
-
69
- end
70
-
71
-
72
- module ConnectionAdapters # :nodoc:
73
-
74
-
75
- class PostGisAdapter < PostgreSQLAdapter # :nodoc:
76
-
77
-
78
- ADAPTER_NAME = 'PostGIS'.freeze
79
-
80
- @@native_database_types = nil
81
-
82
-
83
- def native_database_types
84
- @@native_database_types ||= super.merge(:geometry => {:name => "geometry"}, :point => {:name => "point"}, :line_string => {:name => "linestring"}, :polygon => {:name => "polygon"}, :geometry_collection => {:name => "geometrycollection"}, :multi_point => {:name => "multipoint"}, :multi_line_string => {:name => "multilinestring"}, :multi_polygon => {:name => "multipolygon"})
85
- end
86
-
87
-
88
- def adapter_name
89
- ADAPTER_NAME
90
- end
91
-
92
-
93
- def postgis_lib_version
94
- unless defined?(@postgis_lib_version)
95
- @postgis_lib_version = select_value("SELECT PostGIS_Lib_Version()") rescue nil
96
- end
97
- @postgis_lib_version
98
- end
99
-
100
-
101
- def quote(value_, column_=nil)
102
- if ::RGeo::Feature::Geometry.check_type(value_)
103
- "'#{::RGeo::WKRep::WKBGenerator.new(:hex_format => true, :type_format => :ewkb, :emit_ewkb_srid => true).generate(value_)}'"
104
- else
105
- super
106
- end
107
- end
108
-
109
-
110
- def columns(table_name_, name_=nil) #:nodoc:
111
- table_name_ = table_name_.to_s
112
- spatial_info_ = spatial_column_info(table_name_)
113
- column_definitions(table_name_).collect do |name_, type_, default_, notnull_|
114
- SpatialColumn.new(name_, default_, type_, notnull_ == 'f', type_ =~ /geometry/i ? spatial_info_[name_] : nil)
115
- end
116
- end
117
-
118
-
119
- def create_table(table_name_, options_={})
120
- table_name_ = table_name_.to_s
121
- table_definition_ = SpatialTableDefinition.new(self)
122
- table_definition_.primary_key(options_[:primary_key] || ::ActiveRecord::Base.get_primary_key(table_name_.singularize)) unless options_[:id] == false
123
- yield table_definition_ if block_given?
124
- if options_[:force] && table_exists?(table_name_)
125
- drop_table(table_name_, options_)
126
- end
127
-
128
- create_sql_ = "CREATE#{' TEMPORARY' if options_[:temporary]} TABLE "
129
- create_sql_ << "#{quote_table_name(table_name_)} ("
130
- create_sql_ << table_definition_.to_sql
131
- create_sql_ << ") #{options_[:options]}"
132
- execute create_sql_
133
-
134
- table_definition_.non_geographic_spatial_columns.each do |col_|
135
- type_ = col_.type.to_s.gsub('_', '').upcase
136
- has_z_ = col_.has_z?
137
- has_m_ = col_.has_m?
138
- type_ = "#{type_}M" if has_m_ && !has_z_
139
- dimensions_ = 2
140
- dimensions_ += 1 if has_z_
141
- dimensions_ += 1 if has_m_
142
- execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(col_.name)}', #{col_.srid}, '#{quote_string(type_)}', #{dimensions_})")
143
- end
144
- end
145
-
146
-
147
- def drop_table(table_name_, options_={})
148
- execute("DELETE from geometry_columns where f_table_name='#{quote_string(table_name_.to_s)}'")
149
- super
150
- end
151
-
152
-
153
- def add_column(table_name_, column_name_, type_, options_={})
154
- table_name_ = table_name_.to_s
155
- if ::RGeo::ActiveRecord::GEOMETRY_TYPES.include?(type_.to_sym)
156
- type_ = type_.to_s.gsub('_', '').upcase
157
- has_z_ = options_[:has_z]
158
- has_m_ = options_[:has_m]
159
- srid_ = (options_[:srid] || 4326).to_i
160
- if options_[:geographic]
161
- type_ << 'Z' if has_z_
162
- type_ << 'M' if has_m_
163
- execute("ALTER TABLE #{quote_table_name(table_name_)} ADD COLUMN #{quote_column_name(column_name_)} GEOGRAPHY(#{type_},#{srid_})")
164
- change_column_default(table_name_, column_name_, options_[:default]) if options_include_default?(options_)
165
- change_column_null(table_name_, column_name_, false, options_[:default]) if options_[:null] == false
166
- else
167
- type_ = "#{type_}M" if has_m_ && !has_z_
168
- dimensions_ = 2
169
- dimensions_ += 1 if has_z_
170
- dimensions_ += 1 if has_m_
171
- execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(column_name_.to_s)}', #{srid_}, '#{quote_string(type_)}', #{dimensions_})")
172
- end
173
- else
174
- super
175
- end
176
- end
177
-
178
-
179
- def remove_column(table_name_, *column_names_)
180
- column_names_ = column_names_.flatten.map{ |n_| n_.to_s }
181
- spatial_info_ = spatial_column_info(table_name_)
182
- remaining_column_names_ = []
183
- column_names_.each do |name_|
184
- if spatial_info_.include?(name_)
185
- execute("SELECT DropGeometryColumn('#{quote_string(table_name_.to_s)}','#{quote_string(name_)}')")
186
- else
187
- remaining_column_names_ << name_.to_sym
188
- end
189
- end
190
- if remaining_column_names_.size > 0
191
- super(table_name_, *remaining_column_names_)
192
- end
193
- end
194
-
195
-
196
- def add_index(table_name_, column_name_, options_={})
197
- table_name_ = table_name_.to_s
198
- column_names_ = ::Array.wrap(column_name_)
199
- index_name_ = index_name(table_name_, :column => column_names_)
200
- gist_clause_ = ''
201
- index_type_ = ''
202
- if ::Hash === options_ # legacy support, since this param was a string
203
- index_type_ = 'UNIQUE' if options_[:unique]
204
- index_name_ = options_[:name].to_s if options_.key?(:name)
205
- gist_clause_ = 'USING GIST' if options_[:spatial]
206
- else
207
- index_type_ = options_
208
- end
209
- if index_name_.length > index_name_length
210
- raise ::ArgumentError, "Index name '#{index_name_}' on table '#{table_name_}' is too long; the limit is #{index_name_length} characters"
211
- end
212
- if index_name_exists?(table_name_, index_name_, false)
213
- raise ::ArgumentError, "Index name '#{index_name_}' on table '#{table_name_}' already exists"
214
- end
215
- quoted_column_names_ = quoted_columns_for_index(column_names_, options_).join(", ")
216
- execute "CREATE #{index_type_} INDEX #{quote_column_name(index_name_)} ON #{quote_table_name(table_name_)} #{gist_clause_} (#{quoted_column_names_})"
217
- end
218
-
219
-
220
- def spatial_column_info(table_name_)
221
- info_ = query("SELECT * FROM geometry_columns WHERE f_table_name='#{quote_string(table_name_.to_s)}'")
222
- result_ = {}
223
- info_.each do |row_|
224
- name_ = row_[3]
225
- type_ = row_[6]
226
- dimension_ = row_[4].to_i
227
- has_m_ = type_ =~ /m$/i ? true : false
228
- type_.sub!(/m$/, '')
229
- has_z_ = dimension_ > 3 || dimension_ == 3 && !has_m_
230
- result_[name_] = {
231
- :name => name_,
232
- :type => type_,
233
- :dimension => dimension_,
234
- :srid => row_[5].to_i,
235
- :has_z => has_z_,
236
- :has_m => has_m_,
237
- }
238
- end
239
- result_
240
- end
241
-
242
-
243
- class SpatialTableDefinition < ConnectionAdapters::TableDefinition # :nodoc:
244
-
245
- attr_reader :spatial_columns
246
-
247
- def initialize(base_)
248
- super
249
- end
250
-
251
- def column(name_, type_, options_={})
252
- super
253
- col_ = self[name_]
254
- if ::RGeo::ActiveRecord::GEOMETRY_TYPES.include?(col_.type.to_sym)
255
- col_.extend(GeometricColumnDefinitionMethods) unless col_.respond_to?(:geographic?)
256
- col_.set_geographic(options_[:geographic])
257
- col_.set_srid((options_[:srid] || 4326).to_i)
258
- col_.set_has_z(options_[:has_z])
259
- col_.set_has_m(options_[:has_m])
260
- end
261
- self
262
- end
263
-
264
- def to_sql
265
- @columns.find_all{ |c_| !c_.respond_to?(:geographic?) || c_.geographic? }.map{ |c_| c_.to_sql } * ', '
266
- end
267
-
268
- def non_geographic_spatial_columns
269
- @columns.find_all{ |c_| c_.respond_to?(:geographic?) && !c_.geographic? }
270
- end
271
-
272
- end
273
-
274
-
275
- module GeometricColumnDefinitionMethods # :nodoc:
276
-
277
- def geographic?
278
- defined?(@geographic) && @geographic
279
- end
280
-
281
- def srid
282
- defined?(@srid) ? @srid : 4326
283
- end
284
-
285
- def has_z?
286
- defined?(@has_z) && @has_z
287
- end
288
-
289
- def has_m?
290
- defined?(@has_m) && @has_m
291
- end
292
-
293
- def set_geographic(value_)
294
- @geographic = value_ ? true : false
295
- end
296
-
297
- def set_srid(value_)
298
- @srid = value_
299
- end
300
-
301
- def set_has_z(value_)
302
- @has_z = value_ ? true : false
303
- end
304
-
305
- def set_has_m(value_)
306
- @has_m = value_ ? true : false
307
- end
308
-
309
- def sql_type
310
- type_ = type.to_s.upcase.gsub('_', '')
311
- type_ << 'Z' if has_z?
312
- type_ << 'M' if has_m?
313
- "GEOGRAPHY(#{type_},#{srid})"
314
- end
315
-
316
- end
317
-
318
-
319
- class SpatialColumn < ConnectionAdapters::PostgreSQLColumn # :nodoc:
320
-
321
-
322
- def initialize(name_, default_, sql_type_=nil, null_=true, opts_=nil)
323
- super(name_, default_, sql_type_, null_)
324
- @geographic = sql_type_ =~ /^geography/ ? true : false
325
- if opts_
326
- @geometric_type = ::RGeo::ActiveRecord::Common.geometric_type_from_name(opts_[:type])
327
- @srid = opts_[:srid].to_i
328
- @has_z = opts_[:has_z]
329
- @has_m = opts_[:has_m]
330
- elsif @geographic
331
- if sql_type_ =~ /geography\((\w+[^,zm])(z?)(m?),(\d+)\)/i
332
- @has_z = $2.length > 0
333
- @has_m = $3.length > 0
334
- @srid = $4.to_i
335
- @geometric_type = ::RGeo::ActiveRecord::Common.geometric_type_from_name($1)
336
- else
337
- @geometric_type = ::RGeo::Feature::Geometry
338
- @srid = 4326
339
- @has_z = @has_m = false
340
- end
341
- else
342
- @geometric_type = @has_z = @has_m = nil
343
- @srid = 0
344
- end
345
- @ar_class = ::ActiveRecord::Base
346
- end
347
-
348
-
349
- def set_ar_class(val_)
350
- @ar_class = val_
351
- end
352
-
353
-
354
- attr_reader :srid
355
- attr_reader :geometric_type
356
- attr_reader :has_z
357
- attr_reader :has_m
358
-
359
-
360
- def spatial?
361
- type == :geometry
362
- end
363
-
364
-
365
- def geographic?
366
- @geographic
367
- end
368
-
369
-
370
- def klass
371
- type == :geometry ? ::RGeo::Feature::Geometry : super
372
- end
373
-
374
-
375
- def type_cast(value_)
376
- type == :geometry ? SpatialColumn.string_to_geometry(value_, @ar_class, @geographic, @srid, @has_z, @has_m) : super
377
- end
378
-
379
-
380
- def type_cast_code(var_name_)
381
- type == :geometry ? "::ActiveRecord::ConnectionAdapters::PostGisAdapter::SpatialColumn.string_to_geometry(#{var_name_}, self.class, #{@geographic ? 'true' : 'false'}, #{@srid.inspect}, #{@has_z ? 'true' : 'false'}, #{@has_m ? 'true' : 'false'})" : super
382
- end
383
-
384
-
385
- private
386
-
387
-
388
- def simplified_type(sql_type_)
389
- sql_type_ =~ /geography|geometry|point|linestring|polygon/i ? :geometry : super
390
- end
391
-
392
-
393
- def self.string_to_geometry(str_, ar_class_, geographic_, srid_, has_z_, has_m_)
394
- case str_
395
- when ::RGeo::Feature::Geometry
396
- str_
397
- when ::String
398
- if str_.length == 0
399
- nil
400
- else
401
- factory_ = ar_class_.rgeo_factory_generator.call(:srid => srid_, :has_z_coordinate => has_z_, :has_m_coordinate => has_m_, :geographic => geographic_)
402
- marker_ = str_[0,1]
403
- if marker_ == "\x00" || marker_ == "\x01"
404
- ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true).parse(str_) rescue nil
405
- elsif str_[0,4] =~ /[0-9a-fA-F]{4}/
406
- ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true).parse_hex(str_) rescue nil
407
- else
408
- ::RGeo::WKRep::WKTParser.new(factory_, :support_ewkt => true).parse(str_) rescue nil
409
- end
410
- end
411
- else
412
- nil
413
- end
414
- end
415
-
416
-
417
- end
418
-
419
-
420
- end
421
-
422
-
423
- end
424
-
425
-
426
- end