open_location_code 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 82d946e05ee8c635c1aea25b7c11bf8309d195a2
4
+ data.tar.gz: 370f96971c64f1384c387a913ee5494f49e77b18
5
+ SHA512:
6
+ metadata.gz: ddc3ec4f227802e24bdd6072055c6987771b6ded212f13ab70e62e7b351e5c1b16e60d67cd47314ad7625ff10e24167d6ee431d616059dda89dbb3719650a393
7
+ data.tar.gz: 24affbebbf437f9dedf76472ecfda8641345c63709406134fcd8dd007cb88573d2b8ee9ff04431595d8ad16f51188f28275ef7e445b5abfedf57ebc818ab01f3
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.swp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in open_location_code.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Jiren
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # OpenLocationCode
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/open_location_code`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ Ref: https://github.com/google/open-location-code
6
+
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'open_location_code', github: 'jiren/open_location_code'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install open_location_code
23
+
24
+ ## Usage
25
+
26
+ ```ruby
27
+ code = OpenLocationCode.encode(47.365590, 8.524997) # 8FVC9G8F+6X
28
+ code = OpenLocationCode.encode(47.365590, 8.524997, 12) #8FVC9G8F+6XQH
29
+
30
+ code_area = OpenLocationCode.decode(code)
31
+ # #<OpenLocationCode::CodeArea:0x007fe7eb050110 @latitude_lo=47.36557499999997, @longitude_lo=8.524968750000008, @latitude_hi=47.36557499999997, @longitude_hi=8.52500000000001, @code_length=12, @latitude_center=47.36557499999997, @longitude_center=8.52498437500001>
32
+ ```
33
+
34
+ ## Development
35
+
36
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
37
+
38
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
39
+
40
+ ## Contributing
41
+
42
+ 1. Fork it ( https://github.com/[my-github-username]/open_location_code/fork )
43
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
44
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
45
+ 4. Push to the branch (`git push origin my-new-feature`)
46
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "open_location_code"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,31 @@
1
+ module OpenLocationCode
2
+ #
3
+ # Coordinates of a decoded Open Location Code.
4
+ #
5
+ # The coordinates include the latitude and longitude of the lower left and
6
+ # upper right corners and the center of the bounding box for the area the
7
+ # code represents.
8
+ #
9
+ class CodeArea
10
+ attr_accessor :latitude_lo, :longitude_lo, :latitude_hi, :longitude_hi
11
+ attr_accessor :latitude_center, :longitude_center, :code_length
12
+
13
+ def initialize(latitude_lo, longitude_lo, latitude_hi, longitude_hi, code_length)
14
+ @latitude_lo = latitude_lo
15
+ @longitude_lo = longitude_lo
16
+ @latitude_hi = latitude_hi
17
+ @longitude_hi = longitude_hi
18
+ @code_length = code_length
19
+
20
+ set_center
21
+ end
22
+
23
+ #
24
+ # Calculate center latitude and longitude
25
+ #
26
+ def set_center
27
+ @latitude_center = [ latitude_lo + (latitude_hi - latitude_lo) / 2.0, LATITUDE_MAX].min
28
+ @longitude_center = [ longitude_lo + (longitude_hi - longitude_lo)/ 2.0, LONGITUDE_MAX].min
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,261 @@
1
+ module OpenLocationCode
2
+ #
3
+ # Decode open location code to latitude and longitude of the lower left and
4
+ # upper right corners and the center of the bounding box for the area
5
+ #
6
+ class Decoder
7
+ attr_accessor :code
8
+
9
+ def initialize(code)
10
+ @code = code.dup
11
+ end
12
+
13
+ #
14
+ # Decode opne location code
15
+ #
16
+ # @return [CodeArea]
17
+ #
18
+ def process
19
+ unless full?
20
+ raise OLCError, "Passed Open Location Code is not a valid full code: #{code}"
21
+ end
22
+
23
+ # Strip out separator character (we've already established the code is
24
+ # valid so the maximum is one), padding characters and convert to upper case.
25
+ code.sub!(SEPARATOR, '')
26
+ code.sub!(/#{PADDING_CHARACTER}+/, '')
27
+ code.upcase!
28
+
29
+ # Decode the lat/lng pair component.
30
+ code_area = decode_pairs(code[0, PAIR_CODE_LENGTH])
31
+
32
+ # If there is a grid refinement component, decode that.
33
+ return code_area if code.length <= PAIR_CODE_LENGTH
34
+
35
+ grid_area = decode_grid(code[PAIR_CODE_LENGTH, code.length - 1])
36
+
37
+ CodeArea.new(
38
+ code_area.latitude_lo + grid_area.latitude_lo,
39
+ code_area.longitude_lo + grid_area.longitude_lo,
40
+ code_area.latitude_lo + grid_area.latitude_hi,
41
+ code_area.longitude_lo + grid_area.longitude_hi,
42
+ code_area.code_length + grid_area.code_length
43
+ )
44
+ end
45
+
46
+ #
47
+ # Decode an OLC code made up of lat/lng pairs.
48
+ #
49
+ # This decodes an OLC code made up of alternating latitude and longitude
50
+ # characters, encoded using base 20.
51
+ #
52
+ # @param [String] code
53
+ # A valid OLC code, presumed to be full, but with the separator
54
+ # removed.
55
+ # @return [CodeArea]
56
+ #
57
+ def decode_pairs(code)
58
+ # Get the latitude and longitude values. These will need correcting from
59
+ # positive ranges.
60
+
61
+ latitude_pair = decode_pairs_sequence(code, 0)
62
+ longitude_pair = decode_pairs_sequence(code, 1)
63
+
64
+ # Correct the values and set them into the CodeArea object.
65
+ return CodeArea.new(
66
+ latitude_pair[0] - LATITUDE_MAX,
67
+ longitude_pair[0] - LONGITUDE_MAX,
68
+ latitude_pair[1] - LATITUDE_MAX,
69
+ longitude_pair[1] - LONGITUDE_MAX,
70
+ code.length)
71
+ end
72
+
73
+ #
74
+ # Decode either a latitude or longitude sequence.
75
+ #
76
+ # This decodes the latitude or longitude sequence of a lat/lng pair encoding.
77
+ # Starting at the character at position offset, every second character is
78
+ # decoded and the value returned.
79
+ #
80
+ # @param [String] code
81
+ # A valid OLC code, presumed to be full, with the separator removed.
82
+ # @param [Integer] offset
83
+ # The character to start from.
84
+ # @return [CodeArea]
85
+ # A pair of the low and high values. The low value comes from decoding the
86
+ # characters. The high value is the low value plus the resolution of the
87
+ # last position. Both values are offset into positive ranges and will need
88
+ # to be corrected before use.
89
+ #
90
+ def decode_pairs_sequence(code, offset)
91
+ i = 0
92
+ value = 0
93
+
94
+ while (i * 2 + offset) < code.length do
95
+ value += CODE_ALPHABET.index(code[i * 2 + offset]) * PAIR_RESOLUTIONS[i]
96
+ i += 1
97
+ end
98
+
99
+ [value, value + PAIR_RESOLUTIONS[i - 1]]
100
+ end
101
+
102
+ #
103
+ # Decode the grid refinement portion of an OLC code.
104
+ #
105
+ # This decodes an OLC code using the grid refinement method.
106
+ #
107
+ # @param [String] code
108
+ # A valid OLC code sequence that is only the grid refinement
109
+ # portion. This is the portion of a code starting at position 11.
110
+ # @return [CodeArea]
111
+ #
112
+ def decode_grid(code)
113
+ latitude_lo = 0.0
114
+ longitude_lo = 0.0
115
+ lat_place_value = GRID_SIZE_DEGREES
116
+ lng_place_value = GRID_SIZE_DEGREES
117
+ i = 0
118
+
119
+ while i < code.length do
120
+ code_index = CODE_ALPHABET.index(code[i])
121
+ row = (code_index.to_f / GRID_COLUMNS).floor
122
+ col = code_index % GRID_COLUMNS
123
+
124
+ lat_place_value /= GRID_ROWS
125
+ lng_place_value /= GRID_COLUMNS
126
+
127
+ latitude_lo += row * lat_place_value
128
+ longitude_lo += col * lng_place_value
129
+ i += 1
130
+ end
131
+
132
+ CodeArea.new(
133
+ latitude_lo,
134
+ longitude_lo,
135
+ latitude_lo + lat_place_value,
136
+ longitude_lo + lng_place_value,
137
+ code.length
138
+ )
139
+ end
140
+
141
+ #
142
+ # Determines if a code is a valid full Open Location Code.
143
+ #
144
+ # Not all possible combinations of Open Location Code characters decode to
145
+ # valid latitude and longitude values. This checks that a code is valid
146
+ # and also that the latitude and longitude values are legal. If the prefix
147
+ # character is present, it must be the first character. If the separator
148
+ # character is present, it must be after four characters.
149
+ #
150
+ # @return [Boolean]
151
+ #
152
+ def full?
153
+ return false unless valid?
154
+
155
+ # If it's short, it's not full.
156
+ return false if short?
157
+
158
+ # Work out what the first latitude character indicates for latitude.
159
+ first_lat_value = CODE_ALPHABET.index(code[0].upcase) * ENCODING_BASE
160
+
161
+ #The code would decode to a latitude of >= 90 degrees.
162
+ return false if first_lat_value >= LATITUDE_MAX * 2
163
+
164
+ if code.length > 1
165
+ # Work out what the first longitude character indicates for longitude.
166
+ first_lng_value = CODE_ALPHABET.index(code[1].upcase) * ENCODING_BASE
167
+
168
+ # The code would decode to a longitude of >= 180 degrees.
169
+ return false if first_lng_value >= LONGITUDE_MAX * 2
170
+ end
171
+
172
+ return true
173
+ end
174
+
175
+ #
176
+ # Determines if a code is valid.
177
+ #
178
+ # To be valid, all characters must be from the Open Location Code character
179
+ # set with at most one separator. The separator can be in any even-numbered
180
+ # position up to the eighth digit.
181
+ #
182
+ # @return [Boolean]
183
+ #
184
+ def valid?
185
+ return false if code.nil? || code.length == 0
186
+
187
+ # The separator is required.
188
+ return false unless code.index(SEPARATOR)
189
+
190
+ if code.index(SEPARATOR) != code.rindex(SEPARATOR)
191
+ return false
192
+ end
193
+
194
+ # Is it in an illegal position?
195
+ if code.index(SEPARATOR) > SEPARATOR_POSITION || code.index(SEPARATOR) % 2 == 1
196
+ return false
197
+ end
198
+
199
+ # We can have an even number of padding characters before the separator,
200
+ # but then it must be the final character.
201
+ if code.index(PADDING_CHARACTER)
202
+ # Not allowed to start with them!
203
+ return false if code.index(PADDING_CHARACTER) == 0
204
+
205
+ # There can only be one group and it must have even length.
206
+
207
+ pad_match = code.scan(Regexp.new('(' + PADDING_CHARACTER + '+)')).collect{|m| m}
208
+
209
+ if (pad_match.length > 1 || pad_match[0].length % 2 == 1 ||
210
+ pad_match[0].length > SEPARATOR_POSITION - 2)
211
+ return false
212
+ end
213
+
214
+ # If the code is long enough to end with a separator, make sure it does.
215
+ return false if code[code.length - 1] != SEPARATOR
216
+ end
217
+
218
+ # If there are characters after the separator, make sure there isn't just
219
+ # one of them (not legal).
220
+ return false if (code.length - code.index(SEPARATOR) - 1) == 1
221
+
222
+ # Strip the separator and any padding characters.
223
+ code.sub!(Regexp.new('\\' + SEPARATOR + '+'), '')
224
+ code.sub!(Regexp.new(PADDING_CHARACTER + '+'), '')
225
+
226
+ # Check the code contains only valid characters.
227
+ code.length.times.each do |i|
228
+ character = code[i].upcase
229
+ if (character != SEPARATOR && CODE_ALPHABET.index(character) == -1)
230
+ return false
231
+ end
232
+ end
233
+ return true
234
+ end
235
+
236
+
237
+ #
238
+ # Determines if a code is a valid short code.
239
+ #
240
+ # A short Open Location Code is a sequence created by removing four or more
241
+ # digits from an Open Location Code. It must include a separator
242
+ # character.
243
+ #
244
+ # @return [Boolean]
245
+ #
246
+ def short?
247
+ # Check it's valid.
248
+ return false unless valid?
249
+
250
+ # If there are less characters than expected before the SEPARATOR.
251
+ separator_index = code.index(SEPARATOR).to_i
252
+
253
+ if separator_index >= 0 && separator_index < SEPARATOR_POSITION
254
+ return true
255
+ end
256
+
257
+ return false
258
+ end
259
+
260
+ end
261
+ end
@@ -0,0 +1,179 @@
1
+ module OpenLocationCode
2
+ #
3
+ # Encode latitude longitude to code
4
+ #
5
+ class Encoder
6
+ attr_accessor :latitude, :longitude, :code_length, :original
7
+
8
+ def initialize(latitude, longitude, code_length)
9
+ @code_length = code_length
10
+
11
+ # Ensure that latitude and longitude are valid.
12
+ @latitude = clip_latitude(latitude)
13
+ @longitude = normalize_longitude(longitude)
14
+
15
+ @original = { latitude: latitude, longitude: longitude }
16
+ end
17
+
18
+ #
19
+ # Clip a latitude into the range -90 to 90.
20
+ #
21
+ # @param [Float] latitude
22
+ # @return [Float]
23
+ #
24
+ def clip_latitude(latitude)
25
+ [ 90, [-90, latitude].max ].min
26
+ end
27
+
28
+ #
29
+ # Normalize a longitude into the range -180 to 180, not including 180.
30
+ #
31
+ # @param [Float] longitude
32
+ # @return [Float]
33
+ #
34
+ def normalize_longitude(longitude)
35
+ while (longitude < -180) do
36
+ longitude += 360
37
+ end
38
+
39
+ while (longitude >= 180) do
40
+ longitude -= 360
41
+ end
42
+
43
+ longitude
44
+ end
45
+
46
+ #
47
+ # Compute the latitude precision value for a given code length. Lengths <=
48
+ # 10 have the same precision for latitude and longitude, but lengths > 10
49
+ # have different precisions due to the grid method having fewer columns than
50
+ # rows.
51
+ #
52
+ def compute_latitude_precision
53
+ if code_length <= 10
54
+ return 20**(code_length/-2.0 + 2).floor
55
+ end
56
+
57
+ (20**-3).to_f/GRID_ROWS**(code_length - 10)
58
+ end
59
+
60
+ #
61
+ # Encode a location into a sequence of OLC lat/lng pairs.
62
+ #
63
+ # This uses pairs of characters (longitude and latitude in that order) to
64
+ # represent each step in a 20x20 grid. Each code, therefore, has 1/400th
65
+ # the area of the previous code.
66
+ #
67
+ # @param [Integer] code_length
68
+ # The number of significant digits in the output code, not
69
+ # including any separator characters.
70
+ #
71
+ def encode_pairs(code_length)
72
+ code = ''
73
+
74
+ # Adjust latitude and longitude so they fall into positive ranges.
75
+ adjusted_latitude = latitude + LATITUDE_MAX
76
+ adjusted_longitude = longitude + LONGITUDE_MAX
77
+
78
+ # Count digits - can't use string length because it may include a separator
79
+ # character.
80
+ digit_count = 0
81
+
82
+ while (digit_count < code_length) do
83
+ # Provides the value of digits in this place in decimal degrees.
84
+ place_value = PAIR_RESOLUTIONS[(digit_count / 2.0).floor]
85
+
86
+ # Do the latitude - gets the digit for this place and subtracts that for
87
+ # the next digit.
88
+ digit_value = (adjusted_latitude / place_value.to_f).floor
89
+ adjusted_latitude -= (digit_value * place_value)
90
+ code += CODE_ALPHABET[digit_value]
91
+ digit_count += 1
92
+
93
+ # And do the longitude - gets the digit for this place and subtracts that
94
+ # for the next digit.
95
+ digit_value = (adjusted_longitude / place_value.to_f).floor
96
+ adjusted_longitude -= (digit_value * place_value)
97
+ code += CODE_ALPHABET[digit_value]
98
+ digit_count += 1
99
+
100
+ # Should we add a separator here?
101
+ if digit_count == SEPARATOR_POSITION && digit_count < code_length
102
+ code += SEPARATOR
103
+ end
104
+ end
105
+
106
+ if code.length < SEPARATOR_POSITION
107
+ code += PADDING_CHARACTER*(SEPARATOR_POSITION - code.length + 1)
108
+ end
109
+
110
+ if code.length == SEPARATOR_POSITION
111
+ code += SEPARATOR
112
+ end
113
+
114
+ return code
115
+ end
116
+
117
+ # Encode a location using the grid refinement method into an OLC string.
118
+ #
119
+ # The grid refinement method divides the area into a grid of 4x5, and uses a
120
+ # single character to refine the area. This allows default accuracy OLC codes
121
+ # to be refined with just a single character.
122
+ #
123
+ # @param [Integer] code_length
124
+ #
125
+ def encode_grid(code_length)
126
+ code = ''
127
+ lat_place_value = GRID_SIZE_DEGREES
128
+ lng_place_value = GRID_SIZE_DEGREES
129
+
130
+ # Adjust latitude and longitude so they fall into positive ranges and
131
+ # get the offset for the required places.
132
+ adjusted_latitude = (latitude + LATITUDE_MAX) % lat_place_value
133
+ adjusted_longitude = (longitude + LONGITUDE_MAX) % lng_place_value
134
+
135
+ code_length.times do |i|
136
+ # Work out the row and column.
137
+ row = (adjusted_latitude / (lat_place_value.to_f / GRID_ROWS)).floor
138
+ col = (adjusted_longitude / (lng_place_value.to_f / GRID_COLUMNS)).floor
139
+
140
+ lat_place_value /= GRID_ROWS
141
+ lng_place_value /= GRID_COLUMNS
142
+
143
+ adjusted_latitude -= (row * lat_place_value)
144
+ adjusted_longitude -= (col * lng_place_value)
145
+
146
+ code += CODE_ALPHABET[row * GRID_COLUMNS + col]
147
+ end
148
+
149
+ return code
150
+ end
151
+
152
+ #
153
+ # Encode latitude and longitude
154
+ #
155
+ # @return [String]
156
+ #
157
+ def process
158
+ if code_length < 2 || (code_length < SEPARATOR_POSITION && code_length % 2 == 1)
159
+ raise OLCError, 'Invalid Open Location Code length'
160
+ end
161
+
162
+ # Latitude 90 needs to be adjusted to be just less, so the returned code
163
+ # can also be decoded.
164
+ if latitude == 90
165
+ self.latitude -= compute_latitude_precision
166
+ end
167
+
168
+ code = encode_pairs([code_length, PAIR_CODE_LENGTH].min)
169
+
170
+ # If the requested length indicates we want grid refined codes.
171
+ if code_length > PAIR_CODE_LENGTH
172
+ code += encode_grid(code_length - PAIR_CODE_LENGTH)
173
+ end
174
+
175
+ code
176
+ end
177
+
178
+ end
179
+ end
@@ -0,0 +1,6 @@
1
+ module OpenLocationCode
2
+ #
3
+ # Version of lib
4
+ #
5
+ VERSION = "0.1.0"
6
+ end
@@ -0,0 +1,109 @@
1
+ require 'open_location_code/version'
2
+ require 'open_location_code/code_area'
3
+ require 'open_location_code/encoder'
4
+ require 'open_location_code/decoder'
5
+
6
+ #
7
+ # Example:
8
+ #
9
+ # # Encode a location, default accuracy:
10
+ # code = OpenLocationCode.encode(47.365590, 8.524997) # 8FVC9G8F+6X
11
+ #
12
+ # # Encode a location using one stage of additional refinement:
13
+ # code = OpenLocationCode.encode(47.365590, 8.524997, 11) # 8FVC9G8F+6XQ
14
+ #
15
+ # # Decode a full code:
16
+ # coord = OpenLocationCode.decode(code)
17
+ #
18
+ module OpenLocationCode
19
+
20
+ # A separator used to break the code into two parts to aid memorability
21
+ SEPARATOR = '+'
22
+
23
+ # The number of characters to place before the separator.
24
+ SEPARATOR_POSITION = 8
25
+
26
+ # The character used to pad codes.
27
+ PADDING_CHARACTER = '0'
28
+
29
+ # The character set used to encode the values.
30
+ CODE_ALPHABET = '23456789CFGHJMPQRVWX'
31
+
32
+ # The base to use to convert numbers to/from.
33
+ ENCODING_BASE = CODE_ALPHABET.length
34
+
35
+ # The maximum value for latitude in degrees.
36
+ LATITUDE_MAX = 90
37
+
38
+ # The maximum value for longitude in degrees.
39
+ LONGITUDE_MAX = 180
40
+
41
+ # Maxiumum code length using lat/lng pair encoding. The area of such a
42
+ # code is approximately 13x13 meters (at the equator), and should be suitable
43
+ # for identifying buildings. This excludes prefix and separator characters.
44
+ PAIR_CODE_LENGTH = 10
45
+
46
+ # The resolution values in degrees for each position in the lat/lng pair
47
+ # encoding. These give the place value of each position, and therefore the
48
+ # dimensions of the resulting area.
49
+ PAIR_RESOLUTIONS = [20.0, 1.0, 0.05, 0.0025, 0.000125]
50
+
51
+ # Number of columns in the grid refinement method.
52
+ GRID_COLUMNS = 4
53
+
54
+ # Number of rows in the grid refinement method.
55
+ GRID_ROWS = 5
56
+
57
+ # Size of the initial grid in degrees.
58
+ GRID_SIZE_DEGREES = 0.000125
59
+
60
+ # Minimum length of a code that can be shortened.
61
+ MIN_TRIMMABLE_CODE_LEN = 6
62
+
63
+ # Error class
64
+ OLCError = Class.new(StandardError)
65
+
66
+ #
67
+ # OLC alphabet.
68
+ # @return [String]
69
+ #
70
+ def self.alphabet
71
+ CODE_ALPHABET
72
+ end
73
+
74
+ #
75
+ # Encode a location into an Open Location Code.
76
+ #
77
+ # Produces a code of the specified length, or the default length if no length
78
+ # is provided.
79
+ #
80
+ # The length determines the accuracy of the code. The default length is
81
+ # 10 characters, returning a code of approximately 13.5x13.5 meters. Longer
82
+ # codes represent smaller areas, but lengths > 14 are sub-centimetre and so
83
+ # 11 or 12 are probably the limit of useful codes.
84
+ #
85
+ # @param [Float] latitude
86
+ # A latitude in signed decimal degrees. Will be clipped to the range -90 to 90.
87
+ # @param [Float] longitude
88
+ # A longitude in signed decimal degrees. Will be normalised to the range -180 to 180.
89
+ # @param [Integer] code_length
90
+ # The number of significant digits in the output code, not including any separator characters.
91
+ # @return [String]
92
+ # Encoded code
93
+ def self.encode(latitude, longitude, code_length = nil)
94
+ Encoder.new(latitude, longitude, code_length || PAIR_CODE_LENGTH).process
95
+ end
96
+
97
+ #
98
+ # Decodes an Open Location Code into the location coordinates.
99
+ #
100
+ # @param [String] code
101
+ # The Open Location Code to decode.
102
+ # @return [CodeArea]
103
+ # An object that provides the latitude and longitude of two of the
104
+ # corners of the area, the center, and the length of the original code.
105
+ def self.decode(code)
106
+ Decoder.new(code).process
107
+ end
108
+
109
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'open_location_code/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "open_location_code"
8
+ spec.version = OpenLocationCode::VERSION
9
+ spec.authors = ["Jiren"]
10
+ spec.email = ["jirenpatel@gmail.com"]
11
+
12
+ spec.summary = %q{Open Location Codes are a way of encoding location into a form that is
13
+ easier to use than latitude and longitude.}
14
+ spec.description = %q{Open Location Codes are a way of encoding location into a form that is
15
+ easier to use than latitude and longitude.}
16
+ spec.homepage = "https://github.com/jiren/open_location_code"
17
+ spec.license = "MIT"
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.9"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: open_location_code
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jiren
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-05-29 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.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
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
+ description: |-
42
+ Open Location Codes are a way of encoding location into a form that is
43
+ easier to use than latitude and longitude.
44
+ email:
45
+ - jirenpatel@gmail.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".gitignore"
51
+ - ".rspec"
52
+ - ".travis.yml"
53
+ - CODE_OF_CONDUCT.md
54
+ - Gemfile
55
+ - LICENSE.txt
56
+ - README.md
57
+ - Rakefile
58
+ - bin/console
59
+ - bin/setup
60
+ - lib/open_location_code.rb
61
+ - lib/open_location_code/code_area.rb
62
+ - lib/open_location_code/decoder.rb
63
+ - lib/open_location_code/encoder.rb
64
+ - lib/open_location_code/version.rb
65
+ - open_location_code.gemspec
66
+ homepage: https://github.com/jiren/open_location_code
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.2.2
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: Open Location Codes are a way of encoding location into a form that is easier
90
+ to use than latitude and longitude.
91
+ test_files: []
92
+ has_rdoc: