692c20fc63a02e27581a71eee04181d4650c4941
[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_length_of :name, :maximum => 255
7   validates_length_of :description, :maximum => 255
8 #  validates_numericality_of :latitude, :longitude
9   validates_inclusion_of :public, :inserted, :in => [ true, false]
10   
11   belongs_to :user
12   has_many :tags, :class_name => 'Tracetag', :foreign_key => 'gpx_id', :dependent => :delete_all
13   has_many :points, :class_name => 'Tracepoint', :foreign_key => 'gpx_id', :dependent => :delete_all
14
15   def destroy
16     super
17     FileUtils.rm_f(trace_name)
18     FileUtils.rm_f(icon_picture_name)
19     FileUtils.rm_f(large_picture_name)
20   end
21
22   def tagstring
23     return tags.collect {|tt| tt.tag}.join(", ")
24   end
25
26   def tagstring=(s)
27     if s.include?','
28       self.tags = s.split(/\s*,\s*/).collect {|tag|
29         tt = Tracetag.new
30         tt.tag = tag
31         tt
32       }
33     else
34       self.tags = s.split().collect {|tag|
35         tt = Tracetag.new
36         tt.tag = tag
37         tt
38       }
39     end
40   end
41   
42   def large_picture= (data)
43     f = File.new(large_picture_name, "wb")
44     f.syswrite(data)
45     f.close
46   end
47   
48   def icon_picture= (data)
49     f = File.new(icon_picture_name, "wb")
50     f.syswrite(data)
51     f.close
52   end
53
54   def large_picture
55     f = File.new(large_picture_name, "rb")
56     logger.info "large picture file: '#{f.path}', bytes: #{File.size(f.path)}"
57     data = f.sysread(File.size(f.path))
58     logger.info "have read data, bytes: '#{data.length}'"
59     f.close
60     data
61   end
62   
63   def icon_picture
64     f = File.new(icon_picture_name, "rb")
65     logger.info "icon picture file: '#{f.path}'"
66     data = f.sysread(File.size(f.path))
67     f.close
68     data
69   end
70   
71   def large_picture_name
72     "#{GPX_IMAGE_DIR}/#{id}.gif"
73   end
74
75   def icon_picture_name
76     "#{GPX_IMAGE_DIR}/#{id}_icon.gif"
77   end
78
79   def trace_name
80     "#{GPX_TRACE_DIR}/#{id}.gpx"
81   end
82
83   def mime_type
84     filetype = `/usr/bin/file -bz #{trace_name}`.chomp
85     gzipped = filetype =~ /gzip compressed/
86     bzipped = filetype =~ /bzip2 compressed/
87     zipped = filetype =~ /Zip archive/
88
89     if gzipped then
90       mimetype = "application/x-gzip"
91     elsif bzipped then
92       mimetype = "application/x-bzip2"
93     elsif zipped
94       mimetype = "application/x-zip"
95     else
96       mimetype = "text/xml"
97     end
98
99     return mimetype
100   end
101
102   def extension_name
103     filetype = `/usr/bin/file -bz #{trace_name}`.chomp
104     gzipped = filetype =~ /gzip compressed/
105     bzipped = filetype =~ /bzip2 compressed/
106     zipped = filetype =~ /Zip archive/
107     tarred = filetype =~ /tar archive/
108
109     if tarred and gzipped then
110       extension = ".tar.gz"
111     elsif tarred and bzipped then
112       extension = ".tar.bz2"
113     elsif tarred
114       extension = ".tar"
115     elsif gzipped
116       extension = ".gpx.gz"
117     elsif bzipped
118       extension = ".gpx.bz2"
119     elsif zipped
120       extension = ".zip"
121     else
122       extension = ".gpx"
123     end
124
125     return extension
126   end
127
128   def to_xml
129     doc = OSM::API.new.get_xml_doc
130     doc.root << to_xml_node()
131     return doc
132   end
133
134   def to_xml_node
135     el1 = XML::Node.new 'gpx_file'
136     el1['id'] = self.id.to_s
137     el1['name'] = self.name.to_s
138     el1['lat'] = self.latitude.to_s
139     el1['lon'] = self.longitude.to_s
140     el1['user'] = self.user.display_name
141     el1['public'] = self.public.to_s
142     el1['pending'] = (!self.inserted).to_s
143     el1['timestamp'] = self.timestamp.xmlschema
144     return el1
145   end
146
147   def xml_file
148     # TODO *nix specific, could do to work on windows... would be functionally inferior though - check for '.gz'
149     filetype = `/usr/bin/file -bz #{trace_name}`.chomp
150     gzipped = filetype =~ /gzip compressed/
151     bzipped = filetype =~ /bzip2 compressed/
152     zipped = filetype =~ /Zip archive/
153     tarred = filetype =~ /tar archive/
154
155     if gzipped or bzipped or zipped or tarred then
156       tmpfile = Tempfile.new("trace.#{id}");
157
158       if tarred and gzipped then
159         system("tar -zxOf #{trace_name} > #{tmpfile.path}")
160       elsif tarred and bzipped then
161         system("tar -jxOf #{trace_name} > #{tmpfile.path}")
162       elsif tarred
163         system("tar -xOf #{trace_name} > #{tmpfile.path}")
164       elsif gzipped
165         system("gunzip -c #{trace_name} > #{tmpfile.path}")
166       elsif bzipped
167         system("bunzip2 -c #{trace_name} > #{tmpfile.path}")
168       elsif zipped
169         system("unzip -p #{trace_name} -x '__MACOSX/*' > #{tmpfile.path}")
170       end
171
172       tmpfile.unlink
173
174       file = tmpfile.file
175     else
176       file = File.open(trace_name)
177     end
178
179     return file
180   end
181
182   def import
183     logger.info("GPX Import importing #{name} (#{id}) from #{user.email}")
184
185     gpx = GPX::File.new(self.xml_file)
186
187     f_lat = 0
188     f_lon = 0
189     first = true
190
191     # If there are any existing points for this trace then delete
192     # them - we check for existing points first to avoid locking
193     # the table in the common case where there aren't any.
194     if Tracepoint.find(:first, :conditions => ['gpx_id = ?', self.id])
195       Tracepoint.delete_all(['gpx_id = ?', self.id])
196     end
197
198     gpx.points do |point|
199       if first
200         f_lat = point.latitude
201         f_lon = point.longitude
202         first = false
203       end
204
205       tp = Tracepoint.new
206       tp.lat = point.latitude
207       tp.lon = point.longitude
208       tp.altitude = point.altitude
209       tp.timestamp = point.timestamp
210       tp.gpx_id = id
211       tp.trackid = point.segment
212       tp.save!
213     end
214
215     if gpx.actual_points > 0
216       max_lat = Tracepoint.maximum('latitude', :conditions => ['gpx_id = ?', id])
217       min_lat = Tracepoint.minimum('latitude', :conditions => ['gpx_id = ?', id])
218       max_lon = Tracepoint.maximum('longitude', :conditions => ['gpx_id = ?', id])
219       min_lon = Tracepoint.minimum('longitude', :conditions => ['gpx_id = ?', id])
220
221       max_lat = max_lat.to_f / 10000000
222       min_lat = min_lat.to_f / 10000000
223       max_lon = max_lon.to_f / 10000000
224       min_lon = min_lon.to_f / 10000000
225
226       self.latitude = f_lat
227       self.longitude = f_lon
228       self.large_picture = gpx.picture(min_lat, min_lon, max_lat, max_lon, gpx.actual_points)
229       self.icon_picture = gpx.icon(min_lat, min_lon, max_lat, max_lon)
230       self.size = gpx.actual_points
231       self.inserted = true
232       self.save!
233     end
234
235     logger.info "done trace #{id}"
236
237     return gpx
238   end
239 end