Potlatch Rails AMF stuff
authorRichard Fairhurst <richard@systemed.net>
Sat, 5 May 2007 16:09:34 +0000 (16:09 +0000)
committerRichard Fairhurst <richard@systemed.net>
Sat, 5 May 2007 16:09:34 +0000 (16:09 +0000)
app/controllers/amf_controller.rb [new file with mode: 0644]
app/views/site/edit.rhtml
config/potlatch/presets.txt [new file with mode: 0755]
config/routes.rb
public/potlatch/potlatch.swf

diff --git a/app/controllers/amf_controller.rb b/app/controllers/amf_controller.rb
new file mode 100644 (file)
index 0000000..f4f762a
--- /dev/null
@@ -0,0 +1,257 @@
+class AmfController < ApplicationController
+  require 'stringio'
+
+  # Still to do:
+  # - all db interaction
+  # - user authentication
+  # - (also pass lat/lon through from view tab to edit tab)
+
+  # ====================================================================
+  # Main AMF handler
+  
+  # ---- talk  process AMF request
+
+  def talk
+       req=StringIO.new(request.raw_post)      # Get POST data as request
+       req.read(2)                                                     # Skip version indicator and client ID
+       results={}                                                      # Results of each body
+
+       # -------------
+       # Parse request
+
+       headers=getint(req)                                     # Read number of headers
+       for i in (1..headers)                           # Read each header
+               name=getstring(req)                             #  |
+               req.getc                                                #  | skip boolean
+               value=getvalue(req)                             #  |
+               header["name"]=value                    #  |
+       end
+
+       bodies=getint(req)                                      # Read number of bodies
+       for i in (1..bodies)                            # Read each body
+               message=getstring(req)                  #  | get message name
+               index=getstring(req)                    #  | get index in response sequence
+               bytes=getlong(req)                              #  | get total size in bytes
+               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))
+               end
+       end
+
+       # ------------------
+       # Write out response
+
+       response.headers["Content-Type"]="application/x-amf"
+       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
+
+  end
+
+
+       # ====================================================================
+       # Remote calls
+
+       def getpresets
+               presets={}
+               presetmenus={}; presetmenus['point']=[]; presetmenus['way']=[]
+               presetnames={}; presetnames['point']={}; presetnames['way']={}
+               presettype=''
+               presetcategory=''
+               
+               File.open("config/potlatch/presets.txt") do |file|
+                       file.each_line {|line|
+                               t=line.chomp
+                               if (t=~/(\w+)\/(\w+)/) then
+                                       presettype=$1
+                                       presetcategory=$2
+                                       presetmenus[presettype].push(presetcategory)
+                                       presetnames[presettype][presetcategory]=["(no preset)"]
+                               elsif (t=~/^(.+):\s?(.+)$/) then
+                                       pre=$1; kv=$2
+                                       presetnames[presettype][presetcategory].push(pre)
+                                       presets[pre]={}
+                                       kv.split(',').each {|a|
+                                               if (a=~/^(.+)=(.*)$/) then presets[pre][$1]=$2 end
+                                       }
+                               end
+                       }
+               end
+               [presets,presetmenus,presetnames]
+       end
+
+       def whichways(args)
+               # to do
+       end
+       
+       def getway(args)
+               # to do
+       end
+       
+       def putway(args)
+               # to do
+       end
+       
+       def deleteway(args)
+               # to do
+       end
+       
+       # need support functions here too:
+       #       database support functions (readwayquery, createuniquesegments)
+       #       tag2array, array2tag
+       #       getuserid
+
+       # ====================================================================
+       # AMF read subroutines
+       
+       # ----- getint          return two-byte integer
+       # ----- getlong         return four-byte long
+       # ----- getstring       return string with two-byte length
+       # ----- getdouble       return eight-byte double-precision float
+       # ----- getobject       return object/hash
+       # ----- getarray        return numeric array
+       
+       def getint(s)
+               s.getc*256+s.getc
+       end
+       
+       def getlong(s)
+               ((s.getc*256+s.getc)*256+s.getc)*256+s.getc
+       end
+       
+       def getstring(s)
+               len=s.getc*256+s.getc
+               s.read(len)
+       end
+       
+       def getdouble(s)
+               a=s.read(8).unpack('G')                 # G big-endian, E little-endian
+               a[0]
+       end
+       
+       def getarray(s)
+               len=getlong(s)
+               arr=[]
+               for i in (0..len-1)
+                       arr[i]=getvalue(s)
+               end
+               arr
+       end
+       
+       def getobject(s)
+               arr={}
+               while (key=getstring(s))
+                       if (key=='') then break end
+                       arr[key]=getvalue(s)
+               end
+               s.getc          # skip the 9 'end of object' value
+               arr
+       end
+       
+       # ----- getvalue        parse and get value
+       
+       def getvalue(s)
+               case s.getc
+                       when 0; return getdouble(s)                     # number
+                       when 1; return s.getc                           # boolean
+                       when 2; return getstring(s)                     # string
+                       when 3; return getobject(s)                     # object/hash
+                       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
+               end
+       end
+
+       # ====================================================================
+       # AMF write subroutines
+       
+       # ----- putdata         envelope data into AMF writeable form
+       # ----- encodevalue     pack variables as AMF
+       
+       def putdata(index,n)
+               d =encodestring(index+"/onResult")
+               d+=encodestring("null")
+               d+=[-1].pack("N")
+               d+=encodevalue(n)
+       end
+       
+       def encodevalue(n)
+               case n.class.to_s
+                       when 'Array'
+                               a=10.chr+encodelong(n.length)
+                               n.each do |b|
+                                       a+=encodevalue(b)
+                               end
+                               a
+                       when 'Hash'
+                               a=3.chr
+                               n.each do |k,v|
+                                       a+=encodestring(k)+encodevalue(v)
+                               end
+                               a+0.chr+0.chr+9.chr
+                       when 'String'
+                               2.chr+encodestring(n)
+                       when 'Bignum','Fixnum','Float'
+                               0.chr+encodedouble(n)
+                       when 'NilClass'
+                               5.chr
+               end
+       end
+       
+       # ----- encodestring    encode string with two-byte length
+       # ----- encodedouble    encode number as eight-byte double precision float
+       # ----- encodelong              encode number as four-byte long
+       
+       def encodestring(n)
+               a,b=n.size.divmod(256)
+               a.chr+b.chr+n
+       end
+       
+       def encodedouble(n)
+               [n].pack('G')
+       end
+       
+       def encodelong(n)
+               [n].pack('N')
+       end
+       
+       # ====================================================================
+       # Co-ordinate conversion
+       
+       def lat2coord(a)
+               -(lat2y(a)-$basey)*$masterscale+250
+       end
+       
+       def long2coord(a)
+               (a-$baselong)*$masterscale+350
+       end
+       
+       def lat2y(a)
+               180/Math::PI * Math.log(Math.tan(Math::PI/4+a*(Math::PI/180)/2))
+       end
+       
+       def coord2lat(a)
+               y2lat((a-250)/-$masterscale+$basey)
+       end
+       
+       def coord2long(a)
+               (a-350)/$masterscale+$baselong
+       end
+       
+       def y2lat(a)
+               180/Math::PI * (2*Math.atan(Math.exp(a*Math::PI/180))-Math::PI/2)
+       end
+
+
+end
index eb778127f76fb5dea02c41987b63db3a70e73832..55cdcfed62b70b256c26c6cd41563b03c4112b84 100644 (file)
@@ -5,7 +5,7 @@
     for editing OpenStreetMap.</div>
   <script type="text/javascript" src="/javascripts/swfobject.js"></script>
   <script type="text/javascript">
-    var fo = new SWFObject("/potlatch/potlatch.swf", "potlatch", "700", "600", "6", "#FFFFFF");
+    var fo = new SWFObject("/potlatch/potlatch.swf?d="+Math.round(Math.random()*1000), "potlatch", "700", "600", "6", "#FFFFFF");
     function doSWF(lat,lon,sc) {
       fo.addVariable('lat',lat);
       fo.addVariable('long',lon);
diff --git a/config/potlatch/presets.txt b/config/potlatch/presets.txt
new file mode 100755 (executable)
index 0000000..45716cf
--- /dev/null
@@ -0,0 +1,60 @@
+way/road
+motorway: highway=motorway,ref=(type road number)
+trunk road: highway=trunk,ref=(type road number),name=(type road name)
+primary road: highway=primary,ref=(type road number),name=(type road name)
+secondary road: highway=secondary,ref=(type road number),name=(type road name)
+residential road: highway=residential,name=(type road name)
+unclassified road: highway=unclassified,name=(type road name)
+
+way/footway
+footpath: highway=footway,foot=yes
+bridleway: highway=bridleway,foot=yes
+byway: highway=unsurfaced,foot=yes
+permissive path: highway=footway,foot=permissive
+
+way/cycleway
+cycle lane: highway=cycleway,cycleway=lane,ncn_ref=
+cycle track: highway=cycleway,cycleway=track,ncn_ref=
+cycle lane (NCN): highway=cycleway,cycleway=lane,name=(type name here),ncn_ref=(type route number)
+cycle track (NCN): highway=cycleway,cycleway=track,name=(type name here),ncn_ref=(type route number)
+
+way/waterway
+canal: waterway=canal,name=(type name here)
+navigable river: waterway=river,boat=yes,name=(type name here)
+navigable drain: waterway=drain,boat=yes,name=(type name here)
+derelict canal: waterway=derelict_canal,name=(type name here)
+unnavigable river: waterway=river,boat=no,name=(type name here)
+unnavigable drain: waterway=drain,boat=no,name=(type name here)
+
+way/railway
+railway: railway=rail
+tramway: railway=tram
+light railway: railway=light_rail
+preserved railway: railway=preserved
+disused railway tracks: railway=disused
+course of old railway: railway=abandoned
+
+point/road
+mini roundabout: highway=mini_roundabout
+traffic lights: highway=traffic_signals
+
+point/footway
+bridge: highway=bridge
+gate: highway=gate
+stile: highway=stile
+cattle grid: highway=cattle_grid
+
+point/cycleway
+gate: highway=gate
+
+point/waterway
+lock gate: waterway=lock_gate
+weir: waterway=weir
+aqueduct: waterway=aqueduct
+winding hole: waterway=turning_point
+mooring: waterway=mooring
+
+point/railway
+station: railway=station
+viaduct: railway=viaduct
+level crossing: railway=crossing
index ecfdabb302f6ed2ea2268044a164af7a34b436ff..bcf340b8b33af480b0c513aa80e78eef8f8d9eaa 100644 (file)
@@ -31,6 +31,10 @@ ActionController::Routing::Routes.draw do |map|
   map.connect "api/#{API_VERSION}/gpx/:id/details", :controller => 'trace', :action => 'api_details'
   map.connect "api/#{API_VERSION}/gpx/:id/data", :controller => 'trace', :action => 'api_data'
   
+  # Potlatch API
+  
+  map.connect "api/#{API_VERSION}/amf", :controller =>'amf', :action =>'talk'
+  
   # web site
 
   map.connect '/', :controller => 'site', :action => 'index'
index 55a62379a742c0d820a8b4047b956bd3e270482c..0a665e7709773e3fd8cfaece37cd606308573869 100755 (executable)
Binary files a/public/potlatch/potlatch.swf and b/public/potlatch/potlatch.swf differ