gpx 0.7 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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