Class: OpenLocationCode::Decoder
- Inherits:
-
Object
- Object
- OpenLocationCode::Decoder
- Defined in:
- lib/open_location_code/decoder.rb
Overview
Decode open location code to latitude and longitude of the lower left and upper right corners and the center of the bounding box for the area
Instance Attribute Summary collapse
-
#code ⇒ Object
Returns the value of attribute code.
Instance Method Summary collapse
-
#decode_grid(code) ⇒ Object
Decode the grid refinement portion of an OLC code.
-
#decode_pairs(code) ⇒ CodeArea
Decode an OLC code made up of lat/lng pairs.
-
#decode_pairs_sequence(code, offset) ⇒ CodeArea
Decode either a latitude or longitude sequence.
-
#full? ⇒ Boolean
Determines if a code is a valid full Open Location Code.
-
#initialize(code) ⇒ Decoder
constructor
A new instance of Decoder.
-
#process ⇒ CodeArea
Decode opne location code.
-
#short? ⇒ Boolean
Determines if a code is a valid short code.
-
#valid? ⇒ Boolean
Determines if a code is valid.
Constructor Details
#initialize(code) ⇒ Decoder
Returns a new instance of Decoder.
9 10 11 |
# File 'lib/open_location_code/decoder.rb', line 9 def initialize(code) @code = code.dup end |
Instance Attribute Details
#code ⇒ Object
Returns the value of attribute code.
7 8 9 |
# File 'lib/open_location_code/decoder.rb', line 7 def code @code end |
Instance Method Details
#decode_grid(code) ⇒ Object
Decode the grid refinement portion of an OLC code.
This decodes an OLC code using the grid refinement method.
@param [String] code
A valid OLC code sequence that is only the grid refinement
portion. This is the portion of a code starting at position 11.
@return [CodeArea]
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/open_location_code/decoder.rb', line 112 def decode_grid(code) latitude_lo = 0.0 longitude_lo = 0.0 lat_place_value = GRID_SIZE_DEGREES lng_place_value = GRID_SIZE_DEGREES i = 0 while i < code.length do code_index = CODE_ALPHABET.index(code[i]) row = (code_index.to_f / GRID_COLUMNS).floor col = code_index % GRID_COLUMNS lat_place_value /= GRID_ROWS lng_place_value /= GRID_COLUMNS latitude_lo += row * lat_place_value longitude_lo += col * lng_place_value i += 1 end CodeArea.new( latitude_lo, longitude_lo, latitude_lo + lat_place_value, longitude_lo + lng_place_value, code.length ) end |
#decode_pairs(code) ⇒ CodeArea
Decode an OLC code made up of lat/lng pairs.
This decodes an OLC code made up of alternating latitude and longitude
characters, encoded using base 20.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/open_location_code/decoder.rb', line 57 def decode_pairs(code) # Get the latitude and longitude values. These will need correcting from # positive ranges. latitude_pair = decode_pairs_sequence(code, 0) longitude_pair = decode_pairs_sequence(code, 1) # Correct the values and set them into the CodeArea object. return CodeArea.new( latitude_pair[0] - LATITUDE_MAX, longitude_pair[0] - LONGITUDE_MAX, latitude_pair[1] - LATITUDE_MAX, longitude_pair[1] - LONGITUDE_MAX, code.length) end |
#decode_pairs_sequence(code, offset) ⇒ CodeArea
Decode either a latitude or longitude sequence.
This decodes the latitude or longitude sequence of a lat/lng pair encoding.
Starting at the character at position offset, every second character is
decoded and the value returned.
90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/open_location_code/decoder.rb', line 90 def decode_pairs_sequence(code, offset) i = 0 value = 0 while (i * 2 + offset) < code.length do value += CODE_ALPHABET.index(code[i * 2 + offset]) * PAIR_RESOLUTIONS[i] i += 1 end [value, value + PAIR_RESOLUTIONS[i - 1]] end |
#full? ⇒ Boolean
Determines if a code is a valid full Open Location Code.
Not all possible combinations of Open Location Code characters decode to
valid latitude and longitude values. This checks that a code is valid
and also that the latitude and longitude values are legal. If the prefix
character is present, it must be the first character. If the separator
character is present, it must be after four characters.
@return [Boolean]
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/open_location_code/decoder.rb', line 152 def full? return false unless valid? # If it's short, it's not full. return false if short? # Work out what the first latitude character indicates for latitude. first_lat_value = CODE_ALPHABET.index(code[0].upcase) * ENCODING_BASE #The code would decode to a latitude of >= 90 degrees. return false if first_lat_value >= LATITUDE_MAX * 2 if code.length > 1 # Work out what the first longitude character indicates for longitude. first_lng_value = CODE_ALPHABET.index(code[1].upcase) * ENCODING_BASE # The code would decode to a longitude of >= 180 degrees. return false if first_lng_value >= LONGITUDE_MAX * 2 end return true end |
#process ⇒ CodeArea
Decode opne location code
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/open_location_code/decoder.rb', line 18 def process unless full? raise OLCError, "Passed Open Location Code is not a valid full code: #{code}" end # Strip out separator character (we've already established the code is # valid so the maximum is one), padding characters and convert to upper case. code.sub!(SEPARATOR, '') code.sub!(/#{PADDING_CHARACTER}+/, '') code.upcase! # Decode the lat/lng pair component. code_area = decode_pairs(code[0, PAIR_CODE_LENGTH]) # If there is a grid refinement component, decode that. return code_area if code.length <= PAIR_CODE_LENGTH grid_area = decode_grid(code[PAIR_CODE_LENGTH, code.length - 1]) CodeArea.new( code_area.latitude_lo + grid_area.latitude_lo, code_area.longitude_lo + grid_area.longitude_lo, code_area.latitude_lo + grid_area.latitude_hi, code_area.longitude_lo + grid_area.longitude_hi, code_area.code_length + grid_area.code_length ) end |
#short? ⇒ Boolean
Determines if a code is a valid short code.
A short Open Location Code is a sequence created by removing four or more
digits from an Open Location Code. It must include a separator
character.
@return [Boolean]
246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/open_location_code/decoder.rb', line 246 def short? # Check it's valid. return false unless valid? # If there are less characters than expected before the SEPARATOR. separator_index = code.index(SEPARATOR).to_i if separator_index >= 0 && separator_index < SEPARATOR_POSITION return true end return false end |
#valid? ⇒ Boolean
Determines if a code is valid.
To be valid, all characters must be from the Open Location Code character
set with at most one separator. The separator can be in any even-numbered
position up to the eighth digit.
@return [Boolean]
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/open_location_code/decoder.rb', line 184 def valid? return false if code.nil? || code.length == 0 # The separator is required. return false unless code.index(SEPARATOR) if code.index(SEPARATOR) != code.rindex(SEPARATOR) return false end # Is it in an illegal position? if code.index(SEPARATOR) > SEPARATOR_POSITION || code.index(SEPARATOR) % 2 == 1 return false end # We can have an even number of padding characters before the separator, # but then it must be the final character. if code.index(PADDING_CHARACTER) # Not allowed to start with them! return false if code.index(PADDING_CHARACTER) == 0 # There can only be one group and it must have even length. pad_match = code.scan(Regexp.new('(' + PADDING_CHARACTER + '+)')).collect{|m| m} if (pad_match.length > 1 || pad_match[0].length % 2 == 1 || pad_match[0].length > SEPARATOR_POSITION - 2) return false end # If the code is long enough to end with a separator, make sure it does. return false if code[code.length - 1] != SEPARATOR end # If there are characters after the separator, make sure there isn't just # one of them (not legal). return false if (code.length - code.index(SEPARATOR) - 1) == 1 # Strip the separator and any padding characters. code.sub!(Regexp.new('\\' + SEPARATOR + '+'), '') code.sub!(Regexp.new(PADDING_CHARACTER + '+'), '') # Check the code contains only valid characters. code.length.times.each do |i| character = code[i].upcase if (character != SEPARATOR && CODE_ALPHABET.index(character) == -1) return false end end return true end |