Class: Unlocodes::Coordinates

Inherits:
Object
  • Object
show all
Defined in:
lib/unlocodes/coordinates.rb

Overview

Geographic coordinates (WGS-84) for a LOCODE entry.

Parsed from the LOCODE coordinate string ("DDMMDDDMM" where H is one of N/S/E/W) used by the UN/LOCODE manual and the UNCEFACT vocabulary. Exposes decimal degrees so distance / bounding-box queries are practical.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(latitude: nil, longitude: nil) ⇒ Coordinates

Returns a new instance of Coordinates.



12
13
14
15
# File 'lib/unlocodes/coordinates.rb', line 12

def initialize(latitude: nil, longitude: nil)
  @latitude = latitude&.to_f
  @longitude = longitude&.to_f
end

Instance Attribute Details

#latitudeObject (readonly)

Returns the value of attribute latitude.



10
11
12
# File 'lib/unlocodes/coordinates.rb', line 10

def latitude
  @latitude
end

#longitudeObject (readonly)

Returns the value of attribute longitude.



10
11
12
# File 'lib/unlocodes/coordinates.rb', line 10

def longitude
  @longitude
end

Class Method Details

.parse(text) ⇒ Object

Parse a UN/LOCODE coordinate string like "3108N12150E" into decimal degrees. Accepts shorter / longer minute forms; returns a Coordinates with nil lat/lon when the input is blank or unparseable.

Examples:

Coordinates.parse("3108N12150E")    # => lat 31.133..., lon 121.833...
Coordinates.parse("")               # => nil lat/lon


24
25
26
27
28
29
30
31
32
33
34
# File 'lib/unlocodes/coordinates.rb', line 24

def self.parse(text)
  return new(latitude: nil, longitude: nil) if text.nil? || text.to_s.strip.empty?

  m = text.to_s.upcase.strip.match(/\A(\d{2})(\d{2})\s*([NS])\s*
                                     (\d{3})(\d{2})\s*([EW])\Z/x)
  return new(latitude: nil, longitude: nil) unless m

  lat = dms_to_decimal(m[1], m[2], m[3])
  lon = dms_to_decimal(m[4], m[5], m[6])
  new(latitude: lat, longitude: lon)
end

Instance Method Details

#==(other) ⇒ Object



47
48
49
50
51
# File 'lib/unlocodes/coordinates.rb', line 47

def ==(other)
  other.is_a?(Coordinates) &&
    latitude == other.latitude &&
    longitude == other.longitude
end

#distance_to(other) ⇒ Object

Great-circle distance in kilometres to another Coordinates, using the haversine formula. Returns nil if either side lacks coordinates.



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/unlocodes/coordinates.rb', line 61

def distance_to(other)
  return nil if latitude.nil? || longitude.nil? ||
                other.latitude.nil? || other.longitude.nil?

  earth_radius_km = 6371.0
  d_lat = (other.latitude - latitude) * (Math::PI / 180)
  d_lon = (other.longitude - longitude) * (Math::PI / 180)
  a = (Math.sin(d_lat / 2)**2) +
      (Math.cos(latitude * (Math::PI / 180)) *
       Math.cos(other.latitude * (Math::PI / 180)) *
       (Math.sin(d_lon / 2)**2))
  2 * earth_radius_km * Math.asin(Math.sqrt(a))
end

#to_aObject



43
44
45
# File 'lib/unlocodes/coordinates.rb', line 43

def to_a
  [latitude, longitude].compact
end

#to_sObject



53
54
55
56
57
# File 'lib/unlocodes/coordinates.rb', line 53

def to_s
  return '' if latitude.nil? || longitude.nil?

  format('%<lat>.4f %<lon>.4f', lat: latitude, lon: longitude)
end