class SGS::Location
Class for dealing with latitude/longitude. Includes methods for parsing, converting to a printable string, and so forth.
Note that for convenience, we retain latitude and longitude in Radians rather than degrees.
Attributes
Public Class Methods
Create the Location
instance.
# File lib/sgs/location.rb, line 53 def initialize(lat = nil, long = nil) @latitude = lat.to_f if lat @longitude = long.to_f if long end
Create a new location from a lat/long string pair Uses the instance method to parse.
# File lib/sgs/location.rb, line 111 def self.parse(latstr, longstr) loc = new loc.parse(latstr, longstr) loc end
Create a new location from a string. Uses the instance method to parse.
# File lib/sgs/location.rb, line 102 def self.parse_str(str) loc = new loc.parse_str(str) loc end
Public Instance Methods
Calculate a new position from the current position given a bearing (angle and distance)
This code was derived from formulae on the Movable Type site: www.movable-type.co.uk/scripts/latlong.html
var lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
Math.cos(lat1)*Math.sin(d/R)*Math.cos(angle) );
var lon2 = lon1 + Math.atan2(Math.sin(angle)*Math.sin(d/R)*Math.cos(lat1),
Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));
# File lib/sgs/location.rb, line 76 def +(bearing) loc = Location.new sin_angle = Math.sin(bearing.angle) cos_angle = Math.cos(bearing.angle) sin_dstr = Math.sin(bearing.distance / SGS::EARTH_RADIUS) cos_dstr = Math.cos(bearing.distance / SGS::EARTH_RADIUS) sin_lat1 = Math.sin(@latitude) cos_lat1 = Math.cos(@latitude) loc.latitude = Math.asin(sin_lat1*cos_dstr + cos_lat1*sin_dstr*cos_angle) sin_lat2 = Math.sin(@latitude) loc.longitude = @longitude + Math.atan2(sin_angle*sin_dstr*cos_lat1, cos_dstr - sin_lat1*sin_lat2) loc end
The difference between two locations is a Bearing
# File lib/sgs/location.rb, line 60 def -(loc) puts "Distance from #{self} to #{loc}" Bearing.compute(self, loc) end
# File lib/sgs/location.rb, line 165 def latitude_array(fmt = nil) make_ll_array latitude_d, "NS", fmt end
Helper functions for working in degrees.
# File lib/sgs/location.rb, line 157 def latitude_d Bearing.rtod @latitude end
# File lib/sgs/location.rb, line 161 def latitude_d=(val) @latitude = Bearing.dtor val end
# File lib/sgs/location.rb, line 177 def longitude_array(fmt = nil) make_ll_array longitude_d, "EW", fmt end
# File lib/sgs/location.rb, line 169 def longitude_d Bearing.rtod @longitude end
# File lib/sgs/location.rb, line 173 def longitude_d=(val) @longitude = Bearing.dtor val end
Move to the new location
# File lib/sgs/location.rb, line 93 def move!(bearing) loc = calculate(bearing) self.latitude = loc.latitude self.longitude = loc.longitude end
Parse a lat/long value pair (in degrees)
# File lib/sgs/location.rb, line 126 def parse(latstr, longstr) @latitude = ll_parse(latstr.split, "NS") @longitude = ll_parse(longstr.split, "EW") end
Parse a lat/long value (in degrees)
# File lib/sgs/location.rb, line 119 def parse_str(str) latstr, longstr = str.split(',') parse(latstr, longstr) end
Display the lat/long as it would appear in a KML file.
# File lib/sgs/location.rb, line 149 def to_kml(sep = ',') vals = [@longitude, @latitude, 0.0] str_vals = vals.map {|val| "%.8f" % Bearing.rtod(val)} str_vals.join(sep) end
Display the lat/long as a useful string (in degrees).
# File lib/sgs/location.rb, line 139 def to_s if valid? "%s, %s" % [ll_to_s(@latitude, "NS"), ll_to_s(@longitude, "EW")] else "unknown" end end
Is this location valid?
# File lib/sgs/location.rb, line 133 def valid? @latitude and @longitude end
Private Instance Methods
Parse a string into a lat or long.
# File lib/sgs/location.rb, line 190 def ll_parse(args, nsew) dir = args[-1].gsub(/[\d\. ]+/, '').upcase args.map! {|val| val.to_f} val = args.shift val = val + args.shift / 60.0 if args.length > 0 val = val + args.shift / 3600.0 if args.length > 0 Bearing.dtor val * ((nsew.index(dir) == 1) ? -1 : 1) end
# File lib/sgs/location.rb, line 201 def ll_to_s(val, str) if val < 0.0 chr = str[1] val = -val else chr = str[0] end "%8.6f%c" % [Bearing.rtod(val), chr] end
Create a Lat/Long array suitable for an NMEA
output
# File lib/sgs/location.rb, line 213 def make_ll_array(val, nsew, fmt = nil) fmt ||= "%02d%07.4f" if (val < 0) val = -val ne = nsew[1] else ne = nsew[0] end deg = val.to_i val = (val - deg) * 60 [fmt % [deg, val], ne.chr] end