Move the trackpoints call into its own controller (and rename to tracepoints)
[rails.git] / app / controllers / api / tracepoints_controller.rb
1 module Api
2   class TracepointsController < ApplicationController
3     skip_before_action :verify_authenticity_token
4     before_action :api_deny_access_handler
5
6     authorize_resource
7
8     before_action :check_api_readable
9     around_action :api_call_handle_error, :api_call_timeout
10
11     # Get an XML response containing a list of tracepoints that have been uploaded
12     # within the specified bounding box, and in the specified page.
13     def index
14       # retrieve the page number
15       page = params["page"].to_s.to_i
16
17       unless page >= 0
18         report_error("Page number must be greater than or equal to 0")
19         return
20       end
21
22       offset = page * TRACEPOINTS_PER_PAGE
23
24       # Figure out the bbox
25       # check boundary is sane and area within defined
26       # see /config/application.yml
27       begin
28         bbox = BoundingBox.from_bbox_params(params)
29         bbox.check_boundaries
30         bbox.check_size
31       rescue StandardError => err
32         report_error(err.message)
33         return
34       end
35
36       # get all the points
37       ordered_points = Tracepoint.bbox(bbox).joins(:trace).where(:gpx_files => { :visibility => %w[trackable identifiable] }).order("gpx_id DESC, trackid ASC, timestamp ASC")
38       unordered_points = Tracepoint.bbox(bbox).joins(:trace).where(:gpx_files => { :visibility => %w[public private] }).order("gps_points.latitude", "gps_points.longitude", "gps_points.timestamp")
39       points = ordered_points.union_all(unordered_points).offset(offset).limit(TRACEPOINTS_PER_PAGE)
40
41       doc = XML::Document.new
42       doc.encoding = XML::Encoding::UTF_8
43       root = XML::Node.new "gpx"
44       root["version"] = "1.0"
45       root["creator"] = "OpenStreetMap.org"
46       root["xmlns"] = "http://www.topografix.com/GPX/1/0"
47
48       doc.root = root
49
50       # initialise these variables outside of the loop so that they
51       # stay in scope and don't get free'd up by the GC during the
52       # loop.
53       gpx_id = -1
54       trackid = -1
55       track = nil
56       trkseg = nil
57       anon_track = nil
58       anon_trkseg = nil
59       gpx_file = nil
60       timestamps = false
61
62       points.each do |point|
63         if gpx_id != point.gpx_id
64           gpx_id = point.gpx_id
65           trackid = -1
66           gpx_file = Trace.find(gpx_id)
67
68           if gpx_file.trackable?
69             track = XML::Node.new "trk"
70             doc.root << track
71             timestamps = true
72
73             if gpx_file.identifiable?
74               track << (XML::Node.new("name") << gpx_file.name)
75               track << (XML::Node.new("desc") << gpx_file.description)
76               track << (XML::Node.new("url") << url_for(:controller => "/traces", :action => "show", :display_name => gpx_file.user.display_name, :id => gpx_file.id))
77             end
78           else
79             # use the anonymous track segment if the user hasn't allowed
80             # their GPX points to be tracked.
81             timestamps = false
82             if anon_track.nil?
83               anon_track = XML::Node.new "trk"
84               doc.root << anon_track
85             end
86             track = anon_track
87           end
88         end
89
90         if trackid != point.trackid
91           if gpx_file.trackable?
92             trkseg = XML::Node.new "trkseg"
93             track << trkseg
94             trackid = point.trackid
95           else
96             if anon_trkseg.nil?
97               anon_trkseg = XML::Node.new "trkseg"
98               anon_track << anon_trkseg
99             end
100             trkseg = anon_trkseg
101           end
102         end
103
104         trkseg << point.to_xml_node(timestamps)
105       end
106
107       response.headers["Content-Disposition"] = "attachment; filename=\"tracks.gpx\""
108
109       render :xml => doc.to_s
110     end
111   end
112 end