]> git.openstreetmap.org Git - rails.git/blob - app/models/trace.rb
Throw an exception if saving the imported trace fails.
[rails.git] / app / models / trace.rb
1 class Trace < ActiveRecord::Base
2   set_table_name 'gpx_files'
3
4   validates_presence_of :user_id, :name, :timestamp
5   validates_presence_of :description, :on => :create
6   validates_format_of :tagstring, :with => /^[^\/;.,?]*$/
7 #  validates_numericality_of :latitude, :longitude
8   validates_inclusion_of :public, :inserted, :in => [ true, false]
9   
10   belongs_to :user
11   has_many :tags, :class_name => 'Tracetag', :foreign_key => 'gpx_id', :dependent => :delete_all
12   has_many :points, :class_name => 'Tracepoint', :foreign_key => 'gpx_id', :dependent => :delete_all
13
14   def destroy
15     super
16     FileUtils.rm_f(trace_name)
17     FileUtils.rm_f(icon_picture_name)
18     FileUtils.rm_f(large_picture_name)
19   end
20
21   def tagstring
22     return tags.collect {|tt| tt.tag}.join(" ")
23   end
24
25   def tagstring=(s)
26     self.tags = s.split().collect {|tag|
27       tt = Tracetag.new
28       tt.tag = tag
29       tt
30     }
31   end
32   
33   def large_picture= (data)
34     f = File.new(large_picture_name, "wb")
35     f.syswrite(data)
36     f.close
37   end
38   
39   def icon_picture= (data)
40     f = File.new(icon_picture_name, "wb")
41     f.syswrite(data)
42     f.close
43   end
44
45   def large_picture
46     f = File.new(large_picture_name, "rb")
47     logger.info "large picture file: '#{f.path}', bytes: #{File.size(f.path)}"
48     data = f.sysread(File.size(f.path))
49     logger.info "have read data, bytes: '#{data.length}'"
50     f.close
51     data
52   end
53   
54   def icon_picture
55     f = File.new(icon_picture_name, "rb")
56     logger.info "icon picture file: '#{f.path}'"
57     data = f.sysread(File.size(f.path))
58     f.close
59     data
60   end
61   
62   # FIXME change to permanent filestore area
63   def large_picture_name
64     "/home/osm/icons/#{id}.gif"
65   end
66
67   # FIXME change to permanent filestore area
68   def icon_picture_name
69     "/home/osm/icons/#{id}_icon.gif"
70   end
71
72   def trace_name
73     "/home/osm/gpx/#{id}.gpx"
74   end
75
76   def mime_type
77     filetype = `/usr/bin/file -bz #{trace_name}`.chomp
78     gzipped = filetype =~ /gzip compressed/
79     bzipped = filetype =~ /bzip2 compressed/
80     zipped = filetype =~ /Zip archive/
81
82     if gzipped then
83       mimetype = "application/x-gzip"
84     elsif bzipped then
85       mimetype = "application/x-bzip2"
86     elsif zipped
87       mimetype = "application/x-zip"
88     else
89       mimetype = "text/xml"
90     end
91
92     return mimetype
93   end
94
95   def extension_name
96     filetype = `/usr/bin/file -bz #{trace_name}`.chomp
97     gzipped = filetype =~ /gzip compressed/
98     bzipped = filetype =~ /bzip2 compressed/
99     zipped = filetype =~ /Zip archive/
100     tarred = filetype =~ /tar archive/
101
102     if tarred and gzipped then
103       extension = ".tar.gz"
104     elsif tarred and bzipped then
105       extension = ".tar.bz2"
106     elsif tarred
107       extension = ".tar"
108     elsif gzipped
109       extension = ".gpx.gz"
110     elsif bzipped
111       extension = ".gpx.bz2"
112     elsif zipped
113       extension = ".zip"
114     else
115       extension = ".gpx"
116     end
117
118     return extension
119   end
120
121   def to_xml
122     doc = OSM::API.new.get_xml_doc
123     doc.root << to_xml_node()
124     return doc
125   end
126
127   def to_xml_node
128     el1 = XML::Node.new 'gpx_file'
129     el1['id'] = self.id.to_s
130     el1['name'] = self.name.to_s
131     el1['lat'] = self.latitude.to_s
132     el1['lon'] = self.longitude.to_s
133     el1['user'] = self.user.display_name
134     el1['public'] = self.public.to_s
135     el1['pending'] = (!self.inserted).to_s
136     el1['timestamp'] = self.timestamp.xmlschema
137     return el1
138   end
139
140   def import
141     begin
142       logger.info("GPX Import importing #{name} (#{id}) from #{user.email}")
143
144       # TODO *nix specific, could do to work on windows... would be functionally inferior though - check for '.gz'
145       filetype = `/usr/bin/file -bz #{trace_name}`.chomp
146       gzipped = filetype =~ /gzip compressed/
147       bzipped = filetype =~ /bzip2 compressed/
148       zipped = filetype =~ /Zip archive/
149       tarred = filetype =~ /tar archive/
150
151       if tarred and gzipped then
152         filename = tempfile = "/tmp/#{rand}"
153         system("tar -zxOf #{trace_name} > #{filename}")
154       elsif tarred and bzipped then
155         filename = tempfile = "/tmp/#{rand}"
156         system("tar -jxOf #{trace_name} > #{filename}")
157       elsif tarred
158         filename = tempfile = "/tmp/#{rand}"
159         system("tar -xOf #{trace_name} > #{filename}")
160       elsif gzipped
161         filename = tempfile = "/tmp/#{rand}"
162         system("gunzip -c #{trace_name} > #{filename}")
163       elsif bzipped
164         filename = tempfile = "/tmp/#{rand}"
165         system("bunzip2 -c #{trace_name} > #{filename}")
166       elsif zipped
167         filename = tempfile = "/tmp/#{rand}"
168         system("unzip -p #{trace_name} > #{filename}")
169       else
170         filename = trace_name
171       end
172
173       gpx = OSM::GPXImporter.new(filename)
174
175       f_lat = 0
176       f_lon = 0
177       first = true
178
179       Tracepoint.delete_all(['gpx_id = ?', self.id])
180
181       gpx.points do |point|
182         if first
183           f_lat = point['latitude']
184           f_lon = point['longitude']
185         end
186
187         tp = Tracepoint.new
188         tp.lat = point['latitude'].to_f
189         tp.lng = point['longitude'].to_f
190         tp.altitude = point['altitude'].to_f
191         tp.timestamp = point['timestamp']
192         tp.gpx_id = id
193         tp.trackid = point['segment'].to_i
194         tp.save!
195       end
196
197       if gpx.actual_points > 0
198         max_lat = Tracepoint.maximum('latitude', :conditions => ['gpx_id = ?', id])
199         min_lat = Tracepoint.minimum('latitude', :conditions => ['gpx_id = ?', id])
200         max_lon = Tracepoint.maximum('longitude', :conditions => ['gpx_id = ?', id])
201         min_lon = Tracepoint.minimum('longitude', :conditions => ['gpx_id = ?', id])
202
203         max_lat = max_lat.to_f / 1000000
204         min_lat = min_lat.to_f / 1000000
205         max_lon = max_lon.to_f / 1000000
206         min_lon = min_lon.to_f / 1000000
207
208         self.latitude = f_lat
209         self.longitude = f_lon
210         self.large_picture = gpx.get_picture(min_lat, min_lon, max_lat, max_lon, gpx.actual_points)
211         self.icon_picture = gpx.get_icon(min_lat, min_lon, max_lat, max_lon)
212         self.size = gpx.actual_points
213         self.inserted = true
214         self.save!
215       end
216
217       logger.info "done trace #{id}"
218
219       return gpx
220     ensure
221       FileUtils.rm_f(tempfile) if tempfile
222     end
223   end
224 end