source: subversion/sites/rails_port/app/controllers/api_controller.rb @ 2865

Revision 2865, 7.6 KB checked in by steve, 7 years ago (diff)

api fixes

Line 
1class ApiController < ApplicationController
2
3  before_filter :authorize
4  after_filter :compress_output
5
6  helper :user
7  model :user
8
9  #COUNT is the number of map requests to allow before exiting and starting a new process
10  @@count = COUNT
11
12  # The maximum area you're allowed to request, in square degrees
13  MAX_REQUEST_AREA = 0.25
14
15
16  # Number of GPS trace/trackpoints returned per-page
17  TRACEPOINTS_PER_PAGE = 5000
18 
19  def trackpoints
20    @@count+=1
21    response.headers["Content-Type"] = 'text/xml'
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
44    min_lon = bbox[0].to_f
45    min_lat = bbox[1].to_f
46    max_lon = bbox[2].to_f
47    max_lat = bbox[3].to_f
48
49    # check the bbox is sane
50    unless min_lon <= max_lon
51      report_error("The minimum longitude must be less than the maximum longitude, but it wasn't")
52      return
53    end
54    unless min_lat <= max_lat
55      report_error("The minimum latitude must be less than the maximum latitude, but it wasn't")
56      return
57    end
58    unless min_lon >= -180 && min_lat >= -90 && max_lon <= 180 && max_lat <= 90
59      report_error("The latitudes must be between -90 and 90, and longitudes between -180 and 180")
60      return
61    end
62
63    # check the bbox isn't too large
64    requested_area = (max_lat-min_lat)*(max_lon-min_lon)
65    if requested_area > MAX_REQUEST_AREA
66      report_error("The maximum bbox size is " + MAX_REQUEST_AREA.to_s + ", and your request was too large. Either request a smaller area, or use planet.osm")
67      return
68    end
69
70    # integerise
71    min_lat = min_lat * 1000000
72    max_lat = max_lat * 1000000
73    min_lon = min_lon * 1000000
74    max_lon = max_lon * 1000000
75    # get all the points
76    points = Tracepoint.find(:all, :conditions => ['gps_points.latitude > ? AND gps_points.longitude > ? AND gps_points.latitude < ? AND gps_points.longitude < ? AND ( public = 1 OR gpx_files.user_id = ? ) AND visible = 1', min_lat.to_i, min_lon.to_i, max_lat.to_i, max_lon.to_i, @user.id ], :select => "gps_points.*", :joins => "INNER JOIN gpx_files ON gpx_files.id = gpx_id", :offset => offset, :limit => TRACEPOINTS_PER_PAGE, :order => "timestamp DESC" )
77
78    doc = XML::Document.new
79    doc.encoding = 'UTF-8'
80    root = XML::Node.new 'gpx'
81    root['version'] = '1.0'
82    root['creator'] = 'OpenStreetMap.org'
83    root['xmlns'] = "http://www.topografix.com/GPX/1/0/"
84   
85    doc.root = root
86
87    track = XML::Node.new 'trk'
88    doc.root << track
89
90    trkseg = XML::Node.new 'trkseg'
91    track << trkseg
92
93    points.each do |point|
94      trkseg << point.to_xml_node()
95    end
96
97    #exit when we have too many requests
98    if @@count > MAX_COUNT
99      render :text => doc.to_s
100      @@count = COUNT
101      exit!
102    end
103
104    render :text => doc.to_s
105
106  end
107
108  def map
109    GC.start
110    @@count+=1
111
112    response.headers["Content-Type"] = 'text/xml'
113    # Figure out the bbox
114    bbox = params['bbox']
115    unless bbox and bbox.count(',') == 3
116      report_error("The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat")
117      return
118    end
119
120    bbox = bbox.split(',')
121
122    min_lon = bbox[0].to_f
123    min_lat = bbox[1].to_f
124    max_lon = bbox[2].to_f
125    max_lat = bbox[3].to_f
126
127    # check the bbox is sane
128    unless min_lon <= max_lon
129      report_error("The minimum longitude must be less than the maximum longitude, but it wasn't")
130      return
131    end
132    unless min_lat <= max_lat
133      report_error("The minimum latitude must be less than the maximum latitude, but it wasn't")
134      return
135    end
136    unless min_lon >= -180 && min_lat >= -90 && max_lon <= 180 && max_lat <= 90
137      report_error("The latitudes must be between -90 and 90, and longitudes between -180 and 180")
138      return
139    end
140
141    # check the bbox isn't too large
142    requested_area = (max_lat-min_lat)*(max_lon-min_lon)
143    if requested_area > MAX_REQUEST_AREA
144      report_error("The maximum bbox size is " + MAX_REQUEST_AREA.to_s + ", and your request was too large. Either request a smaller area, or use planet.osm")
145      return
146    end
147
148    # get all the nodes
149    nodes = Node.find(:all, :conditions => ['latitude > ? AND longitude > ? AND latitude < ? AND longitude < ? AND visible = 1', min_lat, min_lon, max_lat, max_lon])
150
151    node_ids = nodes.collect {|node| node.id }
152
153    if node_ids.length > 50_000
154      report_error("You requested too many nodes (limit is 50,000). Either request a smaller area, or use planet.osm")
155    end
156
157    if node_ids.length == 0
158      render :text => '<osm></osm>'
159      return
160    end
161
162    # grab the segments
163    segments = Array.new
164    if node_ids.length > 0
165      node_ids_sql = "(#{node_ids.join(',')})"
166      # get the referenced segments
167      segments = Segment.find_by_sql "select * from current_segments where visible = 1 and (node_a in #{node_ids_sql} or node_b in #{node_ids_sql})"
168    end
169    # see if we have any missing nodes
170    segments_nodes = segments.collect {|segment| segment.node_a }
171    segments_nodes += segments.collect {|segment| segment.node_b }
172
173    segments_nodes.uniq!
174
175    missing_nodes = segments_nodes - node_ids
176
177    # get missing nodes if there are any
178    nodes += Node.find(missing_nodes) if missing_nodes.length > 0
179
180    doc = OSM::API.new.get_xml_doc
181
182    # get ways
183    # find which ways are needed
184    segment_ids = segments.collect {|segment| segment.id }
185    ways = Array.new
186    if segment_ids.length > 0
187      way_segments = WaySegment.find_all_by_segment_id(segment_ids)
188      way_ids = way_segments.collect {|way_segment| way_segment.id }
189      ways = Way.find(way_ids) # NB: doesn't pick up segments, tags from db until accessed via way.way_segments etc.
190
191      # seg_ids = way_segments.collect {|way_segment| way_segment.segment_id }
192
193      list_of_way_segs = ways.collect {|way| way.way_segments}
194      list_of_way_segs.flatten!
195
196      list_of_way_segments = list_of_way_segs.collect { |way_seg| way_seg.segment_id }
197
198    end
199
200    # - [0] in case some thing links to segment 0 which doesn't exist. Shouldn't actually ever happen but it does. FIXME: file a ticket for this
201    segments_to_fetch = (list_of_way_segments.uniq - segment_ids) - [0]
202
203    if segments_to_fetch.length > 0
204      segments += Segment.find(segments_to_fetch)
205    end
206
207    # get more nodes
208    #
209
210    segments_nodes = segments.collect {|segment| segment.node_a }
211    segments_nodes += segments.collect {|segment| segment.node_b }
212
213    node_ids_a = nodes.collect {|node| node.id }
214
215    nodes_to_get = segments_nodes - node_ids_a
216    nodes += Node.find(nodes_to_get) if nodes_to_get.length > 0
217
218    visible_nodes = {}
219    user_display_name_cache = {}
220
221    nodes.each do |node|
222      if node.visible?
223        doc.root << node.to_xml_node(user_display_name_cache)
224        visible_nodes[node.id] = node
225      end
226    end
227
228    visible_segments = {}
229
230    segments.each do |segment|
231      if visible_nodes[segment.node_a] and visible_nodes[segment.node_b] and segment.visible?
232        doc.root << segment.to_xml_node(user_display_name_cache) 
233        visible_segments[segment.id] = segment
234      end
235    end
236
237    ways.each do |way|
238      doc.root << way.to_xml_node(visible_segments, user_display_name_cache) if way.visible?
239    end 
240
241    render :text => doc.to_s
242   
243    #exit when we have too many requests
244    if @@count > MAX_COUNT
245      @@count = COUNT
246      exit!
247    end
248
249  end
250end
Note: See TracBrowser for help on using the repository browser.