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