active_record_mysql_spatial 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/README.md +85 -18
- data/lib/active_record_mysql_spatial/active_record/column_methods.rb +6 -1
- data/lib/active_record_mysql_spatial/active_record/mysql/base.rb +37 -7
- data/lib/active_record_mysql_spatial/active_record/mysql/geometrycollection.rb +143 -0
- data/lib/active_record_mysql_spatial/active_record/mysql/linestring.rb +16 -9
- data/lib/active_record_mysql_spatial/active_record/mysql/multilinestring.rb +4 -4
- data/lib/active_record_mysql_spatial/active_record/mysql/multipoint.rb +55 -0
- data/lib/active_record_mysql_spatial/active_record/mysql/multipolygon.rb +60 -0
- data/lib/active_record_mysql_spatial/active_record/mysql/point.rb +14 -10
- data/lib/active_record_mysql_spatial/active_record/mysql/polygon.rb +60 -0
- data/lib/active_record_mysql_spatial/active_record/native_types.rb +15 -0
- data/lib/active_record_mysql_spatial/active_record/quoting.rb +2 -4
- data/lib/active_record_mysql_spatial/active_record/register_types.rb +14 -0
- data/lib/active_record_mysql_spatial/acts_as_spatial.rb +22 -0
- data/lib/active_record_mysql_spatial/geometry.rb +72 -7
- data/lib/active_record_mysql_spatial/version.rb +1 -1
- data/lib/active_record_mysql_spatial.rb +5 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b423da177a4f62465caf3c5a9c8cf08c017500e152d78b5dbf9daeb6b1f196f1
|
4
|
+
data.tar.gz: ebdd1045ed4b4f49fe3c82f803ba7009af50c0d419be7d77cc94786282095acf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
1
|
+
ActiveRecord extension for MySQL Spatial Data.
|
2
2
|
|
3
|
-
|
3
|
+
# Installation
|
4
4
|
|
5
|
-
|
5
|
+
```sh
|
6
|
+
gem 'active_record_mysql_spatial'
|
7
|
+
```
|
6
8
|
|
7
|
-
|
9
|
+
# Migration
|
8
10
|
|
9
|
-
|
11
|
+
This extension provides some data type methods for ActiveRecord migration and also register those types to the schema.
|
10
12
|
|
11
|
-
|
13
|
+
```rb
|
14
|
+
# frozen_string_literal: true
|
12
15
|
|
13
|
-
|
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
|
-
|
23
|
+
t.column :tls, :linestring
|
24
|
+
t.column :tmls, :multilinestring
|
25
|
+
t.column :tpt, :point
|
16
26
|
|
17
|
-
|
27
|
+
t.timestamps
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
18
32
|
|
19
|
-
|
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
|
-
|
35
|
+
```rb
|
36
|
+
# Could not dump table "positions" because of following StandardError
|
37
|
+
# Unknown type 'linestring' for column 'tls'
|
38
|
+
```
|
22
39
|
|
23
|
-
|
40
|
+
# Usage
|
24
41
|
|
25
|
-
|
42
|
+
## Point
|
26
43
|
|
27
|
-
|
44
|
+
```rb
|
45
|
+
position = Position.create!(pt: { x: 1, y: 2 })
|
28
46
|
|
29
|
-
|
47
|
+
position = Position.create!(pt: [1, 2])
|
30
48
|
|
31
|
-
|
49
|
+
p position.pt.x # puts x
|
50
|
+
p position.pt.y # puts y
|
51
|
+
```
|
32
52
|
|
33
|
-
##
|
53
|
+
## Linestring
|
34
54
|
|
35
|
-
|
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
|
-
:
|
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 [
|
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
|
-
|
62
|
+
spatial_data = Geometry.parse_bin(attributes)
|
50
63
|
|
51
|
-
[
|
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
|
68
|
+
[drill_elements(spatial_data), true]
|
56
69
|
else
|
57
70
|
parsed_json = ::ActiveSupport::JSON.decode(attributes)
|
58
71
|
|
59
|
-
|
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 :
|
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 @
|
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
|
-
@
|
30
|
+
@items.map(&:to_coordinate_sql).compact.join(', ')
|
24
31
|
end
|
25
32
|
|
26
33
|
def ==(other)
|
27
|
-
|
28
|
-
|
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
|
-
@
|
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
|
-
|
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
|
-
|
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.
|
51
|
-
|
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
|
-
|
53
|
+
drill_value(value)
|
58
54
|
end
|
59
55
|
|
60
56
|
self
|
61
57
|
rescue StandardError => e
|
62
|
-
|
58
|
+
handle_error(e)
|
59
|
+
end
|
63
60
|
|
64
|
-
|
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/
|
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::
|
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) ||
|
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
|
35
|
+
/^point*.\(/i.match?(value)
|
26
36
|
end
|
27
37
|
|
28
38
|
def linestring?(value)
|
29
|
-
/^linestring
|
39
|
+
/^linestring*.\(/i.match?(value)
|
30
40
|
end
|
31
41
|
|
32
42
|
def multilinestring?(value)
|
33
|
-
/^multilinestring
|
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 |
|
46
|
-
|
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
|
@@ -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.
|
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-
|
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
|