Standardise on double quoted strings
[rails.git] / lib / gpx.rb
1 module GPX
2   class File
3     require "libxml"
4
5     include LibXML
6
7     attr_reader :possible_points
8     attr_reader :actual_points
9     attr_reader :tracksegs
10
11     def initialize(file)
12       @file = file
13     end
14
15     def points
16       @possible_points = 0
17       @actual_points = 0
18       @tracksegs = 0
19
20       @file.rewind
21
22       reader = XML::Reader.io(@file)
23
24       point = nil
25
26       while reader.read
27         if reader.node_type == XML::Reader::TYPE_ELEMENT
28           if reader.name == "trkpt"
29             point = TrkPt.new(@tracksegs, reader["lat"].to_f, reader["lon"].to_f)
30             @possible_points += 1
31           elsif reader.name == "ele" && point
32             point.altitude = reader.read_string.to_f
33           elsif reader.name == "time" && point
34             point.timestamp = DateTime.parse(reader.read_string)
35           end
36         elsif reader.node_type == XML::Reader::TYPE_END_ELEMENT
37           if reader.name == "trkpt" && point && point.valid?
38             point.altitude ||= 0
39             yield point
40             @actual_points += 1
41           elsif reader.name == "trkseg"
42             @tracksegs += 1
43           end
44         end
45       end
46     end
47
48     def picture(min_lat, min_lon, max_lat, max_lon, num_points)
49       frames = 10
50       width = 250
51       height = 250
52       proj = OSM::Mercator.new(min_lat, min_lon, max_lat, max_lon, width, height)
53
54       linegc = Magick::Draw.new
55       linegc.stroke_linejoin("miter")
56       linegc.stroke_width(1)
57       linegc.stroke("#BBBBBB")
58       linegc.fill("#BBBBBB")
59
60       highlightgc = Magick::Draw.new
61       highlightgc.stroke_linejoin("miter")
62       highlightgc.stroke_width(3)
63       highlightgc.stroke("#000000")
64       highlightgc.fill("#000000")
65
66       images = frames.times.collect do
67         Magick::Image.new(width, height) do |image|
68           image.background_color = "white"
69           image.format = "GIF"
70         end
71       end
72
73       oldpx = 0.0
74       oldpy = 0.0
75
76       m = 0
77       mm = 0
78       points do |p|
79         px = proj.x(p.longitude)
80         py = proj.y(p.latitude)
81
82         if m > 0
83           frames.times do |n|
84             if n == mm
85               gc = highlightgc.dup
86             else
87               gc = linegc.dup
88             end
89
90             gc.line(px, py, oldpx, oldpy)
91
92             gc.draw(images[n])
93           end
94         end
95
96         m += 1
97         mm += 1 if m > num_points.to_f / frames.to_f * (mm + 1)
98
99         oldpy = py
100         oldpx = px
101       end
102
103       il = Magick::ImageList.new
104
105       images.each do |f|
106         il << f
107       end
108
109       il.delay = 50
110       il.format = "GIF"
111
112       il.to_blob
113     end
114
115     def icon(min_lat, min_lon, max_lat, max_lon)
116       width = 50
117       height = 50
118       proj = OSM::Mercator.new(min_lat, min_lon, max_lat, max_lon, width, height)
119
120       gc = Magick::Draw.new
121       gc.stroke_linejoin("miter")
122       gc.stroke_width(1)
123       gc.stroke("#000000")
124       gc.fill("#000000")
125
126       image = Magick::Image.new(width, height) do |i|
127         i.background_color = "white"
128         i.format = "GIF"
129       end
130
131       oldpx = 0.0
132       oldpy = 0.0
133
134       first = true
135
136       points do |p|
137         px = proj.x(p.longitude)
138         py = proj.y(p.latitude)
139
140         gc.dup.line(px, py, oldpx, oldpy).draw(image) unless first
141
142         first = false
143         oldpy = py
144         oldpx = px
145       end
146
147       image.to_blob
148     end
149   end
150
151   private
152
153   class TrkPt < Struct.new(:segment, :latitude, :longitude, :altitude, :timestamp)
154     def valid?
155       latitude && longitude && timestamp &&
156         latitude >= -90 && latitude <= 90 &&
157         longitude >= -180 && longitude <= 180
158     end
159   end
160 end