include MapBoundary
session :off
- before_filter :check_write_availability
+ before_filter :check_api_writable
# Main AMF handlers: process the raw AMF string (using AMF library) and
# calls each action (private method) accordingly.
# ** FIXME: refactor to reduce duplication of code across read/write
def amf_read
- req=StringIO.new(request.raw_post+0.chr)# Get POST data as request
- # (cf http://www.ruby-forum.com/topic/122163)
- req.read(2) # Skip version indicator and client ID
- results={} # Results of each body
+ if request.post?
+ req=StringIO.new(request.raw_post+0.chr)# Get POST data as request
+ # (cf http://www.ruby-forum.com/topic/122163)
+ req.read(2) # Skip version indicator and client ID
+ results={} # Results of each body
- # Parse request
+ # Parse request
- headers=AMF.getint(req) # Read number of headers
+ headers=AMF.getint(req) # Read number of headers
- headers.times do # Read each header
- name=AMF.getstring(req) # |
- req.getc # | skip boolean
- value=AMF.getvalue(req) # |
- header["name"]=value # |
- end
+ headers.times do # Read each header
+ name=AMF.getstring(req) # |
+ req.getc # | skip boolean
+ value=AMF.getvalue(req) # |
+ header["name"]=value # |
+ end
- bodies=AMF.getint(req) # Read number of bodies
- bodies.times do # Read each body
- message=AMF.getstring(req) # | get message name
- index=AMF.getstring(req) # | get index in response sequence
- bytes=AMF.getlong(req) # | get total size in bytes
- args=AMF.getvalue(req) # | get response (probably an array)
- logger.info("Executing AMF #{message}:#{index}")
-
- case message
- when 'getpresets'; results[index]=AMF.putdata(index,getpresets())
- when 'whichways'; results[index]=AMF.putdata(index,whichways(*args))
- when 'whichways_deleted'; results[index]=AMF.putdata(index,whichways_deleted(*args))
- when 'getway'; results[index]=AMF.putdata(index,getway(args[0].to_i))
- when 'getrelation'; results[index]=AMF.putdata(index,getrelation(args[0].to_i))
- when 'getway_old'; results[index]=AMF.putdata(index,getway_old(args[0].to_i,args[1]))
- when 'getway_history'; results[index]=AMF.putdata(index,getway_history(args[0].to_i))
- when 'getnode_history'; results[index]=AMF.putdata(index,getnode_history(args[0].to_i))
- when 'findgpx'; results[index]=AMF.putdata(index,findgpx(*args))
- when 'findrelations'; results[index]=AMF.putdata(index,findrelations(*args))
- when 'getpoi'; results[index]=AMF.putdata(index,getpoi(*args))
+ bodies=AMF.getint(req) # Read number of bodies
+ bodies.times do # Read each body
+ message=AMF.getstring(req) # | get message name
+ index=AMF.getstring(req) # | get index in response sequence
+ bytes=AMF.getlong(req) # | get total size in bytes
+ args=AMF.getvalue(req) # | get response (probably an array)
+ logger.info("Executing AMF #{message}:#{index}")
+
+ case message
+ when 'getpresets'; results[index]=AMF.putdata(index,getpresets())
+ when 'whichways'; results[index]=AMF.putdata(index,whichways(*args))
+ when 'whichways_deleted'; results[index]=AMF.putdata(index,whichways_deleted(*args))
+ when 'getway'; r=AMF.putdata(index,getway(args[0].to_i))
+ results[index]=r
+ when 'getrelation'; results[index]=AMF.putdata(index,getrelation(args[0].to_i))
+ when 'getway_old'; results[index]=AMF.putdata(index,getway_old(args[0].to_i,args[1]))
+ when 'getway_history'; results[index]=AMF.putdata(index,getway_history(args[0].to_i))
+ when 'getnode_history'; results[index]=AMF.putdata(index,getnode_history(args[0].to_i))
+ when 'findgpx'; results[index]=AMF.putdata(index,findgpx(*args))
+ when 'findrelations'; results[index]=AMF.putdata(index,findrelations(*args))
+ when 'getpoi'; results[index]=AMF.putdata(index,getpoi(*args))
+ end
end
+ logger.info("encoding AMF results")
+ sendresponse(results)
+ else
+ render :nothing => true, :status => :method_not_allowed
end
- logger.info("encoding AMF results")
- sendresponse(results)
end
def amf_write
- req=StringIO.new(request.raw_post+0.chr)
- req.read(2)
- results={}
- renumberednodes={} # Shared across repeated putways
- renumberedways={} # Shared across repeated putways
-
- headers=AMF.getint(req) # Read number of headers
- headers.times do # Read each header
- name=AMF.getstring(req) # |
- req.getc # | skip boolean
- value=AMF.getvalue(req) # |
- header["name"]=value # |
- end
+ if request.post?
+ req=StringIO.new(request.raw_post+0.chr)
+ req.read(2)
+ results={}
+ renumberednodes={} # Shared across repeated putways
+ renumberedways={} # Shared across repeated putways
+
+ headers=AMF.getint(req) # Read number of headers
+ headers.times do # Read each header
+ name=AMF.getstring(req) # |
+ req.getc # | skip boolean
+ value=AMF.getvalue(req) # |
+ header["name"]=value # |
+ end
- bodies=AMF.getint(req) # Read number of bodies
- bodies.times do # Read each body
- message=AMF.getstring(req) # | get message name
- index=AMF.getstring(req) # | get index in response sequence
- bytes=AMF.getlong(req) # | get total size in bytes
- args=AMF.getvalue(req) # | get response (probably an array)
-
- logger.info("Executing AMF #{message}:#{index}")
- case message
- when 'putway'; r=putway(renumberednodes,*args)
- renumberednodes=r[3]
- if r[1] != r[2] then renumberedways[r[1]] = r[2] end
- results[index]=AMF.putdata(index,r)
- when 'putrelation'; results[index]=AMF.putdata(index,putrelation(renumberednodes, renumberedways, *args))
- when 'deleteway'; results[index]=AMF.putdata(index,deleteway(*args))
- when 'putpoi'; r=putpoi(*args)
- if r[1] != r[2] then renumberednodes[r[1]] = r[2] end
- results[index]=AMF.putdata(index,r)
- when 'startchangeset'; results[index]=AMF.putdata(index,startchangeset(*args))
+ bodies=AMF.getint(req) # Read number of bodies
+ bodies.times do # Read each body
+ message=AMF.getstring(req) # | get message name
+ index=AMF.getstring(req) # | get index in response sequence
+ bytes=AMF.getlong(req) # | get total size in bytes
+ args=AMF.getvalue(req) # | get response (probably an array)
+
+ logger.info("Executing AMF #{message}:#{index}")
+ case message
+ when 'putway'; r=putway(renumberednodes,*args)
+ renumberednodes=r[3]
+ if r[1] != r[2] then renumberedways[r[1]] = r[2] end
+ results[index]=AMF.putdata(index,r)
+ when 'putrelation'; results[index]=AMF.putdata(index,putrelation(renumberednodes, renumberedways, *args))
+ when 'deleteway'; results[index]=AMF.putdata(index,deleteway(*args))
+ when 'putpoi'; r=putpoi(*args)
+ if r[1] != r[2] then renumberednodes[r[1]] = r[2] end
+ results[index]=AMF.putdata(index,r)
+ when 'startchangeset'; results[index]=AMF.putdata(index,startchangeset(*args))
+ end
end
+ logger.info("encoding AMF results")
+ sendresponse(results)
+ else
+ render :nothing => true, :status => :method_not_allowed
end
- logger.info("encoding AMF results")
- sendresponse(results)
end
private
cs.tags = cstags
cs.user_id = user.id
# smsm1 doesn't like the next two lines and thinks they need to be abstracted to the model more/better
- cs.created_at = Time.now
+ cs.created_at = Time.now.getutc
cs.closed_at = cs.created_at + Changeset::IDLE_TIMEOUT
cs.save_with_tags!
return [0,cs.id]
[0, ways, points, relations]
rescue Exception => err
- [-2,"Sorry - I can't get the map for that area."]
+ [-2,"Sorry - I can't get the map for that area. The server said: #{err}"]
end
# Find deleted ways in current bounding box (similar to whichways, but ways
begin
check_boundaries(xmin, ymin, xmax, ymax)
rescue Exception => err
- return [-2,"Sorry - I can't get the map for that area."]
+ return [-2,"Sorry - I can't get the map for that area. The server said: #{err}"]
end
nodes_in_area = Node.find_by_area(ymin, xmin, ymax, xmax, :conditions => ["current_ways.visible = ?", false], :include => :ways_via_history)
# then rails only seems to return the first copy of a node when a
# way includes a node more than once
begin
- way = Way.find(wayid)
+ way = Way.find(wayid, :include => { :nodes => :node_tags })
rescue ActiveRecord::RecordNotFound
return [wayid,[],{}]
end
[wayid, points, tags, version]
end
-
+
# Get an old version of a way, and all constituent nodes.
#
# For undelete (version<0), always uses the most recent version of each node,
old_way = OldWay.find(:first, :conditions => ['visible = ? AND id = ?', true, id], :order => 'version DESC')
points = old_way.get_nodes_undelete unless old_way.nil?
else
- # revert
- timestamp = DateTime.strptime(timestamp, "%d %b %Y, %H:%M:%S")
- old_way = OldWay.find(:first, :conditions => ['id = ? AND timestamp <= ?', id, timestamp], :order => 'timestamp DESC')
- points = old_way.get_nodes_revert(timestamp) unless old_way.nil?
- if !old_way.visible
- return [-1, "Sorry, the way was deleted at that time - please revert to a previous version."]
+ begin
+ # revert
+ timestamp = DateTime.strptime(timestamp.to_s, "%d %b %Y, %H:%M:%S")
+ old_way = OldWay.find(:first, :conditions => ['id = ? AND timestamp <= ?', id, timestamp], :order => 'timestamp DESC')
+ unless old_way.nil?
+ points = old_way.get_nodes_revert(timestamp)
+ if !old_way.visible
+ return [-1, "Sorry, the way was deleted at that time - please revert to a previous version."]
+ end
+ end
+ rescue ArgumentError
+ # thrown by date parsing method. leave old_way as nil for
+ # the superb error handler below.
end
end
if searchterm.to_i>0 then
rel = Relation.find(searchterm.to_i)
if rel and rel.visible then
- rels.push([rel.id, rel.tags, rel.members])
+ rels.push([rel.id, rel.tags, rel.members, rel.version])
end
else
RelationTag.find(:all, :limit => 11, :conditions => ["match(v) against (?)", searchterm] ).each do |t|
if t.relation.visible then
- rels.push([t.relation.id, t.relation.tags, t.relation.members])
+ rels.push([t.relation.id, t.relation.tags, t.relation.members, t.relation.version])
end
end
end
# Returns
# 0. 0 (success),
# 1. original relation id (unchanged),
- # 2. new relation id.
+ # 2. new relation id,
+ # 3. version.
def putrelation(renumberednodes, renumberedways, usertoken, changeset_id, version, relid, tags, members, visible) #:doc:
user = getuser(usertoken)
members.each do |m|
mid = m[1].to_i
if mid < 0
- mid = renumberednodes[mid] if m[0] == 'node'
- mid = renumberedways[mid] if m[0] == 'way'
+ mid = renumberednodes[mid] if m[0] == 'Node'
+ mid = renumberedways[mid] if m[0] == 'Way'
end
if mid
typedmembers << [m[0], mid, m[2]]
new_relation.changeset_id = changeset_id
new_relation.version = version
-
- if id <= 0
+ if relid <= 0
# We're creating the node
new_relation.create_with_history(user)
elsif visible
end
end # transaction
- if id <= 0
+ if relid <= 0
return [0, relid, new_relation.id, new_relation.version]
else
- return [0, relid, relation.id, relation.version]
+ return [0, relid, relid, relation.version]
end
rescue OSM::APIChangesetAlreadyClosedError => ex
return [-1, "The changeset #{ex.changeset.id} was closed at #{ex.changeset.closed_at}."]
# 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 relation since you started editing. Please click the 'Edit' tab to reload the area."]
+ return [-3, "Sorry, someone else has changed this relation since you started editing. Please click the 'Edit' tab to reload the area. The server said: #{ex}"]
rescue OSM::APIAlreadyDeletedError => ex
return [-1, "The relation has already been deleted."]
rescue OSM::APIError => ex
# Some error that we don't specifically catch
- return [-2, "Something really bad happened :-( ."]
+ return [-2, "An unusual error happened (in 'putrelation' #{relid}). The server said: #{ex}"]
end
# Save a way to the database, including all nodes. Any nodes in the previous
uniques=uniques-pointlist
uniques.each do |n|
node = Node.find(n)
- deleteitemrelations(user, changeset_id, id, 'node', node.version)
+ deleteitemrelations(user, changeset_id, id, 'Node', node.version)
new_node = Node.new
new_node.changeset_id = changeset_id
new_node.version = node.version
# 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. Please click the 'Edit' tab to reload the area."]
+ 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, "Something really bad happened :-(."]
+ return [-2, "An unusual error happened (in 'putway' #{originalway}). The server said: #{ex}"]
end
# Save POI to the database.
# 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 point since you started editing. Please click the 'Edit' tab to reload the area."]
+ return [-3, "Sorry, someone else has changed this point since you started editing. Please click the 'Edit' tab to reload the area. The server said: #{ex}"]
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, "Something really bad happened :-()"]
+ return [-2, "An unusual error happened (in 'putpoi' #{id}). The server said: #{ex}"]
end
# Read POI from database
# (only called on revert: POIs are usually read by whichways).
#
- # Returns array of id, long, lat, hash of tags, version.
+ # Returns array of id, long, lat, hash of tags, (current) version.
def getpoi(id,timestamp) #:doc:
- if timestamp == '' then
- n = Node.find(id)
- else
+ n = Node.find(id)
+ v = n.version
+ unless timestamp == ''
n = OldNode.find(id, :conditions=>['timestamp=?',DateTime.strptime(timestamp, "%d %b %Y, %H:%M:%S")])
end
if n
- return [n.id, n.lon, n.lat, n.tags, n.version]
+ return [n.id, n.lon, n.lat, n.tags, v]
else
return [nil, nil, nil, {}, nil]
end
# delete the way
old_way = Way.find(way_id)
+ u = old_way.unshared_node_ids
delete_way = Way.new
delete_way.version = way_version
delete_way.changeset_id = changeset_id
old_way.delete_with_history!(delete_way, user)
- old_way.unshared_node_ids.each do |node_id|
+ u.each do |node_id|
# delete the node
node = Node.find(node_id)
delete_node = Node.new
else
# in case the node wasn't passed (i.e. if it was previously removed
# from the way in Potlatch)
- deleteitemrelations(user, changeset_id, node_id, 'node', node.version)
+ deleteitemrelations(user, changeset_id, node_id, 'Node', node.version)
delete_node.version = node.version
end
node.delete_with_history!(delete_node, user)
return [-1, "The way has already been deleted."]
rescue OSM::APIError => ex
# Some error that we don't specifically catch
- return [-2, "Something really bad happened :-( ."]
+ return [-2, "An unusual error happened (in 'deleteway' #{way_id}). The server said: #{ex}"]
end
def deleteitemrelations(user, changeset_id, objid, type, version) #:doc:
relations = RelationMember.find(:all,
- :conditions => ['member_type = ? and member_id = ?', type, objid],
+ :conditions => ['member_type = ? and member_id = ?', type.classify, objid],
:include => :relation).collect { |rm| rm.relation }.uniq
relations.each do |rel|
SELECT DISTINCT cr.id AS relid,cr.version AS version
FROM current_relations cr
INNER JOIN current_relation_members crm ON crm.id=cr.id
- INNER JOIN current_nodes cn ON crm.member_id=cn.id AND crm.member_type='node'
+ INNER JOIN current_nodes cn ON crm.member_id=cn.id AND crm.member_type='Node'
WHERE #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "cn.")}
EOF
unless way_ids.empty?
SELECT DISTINCT cr.id AS relid,cr.version AS version
FROM current_relations cr
INNER JOIN current_relation_members crm ON crm.id=cr.id
- WHERE crm.member_type='way'
+ WHERE crm.member_type='Way'
AND crm.member_id IN (#{way_ids.join(',')})
EOF
end
- return ActiveRecord::Base.connection.select_all(sql).collect { |a| [a['relid'].to_i,a['version'].to_i] }
+ ActiveRecord::Base.connection.select_all(sql).collect { |a| [a['relid'].to_i,a['version'].to_i] }
end
def sql_get_nodes_in_way(wayid)
points=[]
sql=<<-EOF
- SELECT latitude*0.0000001 AS lat,longitude*0.0000001 AS lon,current_nodes.id
+ SELECT latitude*0.0000001 AS lat,longitude*0.0000001 AS lon,current_nodes.id,current_nodes.version
FROM current_way_nodes,current_nodes
WHERE current_way_nodes.id=#{wayid.to_i}
AND current_way_nodes.node_id=current_nodes.id
nodetags[n['k']]=n['v']
end
nodetags.delete('created_by')
- points << [row['lon'].to_f,row['lat'].to_f,row['id'].to_i,nodetags]
+ points << [row['lon'].to_f,row['lat'].to_f,row['id'].to_i,nodetags,row['version'].to_i]
end
points
end