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

Last change on this file since 4537 was 4537, checked in by tomhughes, 10 years ago

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

File size: 7.6 KB
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.