Class: PlusCodes::OpenLocationCode

Inherits:
Object
  • Object
show all
Defined in:
lib/plus_codes/open_location_code.rb

Overview

OpenLocationCode

implements the Google Open Location Code(Plus+Codes) algorithm.

Author:

  • We-Ming Wu

Instance Method Summary collapse

Instance Method Details

#decode(code) ⇒ CodeArea

Decodes an Open Location Code(Plus+Codes) into a [CodeArea].

Raises:

  • (ArgumentError)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/plus_codes/open_location_code.rb', line 70

def decode(code)
  raise ArgumentError,
  "Open Location Code(Plus+Codes) is not a valid full code: #{code}" unless full?(code)

  code = code.gsub(SEPARATOR, '')
  code = code.gsub(/#{PADDING}+/, '')
  code = code.upcase

  south_latitude = -90.0
  west_longitude = -180.0

  lat_resolution = 400.to_r
  lng_resolution = 400.to_r

  digit = 0
  while digit < code.length
    if digit < 10
      lat_resolution /= 20
      lng_resolution /= 20
      south_latitude += lat_resolution * DECODE[code[digit].ord]
      west_longitude += lng_resolution * DECODE[code[digit + 1].ord]
      digit += 2
    else
      lat_resolution /= 5
      lng_resolution /= 4
      row = DECODE[code[digit].ord] / 4
      column = DECODE[code[digit].ord] % 4
      south_latitude += lat_resolution * row
      west_longitude += lng_resolution * column
      digit += 1
    end
  end

  CodeArea.new(south_latitude, west_longitude, lat_resolution, lng_resolution)
end

#encode(latitude, longitude, code_length = 10) ⇒ String

Converts a latitude and longitude into a Open Location Code(Plus+Codes).

Raises:

  • (ArgumentError)


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/plus_codes/open_location_code.rb', line 44

def encode(latitude, longitude, code_length = 10)
  raise ArgumentError,
  "Invalid Open Location Code(Plus+Codes) length: #{code_length}" if invalid_length?(code_length)

  latitude = clip_latitude(latitude)
  longitude = normalize_longitude(longitude)
  latitude -= precision_by_length(code_length) if latitude == 90

  lat = (latitude + 90).to_r
  lng = (longitude + 180).to_r

  digit = 0
  code = ''
  while digit < code_length
    lat, lng = narrow_region(digit, lat, lng)
    digit, lat, lng = build_code(digit, code, lat, lng)
    code << SEPARATOR if (digit == SEPARATOR_POSITION)
  end

  digit < SEPARATOR_POSITION ? padded(code) : code
end

#full?(code) ⇒ TrueClass, FalseClass

Determines if a string is a valid full Open Location Code(Plus+Codes).



34
35
36
# File 'lib/plus_codes/open_location_code.rb', line 34

def full?(code)
  valid?(code) && !short?(code)
end

#recover_nearest(short_code, reference_latitude, reference_longitude) ⇒ String

Recovers a full Open Location Code(Plus+Codes) from a short code and a reference location.

Raises:

  • (ArgumentError)


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
140
141
142
143
144
# File 'lib/plus_codes/open_location_code.rb', line 112

def recover_nearest(short_code, reference_latitude, reference_longitude)
  return short_code if full?(short_code)
  raise ArgumentError,
  "Open Location Code(Plus+Codes) is not valid: #{short_code}" unless short?(short_code)

  ref_lat = clip_latitude(reference_latitude)
  ref_lng = normalize_longitude(reference_longitude)

  prefix_len = SEPARATOR_POSITION - short_code.index(SEPARATOR)
  code = prefix_by_reference(ref_lat, ref_lng, prefix_len) << short_code
  code_area = decode(code)

  area_range = precision_by_length(prefix_len)
  area_edge = area_range / 2

  latitude = code_area.latitude_center
  latitude_diff = latitude - ref_lat
  if (latitude_diff > area_edge)
    latitude -= area_range
  elsif (latitude_diff < -area_edge)
    latitude += area_range
  end

  longitude = code_area.longitude_center
  longitude_diff = longitude - ref_lng
  if (longitude_diff > area_edge)
    longitude -= area_range
  elsif (longitude_diff < -area_edge)
    longitude += area_range
  end

  encode(latitude, longitude, code.length - SEPARATOR.length)
end

#short?(code) ⇒ TrueClass, FalseClass

Determines if a string is a valid short Open Location Code(Plus+Codes).



26
27
28
# File 'lib/plus_codes/open_location_code.rb', line 26

def short?(code)
  valid?(code) && code.index(SEPARATOR) < SEPARATOR_POSITION
end

#shorten(code, latitude, longitude) ⇒ String

Removes four, six or eight digits from the front of an Open Location Code(Plus+Codes) given a reference location.

Raises:

  • (ArgumentError)


152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/plus_codes/open_location_code.rb', line 152

def shorten(code, latitude, longitude)
  raise ArgumentError,
  "Open Location Code(Plus+Codes) is a valid full code: #{code}" unless full?(code)
  raise ArgumentError,
  "Cannot shorten padded codes: #{code}" unless code.index(PADDING).nil?

  code_area = decode(code)
  lat_diff = (latitude - code_area.latitude_center).abs
  lng_diff = (longitude - code_area.longitude_center).abs
  max_diff = [lat_diff, lng_diff].max
  [8, 6, 4].each do |removal_len|
    area_edge = precision_by_length(removal_len + 2) / 2
    return code[removal_len..-1] if max_diff < area_edge
  end

  code.upcase
end

#valid?(code) ⇒ TrueClass, FalseClass

Determines if a string is a valid sequence of Open Location Code(Plus+Codes) characters.



15
16
17
18
19
20
# File 'lib/plus_codes/open_location_code.rb', line 15

def valid?(code)
  valid_length?(code) &&
  valid_separator?(code) &&
  valid_padding?(code) &&
  valid_character?(code)
end