Move more api-only methods into api_controller
[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 = Time.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       # TODO: create animated gif
55       # https://github.com/openstreetmap/openstreetmap-website/issues/281
56       image = GD2::Image::IndexedColor.new(width, height)
57
58       black = image.palette.allocate(GD2::Color[0, 0, 0])
59       white = image.palette.allocate(GD2::Color[255, 255, 255])
60
61       image.draw do |pen|
62         pen.color = white
63         pen.rectangle(0, 0, width, height, true)
64       end
65
66       image.draw do |pen|
67         pen.color = black
68         pen.anti_aliasing = true
69         pen.dont_blend = false
70
71         oldpx = 0.0
72         oldpy = 0.0
73
74         first = true
75
76         points do |p|
77           px = proj.x(p.longitude)
78           py = proj.y(p.latitude)
79
80           pen.line(px, py, oldpx, oldpy) unless first
81
82           first = false
83           oldpy = py
84           oldpx = px
85         end
86       end
87
88       image.gif
89     end
90
91     def icon(min_lat, min_lon, max_lat, max_lon)
92       width = 50
93       height = 50
94       proj = OSM::Mercator.new(min_lat, min_lon, max_lat, max_lon, width, height)
95
96       image = GD2::Image::IndexedColor.new(width, height)
97
98       black = image.palette.allocate(GD2::Color[0, 0, 0])
99       white = image.palette.allocate(GD2::Color[255, 255, 255])
100
101       image.draw do |pen|
102         pen.color = white
103         pen.rectangle(0, 0, width, height, true)
104       end
105
106       image.draw do |pen|
107         pen.color = black
108         pen.anti_aliasing = true
109         pen.dont_blend = false
110
111         oldpx = 0.0
112         oldpy = 0.0
113
114         first = true
115
116         points do |p|
117           px = proj.x(p.longitude)
118           py = proj.y(p.latitude)
119
120           pen.line(px, py, oldpx, oldpy) unless first
121
122           first = false
123           oldpy = py
124           oldpx = px
125         end
126       end
127
128       image.gif
129     end
130   end
131
132   TrkPt = Struct.new(:segment, :latitude, :longitude, :altitude, :timestamp) do
133     def valid?
134       latitude && longitude && timestamp &&
135         latitude >= -90 && latitude <= 90 &&
136         longitude >= -180 && longitude <= 180
137     end
138   end
139 end