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

Revision 4537, 7.6 KB checked in by tomhughes, 7 years ago (diff)

Dropping duplicate GPS points is very expensive and rarely finds
anything much to drop, so don't bother.

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