class AmfController < ApplicationController
require 'stringio'
+ session :off
+ before_filter :check_availability
+
# to log:
# RAILS_DEFAULT_LOGGER.error("Args: #{args[0]}, #{args[1]}, #{args[2]}, #{args[3]}")
args=getvalue(req) # | get response (probably an array)
case message
- when 'getpresets'; results[index]=putdata(index,getpresets)
- when 'whichways'; results[index]=putdata(index,whichways(args))
- when 'getway'; results[index]=putdata(index,getway(args))
- when 'putway'; results[index]=putdata(index,putway(args))
- when 'deleteway'; results[index]=putdata(index,deleteway(args))
+ when 'getpresets'; results[index]=putdata(index,getpresets)
+ when 'whichways'; results[index]=putdata(index,whichways(args))
+ when 'getway'; results[index]=putdata(index,getway(args))
+ when 'putway'; results[index]=putdata(index,putway(args))
+ when 'deleteway'; results[index]=putdata(index,deleteway(args))
+ when 'makeway'; results[index]=putdata(index,makeway(args))
end
end
# ------------------
# Write out response
- response.headers["Content-Type"]="application/x-amf"
+ RAILS_DEFAULT_LOGGER.info(" Response: start")
a,b=results.length.divmod(256)
- ans=0.chr+0.chr+0.chr+0.chr+a.chr+b.chr
- results.each do |k,v|
- ans+=v
- end
- render :text => ans
+ render :content_type => "application/x-amf", :text => proc { |response, output|
+ output.write 0.chr+0.chr+0.chr+0.chr+a.chr+b.chr
+ results.each do |k,v|
+ output.write(v)
+ end
+ }
+ RAILS_DEFAULT_LOGGER.info(" Response: end")
end
# ====================================================================
# Remote calls
- # ----- getpresets
- # return presets,presetmenus and presetnames arrays
+ # ----- getpresets
+ # return presets,presetmenus and presetnames arrays
def getpresets
presets={}
presettype=''
presetcategory=''
- ActiveRecord::Base.logger.info(" Message: getpresets")
+ RAILS_DEFAULT_LOGGER.info(" Message: getpresets")
# File.open("config/potlatch/presets.txt") do |file|
disused railway tracks: railway=disused
course of old railway: railway=abandoned
+way/natural
+forest: natural=wood,landuse=forest
+woodland: natural=wood,landuse=
+reservoir: natural=water,landuse=reservoir
+lake: natural=water,landuse=
+marsh: natural=marsh
+beach: natural=beach
+coastline: natural=coastline
+
point/road
mini roundabout: highway=mini_roundabout
traffic lights: highway=traffic_signals
station: railway=station
viaduct: railway=viaduct
level crossing: railway=crossing
+
+point/natural
+peak: natural=peak
EOF
StringIO.open(txt) do |file|
return [presets,presetmenus,presetnames]
end
- # ----- whichways(left,bottom,right,top)
- # return array of ways in current bounding box
- # at present, instead of using correct (=more complex) SQL to find
- # corner-crossing ways, it simply enlarges the bounding box by +/- 0.01
+ # ----- whichways(left,bottom,right,top)
+ # return array of ways in current bounding box
+ # at present, instead of using correct (=more complex) SQL to find
+ # corner-crossing ways, it simply enlarges the bounding box by +/- 0.01
def whichways(args)
xmin = args[0].to_f-0.01
xmax = args[2].to_f+0.01
ymax = args[3].to_f+0.01
- ActiveRecord::Base.logger.info(" Message: whichways, bbox=#{xmin},#{ymin},#{xmax},#{ymax}")
+ RAILS_DEFAULT_LOGGER.info(" Message: whichways, bbox=#{xmin},#{ymin},#{xmax},#{ymax}")
- waylist=WaySegment.find_by_sql("SELECT DISTINCT current_way_segments.id AS wayid"+
- " FROM current_way_segments,current_segments,current_nodes,current_ways "+
+ waylist=WayNode.find_by_sql("SELECT DISTINCT current_way_nodes.id AS wayid"+
+ " FROM current_way_nodes,current_segments,current_nodes,current_ways "+
" WHERE segment_id=current_segments.id "+
" AND current_segments.visible=1 "+
" AND node_a=current_nodes.id "+
- " AND current_ways.id=current_way_segments.id "+
+ " AND current_ways.id=current_way_nodes.id "+
" AND current_ways.visible=1 "+
" AND (latitude BETWEEN "+ymin.to_s+" AND "+ymax.to_s+") "+
" AND (longitude BETWEEN "+xmin.to_s+" AND "+xmax.to_s+")")
return [ways,points]
end
- # ----- getway (objectname, way, baselong, basey, masterscale)
- # returns objectname, array of co-ordinates, attributes,
- # xmin,xmax,ymin,ymax
+ # ----- getway (objectname, way, baselong, basey, masterscale)
+ # returns objectname, array of co-ordinates, attributes,
+ # xmin,xmax,ymin,ymax
def getway(args)
objname,wayid,baselong,basey,masterscale=args
xmin = ymin = 999999
xmax = ymax = -999999
- ActiveRecord::Base.logger.info(" Message: getway, id=#{wayid}")
+ RAILS_DEFAULT_LOGGER.info(" Message: getway, id=#{wayid}")
readwayquery(wayid).each {|row|
xs1=long2coord(row['long1'].to_f,baselong,masterscale); ys1=lat2coord(row['lat1'].to_f,basey,masterscale)
ActiveRecord::Base.connection.execute("SET #{db_now}=NOW()")
originalway=originalway.to_i
- ActiveRecord::Base.logger.info(" Message: putway, id=#{way}")
+ RAILS_DEFAULT_LOGGER.info(" Message: putway, id=#{originalway}")
# -- 3. read original way into memory
ymin=[ys,ymin].min; ymax=[ys,ymax].max
node=points[i][2].to_i
tagstr=array2tag(points[i][4])
+ tagstr=tagstr.gsub(/[\000-\037]/,"")
tagsql="'"+sqlescape(tagstr)+"'"
# compare node
if node<0
# new node - create
- newnode=ActiveRecord::Base.connection.insert("INSERT INTO current_nodes ( latitude,longitude,timestamp,user_id,visible,tags) VALUES ( #{ys},#{xs},#{db_now},#{uid},1,#{tagsql})")
- ActiveRecord::Base.connection.insert("INSERT INTO nodes (id,latitude,longitude,timestamp,user_id,visible,tags) VALUES (#{newnode},#{ys},#{xs},#{db_now},#{uid},1,#{tagsql})")
- points[i][2]=newnode
- renumberednodes[node.to_s]=newnode.to_s
+ if renumberednodes[node.to_s].nil?
+ newnode=ActiveRecord::Base.connection.insert("INSERT INTO current_nodes ( latitude,longitude,timestamp,user_id,visible,tags) VALUES ( #{ys},#{xs},#{db_now},#{uid},1,#{tagsql})")
+ ActiveRecord::Base.connection.insert("INSERT INTO nodes (id,latitude,longitude,timestamp,user_id,visible,tags) VALUES (#{newnode},#{ys},#{xs},#{db_now},#{uid},1,#{tagsql})")
+ points[i][2]=newnode
+ renumberednodes[node.to_s]=newnode.to_s
+ else
+ points[i][2]=renumberednodes[node.to_s].to_i
+ end
elsif xc.has_key?(node)
# old node from original way - update
from =points[i ][2].to_i
to =points[i+1][2].to_i
if seg.has_key?(segid)
+ # if segment exists, check it still refers to the same nodes
if seg[segid]=="#{from}-#{to}" then
if (seglist!='') then seglist+=',' end; seglist+=segid.to_s
next
end
+ elsif segid>0
+ # not in previous version of way, but supplied, so assume
+ # that it's come from makeway (i.e. unwayed segments)
+ if (seglist!='') then seglist+=',' end; seglist+=segid.to_s
+ next
end
segid=ActiveRecord::Base.connection.insert("INSERT INTO current_segments ( node_a,node_b,timestamp,user_id,visible,tags) VALUES ( #{from},#{to},#{db_now},#{uid},1,'')")
ActiveRecord::Base.connection.insert("INSERT INTO segments (id,node_a,node_b,timestamp,user_id,visible,tags) VALUES (#{segid},#{from},#{to},#{db_now},#{uid},1,'')")
points[i+1][5]=segid
numberedsegments[(i+1).to_s]=segid.to_s
end
- # numberedsegments.each{|a,b| RAILS_DEFAULT_LOGGER.error("Sending back: seg no. #{a} -> id #{b}") }
# -- 6.ii insert new way segments
ActiveRecord::Base.connection.execute("DROP TABLE #{db_uqs}")
ActiveRecord::Base.connection.execute("DROP TABLE #{db_uqn}")
- # insert new version of route into way_segments
+ # insert new version of route into way_nodes
insertsql =''
currentsql=''
sequence +=1
end
- ActiveRecord::Base.connection.execute("DELETE FROM current_way_segments WHERE id=#{way}");
- ActiveRecord::Base.connection.insert("INSERT INTO way_segments (id,segment_id,version ) VALUES #{insertsql}");
- ActiveRecord::Base.connection.insert("INSERT INTO current_way_segments (id,segment_id,sequence_id) VALUES #{currentsql}");
+ ActiveRecord::Base.connection.execute("DELETE FROM current_way_nodes WHERE id=#{way}");
+ ActiveRecord::Base.connection.insert("INSERT INTO way_nodes (id,segment_id,version ) VALUES #{insertsql}");
+ ActiveRecord::Base.connection.insert("INSERT INTO current_way_nodes (id,segment_id,sequence_id) VALUES #{currentsql}");
# -- 7. insert new way tags
insertsql =''
currentsql=''
attributes.each do |k,v|
- if v=='' then next end
+ if v=='' or v.nil? then next end
if v[0,6]=='(type ' then next end
if insertsql !='' then insertsql +=',' end
if currentsql!='' then currentsql+=',' end
+ k=k.gsub(/[\000-\037]/,"")
+ v=v.gsub(/[\000-\037]/,"")
insertsql +="(#{way},'"+sqlescape(k)+"','"+sqlescape(v)+"',#{version})"
currentsql+="(#{way},'"+sqlescape(k)+"','"+sqlescape(v)+"')"
end
def deleteway(args)
usertoken,way=args
- ActiveRecord::Base.logger.info(" Message: deleteway, id=#{way}")
+ RAILS_DEFAULT_LOGGER.info(" Message: deleteway, id=#{way}")
uid=getuserid(usertoken); if !uid then return end
way=way.to_i
ActiveRecord::Base.connection.insert("INSERT INTO ways (id,user_id,timestamp,visible) VALUES (#{way},#{uid},#{db_now},0)")
ActiveRecord::Base.connection.update("UPDATE current_ways SET user_id=#{uid},timestamp=#{db_now},visible=0 WHERE id=#{way}")
- ActiveRecord::Base.connection.execute("DELETE FROM current_way_segments WHERE id=#{way}")
+ ActiveRecord::Base.connection.execute("DELETE FROM current_way_nodes WHERE id=#{way}")
ActiveRecord::Base.connection.execute("DELETE FROM current_way_tags WHERE id=#{way}")
way
end
+# ----- makeway(x,y,baselong,basey,masterscale)
+# returns way made from unwayed segments
+
+def makeway(args)
+ usertoken,x,y,baselong,basey,masterscale=args
+ uid=getuserid(usertoken)
+ return if !uid
+
+ points=[]
+ toreverse=[] # segments to reverse
+ nodesused={} # so we don't go over the same node twice
+
+ # - find start point near x
+
+ xc=coord2long(x,masterscale,baselong)
+ yc=coord2lat(y,masterscale,basey)
+
+ RAILS_DEFAULT_LOGGER.info(" Message: makeway, xc=#{xc}, y=#{yc}")
+
+ xs1=xc-0.001; xs2=xc+0.001
+ ys1=yc-0.001; ys2=yc+0.001
+
+ sql=<<-EOF
+ SELECT cn1.latitude AS lat1,cn1.longitude AS lon1,cn1.id AS id1,
+ cn2.latitude AS lat2,cn2.longitude AS lon2,cn2.id AS id2, cs.id AS segid
+ FROM current_nodes AS cn1,
+ current_nodes AS cn2,
+ current_segments AS cs
+ LEFT OUTER JOIN current_way_nodes ON segment_id=cs.id
+ WHERE (cn1.longitude BETWEEN #{xs1} AND #{xs2})
+ AND (cn1.latitude BETWEEN #{ys1} AND #{ys2})
+ AND segment_id IS NULL
+ AND cs.visible=1
+ AND cn1.id=node_a AND cn1.visible=1
+ AND cn2.id=node_b AND cn2.visible=1
+ ORDER BY SQRT(POW(cn1.longitude-#{xc},2)+
+ POW(cn1.latitude -#{yc},2))
+ LIMIT 1
+ EOF
+ row=ActiveRecord::Base.connection.select_one sql
+ if row.nil? then return [0,0,0,0,0] end
+ xs1=long2coord(row['lon1'].to_f,baselong,masterscale); ys1=lat2coord(row['lat1'].to_f,basey,masterscale)
+ xs2=long2coord(row['lon2'].to_f,baselong,masterscale); ys2=lat2coord(row['lat2'].to_f,basey,masterscale)
+ xmin=[xs1,xs2].min; xmax=[xs1,xs2].max
+ ymin=[ys1,ys2].min; ymax=[ys1,ys2].max
+ nodesused[row['id1'].to_i]=true
+ nodesused[row['id2'].to_i]=true
+ points<<[xs1,ys1,row['id1'].to_i,1,{},0]
+ points<<[xs2,ys2,row['id2'].to_i,1,{},row['segid'].to_i]
+
+ # - extend at start, then end
+ while (a,point,nodesused,toreverse=findconnect(points[0][2],nodesused,'b',toreverse,baselong,basey,masterscale))[0]
+ points[0][5]=point[5]; point[5]=0 # segment leads to next node
+ points.unshift(point)
+ xmin=[point[0],xmin].min; xmax=[point[0],xmax].max
+ ymin=[point[1],ymin].min; ymax=[point[1],ymax].max
+ end
+ while (a,point,nodesused,toreverse=findconnect(points[-1][2],nodesused,'a',toreverse,baselong,basey,masterscale))[0]
+ points.push(point)
+ xmin=[point[0],xmin].min; xmax=[point[0],xmax].max
+ ymin=[point[1],ymin].min; ymax=[point[1],ymax].max
+ end
+ points[0][3]=0 # start with a move
+
+ # reverse segments in toreverse
+ if toreverse.length>0
+ sql=<<-EOF
+ UPDATE current_segments c1, current_segments c2
+ SET c1.node_a=c2.node_b,c1.node_b=c2.node_a,
+ c1.timestamp=NOW(),c1.user_id=#{uid}
+ WHERE c1.id=c2.id
+ AND c1.id IN (#{toreverse.join(',')})
+ EOF
+ ActiveRecord::Base.connection.update sql
+ sql=<<-EOF
+ INSERT INTO segments
+ (SELECT * FROM current_segments
+ WHERE id IN (#{toreverse.join(',')}))
+ EOF
+ ActiveRecord::Base.connection.insert sql
+ end
+
+ [points,xmin,xmax,ymin,ymax]
+end
+
+def findconnect(id,nodesused,lookfor,toreverse,baselong,basey,masterscale)
+ # get all segments with 'id' as a point
+ # (to look for both node_a and node_b, UNION is faster than node_a=id OR node_b=id)!
+ sql=<<-EOF
+ SELECT cn1.latitude AS lat1,cn1.longitude AS lon1,cn1.id AS id1,
+ cn2.latitude AS lat2,cn2.longitude AS lon2,cn2.id AS id2, cs.id AS segid
+ FROM current_nodes AS cn1,
+ current_nodes AS cn2,
+ current_segments AS cs
+ LEFT OUTER JOIN current_way_nodes ON segment_id=cs.id
+ WHERE segment_id IS NULL
+ AND cs.visible=1
+ AND cn1.id=node_a AND cn1.visible=1
+ AND cn2.id=node_b AND cn2.visible=1
+ AND node_a=#{id}
+ UNION
+ SELECT cn1.latitude AS lat1,cn1.longitude AS lon1,cn1.id AS id1,
+ cn2.latitude AS lat2,cn2.longitude AS lon2,cn2.id AS id2, cs.id AS segid
+ FROM current_nodes AS cn1,
+ current_nodes AS cn2,
+ current_segments AS cs
+ LEFT OUTER JOIN current_way_nodes ON segment_id=cs.id
+ WHERE segment_id IS NULL
+ AND cs.visible=1
+ AND cn1.id=node_a AND cn1.visible=1
+ AND cn2.id=node_b AND cn2.visible=1
+ AND node_b=#{id}
+ EOF
+ connectlist=ActiveRecord::Base.connection.select_all sql
+
+ if lookfor=='b' then tocol='id1'; tolat='lat1'; tolon='lon1'; fromcol='id2'; fromlat='lat2'; fromlon='lon2'
+ else tocol='id2'; tolat='lat2'; tolon='lon2'; fromcol='id1'; fromlat='lat1'; fromlon='lon1'
+ end
+
+ # eliminate those already in the hash
+ connex=0
+ point=nil
+ connectlist.each { |row|
+ tonode=row[tocol].to_i
+ fromnode=row[fromcol].to_i
+ if id==tonode and !nodesused.has_key?(fromnode)
+ # wrong way round; add, then add to 'segments to reverse' list
+ connex+=1
+ nodesused[fromnode]=true
+ point=[long2coord(row[fromlon].to_f,baselong,masterscale),lat2coord(row[fromlat].to_f,basey,masterscale),fromnode,1,{},row['segid'].to_i]
+ toreverse.push(row['segid'].to_i)
+ elsif id==fromnode and !nodesused.has_key?(tonode)
+ # right way round; just add
+ connex+=1
+ point=[long2coord(row[tolon].to_f,baselong,masterscale),lat2coord(row[tolat].to_f,basey,masterscale),tonode,1,{},row['segid'].to_i]
+ nodesused[tonode]=true
+ end
+ }
+
+ # if only one left, then add it; otherwise return false
+ if connex!=1 or point.nil? then
+ return [false,[],nodesused,toreverse]
+ else
+ return [true,point,nodesused,toreverse]
+ end
+end
+
+
# ====================================================================
# Support functions for remote calls
def readwayquery(id)
ActiveRecord::Base.connection.select_all "SELECT n1.latitude AS lat1,n1.longitude AS long1,n1.id AS id1,n1.tags as tags1, "+
" n2.latitude AS lat2,n2.longitude AS long2,n2.id AS id2,n2.tags as tags2,segment_id "+
- " FROM current_way_segments,current_segments,current_nodes AS n1,current_nodes AS n2 "+
- " WHERE current_way_segments.id=#{id} "+
+ " FROM current_way_nodes,current_segments,current_nodes AS n1,current_nodes AS n2 "+
+ " WHERE current_way_nodes.id=#{id} "+
" AND segment_id=current_segments.id "+
" AND current_segments.visible=1 "+
" AND n1.id=node_a and n2.id=node_b "+
sql=<<-EOF
CREATE TEMPORARY TABLE #{uqs_name}
SELECT a.segment_id
- FROM (SELECT DISTINCT segment_id FROM current_way_segments
+ FROM (SELECT DISTINCT segment_id FROM current_way_nodes
WHERE id = #{way}) a
- LEFT JOIN current_way_segments b
+ LEFT JOIN current_way_nodes b
ON b.segment_id = a.segment_id
AND b.id != #{way}
WHERE b.segment_id IS NULL
end
def getuserid(token)
- token=sqlescape(token)
- if (token=~/^(.+)\+(.+)$/) then
- return ActiveRecord::Base.connection.select_value("SELECT id FROM users WHERE active=1 AND email='#{$1}' AND pass_crypt=MD5('#{$2}')")
+ if (token =~ /^(.+)\+(.+)$/) then
+ user = User.authenticate(:username => $1, :password => $2)
else
- return ActiveRecord::Base.connection.select_value("SELECT id FROM users WHERE active=1 AND token='#{token}'")
+ user = User.authenticate(:token => token)
end
+
+ return user ? user.id : nil;
end
when 5; return nil # null
when 6; return nil # undefined
when 8; s.read(4) # mixedArray
- return getobject(s) # |
- when 10;return getarray(s) # array
- else; return nil # error
+ return getobject(s) # |
+ when 10; return getarray(s) # array
+ else; return nil # error
end
end