geohash36 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS.md +29 -0
- data/CHANGELOG.md +0 -0
- data/COPYING.md +12 -0
- data/Gemfile +107 -0
- data/LICENSE.md +14 -0
- data/MAINTAINERS.md +40 -0
- data/README.md +78 -0
- data/Rakefile +52 -0
- data/bin/geohash36 +45 -0
- data/geohash36.gemspec +136 -0
- data/lib/demo.rb +25 -0
- data/lib/geohash36/interface/rake/cucumber.rb +36 -0
- data/lib/geohash36/interface/rake/default.rb +28 -0
- data/lib/geohash36/interface/rake/documentation.rb +46 -0
- data/lib/geohash36/interface/rake/guard.rb +13 -0
- data/lib/geohash36/interface/rake/helpers.rb +27 -0
- data/lib/geohash36/interface/rake/library.rb +126 -0
- data/lib/geohash36/interface/rake/metric.rb +13 -0
- data/lib/geohash36/interface/rake/rspec.rb +8 -0
- data/lib/geohash36/interface/thor/info.thor +292 -0
- data/lib/geohash36/interface/thor/mixin/config_choice.rb +27 -0
- data/lib/geohash36/interface/thor/mixin/configuration.rb +28 -0
- data/lib/geohash36/interface/thor/mixin/default.rb +30 -0
- data/lib/geohash36/interface/thor/mixin/default_config.rb +31 -0
- data/lib/geohash36/interface/thor/mixin/guess.rb +57 -0
- data/lib/geohash36/interface/thor/mixin/logger.rb +43 -0
- data/lib/geohash36/interface/thor/mixin/shell.rb +225 -0
- data/lib/geohash36/interface/thor/version.thor +33 -0
- data/lib/geohash36/library/interval.rb +130 -0
- data/lib/geohash36/version.rb +13 -0
- data/lib/geohash36.rb +164 -0
- data/spec/geohash36_spec.rb +52 -0
- data/spec/library/interval_spec.rb +55 -0
- data/spec/spec_helper.rb +55 -0
- metadata +281 -0
@@ -0,0 +1,225 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
# System includes
|
5
|
+
require 'andand'
|
6
|
+
require 'ptools'
|
7
|
+
require 'tempfile'
|
8
|
+
|
9
|
+
# Custom includes
|
10
|
+
require File.expand_path( File.dirname( __FILE__ ) + '/guess' )
|
11
|
+
|
12
|
+
|
13
|
+
# @module module Mixin
|
14
|
+
# @brief Mixin module contains various functions to be used in other components
|
15
|
+
module Mixin
|
16
|
+
|
17
|
+
# @module Shell Module
|
18
|
+
# @brief Module wrapper around shell commands
|
19
|
+
module Shell
|
20
|
+
|
21
|
+
include ::Mixin::Guess
|
22
|
+
|
23
|
+
# @fn def initialize *args {{{
|
24
|
+
# @brief Default constructor
|
25
|
+
#
|
26
|
+
# @param [Array] args Argument array
|
27
|
+
def initialize *args
|
28
|
+
super
|
29
|
+
end # }}}
|
30
|
+
|
31
|
+
## Actions
|
32
|
+
|
33
|
+
# @fn def run command {{{
|
34
|
+
# @brief Run the given command gracefully (without throwing exceptions)
|
35
|
+
#
|
36
|
+
# @param [String] command The command to run
|
37
|
+
# @param [RegExp] regexp Optional regular expression used for matching output
|
38
|
+
#
|
39
|
+
# @return [String] Returns empty string if command not found or other problem,
|
40
|
+
# otherwise result of command.
|
41
|
+
def run command, regexp = nil
|
42
|
+
|
43
|
+
result = ''
|
44
|
+
|
45
|
+
begin
|
46
|
+
if( regexp.nil? )
|
47
|
+
result = execute( command ).strip
|
48
|
+
else
|
49
|
+
raise ArgumentError, 'Regular Expression input needs to be of type Regexp' unless( regexp.is_a?( Regexp ) )
|
50
|
+
|
51
|
+
result = execute( command ).andand.send( :match, regexp ).andand.send( :to_s ).strip
|
52
|
+
end
|
53
|
+
|
54
|
+
rescue Exception => e
|
55
|
+
say '(EE) ' + e.message, :red
|
56
|
+
result = ''
|
57
|
+
end
|
58
|
+
|
59
|
+
return result
|
60
|
+
end # }}}
|
61
|
+
|
62
|
+
# @fn def execute command {{{
|
63
|
+
# @brief Execute single shell command
|
64
|
+
#
|
65
|
+
# @param [String] command Shell command string with arguments
|
66
|
+
#
|
67
|
+
# @return [String] Returns result of command
|
68
|
+
#
|
69
|
+
# @throws [ArgumentError] Throws exception if command not found
|
70
|
+
def execute command
|
71
|
+
|
72
|
+
exists = which( command )
|
73
|
+
raise ArgumentError, "Command not found" unless( exists )
|
74
|
+
result = `#{command}`
|
75
|
+
|
76
|
+
return result
|
77
|
+
end # }}}
|
78
|
+
|
79
|
+
# @fn def which command {{{
|
80
|
+
# @brief Checks if command is available
|
81
|
+
#
|
82
|
+
# @param [String] command Shell command string with or without arguments.
|
83
|
+
# If arguments are given, they are split on first
|
84
|
+
# whitespace and discarded
|
85
|
+
#
|
86
|
+
# @return [Boolean] Returns boolean true if command is available, false if not
|
87
|
+
#
|
88
|
+
#
|
89
|
+
# @info Crude alternative: `#{command} 2>/dev/null`.strip.empty?
|
90
|
+
def which command
|
91
|
+
result = false
|
92
|
+
|
93
|
+
begin
|
94
|
+
partial = command.to_s
|
95
|
+
partial = partial.split(' ').first.to_s if( partial =~ %r{ }i )
|
96
|
+
path = File.which( partial )
|
97
|
+
|
98
|
+
result = true unless( path.nil? )
|
99
|
+
rescue Exception => e
|
100
|
+
say "(EE) " + e.message, :red
|
101
|
+
result = false
|
102
|
+
end
|
103
|
+
|
104
|
+
return result
|
105
|
+
end # }}}
|
106
|
+
|
107
|
+
# @fn def version command {{{
|
108
|
+
# @brief Prints the version of given command, if command exists
|
109
|
+
#
|
110
|
+
# @param [String] command Command string to probe version for, e.g. ruby
|
111
|
+
#
|
112
|
+
# @return [String] Returns version string or empty if command not found / error
|
113
|
+
def version command
|
114
|
+
|
115
|
+
result = ""
|
116
|
+
|
117
|
+
begin
|
118
|
+
|
119
|
+
# Sanity
|
120
|
+
raise ArgumentError, "Command not found" unless( which( command ) )
|
121
|
+
|
122
|
+
# Get usage help screen for command
|
123
|
+
help = usage( command )
|
124
|
+
|
125
|
+
# Get version flag from help screen
|
126
|
+
flag = version_flag( help )
|
127
|
+
|
128
|
+
return result if( flag.empty? ) # some stupid commands don't follow standard rules, e.g. bundle
|
129
|
+
|
130
|
+
# Get actual version string
|
131
|
+
banner = run( command + " " + flag )
|
132
|
+
|
133
|
+
# Guess way to extract and extract semver
|
134
|
+
result = guess_version( banner.to_s )
|
135
|
+
|
136
|
+
rescue Exception => e
|
137
|
+
say "(EE) " + e.message, :red
|
138
|
+
result = ""
|
139
|
+
end
|
140
|
+
|
141
|
+
return result
|
142
|
+
end # }}}
|
143
|
+
|
144
|
+
|
145
|
+
alias_method :exists?, :which
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
# @fn def usage command {{{
|
150
|
+
# @brief Extracts help/usage screen from command
|
151
|
+
#
|
152
|
+
# @param [String] command Command string
|
153
|
+
#
|
154
|
+
# @return [String Result screen output string
|
155
|
+
def usage command
|
156
|
+
|
157
|
+
result = ""
|
158
|
+
|
159
|
+
begin
|
160
|
+
|
161
|
+
# Some commands misbehave when using wrong arguments
|
162
|
+
# e.g. java
|
163
|
+
help = []
|
164
|
+
|
165
|
+
# Collect all possible outputs
|
166
|
+
%w(--help -h -help).each do |argument|
|
167
|
+
tempfile = Tempfile.new( "geohash36-" )
|
168
|
+
`#{command} #{argument} > #{tempfile.path.to_s} 2>&1`
|
169
|
+
help << File.read( tempfile.path )
|
170
|
+
end
|
171
|
+
|
172
|
+
# Prune to relevent one
|
173
|
+
help.each do |h|
|
174
|
+
|
175
|
+
# Sanity
|
176
|
+
next if( h.strip.empty? )
|
177
|
+
next if( h.split( "\n" ).length < 4 )
|
178
|
+
next unless( h.match( /unknown/i ).to_s.empty? ) # does it contain unknown? (argument)
|
179
|
+
next unless( h.match( /unrecognized/i ).to_s.empty? ) # does it contain unrecognized? (argument)
|
180
|
+
next unless( h.match( /error/i ).to_s.empty? ) # does it contain error
|
181
|
+
next unless( h.match( /does not exist/i ).to_s.empty? ) # does not exist error
|
182
|
+
next unless( result.empty? )
|
183
|
+
|
184
|
+
result = h
|
185
|
+
|
186
|
+
end # of help.each
|
187
|
+
|
188
|
+
|
189
|
+
rescue Exception => e
|
190
|
+
say "(EE) " + e.message, :red
|
191
|
+
result = ""
|
192
|
+
end
|
193
|
+
|
194
|
+
return result
|
195
|
+
end # }}}
|
196
|
+
|
197
|
+
# @fn def version_flag usage {{{
|
198
|
+
# @brief Extracts version flag from usage help screen of command
|
199
|
+
#
|
200
|
+
# @param [String] usage Usage help screen output (string), e.g. ruby --help
|
201
|
+
#
|
202
|
+
# @return [String] Returns the string which according to help screen will print version
|
203
|
+
# e.g. -version for java
|
204
|
+
def version_flag usage
|
205
|
+
|
206
|
+
result = ""
|
207
|
+
|
208
|
+
begin
|
209
|
+
result = usage.match( /--version/i ).to_s
|
210
|
+
result = usage.match( /-version/i ).to_s if( result.empty? )
|
211
|
+
rescue Exception => e
|
212
|
+
say "(EE) " + e.message, :red
|
213
|
+
result = ""
|
214
|
+
end
|
215
|
+
|
216
|
+
return result
|
217
|
+
end # }}}
|
218
|
+
|
219
|
+
end # of Module Shell
|
220
|
+
|
221
|
+
end # of module Mixin
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
# vim:ts=2:tw=100:wm=100:syntax=ruby
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
# @class Server command class
|
5
|
+
# @brief Implements the server command
|
6
|
+
class Version < Thor
|
7
|
+
|
8
|
+
default_task :show
|
9
|
+
|
10
|
+
## API
|
11
|
+
|
12
|
+
# @fn def show {{{
|
13
|
+
# @brief Show version string of app
|
14
|
+
desc 'show', 'Show version of this app'
|
15
|
+
def show
|
16
|
+
version = Geohash36::VERSION
|
17
|
+
|
18
|
+
puts version
|
19
|
+
end # }}}
|
20
|
+
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
no_tasks do
|
25
|
+
|
26
|
+
## Actions
|
27
|
+
|
28
|
+
end # of no_tasks do
|
29
|
+
|
30
|
+
end # of Class Version
|
31
|
+
|
32
|
+
|
33
|
+
# vim:ts=2:tw=100:wm=100:syntax=ruby
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# Designed to provide additional functionality for arrays
|
2
|
+
# to handle geographical intervals.
|
3
|
+
class Geohash36::Interval < Array
|
4
|
+
|
5
|
+
# @param array [Array<Fixnum, Float>] array with length 2.
|
6
|
+
# @param options [Hash]
|
7
|
+
# options affects borders in interval. By default, all borders are included.
|
8
|
+
# If you want to exclude left border, pass: `include_left: false`
|
9
|
+
# If you want to exclude right border, pass: `include_right: false`
|
10
|
+
# @example With default args
|
11
|
+
# Geohash36::Interval.new
|
12
|
+
# @example With array and options
|
13
|
+
# Geohash36::Interval.new([0, 6], include_right: false)
|
14
|
+
def initialize array = [0, 0], options = {}
|
15
|
+
array.try(:compact!)
|
16
|
+
validate_array(array)
|
17
|
+
array.each{|element| self.push element}
|
18
|
+
defaults = {include_right: true, include_left: true }
|
19
|
+
@opts = defaults.merge options
|
20
|
+
end
|
21
|
+
|
22
|
+
# Replace old interval options with new one
|
23
|
+
#
|
24
|
+
# @param options [Hash] new options for interval
|
25
|
+
def configure options = {}
|
26
|
+
@opts.merge! options
|
27
|
+
end
|
28
|
+
|
29
|
+
# Check if `number` between left and right border.
|
30
|
+
#
|
31
|
+
# @param number [Float, Fixmum] number to check
|
32
|
+
def include? number
|
33
|
+
for_left_border = (@opts[:include_left] == true) ? first <= number : first < number
|
34
|
+
for_right_number = (@opts[:include_right] == true) ? number <= last : number < last
|
35
|
+
for_left_border && for_right_number
|
36
|
+
end
|
37
|
+
|
38
|
+
# Split interval into 6 parts
|
39
|
+
# @return array of 6 intervals
|
40
|
+
def split
|
41
|
+
split3.each_with_object([]){|interval, result| result.concat interval.split2}
|
42
|
+
end
|
43
|
+
|
44
|
+
# Change values of interval
|
45
|
+
# @example
|
46
|
+
# my_interval = Geohash36::Interval.new([0, 1])
|
47
|
+
# my_interval.update [0,6]
|
48
|
+
# my_interval # => [0, 6]
|
49
|
+
def update array
|
50
|
+
array.try(:compact!)
|
51
|
+
validate_array(array)
|
52
|
+
self.clear
|
53
|
+
array.each{|element| self.push element}
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return string representation of object
|
57
|
+
def inspect
|
58
|
+
left_br = @opts[:include_left] ? "[" : "("
|
59
|
+
right_br = @opts[:include_right] ? "]" : ")"
|
60
|
+
"#{left_br}#{first}, #{last}#{right_br}"
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return string representation of object
|
64
|
+
def to_s
|
65
|
+
inspect
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return middle of the interval
|
69
|
+
# @example
|
70
|
+
# Geohash36::Interval.new([0, 6]).middle
|
71
|
+
# # => 3.0
|
72
|
+
def middle
|
73
|
+
(first + last)/2.0
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return third part of interval (only positive values)
|
77
|
+
# @example
|
78
|
+
# Geohash36::Interval.new([-2, 4]).third
|
79
|
+
# # => 2.0
|
80
|
+
def third
|
81
|
+
((last - first)/3.0).abs
|
82
|
+
end
|
83
|
+
|
84
|
+
# Split interval into 2 parts
|
85
|
+
# @return array of 2 intervals
|
86
|
+
def split2
|
87
|
+
[[first, middle], [middle, last]].map{|interval| Geohash36::Interval.new interval}
|
88
|
+
end
|
89
|
+
|
90
|
+
# Split interval into 3 parts
|
91
|
+
# @return array of 3 intervals
|
92
|
+
def split3
|
93
|
+
result = [[self.first, self.first+third], [self.first+third, self.first+2*third], [self.first+2*third, self.last]]
|
94
|
+
result.map{|array| Geohash36::Interval.new array}
|
95
|
+
end
|
96
|
+
|
97
|
+
# Convert array of arrays to array of `Geohash36::Interval`s
|
98
|
+
#
|
99
|
+
# @param array [Array] array to convert
|
100
|
+
# @param options [Hash] options for `Geohash36::Interval` object
|
101
|
+
# but it works a little different. It is not affect first\last elements in array.
|
102
|
+
# For example, if we do not want to include left border, only first element
|
103
|
+
# will include left border because this class designed to handle geographical coordinates.
|
104
|
+
# It is not designed for abstract intervals.
|
105
|
+
# @example
|
106
|
+
# # for example, we don't want to include left border of interval,
|
107
|
+
# # so we will have array like `[[0, 0], [0, 0], [0, 0]] ->> [[0, 0], (0, 0], (0, 0]]`
|
108
|
+
#
|
109
|
+
# my_array = [[0, 2], [2, 6], [6, 10]]
|
110
|
+
# Geohash36.convert_array(my_array, include_left: false)
|
111
|
+
# # => [[0, 2], (2, 6], (6, 10]]
|
112
|
+
def self.convert_array array, options = {}
|
113
|
+
intervals = array.map{|interval| Geohash36::Interval.new interval, options }
|
114
|
+
intervals.first.configure(include_left: true) unless options[:include_left]
|
115
|
+
intervals.last.configure(include_right: true) unless options[:include_right]
|
116
|
+
intervals
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
# Check if array has valid values
|
121
|
+
def validate_array(array)
|
122
|
+
unless array.length == 2 && ( array.try(:first) <= array.try(:last) )
|
123
|
+
raise ArgumentError, "Not valid array for geohash interval"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end # of class Geohash36::Interval
|
128
|
+
|
129
|
+
|
130
|
+
# vim:ts=2:tw=100:wm=100:syntax=ruby
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
# @module Geohash36 Module
|
5
|
+
# @brief Implements the Geohash36 module wrapper around the Geohash36 API
|
6
|
+
class Geohash36
|
7
|
+
|
8
|
+
# Version of gem
|
9
|
+
VERSION = `git describe --tags`.split("-").first || "0.1.0"
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
# vim:ts=2:tw=100:wm=100:syntax=ruby
|
data/lib/geohash36.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
# Standard includes
|
5
|
+
require 'bundler'
|
6
|
+
require 'thor'
|
7
|
+
require 'rake'
|
8
|
+
require 'ruby-try'
|
9
|
+
|
10
|
+
# Provides complete solution for geohashing
|
11
|
+
class Geohash36
|
12
|
+
|
13
|
+
# Geocode-36 matrix for map
|
14
|
+
GEOCODE_MATRIX = [
|
15
|
+
['2', '3', '4', '5', '6', '7'],
|
16
|
+
['8', '9', 'b', 'B', 'C', 'd'],
|
17
|
+
['D', 'F', 'g', 'G', 'h', 'H'],
|
18
|
+
['j', 'J', 'K', 'l', 'L', 'M'],
|
19
|
+
['n', 'N', 'P', 'q', 'Q', 'r'],
|
20
|
+
['R', 't', 'T', 'V', 'W', 'X']
|
21
|
+
]
|
22
|
+
# Needed for inversion direction of latitude
|
23
|
+
GEOMATRIX_MAX_INDEX = 5
|
24
|
+
# Standart length of geocode
|
25
|
+
GEOCODE_LENGTH = 10
|
26
|
+
# Accuracy for coordinates when converting from geohash
|
27
|
+
DEFAULT_ACCURACY = 6
|
28
|
+
|
29
|
+
attr_reader :coords
|
30
|
+
attr_reader :hash
|
31
|
+
attr_accessor :accuracy
|
32
|
+
|
33
|
+
# Create new Geohash object from geohash or coordinates.
|
34
|
+
#
|
35
|
+
# @param object [Hash, String]
|
36
|
+
# @example Pass geohash
|
37
|
+
# Geohash36.new "l222222222222"
|
38
|
+
# @example Pass coordinates
|
39
|
+
# Geohash36.new latitude: 80, longitude: 20
|
40
|
+
def initialize(obj = { latitude: 0, longitude: 0 })
|
41
|
+
@accuracy = DEFAULT_ACCURACY
|
42
|
+
if obj.kind_of? Hash
|
43
|
+
Geohash36.validate_coords(obj)
|
44
|
+
@hash = Geohash36.to_geohash(obj)
|
45
|
+
@coords = obj
|
46
|
+
elsif obj.kind_of? String
|
47
|
+
Geohash36.validate_geohash(obj)
|
48
|
+
@hash = obj
|
49
|
+
@coords = Geohash36.to_coords(obj, @accuracy)
|
50
|
+
else
|
51
|
+
raise ArgumentError, "Argument type should be hash or string"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Update geohash value. Coordinates will update automatically
|
56
|
+
def hash=(geohash)
|
57
|
+
raise ArgumenError unless geohash.kind_of? String
|
58
|
+
@hash = geohash
|
59
|
+
@coords = Geohash36.to_coords(geohash, @accuracy)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Update coordinates values. Geohash will update automatically
|
63
|
+
def coords=(coords)
|
64
|
+
raise ArgumenError unless coords.kind_of? Hash
|
65
|
+
@hash = Geohash36.to_geohash(coords)
|
66
|
+
@coords.merge! coords
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# Convert coordinates pair to geohash without creating an object
|
71
|
+
#
|
72
|
+
# @param coords [Hash] coordinates to convert
|
73
|
+
# @return [String] geohash
|
74
|
+
# @example
|
75
|
+
# Geohash36.to_geohash(latitude: 0, longitude: 0)
|
76
|
+
# # => "l222222222"
|
77
|
+
def self.to_geohash(coords)
|
78
|
+
Geohash36.validate_coords(coords)
|
79
|
+
lon_interval = Geohash36.basic_lon_interval
|
80
|
+
lat_interval = Geohash36.basic_lat_interval
|
81
|
+
|
82
|
+
(0...GEOCODE_LENGTH).map{Geohash36.geohash_symbol!(lon_interval, lat_interval, coords)}.join
|
83
|
+
end
|
84
|
+
|
85
|
+
# Convert geohash to coords without creating an object.
|
86
|
+
#
|
87
|
+
# @param [String] geohash
|
88
|
+
# @param accuracy [Fuxnum] accuracy for coordinates values
|
89
|
+
# @return [Hash] coordinates
|
90
|
+
# @example With default accuracy
|
91
|
+
# Geohash36.to_coords("l222222222")
|
92
|
+
# # => {:latitude=>-1.0e-06, :longitude=>3.0e-06}
|
93
|
+
# @example With accuracy 3
|
94
|
+
# Geohash36.to_coords("l222222222", 3)
|
95
|
+
# # => {:latitude=>0.0, :longitude=>0.0}
|
96
|
+
def self.to_coords(geohash, accuracy = DEFAULT_ACCURACY)
|
97
|
+
Geohash36.validate_geohash(geohash)
|
98
|
+
|
99
|
+
lon_interval = Geohash36.basic_lon_interval
|
100
|
+
lat_interval = Geohash36.basic_lat_interval
|
101
|
+
|
102
|
+
geohash.each_char do |c|
|
103
|
+
lon_intervals = Geohash36::Interval.convert_array(lon_interval.split)
|
104
|
+
lat_intervals = Geohash36::Interval.convert_array(lat_interval.split)
|
105
|
+
|
106
|
+
lat_index, lon_index = 0, 0
|
107
|
+
|
108
|
+
GEOCODE_MATRIX.each_with_index do |row, row_index|
|
109
|
+
if row.include? c
|
110
|
+
lat_index = GEOMATRIX_MAX_INDEX-row_index
|
111
|
+
lon_index = row.index(c)
|
112
|
+
break
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
lon_interval.update lon_intervals[lon_index]
|
117
|
+
lat_interval.update lat_intervals[lat_index]
|
118
|
+
end
|
119
|
+
|
120
|
+
{ latitude: lat_interval.middle.round(accuracy) ,
|
121
|
+
longitude: lon_interval.middle.round(accuracy) }
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
def self.basic_lon_interval
|
126
|
+
Geohash36::Interval.new [-180, 180]
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.basic_lat_interval
|
130
|
+
Geohash36::Interval.new [-90, 90]
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.validate_geohash(geohash)
|
134
|
+
unless geohash =~ /\A[23456789bBCdDFgGhHjJKlLMnNPqQrRtTVWX]+{1,10}\z/
|
135
|
+
raise ArgumentError, "It is not Geohash-36."
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.validate_coords(coords)
|
140
|
+
keys = coords.keys
|
141
|
+
raise ArgumentError, "Invalid hash" unless keys.length == 2 && keys.include?(:latitude) && keys.include?(:longitude)
|
142
|
+
lat_inclusion = Geohash36.basic_lat_interval.include? coords[:latitude]
|
143
|
+
lon_inclusion = Geohash36.basic_lon_interval.include? coords[:longitude]
|
144
|
+
raise ArgumentError, "Invalid hash values" unless lat_inclusion && lon_inclusion
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.geohash_symbol!(lon_interval, lat_interval, coords)
|
148
|
+
lon_intervals = Geohash36::Interval.convert_array(lon_interval.split, include_right: false)
|
149
|
+
lat_intervals = Geohash36::Interval.convert_array(lat_interval.split, include_left: false)
|
150
|
+
|
151
|
+
lon_index = lon_intervals.index {|interval| interval.include? coords[:longitude] }
|
152
|
+
lat_index = lat_intervals.index {|interval| interval.include? coords[:latitude] }
|
153
|
+
|
154
|
+
lon_interval.update lon_intervals[lon_index]
|
155
|
+
lat_interval.update lat_intervals[lat_index]
|
156
|
+
|
157
|
+
GEOCODE_MATRIX[GEOMATRIX_MAX_INDEX-lat_index][lon_index]
|
158
|
+
end
|
159
|
+
|
160
|
+
end # of module Geohash36
|
161
|
+
|
162
|
+
Dir[File.dirname(__FILE__) + '/geohash36/library/*.rb'].each {|file| require file }
|
163
|
+
|
164
|
+
# vim:ts=2:tw=100:wm=100:syntax=ruby
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# System include
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
|
7
|
+
describe Geohash36 do
|
8
|
+
|
9
|
+
it { expect{Geohash36.to_geohash(Hash.new)}.to raise_error(ArgumentError) }
|
10
|
+
it { expect{Geohash36.new 111 }.to raise_error(ArgumentError) }
|
11
|
+
it { expect{Geohash36.new Hash.new }.to raise_error(ArgumentError) }
|
12
|
+
it { expect{Geohash36.new "BB99999999"}.not_to raise_error }
|
13
|
+
it { expect{Geohash36.new latitude: 1, longitude: 2}.not_to raise_error }
|
14
|
+
|
15
|
+
|
16
|
+
context "when default args" do
|
17
|
+
context "coordinates" do
|
18
|
+
subject { Geohash36.new }
|
19
|
+
|
20
|
+
let(:default_coords) { {latitude: 0, longitude: 0} }
|
21
|
+
let(:default_hash) { 'l222222222' }
|
22
|
+
let(:some_coords) { {latitude: 54, longitude: 32} }
|
23
|
+
let(:some_hash) { 'BB99999999' }
|
24
|
+
|
25
|
+
its(:coords) { is_expected.to eq(default_coords)}
|
26
|
+
its(:hash) { is_expected.to eq(default_hash)}
|
27
|
+
|
28
|
+
it "should change coords when hash updated" do
|
29
|
+
expect{subject.hash = some_hash}.to change{subject.coords}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should change hash when coords updated" do
|
33
|
+
expect{subject.coords = some_coords}.to change{subject.hash}
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should give appropriate hash for coords" do
|
37
|
+
subject.coords = some_coords
|
38
|
+
expect(subject.hash).to eq(some_hash)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should give appropriate coords for hash" do
|
42
|
+
subject.accuracy = 2
|
43
|
+
subject.hash = some_hash
|
44
|
+
expect(subject.coords).to eq(some_coords)
|
45
|
+
end
|
46
|
+
|
47
|
+
it { expect{subject.hash = ""}.to raise_error(ArgumentError) }
|
48
|
+
end # of context
|
49
|
+
end # of context
|
50
|
+
end # of describe
|
51
|
+
|
52
|
+
# vim:ts=2:tw=100:wm=100:syntax=ruby
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Custom include
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
describe Geohash36::Interval do
|
7
|
+
it { expect{Geohash36::Interval.new [0]}.to raise_error(ArgumentError) }
|
8
|
+
|
9
|
+
context "[0, 6]" do
|
10
|
+
subject { Geohash36::Interval.new([0, 6]) }
|
11
|
+
|
12
|
+
its(:middle) { should == 3.0 }
|
13
|
+
its(:third) { should == 2.0 }
|
14
|
+
its(:split2) { should == [[0, 3.0], [3.0, 6]] }
|
15
|
+
its(:split3) { should == [[0, 2.0], [2.0, 4.0], [4.0, 6]] }
|
16
|
+
its(:split) { should == [[0, 1.0], [1.0, 2.0], [2.0, 3.0], [3.0, 4.0], [4.0, 5.0], [5.0, 6]] }
|
17
|
+
|
18
|
+
context "when include all border" do
|
19
|
+
it { subject.to_s.should eq("[0, 6]")}
|
20
|
+
it { is_expected.to include?(0) }
|
21
|
+
it { is_expected.to include?(2) }
|
22
|
+
it { is_expected.to include?(6) }
|
23
|
+
it { is_expected.to_not include?(9) }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when not include right border" do
|
27
|
+
subject { Geohash36::Interval.new([0, 6], include_right: false) }
|
28
|
+
|
29
|
+
it { subject.to_s.should eq("[0, 6)")}
|
30
|
+
it { is_expected.to include?(0) }
|
31
|
+
it { is_expected.to include?(2) }
|
32
|
+
it { is_expected.to_not include?(6) }
|
33
|
+
it { is_expected.to_not include?(9) }
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when not include left border" do
|
37
|
+
subject { Geohash36::Interval.new([0, 6], include_left: false) }
|
38
|
+
|
39
|
+
it { subject.to_s.should eq("(0, 6]")}
|
40
|
+
it { is_expected.to_not include?(0) }
|
41
|
+
it { is_expected.to include?(2) }
|
42
|
+
it { is_expected.to include?(6) }
|
43
|
+
it { is_expected.to_not include?(9) }
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#update" do
|
47
|
+
it "should properly update interval" do
|
48
|
+
expect{subject.update [1, 3]}.to change{subject}.to([1,3])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end # of context
|
53
|
+
end # of describe Geohash36::Interval
|
54
|
+
|
55
|
+
# vim:ts=2:tw=100:wm=100:syntax=ruby
|