gpx 0.7 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2006 Doug Fales
2
+ # Copyright (c) 2006 Doug Fales
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -21,114 +21,112 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  #++
23
23
  module GPX
24
+ # This class will parse the lat/lon and time data from a Magellan track log,
25
+ # which is a NMEA formatted CSV list of points.
26
+ class MagellanTrackLog
27
+ #PMGNTRK
28
+ # This message is to be used to transmit Track information (basically a list of previous position fixes)
29
+ # which is often displayed on the plotter or map screen of the unit. The first field in this message
30
+ # is the Latitude, followed by N or S. The next field is the Longitude followed by E or W. The next
31
+ # field is the altitude followed by “F” for feet or “M” for meters. The next field is
32
+ # the UTC time of the fix. The next field consists of a status letter of “A” to indicated that
33
+ # the data is valid, or “V” to indicate that the data is not valid. The last character field is
34
+ # the name of the track, for those units that support named tracks. The last field contains the UTC date
35
+ # of the fix. Note that this field is (and its preceding comma) is only produced by the unit when the
36
+ # command PMGNCMD,TRACK,2 is given. It is not present when a simple command of PMGNCMD,TRACK is issued.
37
+
38
+ #NOTE: The Latitude and Longitude Fields are shown as having two decimal
39
+ # places. As many additional decimal places may be added as long as the total
40
+ # length of the message does not exceed 82 bytes.
41
+
42
+ # $PMGNTRK,llll.ll,a,yyyyy.yy,a,xxxxx,a,hhmmss.ss,A,c----c,ddmmyy*hh<CR><LF>
43
+ require 'csv'
44
+
45
+ LAT = 1
46
+ LAT_HEMI = 2
47
+ LON = 3
48
+ LON_HEMI = 4
49
+ ELE = 5
50
+ ELE_UNITS = 6
51
+ TIME = 7
52
+ INVALID_FLAG = 8
53
+ DATE = 10
54
+
55
+ FEET_TO_METERS = 0.3048
56
+
57
+ class << self
58
+
59
+ # Takes the name of a magellan file, converts the contents to GPX, and
60
+ # writes the result to gpx_filename.
61
+ def convert_to_gpx(magellan_filename, gpx_filename)
62
+
63
+ segment = Segment.new
64
+
65
+ CSV.open(magellan_filename, "r").each do |row|
66
+ next if(row.size < 10 or row[INVALID_FLAG] == 'V')
67
+
68
+ lat_deg = row[LAT][0..1]
69
+ lat_min = row[LAT][2...-1]
70
+ lat_hemi = row[LAT_HEMI]
71
+
72
+ lat = lat_deg.to_f + (lat_min.to_f / 60.0)
73
+ lat = (-lat) if(lat_hemi == 'S')
74
+
75
+ lon_deg = row[LON][0..2]
76
+ lon_min = row[LON][3..-1]
77
+ lon_hemi = row[LON_HEMI]
78
+
79
+ lon = lon_deg.to_f + (lon_min.to_f / 60.0)
80
+ lon = (-lon) if(lon_hemi == 'W')
81
+
82
+
83
+ ele = row[ELE]
84
+ ele_units = row[ELE_UNITS]
85
+ ele = ele.to_f
86
+ if(ele_units == 'F')
87
+ ele *= FEET_TO_METERS
88
+ end
89
+
90
+ hrs = row[TIME][0..1].to_i
91
+ mins = row[TIME][2..3].to_i
92
+ secs = row[TIME][4..5].to_i
93
+ day = row[DATE][0..1].to_i
94
+ mon = row[DATE][2..3].to_i
95
+ yr = 2000 + row[DATE][4..5].to_i
96
+
97
+ time = Time.gm(yr, mon, day, hrs, mins, secs)
98
+
99
+ #must create point
100
+ pt = TrackPoint.new(:lat => lat, :lon => lon, :time => time, :elevation => ele)
101
+ segment.append_point(pt)
102
+
103
+ end
104
+
105
+ trk = Track.new
106
+ trk.append_segment(segment)
107
+ gpx_file = GPXFile.new(:tracks => [trk])
108
+ gpx_file.write(gpx_filename)
24
109
 
25
- # This class will parse the lat/lon and time data from a Magellan track log,
26
- # which is a NMEA formatted CSV list of points.
27
-
28
- class MagellanTrackLog
29
- #PMGNTRK
30
- # This message is to be used to transmit Track information (basically a list of previous position fixes)
31
- # which is often displayed on the plotter or map screen of the unit. The first field in this message
32
- # is the Latitude, followed by N or S. The next field is the Longitude followed by E or W. The next
33
- # field is the altitude followed by “F” for feet or “M” for meters. The next field is
34
- # the UTC time of the fix. The next field consists of a status letter of “A” to indicated that
35
- # the data is valid, or “V” to indicate that the data is not valid. The last character field is
36
- # the name of the track, for those units that support named tracks. The last field contains the UTC date
37
- # of the fix. Note that this field is (and its preceding comma) is only produced by the unit when the
38
- # command PMGNCMD,TRACK,2 is given. It is not present when a simple command of PMGNCMD,TRACK is issued.
39
-
40
- #NOTE: The Latitude and Longitude Fields are shown as having two decimal
41
- # places. As many additional decimal places may be added as long as the total
42
- # length of the message does not exceed 82 bytes.
43
-
44
- # $PMGNTRK,llll.ll,a,yyyyy.yy,a,xxxxx,a,hhmmss.ss,A,c----c,ddmmyy*hh<CR><LF>
45
- require 'csv'
46
-
47
- LAT = 1
48
- LAT_HEMI = 2
49
- LON = 3
50
- LON_HEMI = 4
51
- ELE = 5
52
- ELE_UNITS = 6
53
- TIME = 7
54
- INVALID_FLAG = 8
55
- DATE = 10
56
-
57
- FEET_TO_METERS = 0.3048
58
-
59
- class << self
60
-
61
- # Takes the name of a magellan file, converts the contents to GPX, and
62
- # writes the result to gpx_filename.
63
- def convert_to_gpx(magellan_filename, gpx_filename)
64
-
65
- segment = Segment.new
66
-
67
- CSV.open(magellan_filename, "r").each do |row|
68
- next if(row.size < 10 or row[INVALID_FLAG] == 'V')
69
-
70
- lat_deg = row[LAT][0..1]
71
- lat_min = row[LAT][2...-1]
72
- lat_hemi = row[LAT_HEMI]
73
-
74
- lat = lat_deg.to_f + (lat_min.to_f / 60.0)
75
- lat = (-lat) if(lat_hemi == 'S')
76
-
77
- lon_deg = row[LON][0..2]
78
- lon_min = row[LON][3..-1]
79
- lon_hemi = row[LON_HEMI]
80
-
81
- lon = lon_deg.to_f + (lon_min.to_f / 60.0)
82
- lon = (-lon) if(lon_hemi == 'W')
83
-
84
-
85
- ele = row[ELE]
86
- ele_units = row[ELE_UNITS]
87
- ele = ele.to_f
88
- if(ele_units == 'F')
89
- ele *= FEET_TO_METERS
90
- end
91
-
92
- hrs = row[TIME][0..1].to_i
93
- mins = row[TIME][2..3].to_i
94
- secs = row[TIME][4..5].to_i
95
- day = row[DATE][0..1].to_i
96
- mon = row[DATE][2..3].to_i
97
- yr = 2000 + row[DATE][4..5].to_i
98
-
99
- time = Time.gm(yr, mon, day, hrs, mins, secs)
100
-
101
- #must create point
102
- pt = TrackPoint.new(:lat => lat, :lon => lon, :time => time, :elevation => ele)
103
- segment.append_point(pt)
104
-
105
- end
106
-
107
- trk = Track.new
108
- trk.append_segment(segment)
109
- gpx_file = GPXFile.new(:tracks => [trk])
110
- gpx_file.write(gpx_filename)
111
-
112
- end
113
-
114
- # Tests to see if the given file is a magellan NMEA track log.
115
- def is_magellan_file?(filename)
116
- i = 0
117
- File.open(filename, "r") do |f|
118
- f.each do |line|
119
- i += 1
120
- if line =~ /^\$PMGNTRK/
121
- return true
122
- elsif line =~ /<\?xml/
123
- return false
124
- elsif(i > 10)
125
- return false
126
- end
127
- end
110
+ end
111
+
112
+ # Tests to see if the given file is a magellan NMEA track log.
113
+ def is_magellan_file?(filename)
114
+ i = 0
115
+ File.open(filename, "r") do |f|
116
+ f.each do |line|
117
+ i += 1
118
+ if line =~ /^\$PMGNTRK/
119
+ return true
120
+ elsif line =~ /<\?xml/
121
+ return false
122
+ elsif(i > 10)
123
+ return false
128
124
  end
129
- return false
130
- end
125
+ end
126
+ end
127
+ return false
131
128
  end
129
+ end
132
130
 
133
- end
131
+ end
134
132
  end
data/lib/gpx/point.rb CHANGED
@@ -20,89 +20,73 @@
20
20
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  #++
23
- include Math
24
23
  module GPX
25
- # The base class for all points. Trackpoint and Waypoint both descend from this base class.
26
- class Point < Base
27
- D_TO_R = PI/180.0;
28
- attr_accessor :lat, :lon, :time, :elevation, :gpx_file, :speed
29
-
30
- # When you need to manipulate individual points, you can create a Point
31
- # object with a latitude, a longitude, an elevation, and a time. In
32
- # addition, you can pass an XML element to this initializer, and the
33
- # relevant info will be parsed out.
34
- def initialize(opts = {:lat => 0.0, :lon => 0.0, :elevation => 0.0, :time => Time.now } )
35
- @gpx_file = opts[:gpx_file]
36
- if (opts[:element])
37
- elem = opts[:element]
38
- @lat, @lon = elem["lat"].to_f, elem["lon"].to_f
39
- @latr, @lonr = (D_TO_R * @lat), (D_TO_R * @lon)
40
- #'-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
41
- @time = (Time.xmlschema(elem.at("time").inner_text) rescue nil)
42
- @elevation = elem.at("ele").inner_text.to_f unless elem.at("ele").nil?
43
- @speed = elem.at("speed").inner_text.to_f unless elem.at("speed").nil?
44
- else
45
- @lat = opts[:lat]
46
- @lon = opts[:lon]
47
- @elevation = opts[:elevation]
48
- @time = opts[:time]
49
- @speed = opts[:speed]
50
- end
24
+ # The base class for all points. Trackpoint and Waypoint both descend from this base class.
25
+ class Point < Base
26
+ D_TO_R = Math::PI/180.0;
27
+ attr_accessor :lat, :lon, :time, :elevation, :gpx_file, :speed, :extensions
51
28
 
29
+ # When you need to manipulate individual points, you can create a Point
30
+ # object with a latitude, a longitude, an elevation, and a time. In
31
+ # addition, you can pass an XML element to this initializer, and the
32
+ # relevant info will be parsed out.
33
+ def initialize(opts = {:lat => 0.0, :lon => 0.0, :elevation => 0.0, :time => Time.now } )
34
+ @gpx_file = opts[:gpx_file]
35
+ if (opts[:element])
36
+ elem = opts[:element]
37
+ @lat, @lon = elem["lat"].to_f, elem["lon"].to_f
38
+ @latr, @lonr = (D_TO_R * @lat), (D_TO_R * @lon)
39
+ #'-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
40
+ @time = (Time.xmlschema(elem.at("time").inner_text) rescue nil)
41
+ @elevation = elem.at("ele").inner_text.to_f unless elem.at("ele").nil?
42
+ @speed = elem.at("speed").inner_text.to_f unless elem.at("speed").nil?
43
+ @extensions = elem.at("extensions") unless elem.at("extensions").nil?
44
+ else
45
+ @lat = opts[:lat]
46
+ @lon = opts[:lon]
47
+ @elevation = opts[:elevation]
48
+ @time = opts[:time]
49
+ @speed = opts[:speed]
50
+ @extensions = opts[:extensions]
52
51
  end
53
52
 
53
+ end
54
54
 
55
- # Returns the latitude and longitude (in that order), separated by the
56
- # given delimeter. This is useful for passing a point into another API
57
- # (i.e. the Google Maps javascript API).
58
- def lat_lon(delim = ', ')
59
- "#{lat}#{delim}#{lon}"
60
- end
61
55
 
62
- # Returns the longitude and latitude (in that order), separated by the
63
- # given delimeter. This is useful for passing a point into another API
64
- # (i.e. the Google Maps javascript API).
65
- def lon_lat(delim = ', ')
66
- "#{lon}#{delim}#{lat}"
67
- end
56
+ # Returns the latitude and longitude (in that order), separated by the
57
+ # given delimeter. This is useful for passing a point into another API
58
+ # (i.e. the Google Maps javascript API).
59
+ def lat_lon(delim = ', ')
60
+ "#{lat}#{delim}#{lon}"
61
+ end
68
62
 
69
- # Latitude in radians.
70
- def latr
71
- @latr ||= (@lat * D_TO_R)
72
- end
63
+ # Returns the longitude and latitude (in that order), separated by the
64
+ # given delimeter. This is useful for passing a point into another API
65
+ # (i.e. the Google Maps javascript API).
66
+ def lon_lat(delim = ', ')
67
+ "#{lon}#{delim}#{lat}"
68
+ end
73
69
 
74
- # Longitude in radians.
75
- def lonr
76
- @lonr ||= (@lon * D_TO_R)
77
- end
70
+ # Latitude in radians.
71
+ def latr
72
+ @latr ||= (@lat * D_TO_R)
73
+ end
78
74
 
79
- # Set the latitude (in degrees).
80
- def lat=(latitude)
81
- @latr = (latitude * D_TO_R)
82
- @lat = latitude
83
- end
84
-
85
- # Set the longitude (in degrees).
86
- def lon=(longitude)
87
- @lonr = (longitude * D_TO_R)
88
- @lon = longitude
89
- end
75
+ # Longitude in radians.
76
+ def lonr
77
+ @lonr ||= (@lon * D_TO_R)
78
+ end
90
79
 
91
- # Convert this point to a XML::Node.
92
- def to_xml(elem_name = 'trkpt')
93
- pt = Node.new(elem_name)
94
- pt['lat'] = lat.to_s
95
- pt['lon'] = lon.to_s
96
- unless time.nil?
97
- time_elem = Node.new('time')
98
- time_elem << time.xmlschema
99
- pt << time_elem
100
- end
101
- elev = Node.new('ele')
102
- elev << elevation
103
- pt << elev
104
- pt
105
- end
80
+ # Set the latitude (in degrees).
81
+ def lat=(latitude)
82
+ @latr = (latitude * D_TO_R)
83
+ @lat = latitude
84
+ end
106
85
 
107
- end
86
+ # Set the longitude (in degrees).
87
+ def lon=(longitude)
88
+ @lonr = (longitude * D_TO_R)
89
+ @lon = longitude
90
+ end
91
+ end
108
92
  end
data/lib/gpx/route.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2006 Doug Fales
2
+ # Copyright (c) 2006 Doug Fales
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -21,50 +21,38 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  #++
23
23
  module GPX
24
-
25
- # A Route in GPX is very similar to a Track, but it is created by a user
26
- # from a series of Waypoints, whereas a Track is created by the GPS device
27
- # automatically logging your progress at regular intervals.
28
- class Route < Base
29
-
30
- attr_accessor :points, :name, :gpx_file
31
-
32
- # Initialize a Route from a XML::Node.
33
- def initialize(opts = {})
34
- if(opts[:gpx_file] and opts[:element])
35
- rte_element = opts[:element]
36
- @gpx_file = opts[:gpx_file]
37
- @name = rte_element.at("//name").inner_text
38
- @points = []
39
- rte_element.search("//rtept").each do |point|
40
- @points << Point.new(:element => point, :gpx_file => @gpx_file)
41
- end
42
- else
43
- @points = (opts[:points] or [])
44
- @name = (opts[:name])
45
- end
46
-
24
+ # A Route in GPX is very similar to a Track, but it is created by a user
25
+ # from a series of Waypoints, whereas a Track is created by the GPS device
26
+ # automatically logging your progress at regular intervals.
27
+ class Route < Base
28
+
29
+ attr_accessor :points, :name, :gpx_file
30
+
31
+ # Initialize a Route from a XML::Node.
32
+ def initialize(opts = {})
33
+ if(opts[:gpx_file] and opts[:element])
34
+ rte_element = opts[:element]
35
+ @gpx_file = opts[:gpx_file]
36
+ @name = rte_element.at("name").inner_text
37
+ @points = []
38
+ rte_element.search("rtept").each do |point|
39
+ @points << Point.new(:element => point, :gpx_file => @gpx_file)
40
+ end
41
+ else
42
+ @points = (opts[:points] or [])
43
+ @name = (opts[:name])
47
44
  end
48
45
 
49
- # Delete points outside of a given area.
50
- def crop(area)
51
- points.delete_if{ |pt| not area.contains? pt }
52
- end
53
-
54
- # Delete points within the given area.
55
- def delete_area(area)
56
- points.delete_if{ |pt| area.contains? pt }
57
- end
46
+ end
58
47
 
59
- # Convert this Route to a XML::Node.
60
- def to_xml
61
- rte = Node.new('rte')
62
- name_elem = Node.new('name')
63
- name_elem << name
64
- rte << name_elem
65
- points.each { |rte_pt| rte << rte_pt.to_xml('rtept') }
66
- rte
67
- end
48
+ # Delete points outside of a given area.
49
+ def crop(area)
50
+ points.delete_if{ |pt| not area.contains? pt }
51
+ end
68
52
 
69
- end
53
+ # Delete points within the given area.
54
+ def delete_area(area)
55
+ points.delete_if{ |pt| area.contains? pt }
56
+ end
57
+ end
70
58
  end