rp_clustering-rgeo-activerecord 0.0.2 → 0.0.3
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 +8 -8
- data/README.md +117 -23
- data/lib/rp_clustering-rgeo-activerecord.rb +13 -2
- data/lib/rp_clustering-rgeo-activerecord/active_record_base_spatial_expressions.rb +154 -0
- data/lib/rp_clustering-rgeo-activerecord/arel_attribute_spatial_expressions.rb +7 -8
- data/lib/rp_clustering-rgeo-activerecord/arel_table_spatial_expressions.rb +7 -8
- data/lib/rp_clustering-rgeo-activerecord/version.rb +1 -1
- data/test/test_clustering.rb +110 -0
- data/test/test_unit.rb +1 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MzliYTdiMDQyNjMxZmQ3NGM4NGQyNTZmMjgwYmY4NDUyMTk0NGFiYQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YTRmZWE4M2Q5NDhkZDE0NWYwOTEyYzRkYzk5MDU3ZmVmZDVlMDk2Mg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ODEwOGZjNWVmZDg3ODFkNTU5YjZlODQ0OGM5NjlkZTU2NTNkZWU5Mjg3NGIy
|
10
|
+
YzcxOWY5OGJjMzg4ZjJmODhhZWNkNDM2MjIzMGU0NTk4ZmM1NDE0MDdjNWYz
|
11
|
+
NTAwMjllZTRjMTY1MzRjNjI3NWUyZDQ3ODUxNjFiMGU5MjBhYTk=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NzQ1OTU4MWE4MDM3OWJkNTJkYWI0ZWQ2ZTkxN2Q1NTBhNjU1NWJmYTFiNWRm
|
14
|
+
NWRlMjUyN2I1NjExZjJiOWVhZTY1MjllZmQ0ZmViN2E4MmZhMjViOWQzMzFh
|
15
|
+
ODAzMzZlNjA3OGQ0ZWQ1YzczOTg1OTMwZjFkZjU2ZmM3MTNhYzM=
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# RPClustering::RGeo::ActiveRecord
|
2
2
|
|
3
|
+
[](https://codeclimate.com/github/robertpyke/rp_clustering-rgeo-activerecord)
|
4
|
+
|
3
5
|
A RGeo PostGIS extension to provide Active Record (Model) clustering functionality.
|
4
6
|
|
5
7
|
The intention is that this Gem will eventually provide abstracted methods for
|
@@ -8,7 +10,7 @@ both "on the fly" clustering, as well as cached clustering (including associated
|
|
8
10
|
This Gem is currently in early development, so expect changes. On this note, if you'd like a specific clustering
|
9
11
|
algorithm or feature added, please ask.
|
10
12
|
|
11
|
-
If you find a problem with this Gem, please don't hesitate to raise an
|
13
|
+
If you find a problem with this Gem, please don't hesitate to [raise an issue](https://github.com/robertpyke/rp_clustering-rgeo-activerecord/issues).
|
12
14
|
|
13
15
|
## Installation
|
14
16
|
|
@@ -32,6 +34,98 @@ Or install it yourself as:
|
|
32
34
|
|
33
35
|
## Usage
|
34
36
|
|
37
|
+
### Added in Version 0.0.3
|
38
|
+
|
39
|
+
This version allows for the "on the fly" use of a ST_SnapToGrid clustering function.
|
40
|
+
The function is added to ActiveRecord::Base (Models). The function is:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
|
44
|
+
# Cluster using the PostGIS function ST_SnapToGrid
|
45
|
+
# -------------------------------------------------
|
46
|
+
#
|
47
|
+
# attr_to_cluster is the name of attribute to be clustered (a symbol).
|
48
|
+
# The attribute should be geometry attribute.
|
49
|
+
#
|
50
|
+
# Use the options Hash to define what cluster properties you would
|
51
|
+
# like returned.
|
52
|
+
#
|
53
|
+
# Options:
|
54
|
+
#
|
55
|
+
# [:grid_size] if set, will be used to create the cluster. The clustering
|
56
|
+
# works rougly like this; all geometries within 'grid_size'
|
57
|
+
# will be pulled together to form a single cluster. For a detailed
|
58
|
+
# explanation, please see the PostGIS docs for ST_SnapToGrid.
|
59
|
+
#
|
60
|
+
# If no +:grid_size+ is given, clusters will consist of all 'equal'
|
61
|
+
# geometries. E.g. all points at the same
|
62
|
+
# position (x,y) will be pulled together to form a single cluster.
|
63
|
+
# This is actually just a Group By of your +attr_to_cluster+.
|
64
|
+
#
|
65
|
+
# [:cluster_geometry_count] if set to true, the query will select, for
|
66
|
+
# each cluster, the number of geometries in the cluster.
|
67
|
+
#
|
68
|
+
# [:cluster_geometry_count_as] the name to select the
|
69
|
+
# cluster_geometry_count as, defaults to "cluster_geometry_count".
|
70
|
+
#
|
71
|
+
# [:cluster_centroid] if set to true, the query will select, for
|
72
|
+
# each cluster, the cluster centroid. The cluster_centroid returned
|
73
|
+
# will be a WKT string.
|
74
|
+
#
|
75
|
+
# [:cluster_centroid_as] the name to select the
|
76
|
+
# cluster_centroid as, defaults to "cluster_centroid".
|
77
|
+
#
|
78
|
+
# [:cluster_minimum_bounding_circle] if set to true, the query will select,
|
79
|
+
# for each cluster, the minimum bouding circle. The cluster_minimum_bounding_circle
|
80
|
+
# will be a WKT string.
|
81
|
+
#
|
82
|
+
# [:cluster_minimum_bounding_circle_as] the name to select the
|
83
|
+
# cluster_minimum_bounding_circle as, defaults to "cluster_minimum_bounding_circle"
|
84
|
+
#
|
85
|
+
# Note: Using the options hash, you must 'select' at least one attribute,
|
86
|
+
# else this method will rase an ArgumentError.
|
87
|
+
|
88
|
+
cluster_by_st_snap_to_grid(attr_to_cluster, options={})
|
89
|
+
|
90
|
+
```
|
91
|
+
|
92
|
+
e.g.
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
|
96
|
+
|
97
|
+
@layer = Layer.find(params[:id])
|
98
|
+
|
99
|
+
cluster_result = @layer.cluster_by_st_snap_to_grid(
|
100
|
+
:geometry, # the column to cluster
|
101
|
+
grid_size: 0.01,
|
102
|
+
cluster_geometry_count: true,
|
103
|
+
cluster_centroid: true
|
104
|
+
)
|
105
|
+
|
106
|
+
features = []
|
107
|
+
cluster_result.each do |cluster|
|
108
|
+
geom_feature = Layer.rgeo_factory_for_column(:geometry).parse_wkt(cluster.cluster_centroid)
|
109
|
+
feature = RGeo::GeoJSON::Feature.new(geom_feature, nil, { cluster_size: cluster.cluster_geometry_count.to_i })
|
110
|
+
|
111
|
+
features << feature
|
112
|
+
end
|
113
|
+
|
114
|
+
feature_collection = RGeo::GeoJSON::FeatureCollection.new(features)
|
115
|
+
RGeo::GeoJSON.encode(feature_collection)
|
116
|
+
|
117
|
+
# BOOM! You just made some GeoJSON which is ready to be displayed on a map.
|
118
|
+
# You've also embedded the cluster_size in the GeoJSON, so you can do some
|
119
|
+
# fancy client side interaction based on cluster size. For example, you could
|
120
|
+
# make outliers (small clusters) bright red, or you could vary the size of the
|
121
|
+
# cluster centroid based on the size of cluster.
|
122
|
+
#
|
123
|
+
# Ideally, you could vary your +grid_size+ based on the user's view port.
|
124
|
+
# For example, you could set it to fixed values based on the user's zoom level.
|
125
|
+
# You could dynamically generate it based on some fraction of the user's view port bbox.
|
126
|
+
|
127
|
+
```
|
128
|
+
|
35
129
|
### Added in Version 0.0.1
|
36
130
|
|
37
131
|
This version allows for hand-coded low-level clustering via the Arel interface.
|
@@ -40,37 +134,37 @@ e.g.
|
|
40
134
|
|
41
135
|
```ruby
|
42
136
|
|
43
|
-
|
137
|
+
# Get the Arel handle for the model
|
44
138
|
|
45
|
-
|
139
|
+
arel_table = MyModel.arel_table
|
46
140
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
141
|
+
# Our cluster grid size.
|
142
|
+
# Smaller grid_size means more clusters.
|
143
|
+
# Larger grid_size means less clusters (cluster covers a larger area).
|
144
|
+
# See http://www.postgis.org/docs/ST_SnapToGrid.html for more info.
|
51
145
|
|
52
|
-
|
146
|
+
grid_size = 0.1
|
53
147
|
|
54
|
-
|
55
|
-
|
148
|
+
# Cluster against our model's :latlon attribute with a grid size of '0.1'.
|
149
|
+
# Return the centroid of each cluster as "cluster_centroid".
|
56
150
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
151
|
+
query = MyModel.select(
|
152
|
+
arel_table.st_astext(
|
153
|
+
arel_table.st_centroid(arel_table.st_collect(arel_table[:latlon]))
|
154
|
+
).as("cluster_centroid")
|
155
|
+
).group(arel_table[:latlon].st_snaptogrid(grid_size))
|
62
156
|
|
63
|
-
|
64
|
-
|
157
|
+
# Iterate over our clusters
|
158
|
+
query.all.each do |cluster|
|
65
159
|
|
66
|
-
|
67
|
-
|
160
|
+
# print the cluster_centroid (a point) as WKT
|
161
|
+
puts cluster["cluster_centroid"]
|
68
162
|
|
69
|
-
|
70
|
-
|
163
|
+
# convert the WKT into a RGeo Geometry (a point)
|
164
|
+
geographic_factory.parse_wkt(cluster["cluster_centroid")
|
71
165
|
|
72
|
-
|
73
|
-
|
166
|
+
# ...
|
167
|
+
end
|
74
168
|
|
75
169
|
```
|
76
170
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "rp_clustering-rgeo-activerecord/version"
|
2
|
+
require "rp_clustering-rgeo-activerecord/active_record_base_spatial_expressions"
|
2
3
|
require "rp_clustering-rgeo-activerecord/arel_attribute_spatial_expressions"
|
3
4
|
require "rp_clustering-rgeo-activerecord/arel_table_spatial_expressions"
|
4
5
|
|
@@ -8,12 +9,22 @@ module RPClustering
|
|
8
9
|
|
9
10
|
module ActiveRecord
|
10
11
|
|
12
|
+
# Spatial Expressions to be attached directly to ActiveRecord::Base
|
13
|
+
|
14
|
+
module BaseSpatialExpressions
|
15
|
+
end
|
16
|
+
|
17
|
+
# Attach our Spatial Expression methods onto the ActiveRecord::Base class (as class methods).
|
18
|
+
class ::ActiveRecord::Base
|
19
|
+
include ::RPClustering::RGeo::ActiveRecord::BaseSpatialExpressions
|
20
|
+
end
|
21
|
+
|
11
22
|
# Spatial Expressions to be attached directly to Arel Attributes (DB columns)
|
12
23
|
|
13
24
|
module ArelAttributeSpatialExpressions
|
14
25
|
end
|
15
26
|
|
16
|
-
# Attach our Spatial Expression methods onto the Arel::Attribute class.
|
27
|
+
# Attach our Spatial Expression methods onto the Arel::Attribute class (as instance methods).
|
17
28
|
#
|
18
29
|
# i.e. As stated in the RGeo::ActiveRecord docs.. Allow chaining of spatial expressions from attributes
|
19
30
|
|
@@ -26,7 +37,7 @@ module RPClustering
|
|
26
37
|
module ArelTableSpatialExpressions
|
27
38
|
end
|
28
39
|
|
29
|
-
# Attach our Spatial Expression methods onto the Arel::Table class.
|
40
|
+
# Attach our Spatial Expression methods onto the Arel::Table class (as instance methods).
|
30
41
|
|
31
42
|
::Arel::Table.class_eval do
|
32
43
|
include ::RPClustering::RGeo::ActiveRecord::ArelTableSpatialExpressions
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# Author: Robert Pyke
|
2
|
+
|
3
|
+
module RPClustering
|
4
|
+
|
5
|
+
module RGeo
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
|
9
|
+
module BaseSpatialExpressions
|
10
|
+
|
11
|
+
# Use ActiveSupport::Concern to seperate our
|
12
|
+
# class and instance methods for inclusion into ActiveRecord::Base
|
13
|
+
extend ActiveSupport::Concern
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
|
17
|
+
# Cluster using the PostGIS function ST_SnapToGrid
|
18
|
+
# -------------------------------------------------
|
19
|
+
#
|
20
|
+
# attr_to_cluster is the name of attribute to be clustered (a symbol).
|
21
|
+
# The attribute should be geometry attribute.
|
22
|
+
#
|
23
|
+
# Use the options Hash to define what cluster properties you would
|
24
|
+
# like returned.
|
25
|
+
#
|
26
|
+
# Options:
|
27
|
+
#
|
28
|
+
# [:grid_size] if set, will be used to create the cluster. The clustering
|
29
|
+
# works rougly like this; all geometries within 'grid_size'
|
30
|
+
# will be pulled together to form a single cluster. For a detailed
|
31
|
+
# explanation, please see the PostGIS docs for ST_SnapToGrid.
|
32
|
+
#
|
33
|
+
# If no +:grid_size+ is given, clusters will consist of all 'equal'
|
34
|
+
# geometries. E.g. all points at the same
|
35
|
+
# position (x,y) will be pulled together to form a single cluster.
|
36
|
+
# This is actually just a Group By of your +attr_to_cluster+.
|
37
|
+
#
|
38
|
+
# [:cluster_geometry_count] if set to true, the query will select, for
|
39
|
+
# each cluster, the number of geometries in the cluster.
|
40
|
+
#
|
41
|
+
# [:cluster_geometry_count_as] the name to select the
|
42
|
+
# cluster_geometry_count as, defaults to "cluster_geometry_count".
|
43
|
+
#
|
44
|
+
# [:cluster_centroid] if set to true, the query will select, for
|
45
|
+
# each cluster, the cluster centroid. The cluster_centroid returned
|
46
|
+
# will be a WKT string.
|
47
|
+
#
|
48
|
+
# [:cluster_centroid_as] the name to select the
|
49
|
+
# cluster_centroid as, defaults to "cluster_centroid".
|
50
|
+
#
|
51
|
+
# [:cluster_minimum_bounding_circle] if set to true, the query will select,
|
52
|
+
# for each cluster, the minimum bouding circle. The cluster_minimum_bounding_circle
|
53
|
+
# will be a WKT string.
|
54
|
+
#
|
55
|
+
# [:cluster_minimum_bounding_circle_as] the name to select the
|
56
|
+
# cluster_minimum_bounding_circle as, defaults to "cluster_minimum_bounding_circle"
|
57
|
+
#
|
58
|
+
# Note: Using the options hash, you must 'select' at least one attribute,
|
59
|
+
# else this method will raise an ArgumentError.
|
60
|
+
#
|
61
|
+
|
62
|
+
def cluster_by_st_snap_to_grid(attr_to_cluster, options={})
|
63
|
+
raise ArgumentError, "Invalid cluster_by_st_snap_to_grid options provided" unless _are_cluster_options_valid?(options)
|
64
|
+
|
65
|
+
grid_size = options[:grid_size]
|
66
|
+
|
67
|
+
arel_table = self.arel_table
|
68
|
+
arel_attr = arel_table[attr_to_cluster]
|
69
|
+
|
70
|
+
q = self
|
71
|
+
|
72
|
+
# Get the cluster geometry count (if asked to)
|
73
|
+
if options[:cluster_geometry_count]
|
74
|
+
cluster_geometry_count_as = options[:cluster_geometry_count_as] || "cluster_geometry_count"
|
75
|
+
q = q._select_cluster_geometry_count(attr_to_cluster, cluster_geometry_count_as)
|
76
|
+
end
|
77
|
+
|
78
|
+
if options[:cluster_centroid]
|
79
|
+
cluster_centroid_as = options[:cluster_centroid_as] || "cluster_centroid"
|
80
|
+
q = q._select_cluster_centroid_as_wkt(attr_to_cluster, cluster_centroid_as)
|
81
|
+
end
|
82
|
+
|
83
|
+
if options[:cluster_minimum_bounding_circle]
|
84
|
+
cluster_minimum_bounding_circle_as = options[:cluster_minimum_bounding_circle_as] || "cluster_minimum_bounding_circle"
|
85
|
+
q = q._select_cluster_minimum_bounding_circle_as_wkt(attr_to_cluster, cluster_minimum_bounding_circle_as)
|
86
|
+
end
|
87
|
+
|
88
|
+
if grid_size
|
89
|
+
q = q.group(arel_attr.st_snaptogrid(grid_size))
|
90
|
+
else
|
91
|
+
q = q.group(arel_attr)
|
92
|
+
end
|
93
|
+
|
94
|
+
q
|
95
|
+
end
|
96
|
+
|
97
|
+
# Ensure the user is selecting something, and that
|
98
|
+
# the grid size is either nil, or is a valid integer
|
99
|
+
|
100
|
+
def _are_cluster_options_valid?(options)
|
101
|
+
selecting_something = (
|
102
|
+
options[:cluster_geometry_count] or
|
103
|
+
options[:cluster_centroid] or
|
104
|
+
options[:cluster_minimum_bounding_circle]
|
105
|
+
)
|
106
|
+
|
107
|
+
grid_size_valid = (
|
108
|
+
options[:grid_size].nil? or
|
109
|
+
( options[:grid_size].is_a? Numeric and options[:grid_size] >= 0 )
|
110
|
+
)
|
111
|
+
|
112
|
+
valid = selecting_something and grid_size_valid
|
113
|
+
end
|
114
|
+
|
115
|
+
# Select the cluster geometry count
|
116
|
+
|
117
|
+
def _select_cluster_geometry_count(attr_to_cluster, as)
|
118
|
+
arel_table = self.arel_table
|
119
|
+
arel_attr = arel_table[attr_to_cluster]
|
120
|
+
|
121
|
+
select(arel_attr.count().as(as))
|
122
|
+
end
|
123
|
+
|
124
|
+
# Select the cluster centroid as WKT.
|
125
|
+
|
126
|
+
def _select_cluster_centroid_as_wkt(attr_to_cluster, as)
|
127
|
+
arel_table = self.arel_table
|
128
|
+
arel_attr = arel_table[attr_to_cluster]
|
129
|
+
|
130
|
+
select(
|
131
|
+
arel_table.st_astext(
|
132
|
+
arel_attr.st_collect
|
133
|
+
).as(as)
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Select the cluster minimum bounding circle as WKT.
|
138
|
+
|
139
|
+
def _select_cluster_minimum_bounding_circle_as_wkt(attr_to_cluster, as)
|
140
|
+
arel_table = self.arel_table
|
141
|
+
arel_attr = arel_table[attr_to_cluster]
|
142
|
+
|
143
|
+
select(
|
144
|
+
arel_table.st_astext(
|
145
|
+
arel_table.st_minimumboundingcircle(arel_attr.st_collect)
|
146
|
+
).as(as)
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -67,17 +67,16 @@ module RPClustering
|
|
67
67
|
|
68
68
|
def st_minimumboundingcircle(num_segs=nil)
|
69
69
|
args = [self]
|
70
|
+
spatial_flags = [true, true]
|
71
|
+
|
70
72
|
if num_segs
|
71
73
|
args << num_segs.to_s
|
72
|
-
|
73
|
-
::RGeo::ActiveRecord::SpatialNamedFunction.new(
|
74
|
-
'ST_MinimumBoundingCircle', args, [true, true, false]
|
75
|
-
)
|
76
|
-
else
|
77
|
-
::RGeo::ActiveRecord::SpatialNamedFunction.new(
|
78
|
-
'ST_MinimumBoundingCircle', args, [true, true]
|
79
|
-
)
|
74
|
+
spatial_flags << false
|
80
75
|
end
|
76
|
+
|
77
|
+
::RGeo::ActiveRecord::SpatialNamedFunction.new(
|
78
|
+
'ST_MinimumBoundingCircle', args, spatial_flags
|
79
|
+
)
|
81
80
|
end
|
82
81
|
|
83
82
|
end
|
@@ -83,17 +83,16 @@ module RPClustering
|
|
83
83
|
|
84
84
|
def st_minimumboundingcircle(g, num_segs=nil)
|
85
85
|
args = [g]
|
86
|
+
spatial_flags = [true, true]
|
87
|
+
|
86
88
|
if num_segs
|
87
89
|
args << num_segs.to_s
|
88
|
-
|
89
|
-
::RGeo::ActiveRecord::SpatialNamedFunction.new(
|
90
|
-
'ST_MinimumBoundingCircle', args, [true, true, false]
|
91
|
-
)
|
92
|
-
else
|
93
|
-
::RGeo::ActiveRecord::SpatialNamedFunction.new(
|
94
|
-
'ST_MinimumBoundingCircle', args, [true, true]
|
95
|
-
)
|
90
|
+
spatial_flags << false
|
96
91
|
end
|
92
|
+
|
93
|
+
::RGeo::ActiveRecord::SpatialNamedFunction.new(
|
94
|
+
'ST_MinimumBoundingCircle', args, spatial_flags
|
95
|
+
)
|
97
96
|
end
|
98
97
|
|
99
98
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rgeo/active_record/adapter_test_helper'
|
3
|
+
require 'rp_clustering-rgeo-activerecord'
|
4
|
+
require 'squeel'
|
5
|
+
|
6
|
+
module RPClustering
|
7
|
+
module RGeo
|
8
|
+
module ActiveRecord
|
9
|
+
module Tests
|
10
|
+
class MyUnitTest < Test::Unit::TestCase # :nodoc:
|
11
|
+
|
12
|
+
# Use the RGEO active record adapter test helper
|
13
|
+
|
14
|
+
DATABASE_CONFIG_PATH = ::File.dirname(__FILE__)+'/database.yml'
|
15
|
+
OVERRIDE_DATABASE_CONFIG_PATH = ::File.dirname(__FILE__)+'/database_local.yml'
|
16
|
+
include ::RGeo::ActiveRecord::AdapterTestHelper
|
17
|
+
|
18
|
+
|
19
|
+
define_test_methods do
|
20
|
+
|
21
|
+
def populate_ar_class(content_)
|
22
|
+
klass_ = create_ar_class
|
23
|
+
case content_
|
24
|
+
when :latlon_point
|
25
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
26
|
+
t_.column 'latlon', :point, :srid => 4326
|
27
|
+
end
|
28
|
+
end
|
29
|
+
klass_
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_cluster_by_st_snap_to_grid_exists
|
33
|
+
arel_klass = populate_ar_class(:latlon_point)
|
34
|
+
assert(
|
35
|
+
arel_klass.methods.include?(:cluster_by_st_snap_to_grid),
|
36
|
+
"ActiveRecord::Base should now have a cluster_by_st_snap_to_grid function. " +
|
37
|
+
"Found:\n#{arel_klass.methods.sort}"
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_cluster_by_st_snap_to_grid_should_exception_with_invalid_options
|
42
|
+
arel_klass = populate_ar_class(:latlon_point)
|
43
|
+
|
44
|
+
assert_raise(ArgumentError) do
|
45
|
+
res = arel_klass.cluster_by_st_snap_to_grid(:latlon, grid_size: 10)
|
46
|
+
end
|
47
|
+
|
48
|
+
assert_raise(ArgumentError) do
|
49
|
+
res = arel_klass.cluster_by_st_snap_to_grid(:latlon, grid_size: -12, cluster_geometry_count: true)
|
50
|
+
end
|
51
|
+
|
52
|
+
assert_raise(ArgumentError) do
|
53
|
+
res = arel_klass.cluster_by_st_snap_to_grid(:latlon, grid_size: "2", cluster_geometry_count: true)
|
54
|
+
end
|
55
|
+
|
56
|
+
assert_nothing_thrown do
|
57
|
+
res = arel_klass.cluster_by_st_snap_to_grid(:latlon, grid_size: 2, cluster_geometry_count: true)
|
58
|
+
end
|
59
|
+
|
60
|
+
assert_nothing_thrown do
|
61
|
+
res = arel_klass.cluster_by_st_snap_to_grid(:latlon, grid_size: 2, cluster_centroid: true)
|
62
|
+
end
|
63
|
+
|
64
|
+
assert_nothing_thrown do
|
65
|
+
res = arel_klass.cluster_by_st_snap_to_grid(:latlon, grid_size: 2, cluster_minimum_bounding_circle: true)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_clustering_with_a_sufficiently_large_grid_size_reduces_count
|
71
|
+
arel_klass = populate_ar_class(:latlon_point)
|
72
|
+
|
73
|
+
points_generated = 0
|
74
|
+
(-5..5).each do |lng|
|
75
|
+
(-5..5).each do |lat|
|
76
|
+
obj = arel_klass.new
|
77
|
+
obj.latlon = @geographic_factory.point(lng, lat)
|
78
|
+
obj.save!
|
79
|
+
points_generated+=1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
res = arel_klass.cluster_by_st_snap_to_grid(:latlon, grid_size: 5, cluster_geometry_count: true)
|
84
|
+
clusters = res.all
|
85
|
+
total_clusters = clusters.count()
|
86
|
+
|
87
|
+
points_found_in_clusters = 0
|
88
|
+
|
89
|
+
res.all.each do |cluster|
|
90
|
+
points_found_in_clusters += cluster["cluster_geometry_count"].to_i
|
91
|
+
end
|
92
|
+
|
93
|
+
assert_equal(points_generated, points_found_in_clusters,
|
94
|
+
"The sum of the size of our clusters should equal the number of points in the table"
|
95
|
+
)
|
96
|
+
|
97
|
+
# We should have less clusters than we have points
|
98
|
+
assert(total_clusters < points_generated, "we should have less clusters than we have points")
|
99
|
+
|
100
|
+
# We should have more than 1 cluster with this size grid
|
101
|
+
assert(total_clusters > 1, "we should have more than 1 cluster with this grid size")
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/test/test_unit.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rp_clustering-rgeo-activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Pyke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -178,11 +178,13 @@ files:
|
|
178
178
|
- README.md
|
179
179
|
- Rakefile
|
180
180
|
- lib/rp_clustering-rgeo-activerecord.rb
|
181
|
+
- lib/rp_clustering-rgeo-activerecord/active_record_base_spatial_expressions.rb
|
181
182
|
- lib/rp_clustering-rgeo-activerecord/arel_attribute_spatial_expressions.rb
|
182
183
|
- lib/rp_clustering-rgeo-activerecord/arel_table_spatial_expressions.rb
|
183
184
|
- lib/rp_clustering-rgeo-activerecord/version.rb
|
184
185
|
- rp_clustering-rgeo-activerecord.gemspec
|
185
186
|
- test/database.yml
|
187
|
+
- test/test_clustering.rb
|
186
188
|
- test/test_unit.rb
|
187
189
|
homepage: https://github.com/robertpyke/rp_clustering-rgeo-activerecord
|
188
190
|
licenses:
|
@@ -210,4 +212,6 @@ specification_version: 4
|
|
210
212
|
summary: RGeo PostGIS extension to provide clustering functionality
|
211
213
|
test_files:
|
212
214
|
- test/database.yml
|
215
|
+
- test/test_clustering.rb
|
213
216
|
- test/test_unit.rb
|
217
|
+
has_rdoc:
|