]> git.openstreetmap.org Git - rails.git/blob - app/controllers/relation_controller.rb
Optimise retrieval of full relations.
[rails.git] / app / controllers / relation_controller.rb
1 class RelationController < ApplicationController
2   require 'xml/libxml'
3
4   session :off
5   before_filter :authorize, :only => [:create, :update, :delete]
6   before_filter :require_public_data, :only => [:create, :update, :delete]
7   before_filter :check_api_writable, :only => [:create, :update, :delete]
8   before_filter :check_api_readable, :except => [:create, :update, :delete]
9   after_filter :compress_output
10
11   def create
12     begin
13       if request.put?
14         relation = Relation.from_xml(request.raw_post, true)
15
16         # We assume that an exception has been thrown if there was an error 
17         # generating the relation
18         #if relation
19           relation.create_with_history @user
20           render :text => relation.id.to_s, :content_type => "text/plain"
21         #else
22          # render :text => "Couldn't get turn the input into a relation.", :status => :bad_request
23         #end
24       else
25         render :nothing => true, :status => :method_not_allowed
26       end
27     rescue OSM::APIError => ex
28       render ex.render_opts
29     end
30   end
31
32   def read
33     begin
34       relation = Relation.find(params[:id])
35       response.headers['Last-Modified'] = relation.timestamp.rfc822
36       if relation.visible
37         render :text => relation.to_xml.to_s, :content_type => "text/xml"
38       else
39         render :text => "", :status => :gone
40       end
41     rescue ActiveRecord::RecordNotFound
42       render :nothing => true, :status => :not_found
43     rescue
44       render :nothing => true, :status => :internal_server_error
45     end
46   end
47
48   def update
49     logger.debug request.raw_post
50     begin
51       relation = Relation.find(params[:id])
52       new_relation = Relation.from_xml(request.raw_post)
53
54       if new_relation and new_relation.id == relation.id
55         relation.update_from new_relation, @user
56         render :text => relation.version.to_s, :content_type => "text/plain"
57       else
58         render :nothing => true, :status => :bad_request
59       end
60     rescue ActiveRecord::RecordNotFound
61       render :nothing => true, :status => :not_found
62     rescue OSM::APIError => ex
63       render ex.render_opts
64     end
65   end
66
67   def delete
68     begin
69       relation = Relation.find(params[:id])
70       new_relation = Relation.from_xml(request.raw_post)
71       if new_relation and new_relation.id == relation.id
72         relation.delete_with_history!(new_relation, @user)
73         render :text => relation.version.to_s, :content_type => "text/plain"
74       else
75         render :nothing => true, :status => :bad_request
76       end
77     rescue OSM::APIError => ex
78       render ex.render_opts
79     rescue ActiveRecord::RecordNotFound
80       render :nothing => true, :status => :not_found
81     end
82   end
83
84   # -----------------------------------------------------------------
85   # full
86   # 
87   # input parameters: id
88   #
89   # returns XML representation of one relation object plus all its
90   # members, plus all nodes part of member ways
91   # -----------------------------------------------------------------
92   def full
93     begin
94       relation = Relation.find(params[:id])
95
96       if relation.visible
97
98         # first find the ids of nodes, ways and relations referenced by this
99         # relation - note that we exclude this relation just in case.
100
101         node_ids = relation.members.select { |m| m[0] == 'Node' }.map { |m| m[1] }
102         way_ids = relation.members.select { |m| m[0] == 'Way' }.map { |m| m[1] }
103         relation_ids = relation.members.select { |m| m[0] == 'Relation' and m[1] != relation.id }.map { |m| m[1] }
104
105         # next load the relations and the ways.
106
107         relations = Relation.find(relation_ids, :include => [:relation_tags])
108         ways = Way.find(way_ids, :include => [:way_nodes, :way_tags])
109
110         # now additionally collect nodes referenced by ways. Note how we 
111         # recursively evaluate ways but NOT relations.
112
113         way_node_ids = ways.collect { |way|
114            way.way_nodes.collect { |way_node| way_node.node_id }
115         }
116         node_ids += way_node_ids.flatten
117         nodes = Node.find(node_ids.uniq, :include => :node_tags)
118     
119         # create XML.
120         doc = OSM::API.new.get_xml_doc
121         visible_nodes = {}
122         visible_members = { "Node" => [], "Way" => [], "Relation" => [] }
123         changeset_cache = {}
124         user_display_name_cache = {}
125
126         nodes.each do |node|
127           if node.visible? # should be unnecessary if data is consistent.
128             doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
129             visible_nodes[node.id] = node
130             visible_members["Node"][node.id] = true
131           end
132         end
133         ways.each do |way|
134           if way.visible? # should be unnecessary if data is consistent.
135             doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
136             visible_members["Way"][way.id] = true
137           end
138         end
139         relations.each do |rel|
140           if rel.visible? # should be unnecessary if data is consistent.
141             doc.root << rel.to_xml_node(nil, changeset_cache, user_display_name_cache)
142             visible_members["Relation"][rel.id] = true
143           end
144         end
145         # finally add self and output
146         doc.root << relation.to_xml_node(visible_members, changeset_cache, user_display_name_cache)
147         render :text => doc.to_s, :content_type => "text/xml"
148
149       else
150         render :nothing => true, :status => :gone
151       end
152
153     rescue ActiveRecord::RecordNotFound
154       render :nothing => true, :status => :not_found
155
156     rescue
157       render :nothing => true, :status => :internal_server_error
158     end
159   end
160
161   def relations
162     ids = params['relations'].split(',').collect { |w| w.to_i }
163
164     if ids.length > 0
165       doc = OSM::API.new.get_xml_doc
166
167       Relation.find(ids).each do |relation|
168         doc.root << relation.to_xml_node
169       end
170
171       render :text => doc.to_s, :content_type => "text/xml"
172     else
173       render :text => "You need to supply a comma separated list of ids.", :status => :bad_request
174     end
175   rescue ActiveRecord::RecordNotFound
176     render :text => "Could not find one of the relations", :status => :not_found
177   end
178
179   def relations_for_way
180     relations_for_object("Way")
181   end
182   def relations_for_node
183     relations_for_object("Node")
184   end
185   def relations_for_relation
186     relations_for_object("Relation")
187   end
188
189   def relations_for_object(objtype)
190     relationids = RelationMember.find(:all, :conditions => ['member_type=? and member_id=?', objtype, params[:id]]).collect { |ws| ws.id[0] }.uniq
191
192     doc = OSM::API.new.get_xml_doc
193
194     Relation.find(relationids).each do |relation|
195       doc.root << relation.to_xml_node if relation.visible
196     end
197
198     render :text => doc.to_s, :content_type => "text/xml"
199   end
200 end