open-location-code 0.0.1
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/lib/plus_codes.rb +26 -0
- data/lib/plus_codes/code_area.rb +38 -0
- data/lib/plus_codes/open_location_code.rb +264 -0
- data/test/plus_codes_test.rb +90 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 378b33bb5f5bdf0ec82c286db7e7b54b762f2723
|
4
|
+
data.tar.gz: b2f0ded83b4ed9c914a0220c177ae6bb3e43adf5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 052d9fb0aeeab2fd7969ae25b00824813e9f63b4877d7e73d5a5aa75d5d51c17fc1eeb51482c1f08f8cfa3d50ca3793ca2e44263c225c9244b9e5fa541f94c51
|
7
|
+
data.tar.gz: 34f7c50ad9dd924e7afaa441ab3cdefe80b32de655b97cdb17f5c06d3c698738855e5c80af8d3c08f1cdce82bb494e23b22580933893d8a77cfb686fe4fa5f47
|
data/lib/plus_codes.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Plus+Codes is a Ruby implementation of Google Open Location Code(Plus+Codes).
|
2
|
+
#
|
3
|
+
# @author We-Ming Wu
|
4
|
+
module PlusCodes
|
5
|
+
|
6
|
+
# A separator used to separate the code into two parts.
|
7
|
+
SEPARATOR = '+'.freeze
|
8
|
+
|
9
|
+
# The max number of characters can be placed before the separator.
|
10
|
+
SEPARATOR_POSITION = 8
|
11
|
+
|
12
|
+
# The character used to pad a code
|
13
|
+
PADDING = '0'.freeze
|
14
|
+
|
15
|
+
# The character set used to encode coordinates.
|
16
|
+
CODE_ALPHABET = '23456789CFGHJMPQRVWX'.freeze
|
17
|
+
|
18
|
+
# ASCII lookup table.
|
19
|
+
DECODE = (CODE_ALPHABET.chars + [PADDING, SEPARATOR]).reduce([]) do |ary, c|
|
20
|
+
ary[c.ord] = CODE_ALPHABET.index(c)
|
21
|
+
ary[c.downcase.ord] = CODE_ALPHABET.index(c)
|
22
|
+
ary[c.ord] ||= -1
|
23
|
+
ary
|
24
|
+
end.freeze
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module PlusCodes
|
2
|
+
|
3
|
+
# [CodeArea] contains coordinates of a decoded Open Location Code(Plus+Codes).
|
4
|
+
# The coordinates include the latitude and longitude of the lower left and
|
5
|
+
# upper right corners and the center of the bounding box for the area the
|
6
|
+
# code represents.
|
7
|
+
#
|
8
|
+
# @author We-Ming Wu
|
9
|
+
class CodeArea
|
10
|
+
attr_accessor :south_latitude, :west_longitude, :latitude_height,
|
11
|
+
:longitude_width, :latitude_center, :longitude_center
|
12
|
+
|
13
|
+
# Creates a [CodeArea].
|
14
|
+
#
|
15
|
+
# @param south_latitude [Numeric] the latitude of the SW corner in degrees
|
16
|
+
# @param west_longitude [Numeric] the longitude of the SW corner in degrees
|
17
|
+
# @param latitude_height [Numeric] the height from the SW corner in degrees
|
18
|
+
# @param longitude_width [Numeric] the width from the SW corner in degrees
|
19
|
+
# @return [CodeArea] a code area which contains the coordinates
|
20
|
+
def initialize(south_latitude, west_longitude, latitude_height, longitude_width)
|
21
|
+
@south_latitude = south_latitude
|
22
|
+
@west_longitude = west_longitude
|
23
|
+
@latitude_height = latitude_height
|
24
|
+
@longitude_width = longitude_width
|
25
|
+
@latitude_center = south_latitude + latitude_height / 2.0
|
26
|
+
@longitude_center = west_longitude + longitude_width / 2.0
|
27
|
+
end
|
28
|
+
|
29
|
+
def north_latitude
|
30
|
+
@south_latitude + @latitude_height
|
31
|
+
end
|
32
|
+
|
33
|
+
def east_longitude
|
34
|
+
@west_longitude + @longitude_width
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,264 @@
|
|
1
|
+
require_relative '../plus_codes'
|
2
|
+
require_relative '../plus_codes/code_area'
|
3
|
+
|
4
|
+
module PlusCodes
|
5
|
+
|
6
|
+
# [OpenLocationCode] implements the Google Open Location Code(Plus+Codes) algorithm.
|
7
|
+
#
|
8
|
+
# @author We-Ming Wu
|
9
|
+
class OpenLocationCode
|
10
|
+
|
11
|
+
# Determines if a string is a valid sequence of Open Location Code(Plus+Codes) characters.
|
12
|
+
#
|
13
|
+
# @param code [String] a plus+codes
|
14
|
+
# @return [TrueClass, FalseClass] true if the code is valid, false otherwise
|
15
|
+
def valid?(code)
|
16
|
+
valid_length?(code) &&
|
17
|
+
valid_separator?(code) &&
|
18
|
+
valid_padding?(code) &&
|
19
|
+
valid_character?(code)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Determines if a string is a valid short Open Location Code(Plus+Codes).
|
23
|
+
#
|
24
|
+
# @param code [String] a plus+codes
|
25
|
+
# @return [TrueClass, FalseClass] true if the code is short, false otherwise
|
26
|
+
def short?(code)
|
27
|
+
valid?(code) && code.index(SEPARATOR) < SEPARATOR_POSITION
|
28
|
+
end
|
29
|
+
|
30
|
+
# Determines if a string is a valid full Open Location Code(Plus+Codes).
|
31
|
+
#
|
32
|
+
# @param code [String] a plus+codes
|
33
|
+
# @return [TrueClass, FalseClass] true if the code is full, false otherwise
|
34
|
+
def full?(code)
|
35
|
+
valid?(code) && !short?(code)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Converts a latitude and longitude into a Open Location Code(Plus+Codes).
|
39
|
+
#
|
40
|
+
# @param latitude [Numeric] a latitude in degrees
|
41
|
+
# @param longitude [Numeric] a longitude in degrees
|
42
|
+
# @param code_length [Integer] the number of characters in the code, this excludes the separator
|
43
|
+
# @return [String] a plus+codes
|
44
|
+
def encode(latitude, longitude, code_length = 10)
|
45
|
+
raise ArgumentError,
|
46
|
+
"Invalid Open Location Code(Plus+Codes) length: #{code_length}" if invalid_length?(code_length)
|
47
|
+
|
48
|
+
latitude = clip_latitude(latitude)
|
49
|
+
longitude = normalize_longitude(longitude)
|
50
|
+
latitude -= precision_by_length(code_length) if latitude == 90
|
51
|
+
|
52
|
+
lat = (latitude + 90).to_r
|
53
|
+
lng = (longitude + 180).to_r
|
54
|
+
|
55
|
+
digit = 0
|
56
|
+
code = ''
|
57
|
+
while digit < code_length
|
58
|
+
lat, lng = narrow_region(digit, lat, lng)
|
59
|
+
digit, lat, lng = build_code(digit, code, lat, lng)
|
60
|
+
code << SEPARATOR if (digit == SEPARATOR_POSITION)
|
61
|
+
end
|
62
|
+
|
63
|
+
digit < SEPARATOR_POSITION ? padded(code) : code
|
64
|
+
end
|
65
|
+
|
66
|
+
# Decodes an Open Location Code(Plus+Codes) into a [CodeArea].
|
67
|
+
#
|
68
|
+
# @param code [String] a plus+codes
|
69
|
+
# @return [CodeArea] a code area which contains the coordinates
|
70
|
+
def decode(code)
|
71
|
+
raise ArgumentError,
|
72
|
+
"Open Location Code(Plus+Codes) is not a valid full code: #{code}" unless full?(code)
|
73
|
+
|
74
|
+
code = code.gsub(SEPARATOR, '')
|
75
|
+
code = code.gsub(/#{PADDING}+/, '')
|
76
|
+
code = code.upcase
|
77
|
+
|
78
|
+
south_latitude = -90.0
|
79
|
+
west_longitude = -180.0
|
80
|
+
|
81
|
+
lat_resolution = 400.to_r
|
82
|
+
lng_resolution = 400.to_r
|
83
|
+
|
84
|
+
digit = 0
|
85
|
+
while digit < code.length
|
86
|
+
if digit < 10
|
87
|
+
lat_resolution /= 20
|
88
|
+
lng_resolution /= 20
|
89
|
+
south_latitude += lat_resolution * DECODE[code[digit].ord]
|
90
|
+
west_longitude += lng_resolution * DECODE[code[digit + 1].ord]
|
91
|
+
digit += 2
|
92
|
+
else
|
93
|
+
lat_resolution /= 5
|
94
|
+
lng_resolution /= 4
|
95
|
+
row = DECODE[code[digit].ord] / 4
|
96
|
+
column = DECODE[code[digit].ord] % 4
|
97
|
+
south_latitude += lat_resolution * row
|
98
|
+
west_longitude += lng_resolution * column
|
99
|
+
digit += 1
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
CodeArea.new(south_latitude, west_longitude, lat_resolution, lng_resolution)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Recovers a full Open Location Code(Plus+Codes) from a short code and a reference location.
|
107
|
+
#
|
108
|
+
# @param short_code [String] a plus+codes
|
109
|
+
# @param reference_latitude [Numeric] a reference latitude in degrees
|
110
|
+
# @param reference_longitude [Numeric] a reference longitude in degrees
|
111
|
+
# @return [String] a plus+codes
|
112
|
+
def recover_nearest(short_code, reference_latitude, reference_longitude)
|
113
|
+
return short_code if full?(short_code)
|
114
|
+
raise ArgumentError,
|
115
|
+
"Open Location Code(Plus+Codes) is not valid: #{short_code}" unless short?(short_code)
|
116
|
+
|
117
|
+
ref_lat = clip_latitude(reference_latitude)
|
118
|
+
ref_lng = normalize_longitude(reference_longitude)
|
119
|
+
|
120
|
+
prefix_len = SEPARATOR_POSITION - short_code.index(SEPARATOR)
|
121
|
+
code = prefix_by_reference(ref_lat, ref_lng, prefix_len) << short_code
|
122
|
+
code_area = decode(code)
|
123
|
+
|
124
|
+
area_range = precision_by_length(prefix_len)
|
125
|
+
area_edge = area_range / 2
|
126
|
+
|
127
|
+
latitude = code_area.latitude_center
|
128
|
+
latitude_diff = latitude - ref_lat
|
129
|
+
if (latitude_diff > area_edge)
|
130
|
+
latitude -= area_range
|
131
|
+
elsif (latitude_diff < -area_edge)
|
132
|
+
latitude += area_range
|
133
|
+
end
|
134
|
+
|
135
|
+
longitude = code_area.longitude_center
|
136
|
+
longitude_diff = longitude - ref_lng
|
137
|
+
if (longitude_diff > area_edge)
|
138
|
+
longitude -= area_range
|
139
|
+
elsif (longitude_diff < -area_edge)
|
140
|
+
longitude += area_range
|
141
|
+
end
|
142
|
+
|
143
|
+
encode(latitude, longitude, code.length - SEPARATOR.length)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Removes four, six or eight digits from the front of an Open Location Code(Plus+Codes) given a reference location.
|
147
|
+
#
|
148
|
+
# @param code [String] a plus+codes
|
149
|
+
# @param latitude [Numeric] a latitude in degrees
|
150
|
+
# @param longitude [Numeric] a longitude in degrees
|
151
|
+
# @return [String] a short plus+codes
|
152
|
+
def shorten(code, latitude, longitude)
|
153
|
+
raise ArgumentError,
|
154
|
+
"Open Location Code(Plus+Codes) is a valid full code: #{code}" unless full?(code)
|
155
|
+
raise ArgumentError,
|
156
|
+
"Cannot shorten padded codes: #{code}" unless code.index(PADDING).nil?
|
157
|
+
|
158
|
+
code_area = decode(code)
|
159
|
+
lat_diff = (latitude - code_area.latitude_center).abs
|
160
|
+
lng_diff = (longitude - code_area.longitude_center).abs
|
161
|
+
max_diff = [lat_diff, lng_diff].max
|
162
|
+
[8, 6, 4].each do |removal_len|
|
163
|
+
area_edge = precision_by_length(removal_len + 2) / 2
|
164
|
+
return code[removal_len..-1] if max_diff < area_edge
|
165
|
+
end
|
166
|
+
|
167
|
+
code.upcase
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def prefix_by_reference(latitude, longitude, prefix_len)
|
173
|
+
precision = precision_by_length(prefix_len)
|
174
|
+
rounded_latitude = (latitude / precision).floor * precision
|
175
|
+
rounded_longitude = (longitude / precision).floor * precision
|
176
|
+
encode(rounded_latitude, rounded_longitude)[0...prefix_len]
|
177
|
+
end
|
178
|
+
|
179
|
+
def narrow_region(digit, latitude, longitude)
|
180
|
+
if digit == 0
|
181
|
+
latitude /= 20
|
182
|
+
longitude /= 20
|
183
|
+
elsif digit < 10
|
184
|
+
latitude *= 20
|
185
|
+
longitude *= 20
|
186
|
+
else
|
187
|
+
latitude *= 5
|
188
|
+
longitude *= 4
|
189
|
+
end
|
190
|
+
[latitude, longitude]
|
191
|
+
end
|
192
|
+
|
193
|
+
def build_code(digit_count, code, latitude, longitude)
|
194
|
+
lat_digit = latitude.to_i
|
195
|
+
lng_digit = longitude.to_i
|
196
|
+
if digit_count < 10
|
197
|
+
code << CODE_ALPHABET[lat_digit]
|
198
|
+
code << CODE_ALPHABET[lng_digit]
|
199
|
+
[digit_count + 2, latitude - lat_digit, longitude - lng_digit]
|
200
|
+
else
|
201
|
+
code << CODE_ALPHABET[4 * lat_digit + lng_digit]
|
202
|
+
[digit_count + 1, latitude - lat_digit, longitude - lng_digit]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def valid_length?(code)
|
207
|
+
!code.nil? && code.length >= 2 + SEPARATOR.length && code.split(SEPARATOR).last.length != 1
|
208
|
+
end
|
209
|
+
|
210
|
+
def valid_separator?(code)
|
211
|
+
separator_idx = code.index(SEPARATOR)
|
212
|
+
code.count(SEPARATOR) == 1 && separator_idx <= SEPARATOR_POSITION && separator_idx.even?
|
213
|
+
end
|
214
|
+
|
215
|
+
def valid_padding?(code)
|
216
|
+
if code.include?(PADDING)
|
217
|
+
return false if code.start_with?(PADDING)
|
218
|
+
return false if code[-2..-1] != PADDING + SEPARATOR
|
219
|
+
|
220
|
+
paddings = code.scan(/#{PADDING}+/)
|
221
|
+
return false if !paddings.one? || paddings[0].length.odd?
|
222
|
+
return false if paddings[0].length > SEPARATOR_POSITION - 2
|
223
|
+
end
|
224
|
+
true
|
225
|
+
end
|
226
|
+
|
227
|
+
def valid_character?(code)
|
228
|
+
code.chars.each { |ch| return false if DECODE[ch.ord].nil? }
|
229
|
+
true
|
230
|
+
end
|
231
|
+
|
232
|
+
def invalid_length?(code_length)
|
233
|
+
code_length < 2 || (code_length < SEPARATOR_POSITION && code_length.odd?)
|
234
|
+
end
|
235
|
+
|
236
|
+
def padded(code)
|
237
|
+
code << PADDING * (SEPARATOR_POSITION - code.length) << SEPARATOR
|
238
|
+
end
|
239
|
+
|
240
|
+
def precision_by_length(code_length)
|
241
|
+
if code_length <= 10
|
242
|
+
precision = 20 ** ((code_length / -2).to_i + 2)
|
243
|
+
else
|
244
|
+
precision = (20 ** -3) / (5 ** (code_length - 10))
|
245
|
+
end
|
246
|
+
precision.to_r
|
247
|
+
end
|
248
|
+
|
249
|
+
def clip_latitude(latitude)
|
250
|
+
[90.0, [-90.0, latitude].max].min
|
251
|
+
end
|
252
|
+
|
253
|
+
def normalize_longitude(longitude)
|
254
|
+
until longitude < 180
|
255
|
+
longitude -= 360
|
256
|
+
end
|
257
|
+
until longitude >= -180
|
258
|
+
longitude += 360
|
259
|
+
end
|
260
|
+
longitude
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require_relative '../lib/plus_codes/open_location_code'
|
3
|
+
|
4
|
+
class PlusCodesTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@test_data_folder_path = File.join(File.dirname(__FILE__), '..', '..', 'test_data')
|
8
|
+
@olc = PlusCodes::OpenLocationCode.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_validity
|
12
|
+
read_csv_lines('validityTests.csv').each do |line|
|
13
|
+
cols = line.split(',')
|
14
|
+
code = cols[0]
|
15
|
+
is_valid = cols[1] == 'true'
|
16
|
+
is_short = cols[2] == 'true'
|
17
|
+
is_full = cols[3] == 'true'
|
18
|
+
is_valid_olc = @olc.valid?(code)
|
19
|
+
is_short_olc = @olc.short?(code)
|
20
|
+
is_full_olc = @olc.full?(code)
|
21
|
+
result = is_valid_olc == is_valid && is_short_olc == is_short && is_full_olc == is_full
|
22
|
+
assert_true(result)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_encode_decode
|
27
|
+
read_csv_lines('encodingTests.csv').each do |line|
|
28
|
+
cols = line.split(',')
|
29
|
+
code_area = @olc.decode(cols[0])
|
30
|
+
if cols[0].index('0')
|
31
|
+
code = @olc.encode(cols[1].to_f, cols[2].to_f, cols[0].index('0'))
|
32
|
+
else
|
33
|
+
code = @olc.encode(cols[1].to_f, cols[2].to_f, cols[0].length - 1)
|
34
|
+
end
|
35
|
+
assert_equal(cols[0], code)
|
36
|
+
assert_true((code_area.south_latitude - cols[3].to_f).abs < 0.001)
|
37
|
+
assert_true((code_area.west_longitude - cols[4].to_f).abs < 0.001)
|
38
|
+
assert_true((code_area.north_latitude - cols[5].to_f).abs < 0.001)
|
39
|
+
assert_true((code_area.east_longitude - cols[6].to_f).abs < 0.001)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_shorten
|
44
|
+
read_csv_lines('shortCodeTests.csv').each do |line|
|
45
|
+
cols = line.split(',')
|
46
|
+
code = cols[0]
|
47
|
+
lat = cols[1].to_f
|
48
|
+
lng = cols[2].to_f
|
49
|
+
short_code = cols[3]
|
50
|
+
short = @olc.shorten(code, lat, lng)
|
51
|
+
assert_equal(short_code, short)
|
52
|
+
expanded = @olc.recover_nearest(short, lat, lng)
|
53
|
+
assert_equal(code, expanded)
|
54
|
+
end
|
55
|
+
@olc.shorten('9C3W9QCJ+2VX', 60.3701125, 10.202665625)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_longer_encoding_with_special_case
|
59
|
+
assert_equal('CFX3X2X2+X2RRRRJ', @olc.encode(90.0, 1.0, 15));
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_exceptions
|
63
|
+
assert_raise ArgumentError do
|
64
|
+
@olc.encode(20, 30, 1)
|
65
|
+
end
|
66
|
+
assert_raise ArgumentError do
|
67
|
+
@olc.recover_nearest('9C3W9QCJ-2VX', 51.3708675, -1.217765625)
|
68
|
+
end
|
69
|
+
@olc.recover_nearest('9C3W9QCJ+2VX', 51.3708675, -1.217765625)
|
70
|
+
assert_raise ArgumentError do
|
71
|
+
@olc.decode('sfdg')
|
72
|
+
end
|
73
|
+
assert_raise ArgumentError do
|
74
|
+
@olc.shorten('9C3W9Q+', 1, 2)
|
75
|
+
end
|
76
|
+
assert_raise ArgumentError do
|
77
|
+
@olc.shorten('9C3W9Q00+', 1, 2)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_valid_with_special_case
|
82
|
+
assert_false(@olc.valid?('3W00CJJJ+'))
|
83
|
+
end
|
84
|
+
|
85
|
+
def read_csv_lines(csv_file)
|
86
|
+
f = File.open(File.join(@test_data_folder_path, csv_file), 'r')
|
87
|
+
f.each_line.lazy.select { |line| line !~ /^\s*#/ }.map { |line| line.chop }
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: open-location-code
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Wei-Ming Wu
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: test-unit
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Ruby implementation of Google Open Location Code(Plus+Codes)
|
84
|
+
email:
|
85
|
+
- wnameless@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- lib/plus_codes.rb
|
91
|
+
- lib/plus_codes/code_area.rb
|
92
|
+
- lib/plus_codes/open_location_code.rb
|
93
|
+
- test/plus_codes_test.rb
|
94
|
+
homepage: https://github.com/google/open-location-code
|
95
|
+
licenses:
|
96
|
+
- Apache License, Version 2.0
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.4.5
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Ruby implementation of Google Open Location Code(Plus+Codes)
|
118
|
+
test_files:
|
119
|
+
- test/plus_codes_test.rb
|