]> git.openstreetmap.org Git - rails.git/blob - lib/gpx.rb
Fix gap for iconless nodes in history view
[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" and point
32             point.altitude = reader.read_string.to_f
33           elsif reader.name == "time" and 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" and point and 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 = []
67
68       frames.times do
69         image = Magick::Image.new(width, height) do |image|
70           image.background_color = 'white'
71           image.format = 'GIF'
72         end
73
74         images << image
75       end
76
77       oldpx = 0.0
78       oldpy = 0.0
79
80       first = true
81
82       m = 0
83       mm = 0
84       points do |p|
85         px = proj.x(p.longitude)
86         py = proj.y(p.latitude)
87
88         if m > 0
89           frames.times do |n|
90             if n == mm
91               gc = highlightgc.dup
92             else
93               gc = linegc.dup
94             end
95
96             gc.line(px, py, oldpx, oldpy)
97
98             gc.draw(images[n])
99           end
100         end
101
102         m += 1
103         if m > num_points.to_f / frames.to_f * (mm+1)
104           mm += 1
105         end
106
107         oldpy = py
108         oldpx = px
109       end
110
111       il = Magick::ImageList.new
112
113       images.each do |f|
114         il << f
115       end
116
117       il.delay = 50
118       il.format = 'GIF'
119
120       return il.to_blob
121     end
122
123     def icon(min_lat, min_lon, max_lat, max_lon)
124       width = 50
125       height = 50
126       proj = OSM::Mercator.new(min_lat, min_lon, max_lat, max_lon, width, height)
127
128       gc = Magick::Draw.new
129       gc.stroke_linejoin('miter')
130       gc.stroke_width(1)
131       gc.stroke('#000000')
132       gc.fill('#000000')
133
134       image = Magick::Image.new(width, height) do |image|
135         image.background_color = 'white'
136         image.format = 'GIF'
137       end
138
139       oldpx = 0.0
140       oldpy = 0.0
141
142       first = true
143
144       points do |p|
145         px = proj.x(p.longitude)
146         py = proj.y(p.latitude)
147
148         gc.dup.line(px, py, oldpx, oldpy).draw(image) unless first
149
150         first = false
151         oldpy = py
152         oldpx = px
153       end
154
155       return image.to_blob
156     end
157   end
158
159 private
160
161   class TrkPt < Struct.new(:segment, :latitude, :longitude, :altitude, :timestamp)
162     def valid?
163       self.latitude and self.longitude and self.timestamp and
164       self.latitude >= -90 and self.latitude <= 90 and
165       self.longitude >= -180 and self.longitude <= 180
166     end
167   end
168 end