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