openstreetmap-website/app/models/trace.rb
Tom Hughes 565171486f Add extra visibility options for GPS traces, and enhance the API call
that returns trace data to return more information if the owner of the
trace has allowed it. Based on a patch from Matt Amos.
2009-08-13 17:06:11 +00:00

253 lines
6.6 KiB
Ruby

class Trace < ActiveRecord::Base
set_table_name 'gpx_files'
validates_presence_of :user_id, :name, :timestamp
validates_presence_of :description, :on => :create
validates_length_of :name, :maximum => 255
validates_length_of :description, :maximum => 255
# validates_numericality_of :latitude, :longitude
validates_inclusion_of :inserted, :in => [ true, false ]
validates_inclusion_of :visibility, :in => ["private", "public", "trackable", "identifiable"]
belongs_to :user
has_many :tags, :class_name => 'Tracetag', :foreign_key => 'gpx_id', :dependent => :delete_all
has_many :points, :class_name => 'Tracepoint', :foreign_key => 'gpx_id', :dependent => :delete_all
def destroy
super
FileUtils.rm_f(trace_name)
FileUtils.rm_f(icon_picture_name)
FileUtils.rm_f(large_picture_name)
end
def tagstring
return tags.collect {|tt| tt.tag}.join(", ")
end
def tagstring=(s)
if s.include? ','
self.tags = s.split(/\s*,\s*/).collect {|tag|
tt = Tracetag.new
tt.tag = tag
tt
}
else
#do as before for backwards compatibility:
self.tags = s.split().collect {|tag|
tt = Tracetag.new
tt.tag = tag
tt
}
end
end
def public?
visibility == "public" || visibility == "identifiable"
end
def trackable?
visibility == "trackable" || visibility == "identifiable"
end
def identifiable?
visibility == "identifiable"
end
def large_picture= (data)
f = File.new(large_picture_name, "wb")
f.syswrite(data)
f.close
end
def icon_picture= (data)
f = File.new(icon_picture_name, "wb")
f.syswrite(data)
f.close
end
def large_picture
f = File.new(large_picture_name, "rb")
logger.info "large picture file: '#{f.path}', bytes: #{File.size(f.path)}"
data = f.sysread(File.size(f.path))
logger.info "have read data, bytes: '#{data.length}'"
f.close
data
end
def icon_picture
f = File.new(icon_picture_name, "rb")
logger.info "icon picture file: '#{f.path}'"
data = f.sysread(File.size(f.path))
f.close
data
end
def large_picture_name
"#{GPX_IMAGE_DIR}/#{id}.gif"
end
def icon_picture_name
"#{GPX_IMAGE_DIR}/#{id}_icon.gif"
end
def trace_name
"#{GPX_TRACE_DIR}/#{id}.gpx"
end
def mime_type
filetype = `/usr/bin/file -bz #{trace_name}`.chomp
gzipped = filetype =~ /gzip compressed/
bzipped = filetype =~ /bzip2 compressed/
zipped = filetype =~ /Zip archive/
if gzipped then
mimetype = "application/x-gzip"
elsif bzipped then
mimetype = "application/x-bzip2"
elsif zipped
mimetype = "application/x-zip"
else
mimetype = "text/xml"
end
return mimetype
end
def extension_name
filetype = `/usr/bin/file -bz #{trace_name}`.chomp
gzipped = filetype =~ /gzip compressed/
bzipped = filetype =~ /bzip2 compressed/
zipped = filetype =~ /Zip archive/
tarred = filetype =~ /tar archive/
if tarred and gzipped then
extension = ".tar.gz"
elsif tarred and bzipped then
extension = ".tar.bz2"
elsif tarred
extension = ".tar"
elsif gzipped
extension = ".gpx.gz"
elsif bzipped
extension = ".gpx.bz2"
elsif zipped
extension = ".zip"
else
extension = ".gpx"
end
return extension
end
def to_xml
doc = OSM::API.new.get_xml_doc
doc.root << to_xml_node()
return doc
end
def to_xml_node
el1 = XML::Node.new 'gpx_file'
el1['id'] = self.id.to_s
el1['name'] = self.name.to_s
el1['lat'] = self.latitude.to_s
el1['lon'] = self.longitude.to_s
el1['user'] = self.user.display_name
el1['visibility'] = self.visibility
el1['pending'] = (!self.inserted).to_s
el1['timestamp'] = self.timestamp.xmlschema
return el1
end
def xml_file
# TODO *nix specific, could do to work on windows... would be functionally inferior though - check for '.gz'
filetype = `/usr/bin/file -bz #{trace_name}`.chomp
gzipped = filetype =~ /gzip compressed/
bzipped = filetype =~ /bzip2 compressed/
zipped = filetype =~ /Zip archive/
tarred = filetype =~ /tar archive/
if gzipped or bzipped or zipped or tarred then
tmpfile = Tempfile.new("trace.#{id}");
if tarred and gzipped then
system("tar -zxOf #{trace_name} > #{tmpfile.path}")
elsif tarred and bzipped then
system("tar -jxOf #{trace_name} > #{tmpfile.path}")
elsif tarred
system("tar -xOf #{trace_name} > #{tmpfile.path}")
elsif gzipped
system("gunzip -c #{trace_name} > #{tmpfile.path}")
elsif bzipped
system("bunzip2 -c #{trace_name} > #{tmpfile.path}")
elsif zipped
system("unzip -p #{trace_name} -x '__MACOSX/*' > #{tmpfile.path}")
end
tmpfile.unlink
file = tmpfile.file
else
file = File.open(trace_name)
end
return file
end
def import
logger.info("GPX Import importing #{name} (#{id}) from #{user.email}")
gpx = GPX::File.new(self.xml_file)
f_lat = 0
f_lon = 0
first = true
# If there are any existing points for this trace then delete
# them - we check for existing points first to avoid locking
# the table in the common case where there aren't any.
if Tracepoint.find(:first, :conditions => ['gpx_id = ?', self.id])
Tracepoint.delete_all(['gpx_id = ?', self.id])
end
gpx.points do |point|
if first
f_lat = point.latitude
f_lon = point.longitude
first = false
end
tp = Tracepoint.new
tp.lat = point.latitude
tp.lon = point.longitude
tp.altitude = point.altitude
tp.timestamp = point.timestamp
tp.gpx_id = id
tp.trackid = point.segment
tp.save!
end
if gpx.actual_points > 0
max_lat = Tracepoint.maximum('latitude', :conditions => ['gpx_id = ?', id])
min_lat = Tracepoint.minimum('latitude', :conditions => ['gpx_id = ?', id])
max_lon = Tracepoint.maximum('longitude', :conditions => ['gpx_id = ?', id])
min_lon = Tracepoint.minimum('longitude', :conditions => ['gpx_id = ?', id])
max_lat = max_lat.to_f / 10000000
min_lat = min_lat.to_f / 10000000
max_lon = max_lon.to_f / 10000000
min_lon = min_lon.to_f / 10000000
self.latitude = f_lat
self.longitude = f_lon
self.large_picture = gpx.picture(min_lat, min_lon, max_lat, max_lon, gpx.actual_points)
self.icon_picture = gpx.icon(min_lat, min_lon, max_lat, max_lon)
self.size = gpx.actual_points
self.inserted = true
self.save!
end
logger.info "done trace #{id}"
return gpx
end
end