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].

Parameters:

  • code (String)

    a plus+codes

Returns:

  • (CodeArea)

    a code area which contains the coordinates

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 < PAIR_CODE_LENGTH
      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 = PAIR_CODE_LENGTH) ⇒ String

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

Parameters:

  • latitude (Numeric)

    a latitude in degrees

  • longitude (Numeric)

    a longitude in degrees

  • code_length (Integer) (defaults to: PAIR_CODE_LENGTH)

    the number of characters in the code, this excludes the separator

Returns:

  • (String)

    a 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 = PAIR_CODE_LENGTH)
  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).

Parameters:

  • code (String)

    a plus+codes

Returns:

  • (TrueClass, FalseClass)

    true if the code is full, false otherwise



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.

Parameters:

  • short_code (String)

    a plus+codes

  • reference_latitude (Numeric)

    a reference latitude in degrees

  • reference_longitude (Numeric)

    a reference longitude in degrees

Returns:

  • (String)

    a plus+codes

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
# 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)

  resolution = precision_by_length(prefix_len)
  half_res = resolution / 2

  latitude = code_area.latitude_center
  if (ref_lat + half_res < latitude && latitude - resolution >= -90)
    latitude -= resolution
  elsif (ref_lat - half_res > latitude && latitude + resolution <= 90)
    latitude += resolution
  end

  longitude = code_area.longitude_center
  if (ref_lng + half_res < longitude)
    longitude -= resolution
  elsif (ref_lng - half_res > longitude)
    longitude += resolution
  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).

Parameters:

  • code (String)

    a plus+codes

Returns:

  • (TrueClass, FalseClass)

    true if the code is short, false otherwise



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.

Parameters:

  • code (String)

    a plus+codes

  • latitude (Numeric)

    a latitude in degrees

  • longitude (Numeric)

    a longitude in degrees

Returns:

  • (String)

    a short plus+codes

Raises:

  • (ArgumentError)


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

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) * 0.3
    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.

Parameters:

  • code (String)

    a plus+codes

Returns:

  • (TrueClass, FalseClass)

    true if the code is valid, false otherwise



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