geohash36 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|