From 07e84496255ebd1d53108894e0fb8ce98b55ccee Mon Sep 17 00:00:00 2001 From: Tom Hughes Date: Wed, 23 Jan 2008 13:40:47 +0000 Subject: [PATCH] Allow a GPX to be fetched in XML format by fetching data.xml instead of data (the data method returns it in the format it is stored in). Closes #534. --- app/controllers/trace_controller.rb | 6 +- app/models/trace.rb | 140 ++++++++++++++-------------- config/initializers/streaming.rb | 31 ++++++ config/routes.rb | 2 + lib/osm.rb | 8 +- 5 files changed, 113 insertions(+), 74 deletions(-) create mode 100644 config/initializers/streaming.rb diff --git a/app/controllers/trace_controller.rb b/app/controllers/trace_controller.rb index 7c5bd7db0..c56ff4a74 100644 --- a/app/controllers/trace_controller.rb +++ b/app/controllers/trace_controller.rb @@ -121,7 +121,11 @@ class TraceController < ApplicationController trace = Trace.find(params[:id]) if trace.visible? and (trace.public? or (@user and @user == trace.user)) - send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment') + if request.format == Mime::XML + send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => Mime::XML.to_s, :disposition => 'attachment') + else + send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => 'attachment') + end else render :nothing, :status => :not_found end diff --git a/app/models/trace.rb b/app/models/trace.rb index baf2ea7d2..fef9983dd 100644 --- a/app/models/trace.rb +++ b/app/models/trace.rb @@ -136,93 +136,93 @@ class Trace < ActiveRecord::Base return el1 end - def import - begin - logger.info("GPX Import importing #{name} (#{id}) from #{user.email}") + 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/ - # 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 + file = Tempfile.new("trace.#{id}"); if tarred and gzipped then - filename = tempfile = "/tmp/#{rand}" - system("tar -zxOf #{trace_name} > #{filename}") + system("tar -zxOf #{trace_name} > #{file.path}") elsif tarred and bzipped then - filename = tempfile = "/tmp/#{rand}" - system("tar -jxOf #{trace_name} > #{filename}") + system("tar -jxOf #{trace_name} > #{file.path}") elsif tarred - filename = tempfile = "/tmp/#{rand}" - system("tar -xOf #{trace_name} > #{filename}") + system("tar -xOf #{trace_name} > #{file.path}") elsif gzipped - filename = tempfile = "/tmp/#{rand}" - system("gunzip -c #{trace_name} > #{filename}") + system("gunzip -c #{trace_name} > #{file.path}") elsif bzipped - filename = tempfile = "/tmp/#{rand}" - system("bunzip2 -c #{trace_name} > #{filename}") + system("bunzip2 -c #{trace_name} > #{file.path}") elsif zipped - filename = tempfile = "/tmp/#{rand}" - system("unzip -p #{trace_name} > #{filename}") - else - filename = trace_name + system("unzip -p #{trace_name} > #{file.path}") end - gpx = OSM::GPXImporter.new(filename) + file.unlink + else + file = File.open(trace_name) + end + + return file + end - f_lat = 0 - f_lon = 0 - first = true + def import + logger.info("GPX Import importing #{name} (#{id}) from #{user.email}") - # 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.exists?(['gpx_id = ?', self.id]) - Tracepoint.delete_all(['gpx_id = ?', self.id]) - end + gpx = OSM::GPXImporter.new(self.xml_file) - gpx.points do |point| - if first - f_lat = point['latitude'] - f_lon = point['longitude'] - end - - tp = Tracepoint.new - tp.lat = point['latitude'].to_f - tp.lon = point['longitude'].to_f - tp.altitude = point['altitude'].to_f - tp.timestamp = point['timestamp'] - tp.gpx_id = id - tp.trackid = point['segment'].to_i - tp.save! - end + f_lat = 0 + f_lon = 0 + first = true - 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.get_picture(min_lat, min_lon, max_lat, max_lon, gpx.actual_points) - self.icon_picture = gpx.get_icon(min_lat, min_lon, max_lat, max_lon) - self.size = gpx.actual_points - self.inserted = true - self.save! + # 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.exists?(['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'] end - logger.info "done trace #{id}" + tp = Tracepoint.new + tp.lat = point['latitude'].to_f + tp.lon = point['longitude'].to_f + tp.altitude = point['altitude'].to_f + tp.timestamp = point['timestamp'] + tp.gpx_id = id + tp.trackid = point['segment'].to_i + tp.save! + end - return gpx - ensure - FileUtils.rm_f(tempfile) if tempfile + 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.get_picture(min_lat, min_lon, max_lat, max_lon, gpx.actual_points) + self.icon_picture = gpx.get_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 diff --git a/config/initializers/streaming.rb b/config/initializers/streaming.rb new file mode 100644 index 000000000..116ca51dd --- /dev/null +++ b/config/initializers/streaming.rb @@ -0,0 +1,31 @@ +# Hack ActionController::Streaming to allow streaming from a file handle +module ActionController + module Streaming + alias_method :old_send_file, :send_file + + def send_file(file, options = {}) + if file.is_a? File or file.is_a? Tempfile + options[:length] ||= file.stat.size + options[:filename] ||= File.basename(file.path) unless options[:url_based_filename] + send_file_headers! options + + @performed_render = false + + if options[:stream] + render :status => options[:status], :text => Proc.new { |response, output| + logger.info "Streaming file #{file.path}" unless logger.nil? + len = options[:buffer_size] || 4096 + while buf = file.read(len) + output.write(buf) + end + } + else + logger.info "Sending file #{file.path}" unless logger.nil? + render :status => options[:status], :text => file.read + end + else + old_send_file(file, options) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 92d849b4b..025d5d2b0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -79,6 +79,8 @@ ActionController::Routing::Routes.draw do |map| map.connect '/traces/mine/tag/:tag', :controller => 'trace', :action => 'mine' map.connect '/traces/mine/tag/:tag/page/:page', :controller => 'trace', :action => 'mine' map.connect '/trace/create', :controller => 'trace', :action => 'create' + map.connect '/trace/:id/data', :controller => 'trace', :action => 'data' + map.connect '/trace/:id/data.:format', :controller => 'trace', :action => 'data' map.connect '/trace/:id/edit', :controller => 'trace', :action => 'edit' map.connect '/trace/:id/delete', :controller => 'trace', :action => 'delete' map.connect '/trace/:id/make_public', :controller => 'trace', :action => 'make_public' diff --git a/lib/osm.rb b/lib/osm.rb index aca4031eb..de5e73d29 100644 --- a/lib/osm.rb +++ b/lib/osm.rb @@ -68,8 +68,8 @@ module OSM attr_reader :actual_points attr_reader :tracksegs - def initialize(filename) - @filename = filename + def initialize(file) + @file = file end def points @@ -85,7 +85,9 @@ module OSM gotele = false gotdate = false - parser = REXML::Parsers::SAX2Parser.new(File.new(@filename)) + @file.rewind + + parser = REXML::Parsers::SAX2Parser.new(@file) parser.listen( :start_element, %w{ trkpt }) do |uri,localname,qname,attributes| lat = attributes['lat'].to_f -- 2.43.2