- uid=getuserid(usertoken)
- if !uid then return -1,"You are not logged in, so the way could not be saved." end
- originalway=originalway.to_i
-
- points.each do |a|
- if a[2]==0 or a[2].nil? then return -2,"Server error - node with id 0 found in way #{originalway}." end
- if a[1]==90 then return -2,"Server error - node with lat -90 found in way #{originalway}." end
- end
-
- if points.length<2 then return -2,"Server error - way is only #{points.length} points long." end
-
- # -- Get unique nodes
-
- if originalway<0
- way=Way.new
- uniques=[]
- else
- way=Way.find(originalway)
- uniques=way.unshared_node_ids
- end
-
- # -- Compare nodes and save changes to any that have changed
-
- nodes=[]
-
- points.each do |n|
- lon=n[0].to_f
- lat=n[1].to_f
- id =n[2].to_i
- savenode=false
- if renumberednodes[id]
- id=renumberednodes[id]
- elsif id<0
- # Create new node
- node=Node.new
- savenode=true
- else
- node=Node.find(id)
- if (!fpcomp(lat,node.lat) or !fpcomp(lon,node.lon) or \
- Tags.join(n[4])!=node.tags or node.visible==0)
- savenode=true
- end
- end
- if savenode
- node.user_id=uid
- node.lat=lat; node.lon=lon
- node.tags=Tags.join(n[4])
- node.visible=true
- node.save_with_history!
- if id!=node.id
- renumberednodes[id]=node.id
- id=node.id
- end
- end
- uniques=uniques-[id]
- nodes.push(id)
- end
-
- # -- Delete any unique nodes
-
- uniques.each do |n|
- deleteitemrelations(n,'node')
- node=Node.find(n)
- node.user_id=uid
- node.visible=false
- node.save_with_history!
- end
-
- # -- Save revised way
-
- way.tags=attributes
- way.nds=nodes
- way.user_id=uid
- way.visible=true
- way.save_with_history!
-
- [0,originalway,way.id,renumberednodes]
+ user = getuser(usertoken)
+ if !user then return -1,"You are not logged in, so the way could not be saved." end
+ if pointlist.length < 2 then return -2,"Server error - way is only #{points.length} points long." end
+
+ originalway = originalway.to_i
+ pointlist.collect! {|a| a.to_i }
+
+ way=nil # this is returned, so scope it outside the transaction
+ nodeversions = {}
+ Way.transaction do
+
+ # -- Get unique nodes
+
+ if originalway <= 0
+ uniques = []
+ else
+ way = Way.find(originalway)
+ uniques = way.unshared_node_ids
+ end
+
+ #-- Update each changed node
+
+ nodes.each do |a|
+ lon = a[0].to_f
+ lat = a[1].to_f
+ id = a[2].to_i
+ version = a[3].to_i
+ if id == 0 then return -2,"Server error - node with id 0 found in way #{originalway}." end
+ if lat== 90 then return -2,"Server error - node with latitude -90 found in way #{originalway}." end
+ if renumberednodes[id] then id = renumberednodes[id] end
+
+ node = Node.new
+ node.changeset_id = changeset_id
+ node.lat = lat
+ node.lon = lon
+ node.tags = a[4]
+ node.tags.delete('created_by')
+ node.version = version
+ if id <= 0
+ # We're creating the node
+ node.create_with_history(user)
+ renumberednodes[id] = node.id
+ nodeversions[node.id] = node.version
+ else
+ # We're updating an existing node
+ previous=Node.find(id)
+ previous.update_from(node, user)
+ nodeversions[previous.id] = previous.version
+ end
+ end
+
+ # -- Save revised way
+
+ pointlist.collect! {|a|
+ renumberednodes[a] ? renumberednodes[a]:a
+ } # renumber nodes
+ new_way = Way.new
+ new_way.tags = attributes
+ new_way.nds = pointlist
+ new_way.changeset_id = changeset_id
+ new_way.version = wayversion
+ if originalway <= 0
+ new_way.create_with_history(user)
+ way=new_way # so we can get way.id and way.version
+ elsif way.tags!=attributes or way.nds!=pointlist or !way.visible?
+ way.update_from(new_way, user)
+ end
+
+ # -- Delete any unique nodes no longer used
+
+ uniques=uniques-pointlist
+ uniques.each do |n|
+ node = Node.find(n)
+ deleteitemrelations(user, changeset_id, id, 'Node', node.version)
+ new_node = Node.new
+ new_node.changeset_id = changeset_id
+ new_node.version = node.version
+ node.delete_with_history!(new_node, user)
+ end
+
+ end # transaction
+
+ [0, originalway, way.id, renumberednodes, way.version, nodeversions]
+ rescue OSM::APIChangesetAlreadyClosedError => ex
+ return [-2, "Sorry, your changeset #{ex.changeset.id} has been closed (at #{ex.changeset.closed_at})."]
+ rescue OSM::APIVersionMismatchError => ex
+ # Really need to check to see whether this is a server load issue, and the
+ # last version was in the same changeset, or belongs to the same user, then
+ # we can return something different
+ return [-3, "Sorry, someone else has changed this way since you started editing. Click the 'Edit' tab to reload the area. The server said: #{ex}"]
+ rescue OSM::APITooManyWayNodesError => ex
+ return [-1, "You have tried to upload a really long way with #{ex.provided} points: only #{ex.max} are allowed."]
+ rescue OSM::APIAlreadyDeletedError => ex
+ return [-1, "The point has already been deleted."]
+ rescue OSM::APIError => ex
+ # Some error that we don't specifically catch
+ return [-2, "An unusual error happened (in 'putway' #{originalway}). The server said: #{ex}"]