active_record_mysql_spatial 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c1fb8466cfa0fa405fd35472dfa05122712db0dc27b7a3daad65d6c0329736ab
4
- data.tar.gz: c58c26a2abff58ae9d9c32e710b7898db5648a5980db891deb71911b82336140
3
+ metadata.gz: b423da177a4f62465caf3c5a9c8cf08c017500e152d78b5dbf9daeb6b1f196f1
4
+ data.tar.gz: ebdd1045ed4b4f49fe3c82f803ba7009af50c0d419be7d77cc94786282095acf
5
5
  SHA512:
6
- metadata.gz: ded6ba2548871b914758b5306c8b0b6b68f3922e059c3895e2e938e7a7fe6cb781f42e084d46b9871ba71e7a429097f2b9ed52d66b5e415f1bd2132b9d17b7c1
7
- data.tar.gz: 162ab35dd4901102eb470bf71c7086ea1febd6de8d24695415a4be983df2b51cf1bb0399b0dac7e9fa1b0aca32e7cbea945fd588ffbcfd14e2f1de27b14876e0
6
+ metadata.gz: 3e55747a645a35d839a30c79a2c81b0d749ce9d13a83a767c0e3fbb6a29640a74e701e1acd9416b72dee20592fc36d2737f2091875b5d52eaa155fee2cd5b707
7
+ data.tar.gz: af598824bcf3cf9ab56189525ee153ba7fa398a022ba5afcbe875b38d9e4bf96be5257472c8bbf9925462cbe63a91c2a1a677139464974683d340ada34919ad3
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper --format documentation
data/README.md CHANGED
@@ -1,35 +1,102 @@
1
- # ActiveRecordMysqlSpatial
1
+ ActiveRecord extension for MySQL Spatial Data.
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
3
+ # Installation
4
4
 
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/active_record_mysql_spatial`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+ ```sh
6
+ gem 'active_record_mysql_spatial'
7
+ ```
6
8
 
7
- ## Installation
9
+ # Migration
8
10
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
11
+ This extension provides some data type methods for ActiveRecord migration and also register those types to the schema.
10
12
 
11
- Install the gem and add to the application's Gemfile by executing:
13
+ ```rb
14
+ # frozen_string_literal: true
12
15
 
13
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
16
+ class CreatePositions < ActiveRecord::Migration[7.2]
17
+ def change
18
+ create_table :positions do |t|
19
+ t.linestring :ls
20
+ t.multilinestring :mls
21
+ t.point :pt
14
22
 
15
- If bundler is not being used to manage dependencies, install the gem by executing:
23
+ t.column :tls, :linestring
24
+ t.column :tmls, :multilinestring
25
+ t.column :tpt, :point
16
26
 
17
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
27
+ t.timestamps
28
+ end
29
+ end
30
+ end
31
+ ```
18
32
 
19
- ## Usage
33
+ Without this extension, even you use `t.column` to define the column for the table, you will receive the error message in the `schema.rb`
20
34
 
21
- TODO: Write usage instructions here
35
+ ```rb
36
+ # Could not dump table "positions" because of following StandardError
37
+ # Unknown type 'linestring' for column 'tls'
38
+ ```
22
39
 
23
- ## Development
40
+ # Usage
24
41
 
25
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
42
+ ## Point
26
43
 
27
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
44
+ ```rb
45
+ position = Position.create!(pt: { x: 1, y: 2 })
28
46
 
29
- ## Contributing
47
+ position = Position.create!(pt: [1, 2])
30
48
 
31
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/active_record_mysql_spatial.
49
+ p position.pt.x # puts x
50
+ p position.pt.y # puts y
51
+ ```
32
52
 
33
- ## License
53
+ ## Linestring
34
54
 
35
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
55
+ ```rb
56
+ position = Position.create!(ls: [[1, 2], [2, 3]])
57
+
58
+ p position.ls.coordinates # puts all points
59
+ p position.ls.coordinates.first.x # puts x of first point
60
+ p position.ls.coordinates.last.y # puts y of last point
61
+ ```
62
+
63
+ ## Multilinestring
64
+
65
+ ```rb
66
+ position = Position.create!(mls: [[[1, 2], [2, 3]]])
67
+
68
+ p position.mls.items # puts all linestrings
69
+ p position.mls.items.first.coordinates.first.x # puts x of first point of first linestring
70
+ p position.mls.items.last.coordinates.last.y # puts y of last point of last linestring
71
+ ```
72
+
73
+ # Custom result class
74
+
75
+ In your business, you may need to use spatial data for a purpose. To load data and map to the semantic data for your business, create a class and override the method `cast_value`.
76
+
77
+ ```rb
78
+ class YourClass < ActiveRecordMysqlSpatial::ActiveRecord::MySQL::Linestring
79
+ attr_reader :sum_x, :sum_y
80
+
81
+ private
82
+
83
+ def cast_value(value)
84
+ super
85
+
86
+ @sum_x, @sum_y = @coordinates.reduce([0, 0]) do |sum, point|
87
+ sum[0] += point.x.to_i
88
+ sum[1] += point.y.to_i
89
+ sum
90
+ end
91
+
92
+ self
93
+ end
94
+ end
95
+
96
+ # models/position.rb
97
+ class Position < ApplicationRecord
98
+ include ActiveRecordMysqlSpatial::ActsAsSpatial
99
+
100
+ acts_as_linestring :ls, serializer: YourClass
101
+ end
102
+ ```
@@ -9,8 +9,13 @@ module ActiveRecordMysqlSpatial
9
9
 
10
10
  included do
11
11
  define_column_methods :point,
12
+ :polygon,
12
13
  :linestring,
13
- :multilinestring
14
+ :multipoint,
15
+ :multipolygon,
16
+ :geomcollection,
17
+ :multilinestring,
18
+ :geometrycollection
14
19
  end
15
20
  end
16
21
  end
@@ -5,7 +5,8 @@ module ActiveRecordMysqlSpatial
5
5
  module MySQL
6
6
  class Base < ::ActiveRecord::Type::Json
7
7
  attr_reader :raw,
8
- :error
8
+ :error,
9
+ :error_backtrace
9
10
 
10
11
  def type
11
12
  raise NotImplementedError
@@ -35,33 +36,52 @@ module ActiveRecordMysqlSpatial
35
36
  raise NotImplementedError
36
37
  end
37
38
 
39
+ def ==(other)
40
+ self.class == other.class
41
+ end
42
+
38
43
  private
39
44
 
45
+ def handle_error(error)
46
+ @error = error.message
47
+ @error_backtrace = error.backtrace
48
+
49
+ self
50
+ end
51
+
40
52
  def cast_value(value)
41
53
  raise NotImplementedError
42
54
  end
43
55
 
44
56
  def extract_coordinates(attributes, type: :linestring)
45
- return [drill_coordinates(attributes) || [], true] if valid_hash?(attributes, type: type)
57
+ return [attributes, true] if type == :point && attributes.is_a?(Array)
58
+ return [drill_coordinates(attributes, type: type) || [], true] if valid_hash?(attributes, type: type)
46
59
  return [[], false] unless attributes.is_a?(String)
47
60
 
48
61
  if attributes.encoding.to_s == 'ASCII-8BIT'
49
- linestring = Geometry.parse_bin(attributes)
62
+ spatial_data = Geometry.parse_bin(attributes)
50
63
 
51
- [linestring.coordinates, false]
64
+ [drill_elements(spatial_data), false]
52
65
  elsif Geometry.valid_spatial?(attributes)
53
66
  spatial_data = Geometry.from_text(attributes)
54
67
 
55
- [spatial_data.coordinates, true]
68
+ [drill_elements(spatial_data), true]
56
69
  else
57
70
  parsed_json = ::ActiveSupport::JSON.decode(attributes)
58
71
 
59
- [parsed_json['coordinates'] || [], true]
72
+ coordinates = if type == :geometrycollection
73
+ parsed_json['geometries']
74
+ else
75
+ parsed_json['coordinates']
76
+ end
77
+
78
+ [coordinates || [], true]
60
79
  end
61
80
  end
62
81
 
63
82
  def valid_hash?(attributes, type: :linestring)
64
83
  return false unless attributes.is_a?(Hash)
84
+ return attributes.key?(:geometries) || attributes.key?('geometries') if type == :geometrycollection
65
85
 
66
86
  valid_coord_hash = attributes.key?(:coordinates) || attributes.key?('coordinates') || attributes.key?(:coordinate) || attributes.key?('coordinate')
67
87
 
@@ -70,9 +90,19 @@ module ActiveRecordMysqlSpatial
70
90
  valid_coord_hash || attributes.key?(:x) || attributes.key?('x') || attributes.key?(:y) || attributes.key?('y')
71
91
  end
72
92
 
73
- def drill_coordinates(attributes)
93
+ def drill_coordinates(attributes, type: :linestring)
94
+ return attributes[:geometries].presence || attributes['geometries'] if type == :geometrycollection
95
+
74
96
  attributes[:coordinates].presence || attributes['coordinates'].presence || attributes[:coordinate].presence || attributes['coordinate']
75
97
  end
98
+
99
+ def drill_elements(spatial)
100
+ if spatial.geometry_type == RGeo::Feature::GeometryCollection
101
+ spatial.geometries
102
+ else
103
+ spatial.coordinates
104
+ end
105
+ end
76
106
  end
77
107
  end
78
108
  end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record_mysql_spatial/geometry'
4
+
5
+ require_relative 'base'
6
+ require_relative 'linestring'
7
+ require_relative 'multilinestring'
8
+ require_relative 'multipoint'
9
+ require_relative 'multipolygon'
10
+ require_relative 'point'
11
+ require_relative 'polygon'
12
+
13
+ module ActiveRecordMysqlSpatial
14
+ module ActiveRecord
15
+ module MySQL
16
+ class Geometrycollection < Base
17
+ attr_accessor :items
18
+
19
+ def type
20
+ :geometrycollection
21
+ end
22
+
23
+ def to_sql
24
+ return nil if @items.blank?
25
+
26
+ "GeometryCollection(#{to_coordinates_sql})"
27
+ end
28
+
29
+ def to_coordinates_sql
30
+ items.map(&:to_sql).compact.join(', ')
31
+ end
32
+
33
+ def ==(other)
34
+ return false if super == false
35
+
36
+ items.each_with_index do |item, index|
37
+ return false if item != other.items[index]
38
+ end
39
+
40
+ true
41
+ end
42
+
43
+ private
44
+
45
+ def cast_value(value)
46
+ @raw = value
47
+ elements, create_raw = extract_coordinates(value, type: :geometrycollection)
48
+
49
+ @raw = Geometry.from_geometries(elements).as_binary if create_raw
50
+
51
+ @items = elements.map do |geometry|
52
+ klass, data = extract_data(geometry)
53
+
54
+ klass.new.send(:cast_value, data)
55
+ end
56
+
57
+ self
58
+ rescue StandardError => e
59
+ handle_error(e)
60
+ end
61
+
62
+ def extract_data(geometry)
63
+ if geometry.is_a?(Hash)
64
+ extract_hash_data(geometry)
65
+ else
66
+ extract_cartesian_data(geometry)
67
+ end
68
+ end
69
+
70
+ def extract_cartesian_data(geometry)
71
+ case geometry.geometry_type
72
+ when RGeo::Feature::Point
73
+ [
74
+ Point,
75
+ {
76
+ coordinates: geometry.coordinates
77
+ }
78
+ ]
79
+ when RGeo::Feature::GeometryCollection
80
+ [
81
+ Geometrycollection,
82
+ {
83
+ geometries: geometry.geometries
84
+ }
85
+ ]
86
+ when RGeo::Feature::LineString
87
+ [
88
+ Linestring,
89
+ {
90
+ coordinates: geometry.coordinates
91
+ }
92
+ ]
93
+ when RGeo::Feature::MultiLineString
94
+ [
95
+ Multilinestring,
96
+ {
97
+ coordinates: geometry.coordinates
98
+ }
99
+ ]
100
+ when RGeo::Feature::MultiPoint
101
+ [
102
+ Multipoint,
103
+ {
104
+ coordinates: geometry.coordinates
105
+ }
106
+ ]
107
+ when RGeo::Feature::MultiPolygon
108
+ [
109
+ Multipolygon,
110
+ {
111
+ coordinates: geometry.coordinates
112
+ }
113
+ ]
114
+ else
115
+ [
116
+ Polygon,
117
+ {
118
+ coordinates: geometry.coordinates
119
+ }
120
+ ]
121
+ end
122
+ end
123
+
124
+ def extract_hash_data(geometry)
125
+ type = geometry[:type].to_s.downcase
126
+ klass = "ActiveRecordMysqlSpatial::ActiveRecord::MySQL::#{type.camelize}".constantize
127
+
128
+ data = if type == 'geometrycollection'
129
+ {
130
+ geometries: geometry[:geometries] || geometry['geometries']
131
+ }
132
+ else
133
+ {
134
+ coordinates: geometry[:coordinates] || geometry['coordinates']
135
+ }
136
+ end
137
+
138
+ [klass, data]
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -7,25 +7,34 @@ module ActiveRecordMysqlSpatial
7
7
  module ActiveRecord
8
8
  module MySQL
9
9
  class Linestring < Base
10
- attr_reader :coordinates
10
+ attr_reader :items
11
11
 
12
12
  def type
13
13
  :linestring
14
14
  end
15
15
 
16
+ def coordinates
17
+ # TODO: remove later
18
+ puts 'coordinates is deprecated. Please use items instead.'
19
+
20
+ @items
21
+ end
22
+
16
23
  def to_sql
17
- return nil if @coordinates.blank?
24
+ return nil if @items.blank?
18
25
 
19
26
  "LineString(#{to_coordinates_sql})"
20
27
  end
21
28
 
22
29
  def to_coordinates_sql
23
- @coordinates.map(&:to_coordinate_sql).compact.join(', ')
30
+ @items.map(&:to_coordinate_sql).compact.join(', ')
24
31
  end
25
32
 
26
33
  def ==(other)
27
- coordinates.each_with_index do |coord, index|
28
- return false if coord != other.coordinates[index]
34
+ return false if super == false
35
+
36
+ items.each_with_index do |coord, index|
37
+ return false if coord != other.items[index]
29
38
  end
30
39
 
31
40
  true
@@ -39,15 +48,13 @@ module ActiveRecordMysqlSpatial
39
48
 
40
49
  @raw = Geometry.from_coordinates(coordinates).as_binary if create_raw
41
50
 
42
- @coordinates = coordinates.map do |coord|
51
+ @items = coordinates.map do |coord|
43
52
  Point.new.send(:cast_value, coord)
44
53
  end
45
54
 
46
55
  self
47
56
  rescue StandardError => e
48
- @error = e.message
49
-
50
- self
57
+ handle_error(e)
51
58
  end
52
59
  end
53
60
  end
@@ -20,10 +20,12 @@ module ActiveRecordMysqlSpatial
20
20
  end
21
21
 
22
22
  def to_coordinates_sql
23
- items.map { |linestring| "(#{linestring.to_coordinates_sql})" }.join(',')
23
+ items.map { |linestring| "(#{linestring.to_coordinates_sql})" }.join(', ')
24
24
  end
25
25
 
26
26
  def ==(other)
27
+ return false if super == false
28
+
27
29
  items.each_with_index do |item, index|
28
30
  return false if item != other.items[index]
29
31
  end
@@ -50,9 +52,7 @@ module ActiveRecordMysqlSpatial
50
52
 
51
53
  self
52
54
  rescue StandardError => e
53
- @error = e.message
54
-
55
- self
55
+ handle_error(e)
56
56
  end
57
57
  end
58
58
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'point'
5
+
6
+ module ActiveRecordMysqlSpatial
7
+ module ActiveRecord
8
+ module MySQL
9
+ class Multipoint < Base
10
+ attr_reader :items
11
+
12
+ def type
13
+ :multipoint
14
+ end
15
+
16
+ def to_sql
17
+ return nil if @items.blank?
18
+
19
+ "MultiPoint(#{to_coordinates_sql})"
20
+ end
21
+
22
+ def to_coordinates_sql
23
+ @items.map(&:to_coordinate_sql).compact.join(', ')
24
+ end
25
+
26
+ def ==(other)
27
+ return false if super == false
28
+
29
+ items.each_with_index do |coord, index|
30
+ return false if coord != other.items[index]
31
+ end
32
+
33
+ true
34
+ end
35
+
36
+ private
37
+
38
+ def cast_value(value)
39
+ @raw = value
40
+ coordinates, create_raw = extract_coordinates(value)
41
+
42
+ @raw = Geometry.from_coordinates(coordinates, type: :multipoint).as_binary if create_raw
43
+
44
+ @items = coordinates.map do |coord|
45
+ Point.new.send(:cast_value, coord)
46
+ end
47
+
48
+ self
49
+ rescue StandardError => e
50
+ handle_error(e)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'polygon'
5
+
6
+ module ActiveRecordMysqlSpatial
7
+ module ActiveRecord
8
+ module MySQL
9
+ class Multipolygon < Base
10
+ attr_accessor :items
11
+
12
+ def type
13
+ :multipolygon
14
+ end
15
+
16
+ def to_sql
17
+ return nil if @items.blank?
18
+
19
+ "MultiPolygon(#{to_coordinates_sql})"
20
+ end
21
+
22
+ def to_coordinates_sql
23
+ items.map { |polygon| "(#{polygon.to_coordinates_sql})" }.join(', ')
24
+ end
25
+
26
+ def ==(other)
27
+ return false if super == false
28
+
29
+ items.each_with_index do |item, index|
30
+ return false if item != other.items[index]
31
+ end
32
+
33
+ true
34
+ end
35
+
36
+ private
37
+
38
+ def cast_value(value)
39
+ @raw = value
40
+ coordinates, create_raw = extract_coordinates(value)
41
+
42
+ @raw = Geometry.from_coordinates(coordinates, type: :multipolygon).as_binary if create_raw
43
+
44
+ @items = coordinates.map do |polygon|
45
+ Polygon.new.send(
46
+ :cast_value,
47
+ {
48
+ coordinates: polygon
49
+ }
50
+ )
51
+ end
52
+
53
+ self
54
+ rescue StandardError => e
55
+ handle_error(e)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -36,7 +36,7 @@ module ActiveRecordMysqlSpatial
36
36
  alias to_coordinates_sql to_coordinate_sql
37
37
 
38
38
  def ==(other)
39
- x == other.x && y == other.y
39
+ super && x == other.x && y == other.y
40
40
  end
41
41
 
42
42
  private
@@ -47,21 +47,25 @@ module ActiveRecordMysqlSpatial
47
47
 
48
48
  @raw = Geometry.from_coordinates(coordinate, type: :point).as_binary if create_raw
49
49
 
50
- @x, @y = if coordinate.is_a?(Array) && coordinate.present?
51
- [coordinate[0], coordinate[1]]
52
- elsif value.is_a?(Array)
53
- [value[0], value[1]]
54
- elsif value.is_a?(Hash)
55
- [value[:x] || value['x'], value[:y] || value['y']]
50
+ @x, @y = if coordinate.present?
51
+ drill_value(coordinate)
56
52
  else
57
- [nil, nil]
53
+ drill_value(value)
58
54
  end
59
55
 
60
56
  self
61
57
  rescue StandardError => e
62
- @error = e.message
58
+ handle_error(e)
59
+ end
63
60
 
64
- self
61
+ def drill_value(value)
62
+ if value.is_a?(Array)
63
+ [value[0], value[1]]
64
+ elsif value.is_a?(Hash)
65
+ [value[:x] || value['x'], value[:y] || value['y']]
66
+ else
67
+ [nil, nil]
68
+ end
65
69
  end
66
70
  end
67
71
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'linestring'
5
+
6
+ module ActiveRecordMysqlSpatial
7
+ module ActiveRecord
8
+ module MySQL
9
+ class Polygon < Base
10
+ attr_accessor :items
11
+
12
+ def type
13
+ :polygon
14
+ end
15
+
16
+ def to_sql
17
+ return nil if @items.blank?
18
+
19
+ "Polygon(#{to_coordinates_sql})"
20
+ end
21
+
22
+ def to_coordinates_sql
23
+ items.map { |linestring| "(#{linestring.to_coordinates_sql})" }.join(', ')
24
+ end
25
+
26
+ def ==(other)
27
+ return false if super == false
28
+
29
+ items.each_with_index do |item, index|
30
+ return false if item != other.items[index]
31
+ end
32
+
33
+ true
34
+ end
35
+
36
+ private
37
+
38
+ def cast_value(value)
39
+ @raw = value
40
+ coordinates, create_raw = extract_coordinates(value)
41
+
42
+ @raw = Geometry.from_coordinates(coordinates, type: :polygon).as_binary if create_raw
43
+
44
+ @items = coordinates.map do |linestring|
45
+ Linestring.new.send(
46
+ :cast_value,
47
+ {
48
+ coordinates: linestring
49
+ }
50
+ )
51
+ end
52
+
53
+ self
54
+ rescue StandardError => e
55
+ handle_error(e)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -14,11 +14,26 @@ module ActiveRecordMysqlSpatial
14
14
  point: {
15
15
  name: 'point'
16
16
  },
17
+ polygon: {
18
+ name: 'polygon'
19
+ },
17
20
  linestring: {
18
21
  name: 'linestring'
19
22
  },
23
+ multipoint: {
24
+ name: 'multipoint'
25
+ },
26
+ multipolygon: {
27
+ name: 'multipolygon'
28
+ },
20
29
  multilinestring: {
21
30
  name: 'multilinestring'
31
+ },
32
+ geomcollection: {
33
+ name: 'geomcollection'
34
+ },
35
+ geometrycollection: {
36
+ name: 'geometrycollection'
22
37
  }
23
38
  )
24
39
  end
@@ -1,15 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'mysql/linestring'
4
- require_relative 'mysql/multilinestring'
5
- require_relative 'mysql/point'
3
+ require_relative 'mysql/base'
6
4
 
7
5
  module ActiveRecordMysqlSpatial
8
6
  module ActiveRecord
9
7
  module Quoting
10
8
  def quote(value)
11
9
  case value
12
- when MySQL::Linestring, MySQL::Multilinestring, MySQL::Point
10
+ when MySQL::Base
13
11
  quote_geom(value)
14
12
  else
15
13
  super
@@ -2,9 +2,13 @@
2
2
 
3
3
  require 'active_support/concern'
4
4
 
5
+ require_relative 'mysql/geometrycollection'
5
6
  require_relative 'mysql/linestring'
6
7
  require_relative 'mysql/multilinestring'
8
+ require_relative 'mysql/multipoint'
9
+ require_relative 'mysql/multipolygon'
7
10
  require_relative 'mysql/point'
11
+ require_relative 'mysql/polygon'
8
12
 
9
13
  module ActiveRecordMysqlSpatial
10
14
  module ActiveRecord
@@ -18,8 +22,13 @@ module ActiveRecordMysqlSpatial
18
22
 
19
23
  def custom_initialize_type_map(type_map)
20
24
  type_map.register_type('point', MySQL::Point.new)
25
+ type_map.register_type('polygon', MySQL::Polygon.new)
21
26
  type_map.register_type('linestring', MySQL::Linestring.new)
27
+ type_map.register_type('multipoint', MySQL::Multipoint.new)
28
+ type_map.register_type('multipolygon', MySQL::Multipolygon.new)
22
29
  type_map.register_type('multilinestring', MySQL::Multilinestring.new)
30
+ type_map.register_type('geomcollection', MySQL::Geometrycollection.new)
31
+ type_map.register_type('geometrycollection', MySQL::Geometrycollection.new)
23
32
  end
24
33
  end
25
34
 
@@ -34,8 +43,13 @@ module ActiveRecordMysqlSpatial
34
43
  end
35
44
 
36
45
  ::ActiveRecord::Type.register(:point, MySQL::Point, adapter: :mysql2)
46
+ ::ActiveRecord::Type.register(:polygon, MySQL::Polygon, adapter: :mysql2)
37
47
  ::ActiveRecord::Type.register(:linestring, MySQL::Linestring, adapter: :mysql2)
48
+ ::ActiveRecord::Type.register(:multipoint, MySQL::Multipoint, adapter: :mysql2)
49
+ ::ActiveRecord::Type.register(:multipolygon, MySQL::Multipolygon, adapter: :mysql2)
38
50
  ::ActiveRecord::Type.register(:multilinestring, MySQL::Multilinestring, adapter: :mysql2)
51
+ ::ActiveRecord::Type.register(:geomcollection, MySQL::Geometrycollection, adapter: :mysql2)
52
+ ::ActiveRecord::Type.register(:geometrycollection, MySQL::Geometrycollection, adapter: :mysql2)
39
53
  end
40
54
  end
41
55
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module ActiveRecordMysqlSpatial
6
+ module ActsAsSpatial
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def acts_as_linestring(*columns, serializer: nil)
11
+ return if serializer.blank?
12
+
13
+ columns.each do |col|
14
+ attribute(col, serializer.new)
15
+ end
16
+ end
17
+
18
+ alias_method :acts_as_point, :acts_as_linestring
19
+ alias_method :acts_as_multilinestring, :acts_as_linestring
20
+ end
21
+ end
22
+ end
@@ -11,6 +11,10 @@ module ActiveRecordMysqlSpatial
11
11
  send("#{type}_from_coords", coordinates)
12
12
  end
13
13
 
14
+ def from_geometries(geometries)
15
+ geometrycollection_from_geometries(geometries)
16
+ end
17
+
14
18
  def from_text(value)
15
19
  cartesian_factory.parse_wkt(value)
16
20
  rescue StandardError
@@ -18,32 +22,69 @@ module ActiveRecordMysqlSpatial
18
22
  end
19
23
 
20
24
  def valid_spatial?(value)
21
- point?(value) || linestring?(value) || multilinestring?(value)
25
+ point?(value) ||
26
+ linestring?(value) ||
27
+ multilinestring?(value) ||
28
+ multipoint?(value) ||
29
+ polygon?(value) ||
30
+ multipolygon?(value) ||
31
+ geometrycollection?(value)
22
32
  end
23
33
 
24
34
  def point?(value)
25
- /^point\(/i.match?(value)
35
+ /^point*.\(/i.match?(value)
26
36
  end
27
37
 
28
38
  def linestring?(value)
29
- /^linestring\(/i.match?(value)
39
+ /^linestring*.\(/i.match?(value)
30
40
  end
31
41
 
32
42
  def multilinestring?(value)
33
- /^multilinestring\(/i.match?(value)
43
+ /^multilinestring*.\(/i.match?(value)
44
+ end
45
+
46
+ def multipoint?(value)
47
+ /^multipoint*.\(/i.match?(value)
48
+ end
49
+
50
+ def polygon?(value)
51
+ /^polygon*.\(/i.match?(value)
52
+ end
53
+
54
+ def multipolygon?(value)
55
+ /^multipolygon*.\(/i.match?(value)
56
+ end
57
+
58
+ def geometrycollection?(value)
59
+ /^geometrycollection*.\(/i.match?(value)
34
60
  end
35
61
 
36
62
  private
37
63
 
38
64
  def point_from_coords(coordinate)
39
- x, y = coordinate
65
+ x, y = if coordinate.is_a?(Array)
66
+ coordinate
67
+ else
68
+ [
69
+ coordinate[:x] || coordinate['x'],
70
+ coordinate[:y] || coordinate['y']
71
+ ]
72
+ end
40
73
 
41
74
  cartesian_factory.point(x, y)
42
75
  end
43
76
 
77
+ def multipoint_from_coords(coordinates)
78
+ points = coordinates.map do |coordinate|
79
+ point_from_coords(coordinate)
80
+ end
81
+
82
+ cartesian_factory.multi_point(points)
83
+ end
84
+
44
85
  def linestring_from_coords(coordinates)
45
- points = coordinates.map do |x, y|
46
- cartesian_factory.point(x, y)
86
+ points = coordinates.map do |coordinate|
87
+ point_from_coords(coordinate)
47
88
  end
48
89
 
49
90
  cartesian_factory.line_string(points)
@@ -57,6 +98,30 @@ module ActiveRecordMysqlSpatial
57
98
  cartesian_factory.multi_line_string(line_strings)
58
99
  end
59
100
 
101
+ def polygon_from_coords(coordinates)
102
+ outer_ring, *inner_rings = coordinates.map do |coordinate|
103
+ linestring_from_coords(coordinate)
104
+ end
105
+
106
+ cartesian_factory.polygon(outer_ring, inner_rings)
107
+ end
108
+
109
+ def multipolygon_from_coords(coordinates)
110
+ polygons = coordinates.map do |coordinate|
111
+ polygon_from_coords(coordinate)
112
+ end
113
+
114
+ cartesian_factory.multi_polygon(polygons)
115
+ end
116
+
117
+ def geometrycollection_from_geometries(geometries)
118
+ geos = geometries.map do |geometry|
119
+ send("#{geometry[:type].downcase}_from_coords", geometry[:coordinates] || geometry[:coordinate])
120
+ end
121
+
122
+ cartesian_factory.collection(geos)
123
+ end
124
+
60
125
  def parser
61
126
  @parser ||= RGeo::WKRep::WKBParser.new
62
127
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordMysqlSpatial
4
- VERSION = '0.1.0'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -3,12 +3,17 @@
3
3
  require 'rgeo'
4
4
 
5
5
  require_relative 'active_record_mysql_spatial/active_record/column_methods'
6
+ require_relative 'active_record_mysql_spatial/active_record/mysql/geometrycollection'
6
7
  require_relative 'active_record_mysql_spatial/active_record/mysql/linestring'
7
8
  require_relative 'active_record_mysql_spatial/active_record/mysql/multilinestring'
9
+ require_relative 'active_record_mysql_spatial/active_record/mysql/multipoint'
10
+ require_relative 'active_record_mysql_spatial/active_record/mysql/multipolygon'
8
11
  require_relative 'active_record_mysql_spatial/active_record/mysql/point'
12
+ require_relative 'active_record_mysql_spatial/active_record/mysql/polygon'
9
13
  require_relative 'active_record_mysql_spatial/active_record/native_types'
10
14
  require_relative 'active_record_mysql_spatial/active_record/quoting'
11
15
  require_relative 'active_record_mysql_spatial/active_record/register_types'
16
+ require_relative 'active_record_mysql_spatial/acts_as_spatial'
12
17
  require_relative 'active_record_mysql_spatial/geometry'
13
18
  require_relative 'active_record_mysql_spatial/version'
14
19
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_mysql_spatial
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alpha
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-09-09 00:00:00.000000000 Z
11
+ date: 2024-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rgeo
@@ -37,18 +37,24 @@ executables: []
37
37
  extensions: []
38
38
  extra_rdoc_files: []
39
39
  files:
40
+ - ".rspec"
40
41
  - README.md
41
42
  - Rakefile
42
43
  - active_record_mysql_spatial.gemspec
43
44
  - lib/active_record_mysql_spatial.rb
44
45
  - lib/active_record_mysql_spatial/active_record/column_methods.rb
45
46
  - lib/active_record_mysql_spatial/active_record/mysql/base.rb
47
+ - lib/active_record_mysql_spatial/active_record/mysql/geometrycollection.rb
46
48
  - lib/active_record_mysql_spatial/active_record/mysql/linestring.rb
47
49
  - lib/active_record_mysql_spatial/active_record/mysql/multilinestring.rb
50
+ - lib/active_record_mysql_spatial/active_record/mysql/multipoint.rb
51
+ - lib/active_record_mysql_spatial/active_record/mysql/multipolygon.rb
48
52
  - lib/active_record_mysql_spatial/active_record/mysql/point.rb
53
+ - lib/active_record_mysql_spatial/active_record/mysql/polygon.rb
49
54
  - lib/active_record_mysql_spatial/active_record/native_types.rb
50
55
  - lib/active_record_mysql_spatial/active_record/quoting.rb
51
56
  - lib/active_record_mysql_spatial/active_record/register_types.rb
57
+ - lib/active_record_mysql_spatial/acts_as_spatial.rb
52
58
  - lib/active_record_mysql_spatial/geometry.rb
53
59
  - lib/active_record_mysql_spatial/version.rb
54
60
  homepage: https://github.com/zgid123/active_record_mysql_spatial