]> git.openstreetmap.org Git - rails.git/blob - app/controllers/amf_controller.rb
76a7fff79e59e1dc7e8e76e322a8ecf25ceb6d3f
[rails.git] / app / controllers / amf_controller.rb
1 class AmfController < ApplicationController
2 #=begin
3   require 'stringio'
4
5   # to log:
6   # RAILS_DEFAULT_LOGGER.error("Args: #{args[0]}, #{args[1]}, #{args[2]}, #{args[3]}")
7
8   # ====================================================================
9   # Main AMF handler
10   
11   # ---- talk   process AMF request
12
13   def talk
14         req=StringIO.new(request.raw_post)      # Get POST data as request
15         req.read(2)                                                     # Skip version indicator and client ID
16         results={}                                                      # Results of each body
17
18         # -------------
19         # Parse request
20
21         headers=getint(req)                                     # Read number of headers
22         for i in (1..headers)                           # Read each header
23                 name=getstring(req)                             #  |
24                 req.getc                                                #  | skip boolean
25                 value=getvalue(req)                             #  |
26                 header["name"]=value                    #  |
27         end
28
29         bodies=getint(req)                                      # Read number of bodies
30         for i in (1..bodies)                            # Read each body
31                 message=getstring(req)                  #  | get message name
32                 index=getstring(req)                    #  | get index in response sequence
33                 bytes=getlong(req)                              #  | get total size in bytes
34                 args=getvalue(req)                              #  | get response (probably an array)
35         
36                 case message
37                         when 'getpresets';      results[index]=putdata(index,getpresets)
38                         when 'whichways';       results[index]=putdata(index,whichways(args))
39                         when 'getway';          results[index]=putdata(index,getway(args))
40                         when 'putway';          results[index]=putdata(index,putway(args))
41                         when 'deleteway';       results[index]=putdata(index,deleteway(args))
42                 end
43         end
44
45         # ------------------
46         # Write out response
47
48         response.headers["Content-Type"]="application/x-amf"
49         a,b=results.length.divmod(256)
50         ans=0.chr+0.chr+0.chr+0.chr+a.chr+b.chr
51         results.each do |k,v|
52                 ans+=v
53         end
54         render :text=>ans
55
56   end
57
58         private
59
60         # ====================================================================
61         # Remote calls
62
63         # ----- getpresets
64         #               return presets,presetmenus and presetnames arrays
65
66         def getpresets
67                 presets={}
68                 presetmenus={}; presetmenus['point']=[]; presetmenus['way']=[]
69                 presetnames={}; presetnames['point']={}; presetnames['way']={}
70                 presettype=''
71                 presetcategory=''
72                 
73 #               File.open("config/potlatch/presets.txt") do |file|
74
75                 # Temporary patch to get around filepath problem
76                 # To remove this patch and make the code nice again:
77                 # 1. uncomment above line
78                 # 2. fix the path in the above line
79                 # 3. delete this here document, and the following line (StringIO....)
80                 
81 txt=<<-EOF
82 way/road
83 motorway: highway=motorway,ref=(type road number)
84 trunk road: highway=trunk,ref=(type road number),name=(type road name)
85 primary road: highway=primary,ref=(type road number),name=(type road name)
86 secondary road: highway=secondary,ref=(type road number),name=(type road name)
87 residential road: highway=residential,name=(type road name)
88 unclassified road: highway=unclassified,name=(type road name)
89
90 way/footway
91 footpath: highway=footway,foot=yes
92 bridleway: highway=bridleway,foot=yes
93 byway: highway=unsurfaced,foot=yes
94 permissive path: highway=footway,foot=permissive
95
96 way/cycleway
97 cycle lane: highway=cycleway,cycleway=lane,ncn_ref=
98 cycle track: highway=cycleway,cycleway=track,ncn_ref=
99 cycle lane (NCN): highway=cycleway,cycleway=lane,name=(type name here),ncn_ref=(type route number)
100 cycle track (NCN): highway=cycleway,cycleway=track,name=(type name here),ncn_ref=(type route number)
101
102 way/waterway
103 canal: waterway=canal,name=(type name here)
104 navigable river: waterway=river,boat=yes,name=(type name here)
105 navigable drain: waterway=drain,boat=yes,name=(type name here)
106 derelict canal: waterway=derelict_canal,name=(type name here)
107 unnavigable river: waterway=river,boat=no,name=(type name here)
108 unnavigable drain: waterway=drain,boat=no,name=(type name here)
109
110 way/railway
111 railway: railway=rail
112 tramway: railway=tram
113 light railway: railway=light_rail
114 preserved railway: railway=preserved
115 disused railway tracks: railway=disused
116 course of old railway: railway=abandoned
117
118 point/road
119 mini roundabout: highway=mini_roundabout
120 traffic lights: highway=traffic_signals
121
122 point/footway
123 bridge: highway=bridge
124 gate: highway=gate
125 stile: highway=stile
126 cattle grid: highway=cattle_grid
127
128 point/cycleway
129 gate: highway=gate
130
131 point/waterway
132 lock gate: waterway=lock_gate
133 weir: waterway=weir
134 aqueduct: waterway=aqueduct
135 winding hole: waterway=turning_point
136 mooring: waterway=mooring
137
138 point/railway
139 station: railway=station
140 viaduct: railway=viaduct
141 level crossing: railway=crossing
142 EOF
143
144 StringIO.open(txt) do |file|
145                         file.each_line {|line|
146                                 t=line.chomp
147                                 if (t=~/(\w+)\/(\w+)/) then
148                                         presettype=$1
149                                         presetcategory=$2
150                                         presetmenus[presettype].push(presetcategory)
151                                         presetnames[presettype][presetcategory]=["(no preset)"]
152                                 elsif (t=~/^(.+):\s?(.+)$/) then
153                                         pre=$1; kv=$2
154                                         presetnames[presettype][presetcategory].push(pre)
155                                         presets[pre]={}
156                                         kv.split(',').each {|a|
157                                                 if (a=~/^(.+)=(.*)$/) then presets[pre][$1]=$2 end
158                                         }
159                                 end
160                         }
161                 end
162                 [presets,presetmenus,presetnames]
163         end
164
165         # ----- whichways(left,bottom,right,top)
166         #               return array of ways in current bounding box
167         #               at present, instead of using correct (=more complex) SQL to find
168         #               corner-crossing ways, it simply enlarges the bounding box by +/- 0.01
169         
170         def whichways(args)
171                 waylist=WaySegment.find_by_sql("SELECT DISTINCT current_way_segments.id AS wayid"+
172                          "  FROM current_way_segments,current_segments,current_nodes "+
173                          " WHERE segment_id=current_segments.id "+
174                          "   AND current_segments.visible=1 "+
175                          "   AND node_a=current_nodes.id "+
176                          "   AND (latitude  BETWEEN "+(args[1].to_f-0.01).to_s+" AND "+(args[3].to_f+0.01).to_s+") "+
177                          "   AND (longitude BETWEEN "+(args[0].to_f-0.01).to_s+" AND "+(args[2].to_f+0.01).to_s+")")
178                 ways=[]
179                 waylist.each {|a|
180                         ways<<a.wayid.to_i
181                 }
182
183                 pointlist=ActiveRecord::Base.connection.select_all("SELECT current_nodes.id,current_nodes.tags "+
184                          "  FROM current_nodes "+
185                          "  LEFT OUTER JOIN current_segments cs1 ON cs1.node_a=current_nodes.id "+
186                          "  LEFT OUTER JOIN current_segments cs2 ON cs2.node_b=current_nodes.id "+
187                          " WHERE (latitude  BETWEEN "+(args[1].to_f-0.01).to_s+" AND "+(args[3].to_f+0.01).to_s+") "+
188                          "   AND (longitude BETWEEN "+(args[0].to_f-0.01).to_s+" AND "+(args[2].to_f-0.01).to_s+") "+
189                          "   AND cs1.id IS NULL AND cs2.id IS NULL "+
190                          "   AND current_nodes.visible=1")
191                 points=[]
192                 pointlist.each {|a|
193                         points<<[a['id'],tag2array(a['tags'])]
194                 }
195                 [ways,points]
196         end
197
198         # ----- getway (objectname, way, baselong, basey, masterscale)
199         #               returns objectname, array of co-ordinates, attributes,
200         #                               xmin,xmax,ymin,ymax
201         
202         def getway(args)
203                 objname,wayid,baselong,basey,masterscale=args
204                 wayid=wayid.to_i
205                 points=[]
206                 lastid=-1
207                 xmin=999999; xmax=-999999
208                 ymin=999999; ymax=-999999
209
210                 readwayquery(wayid).each {|row|
211                         xs1=long2coord(row['long1'].to_f,baselong,masterscale); ys1=lat2coord(row['lat1'].to_f,basey,masterscale)
212                         xs2=long2coord(row['long2'].to_f,baselong,masterscale); ys2=lat2coord(row['lat2'].to_f,basey,masterscale)
213                         if (row['id1'].to_i!=lastid)
214                                 points<<[xs1,ys1,row['id1'].to_i,0,tag2array(row['tags1']),0]
215                         end
216                         lastid=row['id2'].to_i
217                         points<<[xs2,ys2,row['id2'].to_i,1,tag2array(row['tags2']),row['segment_id'].to_i]
218                         xmin=[xmin,row['long1'].to_f,row['long2'].to_f].min
219                         xmax=[xmax,row['long1'].to_f,row['long2'].to_f].max
220                         ymin=[ymin,row['lat1'].to_f,row['lat2'].to_f].min
221                         ymax=[ymax,row['lat1'].to_f,row['lat2'].to_f].max
222                 }
223
224                 attributes={}
225                 attrlist=ActiveRecord::Base.connection.select_all "SELECT k,v FROM current_way_tags WHERE id=#{wayid}"
226                 attrlist.each {|a| attributes[a['k']]=a['v'] }
227
228                 [objname,points,attributes,xmin,xmax,ymin,ymax]
229         end
230         
231         # ----- putway (user token, way, array of co-ordinates, array of attributes,
232         #                               baselong, basey, masterscale)
233         #               returns current way ID, new way ID, hash of renumbered nodes,
234         #                               xmin,xmax,ymin,ymax
235
236         def putway(args)
237                 usertoken,originalway,points,attributes,baselong,basey,masterscale=args
238                 uid=getuserid(usertoken); if !uid then return end
239                 db_uqs='uniq'+uid.to_s+originalway.to_i.abs.to_s+Time.new.to_i.to_s     # temp uniquesegments table name, typically 51 chars
240                 db_now='@now'+uid.to_s+originalway.to_i.abs.to_s+Time.new.to_i.to_s     # 'now' variable name, typically 51 chars
241                 ActiveRecord::Base.connection.execute("SET #{db_now}=NOW()")
242                 originalway=originalway.to_i
243                 
244                 # -- 3. read original way into memory
245         
246                 xc={}; yc={}; tagc={}; seg={}
247                 if (originalway>0)
248                         way=originalway
249                         readwayquery(way).each { |row|
250                                 id1=row['id1'].to_i; xc[id1]=row['long1'].to_f; yc[id1]=row['lat1'].to_f; tagc[id1]=row['tags1']
251                                 id2=row['id2'].to_i; xc[id2]=row['long2'].to_f; yc[id2]=row['lat2'].to_f; tagc[id2]=row['tags2']
252                                 seg[row['segment_id'].to_i]=id1.to_s+'-'+id2.to_s
253                         }
254                 else
255                         way=ActiveRecord::Base.connection.insert("INSERT INTO current_ways (user_id,timestamp,visible) VALUES (#{uid},#{db_now},1)")
256                 end
257
258                 # -- 4. get version by inserting new row into ways
259
260                 version=ActiveRecord::Base.connection.insert("INSERT INTO ways (id,user_id,timestamp,visible) VALUES (#{way},#{uid},#{db_now},1)")
261
262                 # -- 5. compare nodes and update xmin,xmax,ymin,ymax
263
264                 xmin=999999; xmax=-999999
265                 ymin=999999; ymax=-999999
266                 insertsql=''
267                 nodelist=''
268                 renumberednodes={}
269         
270                 points.each_index do |i|
271                         xs=coord2long(points[i][0],masterscale,baselong)
272                         ys=coord2lat(points[i][1],masterscale,basey)
273                         xmin=[xs,xmin].min; xmax=[xs,xmax].max
274                         ymin=[ys,ymin].min; ymax=[ys,ymax].max
275                         node=points[i][2].to_i
276                         tagstr=array2tag(points[i][4])
277                         tagsql="'"+sqlescape(tagstr)+"'"
278         
279                         # compare node
280                         if node<0
281                                 # new node - create
282                                 newnode=ActiveRecord::Base.connection.insert("INSERT INTO current_nodes (   latitude,longitude,timestamp,user_id,visible,tags) VALUES (           #{ys},#{xs},#{db_now},#{uid},1,#{tagsql})")
283                                                 ActiveRecord::Base.connection.insert("INSERT INTO nodes         (id,latitude,longitude,timestamp,user_id,visible,tags) VALUES (#{newnode},#{ys},#{xs},#{db_now},#{uid},1,#{tagsql})")
284                                 points[i][2]=newnode
285                                 renumberednodes[node.to_s]=newnode.to_s
286                                 
287                         elsif xc.has_key?(node)
288                                 # old node from original way - update
289                                 if (xs!=xc[node] or (ys/0.0000001).round!=(yc[node]/0.0000001).round or tagstr!=tagc[node])
290                                         ActiveRecord::Base.connection.insert("INSERT INTO nodes (id,latitude,longitude,timestamp,user_id,visible,tags) VALUES (#{node},#{ys},#{xs},#{db_now},#{uid},1,#{tagsql})")
291                                         ActiveRecord::Base.connection.update("UPDATE current_nodes SET latitude=#{ys},longitude=#{xs},timestamp=#{db_now},user_id=#{uid},tags=#{tagsql},visible=1 WHERE id=#{node}")
292                                 else
293                                         if (nodelist!='') then nodelist+=',' end; nodelist+=node.to_s
294                                 end
295                         else
296                                 # old node, created in another way and now added to this way
297                                 if (nodelist!='') then nodelist+=',' end; nodelist+=node.to_s
298                         end
299         
300                 end
301
302                 if nodelist!='' then
303                         ActiveRecord::Base.connection.update("UPDATE current_nodes SET timestamp=#{db_now},user_id=#{uid},visible=1 WHERE id IN (#{nodelist})")
304                 end
305
306                 # -- 6.i compare segments
307         
308                 numberedsegments={}
309                 seglist=''
310                 for i in (0..(points.length-2))
311                         if (points[i+1][3].to_i==0) then next end
312                         segid=points[i+1][5].to_i
313                         from =points[i  ][2].to_i
314                         to   =points[i+1][2].to_i
315                         if seg.has_key?(segid)
316                                 if seg[segid]=="#{from}-#{to}" then 
317                                         if (seglist!='') then seglist+=',' end; seglist+=segid.to_s
318                                         next
319                                 end
320                         end
321                         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,'')")
322                                   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,'')")
323                         points[i+1][5]=segid
324                         numberedsegments[(i+1).to_s]=segid.to_s
325                 end
326                 # numberedsegments.each{|a,b| RAILS_DEFAULT_LOGGER.error("Sending back: seg no. #{a} -> id #{b}") }
327
328                 if seglist!='' then
329                         ActiveRecord::Base.connection.update("UPDATE current_segments SET timestamp=#{db_now},user_id=#{uid},visible=1 WHERE id IN (#{seglist})")
330                 end
331
332
333                 # -- 6.ii insert new way segments
334
335                 createuniquesegments(way,db_uqs)
336
337                 # a=''
338                 # ActiveRecord::Base.connection.select_values("SELECT segment_id FROM #{db_uqs}").each {|b| a+=b+',' }
339                 # RAILS_DEFAULT_LOGGER.error("Unique segments are #{a}")
340                 # a=ActiveRecord::Base.connection.select_value("SELECT #{db_now}")
341                 # RAILS_DEFAULT_LOGGER.error("Timestamp of this edit is #{a}")
342                 # RAILS_DEFAULT_LOGGER.error("Userid of this edit is #{uid}")
343
344                 #               delete nodes from uniquesegments (and not in modified way)
345         
346                 sql=<<-EOF
347                         INSERT INTO nodes (id,latitude,longitude,timestamp,user_id,visible)  
348                         SELECT DISTINCT cn.id,cn.latitude,cn.longitude,#{db_now},#{uid},0 
349                           FROM current_nodes AS cn, 
350                                    current_segments AS cs,
351                                    #{db_uqs} AS us 
352                          WHERE(cn.id=cs.node_a OR cn.id=cs.node_b) 
353                            AND cs.id=us.segment_id AND cs.visible=1 
354                            AND (cn.timestamp!=#{db_now} OR cn.user_id!=#{uid})
355                 EOF
356                 ActiveRecord::Base.connection.insert(sql)
357
358                 sql=<<-EOF
359                         UPDATE current_nodes AS cn, 
360                                    current_segments AS cs, 
361                                    #{db_uqs} AS us 
362                            SET cn.timestamp=#{db_now},cn.visible=0,cn.user_id=#{uid} 
363                          WHERE (cn.id=cs.node_a OR cn.id=cs.node_b) 
364                            AND cs.id=us.segment_id AND cs.visible=1 
365                            AND (cn.timestamp!=#{db_now} OR cn.user_id!=#{uid})
366                 EOF
367                 ActiveRecord::Base.connection.update(sql)
368         
369                 #               delete segments from uniquesegments (and not in modified way)
370         
371                 sql=<<-EOF
372                         INSERT INTO segments (id,node_a,node_b,timestamp,user_id,visible) 
373                         SELECT DISTINCT segment_id,node_a,node_b,#{db_now},#{uid},0
374                           FROM current_segments AS cs, #{db_uqs} AS us
375                          WHERE cs.id=us.segment_id AND cs.visible=1 
376                            AND (cs.timestamp!=#{db_now} OR cs.user_id!=#{uid})
377                 EOF
378                 ActiveRecord::Base.connection.insert(sql)
379                 
380                 sql=<<-EOF
381                            UPDATE current_segments AS cs, #{db_uqs} AS us
382                                   SET cs.timestamp=#{db_now},cs.visible=0,cs.user_id=#{uid} 
383                                 WHERE cs.id=us.segment_id AND cs.visible=1 
384                                   AND (cs.timestamp!=#{db_now} OR cs.user_id!=#{uid})
385                 EOF
386                 ActiveRecord::Base.connection.update(sql)
387                 ActiveRecord::Base.connection.execute("DROP TABLE #{db_uqs}")
388
389                 #               insert new version of route into way_segments
390         
391                 insertsql =''
392                 currentsql=''
393                 sequence  =1
394                 for i in (0..(points.length-2))
395                         if (points[i+1][3].to_i==0) then next end
396                         if insertsql !='' then insertsql +=',' end
397                         if currentsql!='' then currentsql+=',' end
398                         insertsql +="(#{way},#{points[i+1][5]},#{version})"
399                         currentsql+="(#{way},#{points[i+1][5]},#{sequence})"
400                         sequence  +=1
401                 end
402                 
403                 ActiveRecord::Base.connection.execute("DELETE FROM current_way_segments WHERE id=#{way}");
404                 ActiveRecord::Base.connection.insert("INSERT INTO         way_segments (id,segment_id,version    ) VALUES #{insertsql}");
405                 ActiveRecord::Base.connection.insert("INSERT INTO current_way_segments (id,segment_id,sequence_id) VALUES #{currentsql}");
406         
407                 # -- 7. insert new way tags
408         
409                 insertsql =''
410                 currentsql=''
411                 attributes.each do |k,v|
412                         if v=='' then next end
413                         if v[0,6]=='(type ' then next end
414                         if insertsql !='' then insertsql +=',' end
415                         if currentsql!='' then currentsql+=',' end
416                         insertsql +="(#{way},'"+sqlescape(k)+"','"+sqlescape(v)+"',version)"
417                         currentsql+="(#{way},'"+sqlescape(k)+"','"+sqlescape(v)+"')"
418                 end
419                 
420                 ActiveRecord::Base.connection.execute("DELETE FROM current_way_tags WHERE id=#{way}")
421                 if (insertsql !='') then ActiveRecord::Base.connection.insert("INSERT INTO way_tags (id,k,v,version) VALUES #{insertsql}" ) end
422                 if (currentsql!='') then ActiveRecord::Base.connection.insert("INSERT INTO current_way_tags (id,k,v) VALUES #{currentsql}") end
423         
424                 [originalway,way,renumberednodes,numberedsegments,xmin,xmax,ymin,ymax]
425         end
426         
427         # ----- deleteway (user token, way)
428         #               returns way ID only
429         
430         def deleteway(args)
431                 usertoken,way=args
432                 uid=getuserid(usertoken); if !uid then return end
433                 way=way.to_i
434
435                 db_uqs='uniq'+uid.to_s+way.to_i.abs.to_s+Time.new.to_i.to_s     # temp uniquesegments table name, typically 51 chars
436                 db_now='@now'+uid.to_s+way.to_i.abs.to_s+Time.new.to_i.to_s     # 'now' variable name, typically 51 chars
437                 ActiveRecord::Base.connection.execute("SET #{db_now}=NOW()")
438                 createuniquesegments(way,db_uqs)
439         
440                 sql=<<-EOF
441                         INSERT INTO nodes (id,latitude,longitude,timestamp,user_id,visible) 
442                         SELECT DISTINCT cn.id,cn.latitude,cn.longitude,#{db_now},#{uid},0 
443                           FROM current_nodes AS cn, 
444                                    current_segments AS cs, 
445                                    #{db_uqs} AS us
446                          WHERE (cn.id=cs.node_a OR cn.id=cs.node_b) 
447                            AND cs.id=us.segment_id
448                 EOF
449                 ActiveRecord::Base.connection.insert(sql)
450         
451                 sql=<<-EOF
452                         UPDATE current_nodes AS cn, 
453                                    current_segments AS cs, 
454                                    #{db_uqs} AS us
455                            SET cn.timestamp=#{db_now},cn.visible=0,cn.user_id=#{uid} 
456                          WHERE (cn.id=cs.node_a OR cn.id=cs.node_b) 
457                            AND cs.id=us.segment_id
458                 EOF
459                 ActiveRecord::Base.connection.update(sql)
460         
461                 # -     delete any otherwise unused segments
462                                 
463                 sql=<<-EOF
464                         INSERT INTO segments (id,node_a,node_b,timestamp,user_id,visible) 
465                         SELECT DISTINCT segment_id,node_a,node_b,#{db_now},#{uid},0 
466                           FROM current_segments AS cs, #{db_uqs} AS us
467                          WHERE cs.id=us.segment_id
468                 EOF
469                 ActiveRecord::Base.connection.insert(sql)
470                                 
471                 sql=<<-EOF
472                         UPDATE current_segments AS cs, #{db_uqs} AS us
473                            SET cs.timestamp=#{db_now},cs.visible=0,cs.user_id=#{uid} 
474                          WHERE cs.id=us.segment_id
475                 EOF
476                 ActiveRecord::Base.connection.update(sql)
477                 ActiveRecord::Base.connection.execute("DROP TABLE #{db_uqs}")
478         
479                 # - delete way
480                 
481                 ActiveRecord::Base.connection.insert("INSERT INTO ways (id,user_id,timestamp,visible) VALUES (#{way},#{uid},#{db_now},0)")
482                 ActiveRecord::Base.connection.update("UPDATE current_ways SET user_id=#{uid},timestamp=#{db_now},visible=0 WHERE id=#{way}")
483                 ActiveRecord::Base.connection.execute("DELETE FROM current_way_segments WHERE id=#{way}")
484                 ActiveRecord::Base.connection.execute("DELETE FROM current_way_tags WHERE id=#{way}")
485         
486                 way
487         end
488         
489         # ====================================================================
490         # Support functions for remote calls
491
492         def readwayquery(id)
493                 ActiveRecord::Base.connection.select_all "SELECT n1.latitude AS lat1,n1.longitude AS long1,n1.id AS id1,n1.tags as tags1, "+
494                         "                 n2.latitude AS lat2,n2.longitude AS long2,n2.id AS id2,n2.tags as tags2,segment_id "+
495                         "    FROM current_way_segments,current_segments,current_nodes AS n1,current_nodes AS n2 "+
496                         "   WHERE current_way_segments.id=#{id} "+
497                         "     AND segment_id=current_segments.id "+
498                         "     AND n1.id=node_a and n2.id=node_b "+
499                         "   ORDER BY sequence_id"
500         end
501
502         def createuniquesegments(way,uqs_name)
503                 sql=<<-EOF
504                         CREATE TEMPORARY TABLE #{uqs_name}
505                                                         SELECT a.segment_id
506                                                           FROM (SELECT DISTINCT segment_id FROM current_way_segments 
507                                                                         WHERE id = #{way}) a
508                                                  LEFT JOIN current_way_segments b 
509                                                                 ON b.segment_id = a.segment_id
510                                                            AND b.id != #{way}
511                                                          WHERE b.segment_id IS NULL
512                 EOF
513                 ActiveRecord::Base.connection.execute(sql)
514         end
515         
516
517         def sqlescape(a)
518                 a.gsub("'","''").gsub(92.chr,92.chr+92.chr)
519         end
520
521         def tag2array(a)
522                 tags={}
523                 a.gsub(';;;','#%').split(';').each do |b|
524                         b.gsub!('#%',';;;')
525                         b.gsub!('===','#%')
526                         k,v=b.split('=')
527                         if k.nil? then k='' end
528                         if v.nil? then v='' end
529                         tags[k.gsub('#%','=')]=v.gsub('#%','=')
530                 end
531                 tags
532         end
533
534         def array2tag(a)
535                 str=''
536                 a.each do |k,v|
537                         if v=='' then next end
538                         if v[0,6]=='(type ' then next end
539                         if str!='' then str+=';' end
540                         str+=k.gsub(';',';;;').gsub('=','===')+'='+v.gsub(';',';;;').gsub('=','===')
541                 end
542                 str
543         end
544         
545         def getuserid(token)
546                 token=sqlescape(token)
547                 if (token=~/^(.+)\+(.+)$/) then
548                         return ActiveRecord::Base.connection.select_value("SELECT id FROM users WHERE active=1 AND timeout>NOW() AND email='#{$1}' AND pass_crypt=MD5('#{$2}')")
549                 else
550                         return ActiveRecord::Base.connection.select_value("SELECT id FROM users WHERE active=1 AND timeout>NOW() AND token='#{token}'")
551                 end
552         end
553         
554
555
556         # ====================================================================
557         # AMF read subroutines
558         
559         # ----- getint          return two-byte integer
560         # ----- getlong         return four-byte long
561         # ----- getstring       return string with two-byte length
562         # ----- getdouble       return eight-byte double-precision float
563         # ----- getobject       return object/hash
564         # ----- getarray        return numeric array
565         
566         def getint(s)
567                 s.getc*256+s.getc
568         end
569         
570         def getlong(s)
571                 ((s.getc*256+s.getc)*256+s.getc)*256+s.getc
572         end
573         
574         def getstring(s)
575                 len=s.getc*256+s.getc
576                 s.read(len)
577         end
578         
579         def getdouble(s)
580                 a=s.read(8).unpack('G')                 # G big-endian, E little-endian
581                 a[0]
582         end
583         
584         def getarray(s)
585                 len=getlong(s)
586                 arr=[]
587                 for i in (0..len-1)
588                         arr[i]=getvalue(s)
589                 end
590                 arr
591         end
592         
593         def getobject(s)
594                 arr={}
595                 while (key=getstring(s))
596                         if (key=='') then break end
597                         arr[key]=getvalue(s)
598                 end
599                 s.getc          # skip the 9 'end of object' value
600                 arr
601         end
602         
603         # ----- getvalue        parse and get value
604         
605         def getvalue(s)
606                 case s.getc
607                         when 0; return getdouble(s)                     # number
608                         when 1; return s.getc                           # boolean
609                         when 2; return getstring(s)                     # string
610                         when 3; return getobject(s)                     # object/hash
611                         when 5; return nil                                      # null
612                         when 6; return nil                                      # undefined
613                         when 8; s.read(4)                                       # mixedArray
614                                         return getobject(s)                     #  |
615                         when 10;return getarray(s)                      # array
616                         else;   return nil                                      # error
617                 end
618         end
619
620         # ====================================================================
621         # AMF write subroutines
622         
623         # ----- putdata         envelope data into AMF writeable form
624         # ----- encodevalue     pack variables as AMF
625         
626         def putdata(index,n)
627                 d =encodestring(index+"/onResult")
628                 d+=encodestring("null")
629                 d+=[-1].pack("N")
630                 d+=encodevalue(n)
631         end
632         
633         def encodevalue(n)
634                 case n.class.to_s
635                         when 'Array'
636                                 a=10.chr+encodelong(n.length)
637                                 n.each do |b|
638                                         a+=encodevalue(b)
639                                 end
640                                 a
641                         when 'Hash'
642                                 a=3.chr
643                                 n.each do |k,v|
644                                         a+=encodestring(k)+encodevalue(v)
645                                 end
646                                 a+0.chr+0.chr+9.chr
647                         when 'String'
648                                 2.chr+encodestring(n)
649                         when 'Bignum','Fixnum','Float'
650                                 0.chr+encodedouble(n)
651                         when 'NilClass'
652                                 5.chr
653                         else
654                                 RAILS_DEFAULT_LOGGER.error("Unexpected Ruby type for AMF conversion: "+n.class.to_s)
655                 end
656         end
657         
658         # ----- encodestring    encode string with two-byte length
659         # ----- encodedouble    encode number as eight-byte double precision float
660         # ----- encodelong              encode number as four-byte long
661         
662         def encodestring(n)
663                 a,b=n.size.divmod(256)
664                 a.chr+b.chr+n
665         end
666         
667         def encodedouble(n)
668                 [n].pack('G')
669         end
670         
671         def encodelong(n)
672                 [n].pack('N')
673         end
674         
675         # ====================================================================
676         # Co-ordinate conversion
677         
678         def lat2coord(a,basey,masterscale)
679                 -(lat2y(a)-basey)*masterscale+250
680         end
681         
682         def long2coord(a,baselong,masterscale)
683                 (a-baselong)*masterscale+350
684         end
685         
686         def lat2y(a)
687                 180/Math::PI * Math.log(Math.tan(Math::PI/4+a*(Math::PI/180)/2))
688         end
689         
690         def coord2lat(a,masterscale,basey)
691                 y2lat((a-250)/-masterscale+basey)
692         end
693         
694         def coord2long(a,masterscale,baselong)
695                 (a-350)/masterscale+baselong
696         end
697         
698         def y2lat(a)
699                 180/Math::PI * (2*Math.atan(Math.exp(a*Math::PI/180))-Math::PI/2)
700         end
701
702 #=end
703 end