inat-get 0.8.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +16 -0
- data/Rakefile +4 -0
- data/bin/inat-get +59 -0
- data/docs/logo.png +0 -0
- data/inat-get.gemspec +33 -0
- data/lib/extra/enum.rb +184 -0
- data/lib/extra/period.rb +252 -0
- data/lib/extra/uuid.rb +90 -0
- data/lib/inat/app/application.rb +50 -0
- data/lib/inat/app/config/messagelevel.rb +22 -0
- data/lib/inat/app/config/shiftage.rb +24 -0
- data/lib/inat/app/config/updatemode.rb +20 -0
- data/lib/inat/app/config.rb +296 -0
- data/lib/inat/app/globals.rb +80 -0
- data/lib/inat/app/info.rb +21 -0
- data/lib/inat/app/logging.rb +35 -0
- data/lib/inat/app/preamble.rb +27 -0
- data/lib/inat/app/status.rb +74 -0
- data/lib/inat/app/task/context.rb +47 -0
- data/lib/inat/app/task/dsl.rb +24 -0
- data/lib/inat/app/task.rb +75 -0
- data/lib/inat/data/api.rb +218 -0
- data/lib/inat/data/cache.rb +9 -0
- data/lib/inat/data/db.rb +87 -0
- data/lib/inat/data/ddl.rb +18 -0
- data/lib/inat/data/entity/comment.rb +29 -0
- data/lib/inat/data/entity/flag.rb +22 -0
- data/lib/inat/data/entity/identification.rb +45 -0
- data/lib/inat/data/entity/observation.rb +172 -0
- data/lib/inat/data/entity/observationphoto.rb +25 -0
- data/lib/inat/data/entity/observationsound.rb +26 -0
- data/lib/inat/data/entity/photo.rb +31 -0
- data/lib/inat/data/entity/place.rb +57 -0
- data/lib/inat/data/entity/project.rb +94 -0
- data/lib/inat/data/entity/projectadmin.rb +21 -0
- data/lib/inat/data/entity/projectobservationrule.rb +50 -0
- data/lib/inat/data/entity/request.rb +58 -0
- data/lib/inat/data/entity/sound.rb +27 -0
- data/lib/inat/data/entity/taxon.rb +94 -0
- data/lib/inat/data/entity/user.rb +67 -0
- data/lib/inat/data/entity/vote.rb +22 -0
- data/lib/inat/data/entity.rb +291 -0
- data/lib/inat/data/enums/conservationstatus.rb +30 -0
- data/lib/inat/data/enums/geoprivacy.rb +14 -0
- data/lib/inat/data/enums/iconictaxa.rb +23 -0
- data/lib/inat/data/enums/identificationcategory.rb +13 -0
- data/lib/inat/data/enums/licensecode.rb +16 -0
- data/lib/inat/data/enums/projectadminrole.rb +11 -0
- data/lib/inat/data/enums/projecttype.rb +37 -0
- data/lib/inat/data/enums/qualitygrade.rb +12 -0
- data/lib/inat/data/enums/rank.rb +60 -0
- data/lib/inat/data/model.rb +551 -0
- data/lib/inat/data/query.rb +1145 -0
- data/lib/inat/data/sets/dataset.rb +104 -0
- data/lib/inat/data/sets/list.rb +190 -0
- data/lib/inat/data/sets/listers.rb +15 -0
- data/lib/inat/data/sets/wrappers.rb +137 -0
- data/lib/inat/data/types/extras.rb +88 -0
- data/lib/inat/data/types/location.rb +89 -0
- data/lib/inat/data/types/std.rb +293 -0
- data/lib/inat/report/table.rb +135 -0
- data/lib/inat/utils/deep.rb +30 -0
- metadata +137 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'listers'
|
4
|
+
require_relative 'list'
|
5
|
+
|
6
|
+
class DataSet
|
7
|
+
|
8
|
+
attr_reader :time
|
9
|
+
attr_reader :object
|
10
|
+
attr_reader :observations
|
11
|
+
|
12
|
+
def initialize object, observations, time: Time::new
|
13
|
+
@object = object
|
14
|
+
@time = time
|
15
|
+
@observations = observations.sort_by { |o| o.sort_key }.uniq
|
16
|
+
@sorted = true
|
17
|
+
@by_id = {}
|
18
|
+
@observations.each do |o|
|
19
|
+
@by_id[o.id] = o
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.zero
|
24
|
+
new nil, []
|
25
|
+
end
|
26
|
+
|
27
|
+
include Enumerable
|
28
|
+
|
29
|
+
def each
|
30
|
+
if block_given?
|
31
|
+
@observations.sort_by! { |o| o.sort_key } unless @sorted
|
32
|
+
@sorted = true
|
33
|
+
@observations.each do |o|
|
34
|
+
yield o
|
35
|
+
end
|
36
|
+
else
|
37
|
+
to_enum :each
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def reverse_each
|
42
|
+
if block_given?
|
43
|
+
@observations.sort_by! { |o| o.sort_key } unless @sorted
|
44
|
+
@sorted = true
|
45
|
+
@observations.reverse_each do |o|
|
46
|
+
yield o
|
47
|
+
end
|
48
|
+
else
|
49
|
+
to_enum :reverse_each
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def count
|
54
|
+
@observations.size
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_list lister = Listers::SPECIES, sorter: nil
|
58
|
+
List::new self, lister, sorter: sorter, time: @time
|
59
|
+
end
|
60
|
+
|
61
|
+
def include? observation
|
62
|
+
@by_id.has_key? observation.id
|
63
|
+
end
|
64
|
+
|
65
|
+
def empty?
|
66
|
+
@observations.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
def where &block
|
70
|
+
raise ArgumentError, "Block must be provided!", caller unless block_given?
|
71
|
+
DataSet::new nil, @observations.select(&block), time: @time
|
72
|
+
end
|
73
|
+
|
74
|
+
alias :=== :include?
|
75
|
+
|
76
|
+
def << observation
|
77
|
+
raise TypeError, "Argument must be an Observation (#{ observation.inspect })!" unless Observation === observation
|
78
|
+
if !self.include?(observation)
|
79
|
+
@observations << observation
|
80
|
+
@by_id[observation.id] = observation
|
81
|
+
@sorted = false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def | other
|
86
|
+
obj = @object == other.object ? @object : nil
|
87
|
+
DataSet::new obj, @observations + other.observations, time: Time::new
|
88
|
+
end
|
89
|
+
|
90
|
+
def & other
|
91
|
+
obj = @object == other.object ? @object : nil
|
92
|
+
DataSet::new obj, @observations.select { |o| other.include?(o) }, time: Time::new
|
93
|
+
end
|
94
|
+
|
95
|
+
def - other
|
96
|
+
obj = @object == other.object ? @object : nil
|
97
|
+
DataSet::new obj, @observations.select { |o| !other.include?(o) }, time: Time::new
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_a
|
101
|
+
@observations.dup
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
autoload :DataSet, 'inat/data/sets/dataset'
|
4
|
+
|
5
|
+
class List
|
6
|
+
|
7
|
+
attr_reader :lister, :sorter
|
8
|
+
|
9
|
+
DEFAULT_SORTER = lambda { |ds| ds.object.respond_to?(:sort_key) ? ds.object.sort_key : ds.object }
|
10
|
+
|
11
|
+
def initialize source, lister, sorter: nil, time: Time::new
|
12
|
+
@lister = lister
|
13
|
+
@sorter = sorter || DEFAULT_SORTER
|
14
|
+
@time = time
|
15
|
+
@data = {}
|
16
|
+
source.each do |observation|
|
17
|
+
key = @lister.call observation
|
18
|
+
if key != nil
|
19
|
+
@data[key] ||= DataSet::new key, [], time: @time
|
20
|
+
@data[key] << observation
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.zero lister = Listers::SPECIES
|
26
|
+
new [], lister
|
27
|
+
end
|
28
|
+
|
29
|
+
include Enumerable
|
30
|
+
|
31
|
+
def [] key
|
32
|
+
@data[key]
|
33
|
+
end
|
34
|
+
|
35
|
+
def each
|
36
|
+
if block_given?
|
37
|
+
@data.values.sort_by { |ds| @sorter.call(ds) }.each do |ds|
|
38
|
+
yield ds
|
39
|
+
end
|
40
|
+
else
|
41
|
+
to_enum :each
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_dataset
|
46
|
+
observations = []
|
47
|
+
@data.values.each do |ds|
|
48
|
+
observations += ds.observations
|
49
|
+
end
|
50
|
+
DataSet::new nil, observations, time: @time
|
51
|
+
end
|
52
|
+
|
53
|
+
def include? key
|
54
|
+
@data.has_key? key
|
55
|
+
end
|
56
|
+
|
57
|
+
def empty?
|
58
|
+
@data.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
def count
|
62
|
+
@data.size
|
63
|
+
end
|
64
|
+
|
65
|
+
def observation_count
|
66
|
+
@data.values.map { |ds| ds.count }.sum
|
67
|
+
end
|
68
|
+
|
69
|
+
def where &block
|
70
|
+
raise ArgumentError, "Block must be provided!", caller unless block_given?
|
71
|
+
result = List::new [], @lister, sorter: @sorter, time: @time
|
72
|
+
self.each do |ds|
|
73
|
+
result << ds if yield(ds)
|
74
|
+
end
|
75
|
+
result
|
76
|
+
end
|
77
|
+
|
78
|
+
def << some
|
79
|
+
case some
|
80
|
+
when Observation
|
81
|
+
key = @lister.call some
|
82
|
+
if key != nil
|
83
|
+
@data[key] ||= DataSet::new key, [], time: @time
|
84
|
+
@data[key] << some
|
85
|
+
end
|
86
|
+
when DataSet
|
87
|
+
if !some.empty?
|
88
|
+
valid = some.object != nil && some.all? { |o| @lister.call(o) == some.object }
|
89
|
+
if valid
|
90
|
+
if @data[some.object]
|
91
|
+
@data[some.object] = @data[some.object] | some
|
92
|
+
else
|
93
|
+
@data[some.object] = some
|
94
|
+
end
|
95
|
+
else
|
96
|
+
some.each do |o|
|
97
|
+
self << o
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
when Array
|
102
|
+
some.each do |o|
|
103
|
+
self << o
|
104
|
+
end
|
105
|
+
else
|
106
|
+
raise TypeError, "Can not add this object: #{ some.inspect }!", caller
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
alias :=== :include?
|
111
|
+
|
112
|
+
def cover? other
|
113
|
+
other.any? { |ds| self.include?(ds.object) }
|
114
|
+
end
|
115
|
+
|
116
|
+
include Comparable
|
117
|
+
|
118
|
+
def <=> other
|
119
|
+
return nil unless List === other
|
120
|
+
if self.cover?(other)
|
121
|
+
if other.cover?(self)
|
122
|
+
return 0
|
123
|
+
else
|
124
|
+
return 1
|
125
|
+
end
|
126
|
+
else
|
127
|
+
if other.cover?(self)
|
128
|
+
return -1
|
129
|
+
else
|
130
|
+
return nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def apply! *data
|
136
|
+
@lister, @sorter, @time, @data = data
|
137
|
+
self
|
138
|
+
end
|
139
|
+
|
140
|
+
def clone
|
141
|
+
List::zero.apply! @lister, @sorter, @time, @data.dup
|
142
|
+
end
|
143
|
+
|
144
|
+
def add! other
|
145
|
+
other.each do |ds|
|
146
|
+
self << ds
|
147
|
+
end
|
148
|
+
self
|
149
|
+
end
|
150
|
+
|
151
|
+
def mul! other
|
152
|
+
@data.delete_if { | key, _ | !other.include?(key) }
|
153
|
+
other.each do |ds|
|
154
|
+
if self.include?(ds.object)
|
155
|
+
self << ds
|
156
|
+
end
|
157
|
+
end
|
158
|
+
self
|
159
|
+
end
|
160
|
+
|
161
|
+
def sub! other
|
162
|
+
@data.delete_if { | key, _ | other.include?(key) }
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
def add other
|
167
|
+
clone.add! other
|
168
|
+
end
|
169
|
+
|
170
|
+
def mul other
|
171
|
+
# А вот здесь будет эффективней старое решение
|
172
|
+
result = List::new [], @lister, sorter: @sorter, time: @time
|
173
|
+
@data.each do |key, ds|
|
174
|
+
if other.include?(key)
|
175
|
+
summa = ds | other[key]
|
176
|
+
self << summa
|
177
|
+
end
|
178
|
+
end
|
179
|
+
result
|
180
|
+
end
|
181
|
+
|
182
|
+
def sub other
|
183
|
+
clone.sub! other
|
184
|
+
end
|
185
|
+
|
186
|
+
alias :+ :add
|
187
|
+
alias :* :mul
|
188
|
+
alias :- :sub
|
189
|
+
|
190
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'wrappers'
|
4
|
+
|
5
|
+
module Listers
|
6
|
+
|
7
|
+
SPECIES = lambda { |o| o.normalized_taxon(Rank::COMPLEX .. Rank::HYBRID) }
|
8
|
+
GENUS = lambda { |o| o.normalized_taxon(Rank::GENUS) }
|
9
|
+
FAMILY = lambda { |o| o.normalized_taxon(Rank::FAMILY) }
|
10
|
+
YEAR = lambda { |o| Year[o.observed_on] }
|
11
|
+
MONTH = lambda { |o| Month[o.observed_on] }
|
12
|
+
DAY = lambda { |o| Day[o.observed_on] }
|
13
|
+
USER = lambda { |o| o.user }
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
class Year
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
private :new
|
10
|
+
|
11
|
+
def [] source
|
12
|
+
return nil if source == nil
|
13
|
+
return source if Year === source
|
14
|
+
year = case source
|
15
|
+
when Date, Time
|
16
|
+
source.year
|
17
|
+
when String
|
18
|
+
date = Date::parse source
|
19
|
+
date.year
|
20
|
+
when Integer
|
21
|
+
source
|
22
|
+
else
|
23
|
+
raise TypeError, "Invalid year key: #{ source.inspect }!", caller
|
24
|
+
end
|
25
|
+
@years ||= {}
|
26
|
+
@years[year] ||= new year
|
27
|
+
@years[year]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :year
|
33
|
+
|
34
|
+
def initialize year
|
35
|
+
@year = year
|
36
|
+
end
|
37
|
+
|
38
|
+
include Comparable
|
39
|
+
|
40
|
+
def <=> other
|
41
|
+
return nil unless Year === other
|
42
|
+
@year <=> other.year
|
43
|
+
end
|
44
|
+
|
45
|
+
def - num
|
46
|
+
self.class[@year - num]
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"<i class=\"glyphicon glyphicon-calendar\"></i> #{ @year }"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
class Month
|
56
|
+
|
57
|
+
class << self
|
58
|
+
|
59
|
+
private :new
|
60
|
+
|
61
|
+
def [] source
|
62
|
+
return nil if source == nil
|
63
|
+
return source if Month === source
|
64
|
+
month = case source
|
65
|
+
when Date, Time
|
66
|
+
source.month
|
67
|
+
when String
|
68
|
+
date = Date::parse source
|
69
|
+
date.month
|
70
|
+
when Integer
|
71
|
+
source
|
72
|
+
else
|
73
|
+
raise TypeError, "Invalid month key: #{ source.inspect }!", caller
|
74
|
+
end
|
75
|
+
@months ||= {}
|
76
|
+
@months[month] ||= new month
|
77
|
+
@months[month]
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
attr_reader :month
|
83
|
+
|
84
|
+
def initialize month
|
85
|
+
@month = month
|
86
|
+
end
|
87
|
+
|
88
|
+
include Comparable
|
89
|
+
|
90
|
+
def <=> other
|
91
|
+
return nil unless Month === other
|
92
|
+
@month <=> other.month
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
class Day
|
98
|
+
|
99
|
+
class << self
|
100
|
+
|
101
|
+
attr_reader :day
|
102
|
+
|
103
|
+
def [] source
|
104
|
+
return nil if source == nil
|
105
|
+
return source if Day === source
|
106
|
+
day = case source
|
107
|
+
when Date, Time
|
108
|
+
source.day
|
109
|
+
when String
|
110
|
+
date = Date::parse source
|
111
|
+
date.day
|
112
|
+
when Integer
|
113
|
+
source
|
114
|
+
else
|
115
|
+
raise TypeError, "Invalid day key: #{ source.inspect }!", caller
|
116
|
+
end
|
117
|
+
@days ||= {}
|
118
|
+
@days[day] ||= new day
|
119
|
+
@days[day]
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
attr_reader :day
|
125
|
+
|
126
|
+
def initialize day
|
127
|
+
@day = day
|
128
|
+
end
|
129
|
+
|
130
|
+
include Comparable
|
131
|
+
|
132
|
+
def <=> other
|
133
|
+
return nil unless Day === other
|
134
|
+
@day <=> other.day
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'extra/enum'
|
4
|
+
require 'extra/uuid'
|
5
|
+
|
6
|
+
class Enum
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
pre_verbose = $VERBOSE
|
11
|
+
$VERBOSE = nil
|
12
|
+
|
13
|
+
alias :old_parse :parse
|
14
|
+
|
15
|
+
def parse src
|
16
|
+
return nil if src == nil
|
17
|
+
return src if self === src
|
18
|
+
case src
|
19
|
+
when Integer, Symbol
|
20
|
+
self[src]
|
21
|
+
when String
|
22
|
+
self.old_parse src, case_sensitive: false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
$VERBOSE = pre_verbose
|
27
|
+
|
28
|
+
def ddl
|
29
|
+
if any? { |v| Integer === v.data }
|
30
|
+
{
|
31
|
+
name: :TEXT,
|
32
|
+
data: :INTEGER
|
33
|
+
}
|
34
|
+
else
|
35
|
+
:TEXT
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def from_db src
|
40
|
+
return nil if src == nil
|
41
|
+
return self[src.intern] if String === src || Symbol === src
|
42
|
+
name = src[:name]&.intern
|
43
|
+
return nil if name == nil
|
44
|
+
self[name]
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_db
|
50
|
+
if self.class.any? { |v| Integer === v.data }
|
51
|
+
{
|
52
|
+
name: name.to_s,
|
53
|
+
data: data
|
54
|
+
}
|
55
|
+
else
|
56
|
+
name.to_s
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_query
|
61
|
+
to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
class UUID
|
67
|
+
|
68
|
+
class << self
|
69
|
+
|
70
|
+
def ddl
|
71
|
+
:TEXT
|
72
|
+
end
|
73
|
+
|
74
|
+
def from_db src
|
75
|
+
parse src
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_db
|
81
|
+
to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_query
|
85
|
+
to_s
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Location
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def parse src
|
8
|
+
case src
|
9
|
+
when nil
|
10
|
+
nil
|
11
|
+
when String
|
12
|
+
parse src.split(',').map(&:to_f)
|
13
|
+
when Array
|
14
|
+
new(src[0], src[1]).freeze
|
15
|
+
when Hash
|
16
|
+
latitude = src[:latitude] || src['latitude']
|
17
|
+
longitude = src[:longitude] || src['longitude']
|
18
|
+
return nil if latitude == nil && longitude == nil
|
19
|
+
new(latitude, longitude).freeze
|
20
|
+
else
|
21
|
+
raise ArgumentError, "Source must be a String or Array or Hash!", caller
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def from_db src
|
26
|
+
new(src[:latitude] || src['latitude'], src[:longitude] || src['longitude']).freeze
|
27
|
+
end
|
28
|
+
|
29
|
+
def ddl
|
30
|
+
{
|
31
|
+
latitude: :REAL,
|
32
|
+
longitude: :REAL
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :latitude, :longitude
|
39
|
+
|
40
|
+
def initialize latitude, longitude
|
41
|
+
raise ArgumentError, "Latitude must be a Numeric!", caller unless Numeric === latitude || latitude == nil
|
42
|
+
raise ArgumentError, "Longitude must be a Numeric!", caller unless Numeric === longitude || latitude == nil
|
43
|
+
@latitude = latitude
|
44
|
+
@longitude = longitude
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_db
|
48
|
+
{
|
49
|
+
latitude: latitude,
|
50
|
+
longitude: longitude
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class Radius
|
57
|
+
|
58
|
+
attr_reader :latitude, :longitude, :radius
|
59
|
+
|
60
|
+
def initialize latitude, longitude, radius
|
61
|
+
raise ArgumentError, "Latitude must be a Numeric!", caller unless Numeric === latitude
|
62
|
+
raise ArgumentError, "Longitude must be a Numeric!", caller unless Numeric === longitude
|
63
|
+
raise ArgumentError, "Radius must be a Numeric!", caller unless Numeric === radius || radius == nil
|
64
|
+
@latitude, @longitude, @radius = latitude, longitude, radius
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
class Sector
|
70
|
+
|
71
|
+
attr_reader :north, :east, :south, :west
|
72
|
+
|
73
|
+
def initialize north, east, south, west
|
74
|
+
raise ArgumentError, "North must be a Numeric!", caller unless Numeric === north
|
75
|
+
raise ArgumentError, "East must be a Numeric!", caller unless Numeric === east
|
76
|
+
raise ArgumentError, "South must be a Numeric!", caller unless Numeric === south
|
77
|
+
raise ArgumentError, "West must be a Numeric!", caller unless Numeric === west
|
78
|
+
@north, @east, @south, @west = north, east, south, west
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
def radius latitude, longitude, radius
|
84
|
+
Radius::new latitude, longitude, radius
|
85
|
+
end
|
86
|
+
|
87
|
+
def sector north, east, south, west
|
88
|
+
Sector::new north, east, south, west
|
89
|
+
end
|