source: subversion/sites/rails_port_branches/api06/app/controllers/api_controller.rb @ 10902

Revision 10902, 7.8 KB checked in by smsm1, 6 years ago (diff)

Doing a resync from mainline 8633:10895. There was one simple to resolve conflict on app/models/node.rb. Also moving the migrations for API0.6 to new sequence numbers since there was some new migrations added to mainline, where the migration numbers would conflict if not moved.

Line 
1class ApiController < ApplicationController
2
3  session :off
4  before_filter :check_read_availability, :except => [:capabilities]
5  after_filter :compress_output
6
7  # Help methods for checking boundary sanity and area size
8  include MapBoundary
9
10  #COUNT is the number of map requests to allow before exiting and starting a new process
11  @@count = COUNT
12
13  # The maximum area you're allowed to request, in square degrees
14  MAX_REQUEST_AREA = 0.25
15
16  # Number of GPS trace/trackpoints returned per-page
17  TRACEPOINTS_PER_PAGE = 5000
18
19 
20  def trackpoints
21    @@count+=1
22    #retrieve the page number
23    page = params['page'].to_i
24    unless page
25        page = 0;
26    end
27
28    unless page >= 0
29        report_error("Page number must be greater than or equal to 0")
30        return
31    end
32
33    offset = page * TRACEPOINTS_PER_PAGE
34
35    # Figure out the bbox
36    bbox = params['bbox']
37    unless bbox and bbox.count(',') == 3
38      report_error("The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat")
39      return
40    end
41
42    bbox = bbox.split(',')
43    min_lon, min_lat, max_lon, max_lat = sanitise_boundaries(bbox)
44    # check boundary is sane and area within defined
45    # see /config/application.yml
46    begin
47      check_boundaries(min_lon, min_lat, max_lon, max_lat)
48    rescue Exception => err
49      report_error(err.message)
50      return
51    end
52
53    # get all the points
54    points = Tracepoint.find_by_area(min_lat, min_lon, max_lat, max_lon, :offset => offset, :limit => TRACEPOINTS_PER_PAGE, :order => "timestamp DESC" )
55
56    doc = XML::Document.new
57    doc.encoding = 'UTF-8'
58    root = XML::Node.new 'gpx'
59    root['version'] = '1.0'
60    root['creator'] = 'OpenStreetMap.org'
61    root['xmlns'] = "http://www.topografix.com/GPX/1/0/"
62   
63    doc.root = root
64
65    track = XML::Node.new 'trk'
66    doc.root << track
67
68    trkseg = XML::Node.new 'trkseg'
69    track << trkseg
70
71    points.each do |point|
72      trkseg << point.to_xml_node()
73    end
74
75    #exit when we have too many requests
76    if @@count > MAX_COUNT
77      render :text => doc.to_s, :content_type => "text/xml"
78      @@count = COUNT
79      exit!
80    end
81
82    response.headers["Content-Disposition"] = "attachment; filename=\"map.osm\""
83
84    render :text => doc.to_s, :content_type => "text/xml"
85  end
86
87  def map
88    GC.start
89    @@count+=1
90    # Figure out the bbox
91    bbox = params['bbox']
92
93    unless bbox and bbox.count(',') == 3
94      # alternatively: report_error(TEXT['boundary_parameter_required']
95      report_error("The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat")
96      return
97    end
98
99    bbox = bbox.split(',')
100
101    min_lon, min_lat, max_lon, max_lat = sanitise_boundaries(bbox)
102
103    # check boundary is sane and area within defined
104    # see /config/application.yml
105    begin
106      check_boundaries(min_lon, min_lat, max_lon, max_lat)
107    rescue Exception => err
108      report_error(err.message)
109      return
110    end
111
112    @nodes = Node.find_by_area(min_lat, min_lon, max_lat, max_lon, :conditions => "visible = 1", :limit => APP_CONFIG['max_number_of_nodes']+1)
113    # get all the nodes, by tag not yet working, waiting for change from NickB
114    # need to be @nodes (instance var) so tests in /spec can be performed
115    #@nodes = Node.search(bbox, params[:tag])
116
117    node_ids = @nodes.collect(&:id)
118    if node_ids.length > APP_CONFIG['max_number_of_nodes']
119      report_error("You requested too many nodes (limit is 50,000). Either request a smaller area, or use planet.osm")
120      return
121    end
122    if node_ids.length == 0
123      render :text => "<osm version='#{API_VERSION}'></osm>", :content_type => "text/xml"
124      return
125    end
126
127    doc = OSM::API.new.get_xml_doc
128
129    # add bounds
130    bounds = XML::Node.new 'bounds'
131    bounds['minlat'] = min_lat.to_s
132    bounds['minlon'] = min_lon.to_s
133    bounds['maxlat'] = max_lat.to_s
134    bounds['maxlon'] = max_lon.to_s
135    doc.root << bounds
136
137    # get ways
138    # find which ways are needed
139    ways = Array.new
140    if node_ids.length > 0
141      way_nodes = WayNode.find_all_by_node_id(node_ids)
142      way_ids = way_nodes.collect { |way_node| way_node.id[0] }
143      ways = Way.find(way_ids)
144
145      list_of_way_nodes = ways.collect { |way|
146        way.way_nodes.collect { |way_node| way_node.node_id }
147      }
148      list_of_way_nodes.flatten!
149
150    else
151      list_of_way_nodes = Array.new
152    end
153
154    # - [0] in case some thing links to node 0 which doesn't exist. Shouldn't actually ever happen but it does. FIXME: file a ticket for this
155    nodes_to_fetch = (list_of_way_nodes.uniq - node_ids) - [0]
156
157    if nodes_to_fetch.length > 0
158      @nodes += Node.find(nodes_to_fetch)
159    end
160
161    visible_nodes = {}
162    user_display_name_cache = {}
163
164    @nodes.each do |node|
165      if node.visible?
166        doc.root << node.to_xml_node(user_display_name_cache)
167        visible_nodes[node.id] = node
168      end
169    end
170
171    way_ids = Array.new
172    ways.each do |way|
173      if way.visible?
174        doc.root << way.to_xml_node(visible_nodes, user_display_name_cache)
175        way_ids << way.id
176      end
177    end 
178
179    relations = Relation.find_for_nodes(visible_nodes.keys, :conditions => "visible = 1") +
180                Relation.find_for_ways(way_ids, :conditions => "visible = 1")
181
182    # we do not normally return the "other" partners referenced by an relation,
183    # e.g. if we return a way A that is referenced by relation X, and there's
184    # another way B also referenced, that is not returned. But we do make
185    # an exception for cases where an relation references another *relation*;
186    # in that case we return that as well (but we don't go recursive here)
187    relations += Relation.find_for_relations(relations.collect { |r| r.id }, :conditions => "visible = 1")
188
189    # this "uniq" may be slightly inefficient; it may be better to first collect and output
190    # all node-related relations, then find the *not yet covered* way-related ones etc.
191    relations.uniq.each do |relation|
192      doc.root << relation.to_xml_node(user_display_name_cache)
193    end
194
195    response.headers["Content-Disposition"] = "attachment; filename=\"map.osm\""
196
197    render :text => doc.to_s, :content_type => "text/xml"
198   
199    #exit when we have too many requests
200    if @@count > MAX_COUNT
201      @@count = COUNT
202     
203      exit!
204    end
205  end
206
207  def changes
208    zoom = (params[:zoom] || '12').to_i
209
210    if params.include?(:start) and params.include?(:end)
211      starttime = Time.parse(params[:start])
212      endtime = Time.parse(params[:end])
213    else
214      hours = (params[:hours] || '1').to_i.hours
215      endtime = Time.now
216      starttime = endtime - hours
217    end
218
219    if zoom >= 1 and zoom <= 16 and
220       endtime >= starttime and endtime - starttime <= 24.hours
221      mask = (1 << zoom) - 1
222
223      tiles = Node.count(:conditions => ["timestamp BETWEEN ? AND ?", starttime, endtime],
224                         :group => "maptile_for_point(latitude, longitude, #{zoom})")
225
226      doc = OSM::API.new.get_xml_doc
227      changes = XML::Node.new 'changes'
228      changes["starttime"] = starttime.xmlschema
229      changes["endtime"] = endtime.xmlschema
230
231      tiles.each do |tile, count|
232        x = (tile.to_i >> zoom) & mask
233        y = tile.to_i & mask
234
235        t = XML::Node.new 'tile'
236        t["x"] = x.to_s
237        t["y"] = y.to_s
238        t["z"] = zoom.to_s
239        t["changes"] = count.to_s
240
241        changes << t
242      end
243
244      doc.root << changes
245
246      render :text => doc.to_s, :content_type => "text/xml"
247    else
248      render :nothing => true, :status => :bad_request
249    end
250  end
251
252  def capabilities
253    doc = OSM::API.new.get_xml_doc
254
255    api = XML::Node.new 'api'
256    version = XML::Node.new 'version'
257    version['minimum'] = "#{API_VERSION}";
258    version['maximum'] = "#{API_VERSION}";
259    api << version
260    area = XML::Node.new 'area'
261    area['maximum'] = MAX_REQUEST_AREA.to_s;
262    api << area
263   
264    doc.root << api
265
266    render :text => doc.to_s, :content_type => "text/xml"
267  end
268end
Note: See TracBrowser for help on using the repository browser.