]> git.openstreetmap.org Git - rails.git/blob - app/models/way.rb
Now all the unit tests work
[rails.git] / app / models / way.rb
1 class Way < ActiveRecord::Base
2   require 'xml/libxml'
3
4   belongs_to :user
5
6   has_many :nodes, :through => :way_nodes, :order => 'sequence_id'
7   has_many :way_nodes, :foreign_key => 'id', :order => 'sequence_id'
8   has_many :way_tags, :foreign_key => 'id'
9
10   has_many :old_ways, :foreign_key => 'id', :order => 'version'
11
12   set_table_name 'current_ways'
13
14   def self.from_xml(xml, create=false)
15     begin
16       p = XML::Parser.new
17       p.string = xml
18       doc = p.parse
19
20       doc.find('//osm/way').each do |pt|
21         return Way.from_xml_node(pt, create)
22       end
23     rescue
24       return nil
25     end
26   end
27
28   def self.from_xml_node(pt, create=false)
29     way = Way.new
30
31     if !create and pt['id'] != '0'
32       way.id = pt['id'].to_i
33     end
34     
35     way.version = pt['version']
36
37     if create
38       way.timestamp = Time.now
39       way.visible = true
40     else
41       if pt['timestamp']
42         way.timestamp = Time.parse(pt['timestamp'])
43       end
44     end
45
46     pt.find('tag').each do |tag|
47       way.add_tag_keyval(tag['k'], tag['v'])
48     end
49
50     pt.find('nd').each do |nd|
51       way.add_nd_num(nd['ref'])
52     end
53
54     return way
55   end
56
57   # Find a way given it's ID, and in a single SQL call also grab its nodes
58   #
59   
60   # You can't pull in all the tags too unless we put a sequence_id on the way_tags table and have a multipart key
61   def self.find_eager(id)
62     way = Way.find(id, :include => {:way_nodes => :node})
63     #If waytag had a multipart key that was real, you could do this:
64     #way = Way.find(id, :include => [:way_tags, {:way_nodes => :node}])
65   end
66
67   # Find a way given it's ID, and in a single SQL call also grab its nodes and tags
68   def to_xml
69     doc = OSM::API.new.get_xml_doc
70     doc.root << to_xml_node()
71     return doc
72   end
73
74   def to_xml_node(visible_nodes = nil, user_display_name_cache = nil)
75     el1 = XML::Node.new 'way'
76     el1['id'] = self.id.to_s
77     el1['visible'] = self.visible.to_s
78     el1['timestamp'] = self.timestamp.xmlschema
79     el1['version'] = self.version.to_s
80
81     user_display_name_cache = {} if user_display_name_cache.nil?
82
83     if user_display_name_cache and user_display_name_cache.key?(self.user_id)
84       # use the cache if available
85     elsif self.user.data_public?
86       user_display_name_cache[self.user_id] = self.user.display_name
87     else
88       user_display_name_cache[self.user_id] = nil
89     end
90
91     el1['user'] = user_display_name_cache[self.user_id] unless user_display_name_cache[self.user_id].nil?
92
93     # make sure nodes are output in sequence_id order
94     ordered_nodes = []
95     self.way_nodes.each do |nd|
96       if visible_nodes
97         # if there is a list of visible nodes then use that to weed out deleted nodes
98         if visible_nodes[nd.node_id]
99           ordered_nodes[nd.sequence_id] = nd.node_id.to_s
100         end
101       else
102         # otherwise, manually go to the db to check things
103         if nd.node.visible? and nd.node.visible?
104           ordered_nodes[nd.sequence_id] = nd.node_id.to_s
105         end
106       end
107     end
108
109     ordered_nodes.each do |nd_id|
110       if nd_id and nd_id != '0'
111         e = XML::Node.new 'nd'
112         e['ref'] = nd_id
113         el1 << e
114       end
115     end
116
117     self.way_tags.each do |tag|
118       e = XML::Node.new 'tag'
119       e['k'] = tag.k
120       e['v'] = tag.v
121       el1 << e
122     end
123     return el1
124   end 
125
126   def nds
127     unless @nds
128       @nds = Array.new
129       self.way_nodes.each do |nd|
130         @nds += [nd.node_id]
131       end
132     end
133     @nds
134   end
135
136   def tags
137     unless @tags
138       @tags = {}
139       self.way_tags.each do |tag|
140         @tags[tag.k] = tag.v
141       end
142     end
143     @tags
144   end
145
146   def nds=(s)
147     @nds = s
148   end
149
150   def tags=(t)
151     @tags = t
152   end
153
154   def add_nd_num(n)
155     @nds = Array.new unless @nds
156     @nds << n.to_i
157   end
158
159   def add_tag_keyval(k, v)
160     @tags = Hash.new unless @tags
161     @tags[k] = v
162   end
163
164   def save_with_history!
165     t = Time.now
166
167     Way.transaction do
168       self.version += 1
169       self.timestamp = t
170       self.save!
171
172       tags = self.tags
173       WayTag.delete_all(['id = ?', self.id])
174       tags.each do |k,v|
175         tag = WayTag.new
176         tag.k = k
177         tag.v = v
178         tag.id = self.id
179         tag.save!
180       end
181
182       nds = self.nds
183       WayNode.delete_all(['id = ?', self.id])
184       sequence = 1
185       nds.each do |n|
186         nd = WayNode.new
187         nd.id = [self.id, sequence]
188         nd.node_id = n
189         nd.save!
190         sequence += 1
191       end
192
193       old_way = OldWay.from_way(self)
194       old_way.timestamp = t
195       old_way.save_with_dependencies!
196     end
197   end
198
199   def update_from(new_way, user)
200     if !new_way.preconditions_ok?
201       raise OSM::APIPreconditionFailedError.new
202     elsif new_way.version != version
203       raise OSM::APIVersionMismatchError.new(new_way.version, version)
204     else
205       self.user_id = user.id
206       self.tags = new_way.tags
207       self.nds = new_way.nds
208       self.visible = true
209       save_with_history!
210     end
211   end
212
213   def preconditions_ok?
214     return false if self.nds.empty?
215     self.nds.each do |n|
216       node = Node.find(:first, :conditions => ["id = ?", n])
217       unless node and node.visible
218         return false
219       end
220     end
221     return true
222   end
223
224   def delete_with_history(user)
225     if self.visible
226           # FIXME
227           # this should actually delete the relations,
228           # not just throw a PreconditionFailed if it's a member of a relation!!
229
230       # FIXME: this should probably renamed to delete_with_history
231       if RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id",
232                              :conditions => [ "visible = 1 AND member_type='way' and member_id=?", self.id])
233         raise OSM::APIPreconditionFailedError
234       # end FIXME
235       else
236         self.user_id = user.id
237         self.tags = []
238         self.nds = []
239         self.visible = false
240         self.save_with_history!
241       end
242     else
243       raise OSM::APIAlreadyDeletedError
244     end
245   end
246
247   # delete a way and it's nodes that aren't part of other ways, with history
248
249   # FIXME: merge the potlatch code to delete the relations
250   def delete_with_relations_and_nodes_and_history(user)
251     
252     node_ids = self.nodes.collect {|node| node.id }
253     node_ids_not_to_delete = []
254     way_nodes = WayNode.find(:all, :conditions => "node_id in (#{node_ids.join(',')}) and id != #{self.id}")
255     
256     node_ids_not_to_delete = way_nodes.collect {|way_node| way_node.node_id}
257
258     node_ids_to_delete = node_ids - node_ids_not_to_delete
259
260     # delete the nodes not used by other ways
261     node_ids_to_delete.each do |node_id|
262       n = Node.find(node_id)
263       n.user_id = user.id
264       n.visible = false
265       n.save_with_history!
266     end
267     
268     self.user_id = user.id
269
270     self.delete_with_history(user)
271
272   end
273 end