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 +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
|