Gif animation prototype
[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       return enum_for(:points) unless block_given?
17
18       @possible_points = 0
19       @actual_points = 0
20       @tracksegs = 0
21
22       @file.rewind
23
24       reader = XML::Reader.io(@file)
25
26       point = nil
27
28       while reader.read
29         if reader.node_type == XML::Reader::TYPE_ELEMENT
30           if reader.name == "trkpt"
31             point = TrkPt.new(@tracksegs, reader["lat"].to_f, reader["lon"].to_f)
32             @possible_points += 1
33           elsif reader.name == "ele" && point
34             point.altitude = reader.read_string.to_f
35           elsif reader.name == "time" && point
36             point.timestamp = Time.parse(reader.read_string)
37           end
38         elsif reader.node_type == XML::Reader::TYPE_END_ELEMENT
39           if reader.name == "trkpt" && point && point.valid?
40             point.altitude ||= 0
41             yield point
42             @actual_points += 1
43           elsif reader.name == "trkseg"
44             @tracksegs += 1
45           end
46         end
47       end
48     end
49
50     def picture(min_lat, min_lon, max_lat, max_lon, _num_points)
51       nframes = 10
52       width = 250
53       height = 250
54       delay = 50
55
56       ptsper = _num_points / nframes;
57
58       proj = OSM::Mercator.new(min_lat, min_lon, max_lat, max_lon, width, height)
59
60       frames = Array.new(nframes,  GD2::Image::IndexedColor.new(width, height))
61
62       (0..nframes - 1).each do |n|
63         frames[n] = GD2::Image::IndexedColor.new(width, height)
64         black = frames[n].palette.allocate(GD2::Color[0, 0, 0])
65         white = frames[n].palette.allocate(GD2::Color[255, 255, 255])
66         grey = frames[n].palette.allocate(GD2::Color[187, 187, 187])
67
68         frames[n].draw do |pen|
69           pen.color = white
70           pen.rectangle(0, 0, width, height, true)
71         end
72
73         frames[n].draw do |pen|
74           pen.color = black
75           pen.anti_aliasing = true
76           pen.dont_blend = false
77
78           oldpx = 0.0
79           oldpy = 0.0
80
81           first = true
82
83           points.each_with_index do |p, pt|
84             px = proj.x(p.longitude)
85             py = proj.y(p.latitude)
86
87             if ((pt >= (ptsper * n)) && (pt <= (ptsper * (n+1))))
88               pen.thickness=(3)
89               pen.color = black
90             else
91               pen.thickness=(1)
92               pen.color = grey
93             end
94
95             pen.line(px, py, oldpx, oldpy) unless first
96               first = false
97               oldpy = py
98               oldpx = px
99           end
100         end
101       end
102
103       res = GD2::AnimatedGif::gif_anim_begin(frames[0])
104       res << GD2::AnimatedGif::gif_anim_add(frames[0], nil, delay)
105       (0..nframes - 1).each do |n|
106         res << GD2::AnimatedGif::gif_anim_add(frames[n], frames[n-1], delay)
107       end
108       res << GD2::AnimatedGif::gif_anim_end()
109
110       res
111     end
112
113     def icon(min_lat, min_lon, max_lat, max_lon)
114       width = 50
115       height = 50
116       proj = OSM::Mercator.new(min_lat, min_lon, max_lat, max_lon, width, height)
117
118       image = GD2::Image::IndexedColor.new(width, height)
119
120       black = image.palette.allocate(GD2::Color[0, 0, 0])
121       white = image.palette.allocate(GD2::Color[255, 255, 255])
122
123       image.draw do |pen|
124         pen.color = white
125         pen.rectangle(0, 0, width, height, true)
126       end
127
128       image.draw do |pen|
129         pen.color = black
130         pen.anti_aliasing = true
131         pen.dont_blend = false
132
133         oldpx = 0.0
134         oldpy = 0.0
135
136         first = true
137
138         points do |p|
139           px = proj.x(p.longitude)
140           py = proj.y(p.latitude)
141
142           pen.line(px, py, oldpx, oldpy) unless first
143
144           first = false
145           oldpy = py
146           oldpx = px
147         end
148       end
149
150       image.gif
151     end
152   end
153
154   TrkPt = Struct.new(:segment, :latitude, :longitude, :altitude, :timestamp) do
155     def valid?
156       latitude && longitude && timestamp &&
157         latitude >= -90 && latitude <= 90 &&
158         longitude >= -180 && longitude <= 180
159     end
160   end
161 end