From: Gabriel Ebner Date: Thu, 20 Sep 2007 14:37:29 +0000 (+0000) Subject: Merge rails_port as of r4613 & fix tests. X-Git-Tag: live~8129^2~18 X-Git-Url: https://git.openstreetmap.org/rails.git/commitdiff_plain/cfbdd3f7e1c688e2c875ded9fd847fcc1c3a4caf?hp=-c Merge rails_port as of r4613 & fix tests. --- cfbdd3f7e1c688e2c875ded9fd847fcc1c3a4caf diff --combined app/controllers/amf_controller.rb index d30043e59,3ba91fb8c..99c2a48cd --- a/app/controllers/amf_controller.rb +++ b/app/controllers/amf_controller.rb @@@ -43,6 -43,8 +43,8 @@@ class AmfController < ApplicationContro when 'putway'; results[index]=putdata(index,putway(args)) when 'deleteway'; results[index]=putdata(index,deleteway(args)) when 'makeway'; results[index]=putdata(index,makeway(args)) + when 'putpoi'; results[index]=putdata(index,putpoi(args)) + when 'getpoi'; results[index]=putdata(index,getpoi(args)) end end @@@ -71,8 -73,8 +73,8 @@@ def getpresets presets={} - presetmenus={}; presetmenus['point']=[]; presetmenus['way']=[] - presetnames={}; presetnames['point']={}; presetnames['way']={} + presetmenus={}; presetmenus['point']=[]; presetmenus['way']=[]; presetmenus['POI']=[] + presetnames={}; presetnames['point']={}; presetnames['way']={}; presetnames['POI']={} presettype='' presetcategory='' @@@ -97,8 -99,8 +99,8 @@@ unclassified road: highway=unclassified way/footway footpath: highway=footway,foot=yes - bridleway: highway=bridleway,foot=yes,horse=yes,bicycle=yes - byway: highway=byway,foot=yes,horse=yes,bicycle=yes,motorcar=yes + bridleway: highway=bridleway,foot=yes + byway: highway=unsurfaced,foot=yes permissive path: highway=footway,foot=permissive way/cycleway @@@ -124,13 -126,8 +126,8 @@@ disused railway tracks: railway=disuse 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 + lake: landuse=water + forest: landuse=forest point/road mini roundabout: highway=mini_roundabout @@@ -158,7 -155,33 +155,33 @@@ viaduct: railway=viaduc level crossing: railway=crossing point/natural - peak: natural=peak + peak: point=peak + + POI/road + car park: amenity=parking + petrol station: amenity=fuel + + POI/cycleway + bike park: amenity=bicycle_parking + + POI/place + city: place=city,name=(type name here),is_in=(type region or county) + town: place=town,name=(type name here),is_in=(type region or county) + suburb: place=suburb,name=(type name here),is_in=(type region or county) + village: place=village,name=(type name here),is_in=(type region or county) + hamlet: place=hamlet,name=(type name here),is_in=(type region or county) + + POI/tourism + attraction: tourism=attraction,amenity=,religion=,denomination= + church: tourism=,amenity=place_of_worship,name=(type name here),religion=christian,denomination=(type denomination here) + hotel: tourism=hotel,amenity=,religion=,denomination= + other religious: tourism=,amenity=place_of_worship,name=(type name here),religion=(type religion),denomination= + post box: amenity=post_box,tourism=,name=,religion=,denomination= + post office: amenity=post_office,tourism=,name=,religion=,denomination= + pub: tourism=,amenity=pub,name=(type name here),religion=,denomination= + + POI/natural + peak: point=peak EOF StringIO.open(txt) do |file| @@@ -192,22 -215,25 +215,25 @@@ ymin = args[1].to_f-0.01 xmax = args[2].to_f+0.01 ymax = args[3].to_f+0.01 + baselong = args[4] + basey = args[5] + masterscale = args[6] 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+")") ways = waylist.collect {|a| a.wayid.to_i } # get an array of way id's - pointlist =ActiveRecord::Base.connection.select_all("SELECT current_nodes.id,current_nodes.tags "+ + pointlist =ActiveRecord::Base.connection.select_all("SELECT current_nodes.id,latitude,longitude,current_nodes.tags "+ " FROM current_nodes "+ " LEFT OUTER JOIN current_segments cs1 ON cs1.node_a=current_nodes.id "+ " LEFT OUTER JOIN current_segments cs2 ON cs2.node_b=current_nodes.id "+ @@@ -216,7 -242,7 +242,7 @@@ " AND cs1.id IS NULL AND cs2.id IS NULL "+ " AND current_nodes.visible=1") - points = pointlist.collect {|a| [a['id'],tag2array(a['tags'])] } # get a list of node ids and their tags + points = pointlist.collect {|a| [a['id'],long2coord(a['longitude'].to_f,baselong,masterscale),lat2coord(a['latitude'].to_f,basey,masterscale),tag2array(a['tags'])] } # get a list of node ids and their tags return [ways,points] end @@@ -403,7 -429,7 +429,7 @@@ 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='' @@@ -417,9 -443,9 +443,9 @@@ 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 @@@ -443,6 -469,49 +469,49 @@@ [originalway,way,renumberednodes,numberedsegments,xmin,xmax,ymin,ymax] end + # ----- putpoi (user token, id, x,y,tag array,visible,baselong,basey,masterscale) + # returns current id, new id + # if new: add new row to current_nodes and nodes + # if old: add new row to nodes, update current_nodes + + def putpoi(args) + usertoken,id,x,y,tags,visible,baselong,basey,masterscale=args + uid=getuserid(usertoken) + return if !uid + db_now='@now'+uid.to_s+id.to_i.abs.to_s+Time.new.to_i.to_s # 'now' variable name, typically 51 chars + ActiveRecord::Base.connection.execute("SET #{db_now}=NOW()") + + id=id.to_i + visible=visible.to_i + x=coord2long(x.to_f,masterscale,baselong) + y=coord2lat(y.to_f,masterscale,basey) + tagsql="'"+sqlescape(array2tag(tags))+"'" + + if (id>0) then + ActiveRecord::Base.connection.insert("INSERT INTO nodes (id,latitude,longitude,timestamp,user_id,visible,tags) VALUES (#{id},#{y},#{x},#{db_now},#{uid},#{visible},#{tagsql})"); + ActiveRecord::Base.connection.update("UPDATE current_nodes SET latitude=#{y},longitude=#{x},timestamp=#{db_now},user_id=#{uid},visible=#{visible},tags=#{tagsql} WHERE id=#{id}"); + newid=id + else + newid=ActiveRecord::Base.connection.insert("INSERT INTO current_nodes (latitude,longitude,timestamp,user_id,visible,tags) VALUES (#{y},#{x},#{db_now},#{uid},#{visible},#{tagsql})"); + ActiveRecord::Base.connection.update("INSERT INTO nodes (id,latitude,longitude,timestamp,user_id,visible,tags) VALUES (#{newid},#{y},#{x},#{db_now},#{uid},#{visible},#{tagsql})"); + end + [id,newid] + end + + # ----- getpoi (id,baselong,basey,masterscale) + # returns id,x,y,tag array + + def getpoi(args) + id,baselong,basey,masterscale=args; id=id.to_i + poi=ActiveRecord::Base.connection.select_one("SELECT latitude,longitude,tags "+ + "FROM current_nodes WHERE visible=1 AND id=#{id}") + if poi.nil? then return [nil,nil,nil,''] end + [id, + long2coord(poi['longitude'].to_f,baselong,masterscale), + lat2coord(poi['latitude'].to_f,basey,masterscale), + tag2array(poi['tags'])] + end + # ----- deleteway (user token, way) # returns way ID only @@@ -503,7 -572,7 +572,7 @@@ 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 @@@ -537,7 -606,7 +606,7 @@@ def makeway(args FROM current_nodes AS cn1, current_nodes AS cn2, current_segments AS cs - LEFT OUTER JOIN current_way_segments ON segment_id=cs.id + 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 @@@ -603,7 -672,7 +672,7 @@@ def findconnect(id,nodesused,lookfor,to FROM current_nodes AS cn1, current_nodes AS cn2, current_segments AS cs - LEFT OUTER JOIN current_way_segments ON segment_id=cs.id + 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 @@@ -615,7 -684,7 +684,7 @@@ FROM current_nodes AS cn1, current_nodes AS cn2, current_segments AS cs - LEFT OUTER JOIN current_way_segments ON segment_id=cs.id + 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 @@@ -663,8 -732,8 +732,8 @@@ en 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 "+ @@@ -677,9 -746,9 +746,9 @@@ def createuniquesegments(way,uqs_name,s 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 @@@ -691,18 -760,33 +760,33 @@@ en def createuniquenodes(uqs_name,uqn_name) # Finds nodes which appear in uniquesegments but no other segments sql=<<-EOF - CREATE TEMPORARY TABLE #{uqn_name} - SELECT DISTINCT node_id - FROM (SELECT cn.id AS node_id - FROM current_nodes AS cn, - current_segments AS cs, - #{uqs_name} AS us - WHERE cs.id=us.segment_id - AND (cn.id=cs.node_a OR cn.id=cs.node_b)) AS n - LEFT JOIN current_segments AS cs2 ON node_id=cs2.node_a AND cs2.visible=1 - LEFT JOIN current_segments AS cs3 ON node_id=cs3.node_b AND cs3.visible=1 - WHERE cs2.node_a IS NULL - AND cs3.node_b IS NULL + CREATE TEMPORARY TABLE #{uqn_name} + SELECT DISTINCT node_id + FROM (SELECT cn.id AS node_id + FROM current_nodes AS cn, + current_segments AS cs, + #{uqs_name} AS us + WHERE cs.id=us.segment_id + AND cn.id=cs.node_a) AS n + LEFT JOIN current_segments AS cs2 ON node_id=cs2.node_a AND cs2.visible=1 + LEFT JOIN current_segments AS cs3 ON node_id=cs3.node_b AND cs3.visible=1 + WHERE cs2.node_a IS NULL + AND cs3.node_b IS NULL + EOF + ActiveRecord::Base.connection.execute(sql) + sql=<<-EOF + INSERT INTO #{uqn_name} + SELECT DISTINCT node_id + FROM (SELECT cn.id AS node_id + FROM current_nodes AS cn, + current_segments AS cs, + #{uqs_name} AS us + WHERE cs.id=us.segment_id + AND cn.id=cs.node_b) AS n + LEFT JOIN current_segments AS cs2 ON node_id=cs2.node_a AND cs2.visible=1 + LEFT JOIN current_segments AS cs3 ON node_id=cs3.node_b AND cs3.visible=1 + WHERE cs2.node_a IS NULL + AND cs3.node_b IS NULL EOF ActiveRecord::Base.connection.execute(sql) end diff --combined app/controllers/api_controller.rb index 44ae65285,b39cbddf5..6e8e77c2f --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@@ -63,13 -63,8 +63,8 @@@ class ApiController < ApplicationContro return end - # integerise - min_lat = min_lat * 1000000 - max_lat = max_lat * 1000000 - min_lon = min_lon * 1000000 - max_lon = max_lon * 1000000 # get all the points - points = Tracepoint.find(:all, :conditions => ['latitude BETWEEN ? AND ? AND longitude BETWEEN ? AND ?', min_lat.to_i, max_lat.to_i, min_lon.to_i, max_lon.to_i], :select => "DISTINCT *", :offset => offset, :limit => TRACEPOINTS_PER_PAGE, :order => "timestamp DESC" ) + points = Tracepoint.find_by_area(min_lat, min_lon, max_lat, max_lon, :offset => offset, :limit => TRACEPOINTS_PER_PAGE, :order => "timestamp DESC" ) doc = XML::Document.new doc.encoding = 'UTF-8' @@@ -136,14 -131,12 +131,13 @@@ # check the bbox isn't too large requested_area = (max_lat-min_lat)*(max_lon-min_lon) if requested_area > MAX_REQUEST_AREA - report_error("The maximum bbox size is " + MAX_REQUEST_AREA.to_s + ", and your request was too large. Either request a smaller area, or use planet.osm") + report_error("The maximum bbox size is " + MAX_REQUEST_AREA.to_s + + ", and your request was too large. Either request a smaller area, or use planet.osm") return end # get all the nodes - nodes = Node.find(:all, :conditions => - ['latitude > ? AND longitude > ? AND latitude < ? AND longitude < ? AND visible = 1', min_lat, min_lon, max_lat, max_lon]) + nodes = Node.find(:all, :conditions => ['latitude BETWEEN ? AND ? AND longitude BETWEEN ? AND ? AND visible = 1', min_lat, max_lat, min_lon, max_lon]) node_ids = nodes.collect {|node| node.id } @@@ -153,38 -146,68 +147,38 @@@ end if node_ids.length == 0 - render :text => "", :content_type => "text/xml" + render :text => "", :content_type => "text/xml" return end - # grab the segments - segments = Array.new - if node_ids.length > 0 - node_ids_sql = "(#{node_ids.join(',')})" - # get the referenced segments - segments = Segment.find_by_sql "select * from current_segments where visible = 1 and (node_a in #{node_ids_sql} or node_b in #{node_ids_sql})" - end - # see if we have any missing nodes - segments_nodes = segments.collect {|segment| segment.node_a } - segments_nodes += segments.collect {|segment| segment.node_b } - - segments_nodes.uniq! - - missing_nodes = segments_nodes - node_ids - - # get missing nodes if there are any - nodes += Node.find(missing_nodes) if missing_nodes.length > 0 + relations = Array.new doc = OSM::API.new.get_xml_doc # get ways # find which ways are needed - segment_ids = segments.collect {|segment| segment.id } ways = Array.new - if segment_ids.length > 0 - way_segments = WaySegment.find_all_by_segment_id(segment_ids) - way_ids = way_segments.collect {|way_segment| way_segment.id } - ways = Way.find(way_ids) # NB: doesn't pick up segments, tags from db until accessed via way.way_segments etc. - - # seg_ids = way_segments.collect {|way_segment| way_segment.segment_id } - - list_of_way_segs = ways.collect {|way| way.way_segments} - list_of_way_segs.flatten! + if node_ids.length > 0 + way_nodes = WayNode.find_all_by_node_id(node_ids) + way_ids = way_nodes.collect {|way_node| way_node.id } + ways = Way.find(way_ids) - list_of_way_segments = list_of_way_segs.collect { |way_seg| way_seg.segment_id } + list_of_way_nodes = ways.collect { |way| + way.way_nodes.collect { |way_node| way_node.node_id } + } + list_of_way_nodes.flatten! - else - list_of_way_segments = Array.new + else + list_of_way_nodes = Array.new end - # - [0] in case some thing links to segment 0 which doesn't exist. Shouldn't actually ever happen but it does. FIXME: file a ticket for this - segments_to_fetch = (list_of_way_segments.uniq - segment_ids) - [0] + # - [0] in case some thing links to node 0 which doesn't exist. Shouldn't actually ever happen but it does. FIXME: file a ticket for this + nodes_to_fetch = (list_of_way_nodes.uniq - node_ids) - [0] - if segments_to_fetch.length > 0 - segments += Segment.find(segments_to_fetch) + if nodes_to_fetch.length > 0 + nodes += Node.find(nodes_to_fetch) end - # get more nodes - # - - segments_nodes = segments.collect {|segment| segment.node_a } - segments_nodes += segments.collect {|segment| segment.node_b } - - node_ids_a = nodes.collect {|node| node.id } - - nodes_to_get = segments_nodes - node_ids_a - nodes += Node.find(nodes_to_get) if nodes_to_get.length > 0 - visible_nodes = {} user_display_name_cache = {} @@@ -195,41 -218,19 +189,41 @@@ end end - visible_segments = {} - - segments.each do |segment| - if visible_nodes[segment.node_a] and visible_nodes[segment.node_b] and segment.visible? - doc.root << segment.to_xml_node(user_display_name_cache) - visible_segments[segment.id] = segment - end - end - + way_ids = Array.new ways.each do |way| - doc.root << way.to_xml_node(visible_segments, user_display_name_cache) if way.visible? + if way.visible? + doc.root << way.to_xml_node(visible_nodes, user_display_name_cache) + way_ids << way.id + end end + # collect relationships. currently done in one big block at the end; + # may need to move this upwards if people want automatic completion of + # relationships, i.e. deliver referenced objects like we do with ways... + relations = Relation.find_by_sql("select e.* from current_relations e,current_relation_members em where " + + "e.visible=1 and " + + "em.id = e.id and em.member_type='node' and em.member_id in (#{visible_nodes.keys.join(',')})") + relations += Relation.find_by_sql("select e.* from current_relations e,current_relation_members em where " + + "e.visible=1 and " + + "em.id = e.id and em.member_type='way' and em.member_id in (#{way_ids.join(',')})") + # we do not normally return the "other" partners referenced by an relation, + # e.g. if we return a way A that is referenced by relation X, and there's + # another way B also referenced, that is not returned. But we do make + # an exception for cases where an relation references another *relation*; + # in that case we return that as well (but we don't go recursive here) + relation_ids = relations.collect { |relation| relation.id } + if relation_ids.length > 0 + relations += Relation.find_by_sql("select e.* from current_relations e,current_relation_members em where " + + "e.visible=1 and " + + "em.id = e.id and em.member_type='relation' and em.member_id in (#{relation_ids.join(',')})") + end + + # this "uniq" may be slightly inefficient; it may be better to first collect and output + # all node-related relations, then find the *not yet covered* way-related ones etc. + relations.uniq.each do |relation| + doc.root << relation.to_xml_node(user_display_name_cache) + end + render :text => doc.to_s, :content_type => "text/xml" #exit when we have too many requests @@@ -245,8 -246,8 +239,8 @@@ api = XML::Node.new 'api' version = XML::Node.new 'version' - version['minimum'] = '0.4'; - version['maximum'] = '0.4'; + version['minimum'] = '0.5'; + version['maximum'] = '0.5'; api << version area = XML::Node.new 'area' area['maximum'] = MAX_REQUEST_AREA.to_s; diff --combined app/controllers/node_controller.rb index de81f188b,4e2a9b7f2..7c7e3e6e4 --- a/app/controllers/node_controller.rb +++ b/app/controllers/node_controller.rb @@@ -13,12 -13,9 +13,9 @@@ class NodeController < ApplicationContr if node node.user_id = @user.id node.visible = true + node.save_with_history! - if node.save_with_history - render :text => node.id.to_s, :content_type => "text/plain" - else - render :nothing => true, :status => :internal_server_error - end + render :text => node.id.to_s, :content_type => "text/plain" else render :nothing => true, :status => :bad_request end @@@ -38,8 -35,6 +35,6 @@@ end rescue ActiveRecord::RecordNotFound render :nothing => true, :status => :not_found - rescue - render :nothing => true, :status => :internal_server_error end end @@@ -55,12 -50,9 +50,9 @@@ node.latitude = new_node.latitude node.longitude = new_node.longitude node.tags = new_node.tags + node.save_with_history! - if node.save_with_history - render :nothing => true - else - render :nothing => true, :status => :internal_server_error - end + render :nothing => true else render :nothing => true, :status => :bad_request end @@@ -69,8 -61,6 +61,6 @@@ end rescue ActiveRecord::RecordNotFound render :nothing => true, :status => :not_found - rescue - render :nothing => true, :status => :internal_server_error end end @@@ -79,14 -69,13 +69,15 @@@ node = Node.find(params[:id]) if node.visible - if Segment.find(:first, :conditions => [ "visible = 1 and (node_a = ? or node_b = ?)", node.id, node.id]) + if WayNode.find(:first, :joins => "INNER JOIN current_ways ON current_ways.id = current_way_nodes.id", :conditions => [ "current_ways.visible = 1 AND current_way_nodes.node_id = ?", node.id ]) + render :nothing => true, :status => :precondition_failed + elsif RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='node' and member_id=?", params[:id]]) render :nothing => true, :status => :precondition_failed else node.user_id = @user.id node.visible = 0 - node.save_with_history + node.save_with_history! + render :nothing => true end else @@@ -94,8 -83,6 +85,6 @@@ end rescue ActiveRecord::RecordNotFound render :nothing => true, :status => :not_found - rescue - render :nothing => true, :status => :internal_server_error end end diff --combined app/controllers/relation_controller.rb index efd5bf9dc,000000000..d00e9e077 mode 100644,000000..100644 --- a/app/controllers/relation_controller.rb +++ b/app/controllers/relation_controller.rb @@@ -1,227 -1,0 +1,218 @@@ +class RelationController < ApplicationController + require 'xml/libxml' + + before_filter :authorize, :only => [:create, :update, :delete] + before_filter :check_availability, :only => [:create, :update, :delete] + + after_filter :compress_output + + def create + if request.put? + relation = Relation.from_xml(request.raw_post, true) + + if relation + if !relation.preconditions_ok? + render :nothing => true, :status => :precondition_failed + else + relation.user_id = @user.id ++ relation.save_with_history! + - if relation.save_with_history - render :text => relation.id.to_s, :content_type => "text/plain" - else - render :text => "save error", :status => :internal_server_error - end ++ render :text => relation.id.to_s, :content_type => "text/plain" + end + else + render :nothing => true, :status => :bad_request + end + else + render :nothing => true, :status => :method_not_allowed + end + end + + def read + begin + relation = Relation.find(params[:id]) + + if relation.visible + render :text => relation.to_xml.to_s, :content_type => "text/xml" + else + render :nothing => true, :status => :gone + end + rescue ActiveRecord::RecordNotFound + render :nothing => true, :status => :not_found + rescue + render :nothing => true, :status => :internal_server_error + end + end + + def update + begin + relation = Relation.find(params[:id]) + + if relation.visible + new_relation = Relation.from_xml(request.raw_post) + + if new_relation and new_relation.id == relation.id + if !new_relation.preconditions_ok? + render :nothing => true, :status => :precondition_failed + else + relation.user_id = @user.id + relation.tags = new_relation.tags + relation.members = new_relation.members + relation.visible = true ++ relation.save_with_history! + - if relation.save_with_history - render :nothing => true - else - render :nothing => true, :status => :internal_server_error - end ++ render :nothing => true + end + else + render :nothing => true, :status => :bad_request + end + else + render :nothing => true, :status => :gone + end + rescue ActiveRecord::RecordNotFound + render :nothing => true, :status => :not_found + rescue + render :nothing => true, :status => :internal_server_error + end + end + + def delete +#XXX check if member somewhere! + begin + relation = Relation.find(params[:id]) + + if relation.visible + if RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='relation' and member_id=?", params[:id]]) + render :nothing => true, :status => :precondition_failed + else + relation.user_id = @user.id + relation.tags = [] + relation.members = [] + relation.visible = false ++ relation.save_with_history! + - if relation.save_with_history - render :nothing => true - else - render :nothing => true, :status => :internal_server_error - end ++ render :nothing => true + end + else + render :nothing => true, :status => :gone + end + rescue ActiveRecord::RecordNotFound + render :nothing => true, :status => :not_found + rescue + render :nothing => true, :status => :internal_server_error + end + end + + # ----------------------------------------------------------------- + # full + # + # input parameters: id + # + # returns XML representation of one relation object plus all its + # members, plus all nodes part of member ways + # ----------------------------------------------------------------- + def full + begin + relation = Relation.find(params[:id]) + + if relation.visible + + # first collect nodes, ways, and relations referenced by this relation. + + ways = Way.find_by_sql("select w.* from current_ways w,current_relation_members rm where "+ + "rm.member_type='way' and rm.member_id=w.id and rm.id=#{relation.id}"); + nodes = Node.find_by_sql("select n.* from current_nodes n,current_relation_members rm where "+ + "rm.member_type='node' and rm.member_id=n.id and rm.id=#{relation.id}"); + # note query is built to exclude self just in case. + relations = Relation.find_by_sql("select r.* from current_relations r,current_relation_members rm where "+ + "rm.member_type='relation' and rm.member_id=r.id and rm.id=#{relation.id} and r.id<>rm.id"); + + # now additionally collect nodes referenced by ways. Note how we recursively + # evaluate ways but NOT relations. + + node_ids = nodes.collect {|node| node.id } + way_node_ids = ways.collect { |way| + way.way_nodes.collect { |way_node| way_node.node_id } + } + way_node_ids.flatten! + way_node_ids.uniq + way_node_ids -= node_ids + nodes += Node.find(way_node_ids) + + # create XML. + doc = OSM::API.new.get_xml_doc + user_display_name_cache = {} + + nodes.each do |node| + if node.visible? # should be unnecessary if data is consistent. + doc.root << node.to_xml_node(user_display_name_cache) + end + end + ways.each do |way| + if way.visible? # should be unnecessary if data is consistent. + doc.root << way.to_xml_node(user_display_name_cache) + end + end + relations.each do |rel| + if rel.visible? # should be unnecessary if data is consistent. + doc.root << rel.to_xml_node(user_display_name_cache) + end + end + # finally add self and output + doc.root << relation.to_xml_node(user_display_name_cache) + render :text => doc.to_s, :content_type => "text/xml" + + else + + render :nothing => true, :status => :gone + end + + rescue ActiveRecord::RecordNotFound + render :nothing => true, :status => :not_found + + rescue + render :nothing => true, :status => :internal_server_error + end + end + + def relations + ids = params['relations'].split(',').collect { |w| w.to_i } + + if ids.length > 0 + doc = OSM::API.new.get_xml_doc + + Relation.find(ids).each do |relation| + doc.root << relation.to_xml_node + end + + render :text => doc.to_s, :content_type => "text/xml" + else + render :nothing => true, :status => :bad_request + end + end + + def relations_for_way + relations_for_object("way") + end + def relations_for_node + relations_for_object("node") + end + def relations_for_relation + relations_for_object("relation") + end + + def relations_for_object(objtype) + relationids = RelationMember.find(:all, :conditions => ['member_type=? and member_id=?', objtype, params[:id]]).collect { |ws| ws.id }.uniq + + if relationids.length > 0 + doc = OSM::API.new.get_xml_doc + + Relation.find(relationids).each do |relation| + doc.root << relation.to_xml_node + end + + render :text => doc.to_s, :content_type => "text/xml" + else + render :nothing => true, :status => :not_found + end + end +end diff --combined app/controllers/swf_controller.rb index 33e2ee4a6,2200ab652..6b29d1535 --- a/app/controllers/swf_controller.rb +++ b/app/controllers/swf_controller.rb @@@ -20,10 -20,10 +20,10 @@@ class SwfController < ApplicationContro basey =params['basey'].to_f masterscale =params['masterscale'].to_f - xmin=params['xmin'].to_f; xminr=xmin/0.000001 - xmax=params['xmax'].to_f; xmaxr=xmax/0.000001 - ymin=params['ymin'].to_f; yminr=ymin/0.000001 - ymax=params['ymax'].to_f; ymaxr=ymax/0.000001 + xmin=params['xmin'].to_f; + xmax=params['xmax'].to_f; + ymin=params['ymin'].to_f; + ymax=params['ymax'].to_f; # - Begin movie @@@ -47,20 -47,18 +47,18 @@@ if params['token'] user=User.authenticate(:token => params[:token]) - sql="SELECT gps_points.latitude*0.000001 AS lat,gps_points.longitude*0.000001 AS lon,gpx_files.id AS fileid,UNIX_TIMESTAMP(gps_points.timestamp) AS ts "+ + sql="SELECT gps_points.latitude*0.0000001 AS lat,gps_points.longitude*0.0000001 AS lon,gpx_files.id AS fileid,UNIX_TIMESTAMP(gps_points.timestamp) AS ts "+ " FROM gpx_files,gps_points "+ "WHERE gpx_files.id=gpx_id "+ " AND gpx_files.user_id=#{user.id} "+ - " AND (gps_points.longitude BETWEEN #{xminr} AND #{xmaxr}) "+ - " AND (gps_points.latitude BETWEEN #{yminr} AND #{ymaxr}) "+ + " AND "+OSM.sql_for_area(ymin,xmin,ymax,xmax,"gps_points.")+ " AND (gps_points.timestamp IS NOT NULL) "+ "ORDER BY fileid DESC,ts "+ "LIMIT 10000" else - sql="SELECT latitude*0.000001 AS lat,longitude*0.000001 AS lon,gpx_id AS fileid,UNIX_TIMESTAMP(timestamp) AS ts "+ + sql="SELECT latitude*0.0000001 AS lat,longitude*0.0000001 AS lon,gpx_id AS fileid,UNIX_TIMESTAMP(timestamp) AS ts "+ " FROM gps_points "+ - "WHERE (longitude BETWEEN #{xminr} AND #{xmaxr}) "+ - " AND (latitude BETWEEN #{yminr} AND #{ymaxr}) "+ + "WHERE "+OSM.sql_for_area(ymin,xmin,ymax,xmax,"gps_points.")+ " AND (gps_points.timestamp IS NOT NULL) "+ "ORDER BY fileid DESC,ts "+ "LIMIT 10000" @@@ -94,7 -92,7 +92,7 @@@ sql="SELECT cn1.latitude AS lat1,cn1.longitude AS lon1,"+ " cn2.latitude AS lat2,cn2.longitude AS lon2 "+ " FROM current_segments "+ - " LEFT OUTER JOIN current_way_segments"+ + " LEFT OUTER JOIN current_way_nodes"+ " ON segment_id=current_segments.id,"+ " current_nodes AS cn1,current_nodes AS cn2"+ " WHERE (cn1.longitude BETWEEN #{xmin} AND #{xmax})"+ diff --combined app/controllers/way_controller.rb index 2ef960c6e,935ff3a72..2e0623df4 --- a/app/controllers/way_controller.rb +++ b/app/controllers/way_controller.rb @@@ -15,12 -15,9 +15,9 @@@ class WayController < ApplicationContro render :nothing => true, :status => :precondition_failed else way.user_id = @user.id + way.save_with_history! - if way.save_with_history - render :text => way.id.to_s, :content_type => "text/plain" - else - render :nothing => true, :status => :internal_server_error - end + render :text => way.id.to_s, :content_type => "text/plain" end else render :nothing => true, :status => :bad_request @@@ -41,8 -38,6 +38,6 @@@ end rescue ActiveRecord::RecordNotFound render :nothing => true, :status => :not_found - rescue - render :nothing => true, :status => :internal_server_error end end @@@ -59,14 -54,11 +54,11 @@@ else way.user_id = @user.id way.tags = new_way.tags - way.segs = new_way.segs + way.nds = new_way.nds way.visible = true + way.save_with_history! - if way.save_with_history - render :nothing => true - else - render :nothing => true, :status => :internal_server_error - end + render :nothing => true end else render :nothing => true, :status => :bad_request @@@ -76,8 -68,6 +68,6 @@@ end rescue ActiveRecord::RecordNotFound render :nothing => true, :status => :not_found - rescue - render :nothing => true, :status => :internal_server_error end end @@@ -86,27 -76,18 +76,24 @@@ way = Way.find(params[:id]) if way.visible - way.user_id = @user.id - way.tags = [] - way.segs = [] - way.visible = false - way.save_with_history! + if RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='way' and member_id=?", params[:id]]) + render :nothing => true, :status => :precondition_failed + else + way.user_id = @user.id + way.tags = [] + way.nds = [] + way.visible = false ++ way.save_with_history! - if way.save_with_history - render :nothing => true - else - render :nothing => true, :status => :internal_server_error - end - render :nothing => true ++ render :nothing => true + end else render :nothing => true, :status => :gone end rescue ActiveRecord::RecordNotFound render :nothing => true, :status => :not_found - rescue - render :nothing => true, :status => :internal_server_error ++ rescue => ex ++ puts ex end end @@@ -115,14 -96,23 +102,14 @@@ way = Way.find(params[:id]) if way.visible - # In future, we might want to do all the data fetch in one step - seg_ids = way.segs + [-1] - segments = Segment.find_by_sql "select * from current_segments where visible = 1 and id IN (#{seg_ids.join(',')})" - - node_ids = segments.collect {|segment| segment.node_a } - node_ids += segments.collect {|segment| segment.node_b } - node_ids += [-1] - nodes = Node.find(:all, :conditions => "visible = 1 AND id IN (#{node_ids.join(',')})") + nd_ids = way.nds + [-1] + nodes = Node.find(:all, :conditions => "visible = 1 AND id IN (#{nd_ids.join(',')})") # Render doc = OSM::API.new.get_xml_doc nodes.each do |node| doc.root << node.to_xml_node() end - segments.each do |segment| - doc.root << segment.to_xml_node() - end doc.root << way.to_xml_node() render :text => doc.to_s, :content_type => "text/xml" @@@ -131,8 -121,6 +118,6 @@@ end rescue ActiveRecord::RecordNotFound render :nothing => true, :status => :not_found - rescue - render :nothing => true, :status => :internal_server_error end end @@@ -156,8 -144,8 +141,8 @@@ end end - def ways_for_segment - wayids = WaySegment.find(:all, :conditions => ['segment_id = ?', params[:id]]).collect { |ws| ws.id }.uniq + def ways_for_node + wayids = WayNode.find(:all, :conditions => ['node_id = ?', params[:id]]).collect { |ws| ws.id }.uniq if wayids.length > 0 doc = OSM::API.new.get_xml_doc diff --combined app/models/relation.rb index f74a149ec,000000000..a5d463ffb mode 100644,000000..100644 --- a/app/models/relation.rb +++ b/app/models/relation.rb @@@ -1,213 -1,0 +1,207 @@@ +class Relation < ActiveRecord::Base + require 'xml/libxml' + + belongs_to :user + + has_many :relation_members, :foreign_key => 'id' + has_many :relation_tags, :foreign_key => 'id' + + has_many :old_relations, :foreign_key => 'id', :order => 'version' + + set_table_name 'current_relations' + + def self.from_xml(xml, create=false) + begin + p = XML::Parser.new + p.string = xml + doc = p.parse + + relation = Relation.new + + doc.find('//osm/relation').each do |pt| + if !create and pt['id'] != '0' + relation.id = pt['id'].to_i + end + + if create + relation.timestamp = Time.now + relation.visible = true + else + if pt['timestamp'] + relation.timestamp = Time.parse(pt['timestamp']) + end + end + + pt.find('tag').each do |tag| + relation.add_tag_keyval(tag['k'], tag['v']) + end + + pt.find('member').each do |member| + relation.add_member(member['type'], member['ref'], member['role']) + end + end + rescue + relation = nil + end + + return relation + end + + def to_xml + doc = OSM::API.new.get_xml_doc + doc.root << to_xml_node() + return doc + end + + def to_xml_node(user_display_name_cache = nil) + el1 = XML::Node.new 'relation' + el1['id'] = self.id.to_s + el1['visible'] = self.visible.to_s + el1['timestamp'] = self.timestamp.xmlschema + + user_display_name_cache = {} if user_display_name_cache.nil? + + if user_display_name_cache and user_display_name_cache.key?(self.user_id) + # use the cache if available + elsif self.user.data_public? + user_display_name_cache[self.user_id] = self.user.display_name + else + user_display_name_cache[self.user_id] = nil + end + + el1['user'] = user_display_name_cache[self.user_id] unless user_display_name_cache[self.user_id].nil? + + self.relation_members.each do |member| + p=0 + #if visible_members + # # if there is a list of visible members then use that to weed out deleted segments + # if visible_members[member.member_type][member.member_id] + # p=1 + # end + #else + # otherwise, manually go to the db to check things + if member.member.visible? + p=1 + end + #end + if p + e = XML::Node.new 'member' + e['type'] = member.member_type + e['ref'] = member.member_id.to_s + e['role'] = member.member_role + el1 << e + end + end + + self.relation_tags.each do |tag| + e = XML::Node.new 'tag' + e['k'] = tag.k + e['v'] = tag.v + el1 << e + end + return el1 + end + + # FIXME is this really needed? + def members + unless @members + @members = Array.new + self.relation_members.each do |member| + @members += [[member.member_type,member.member_id,member.member_role]] + end + end + @members + end + + def tags + unless @tags + @tags = Hash.new + self.relation_tags.each do |tag| + @tags[tag.k] = tag.v + end + end + @tags + end + + def members=(m) + @members = m + end + + def tags=(t) + @tags = t + end + + def add_member(type,id,role) + @members = Array.new unless @members + @members += [[type,id,role]] + end + + def add_tag_keyval(k, v) + @tags = Hash.new unless @tags + @tags[k] = v + end + - def save_with_history - begin - Relation.transaction do - t = Time.now - self.timestamp = t - self.save! - - tags = self.tags - - RelationTag.delete_all(['id = ?', self.id]) - - tags.each do |k,v| - tag = RelationTag.new - tag.k = k - tag.v = v - tag.id = self.id - tag.save! - end - - members = self.members ++ def save_with_history! ++ Relation.transaction do ++ t = Time.now ++ self.timestamp = t ++ self.save! ++ ++ tags = self.tags ++ ++ RelationTag.delete_all(['id = ?', self.id]) ++ ++ tags.each do |k,v| ++ tag = RelationTag.new ++ tag.k = k ++ tag.v = v ++ tag.id = self.id ++ tag.save! ++ end + - RelationMember.delete_all(['id = ?', self.id]) ++ members = self.members + - members.each do |n| - mem = RelationMember.new - mem.id = self.id - mem.member_type = n[0]; - mem.member_id = n[1]; - mem.member_role = n[2]; - mem.save! - end ++ RelationMember.delete_all(['id = ?', self.id]) + - old_relation = OldRelation.from_relation(self) - old_relation.timestamp = t - old_relation.save_with_dependencies! ++ members.each do |n| ++ mem = RelationMember.new ++ mem.id = self.id ++ mem.member_type = n[0]; ++ mem.member_id = n[1]; ++ mem.member_role = n[2]; ++ mem.save! + end + - return true - rescue Exception => ex - return nil ++ old_relation = OldRelation.from_relation(self) ++ old_relation.timestamp = t ++ old_relation.save_with_dependencies! + end + end + + def preconditions_ok? + self.members.each do |m| + if (m[0] == "node") + n = Node.find(:first, :conditions => ["id = ?", m[1]]) + unless n and n.visible + return false + end + elsif (m[0] == "way") + w = Way.find(:first, :conditions => ["id = ?", m[1]]) + unless w and w.visible and w.preconditions_ok? + return false + end + elsif (m[0] == "relation") + e = Relation.find(:first, :conditions => ["id = ?", m[1]]) + unless e and e.visible and e.preconditions_ok? + return false + end + else + return false + end + end + return true + rescue + return false + end + +end diff --combined app/models/way.rb index b564a3317,c65119235..e0a445074 --- a/app/models/way.rb +++ b/app/models/way.rb @@@ -3,7 -3,7 +3,7 @@@ class Way < ActiveRecord::Bas belongs_to :user - has_many :way_segments, :foreign_key => 'id', :order => 'sequence_id' + has_many :way_nodes, :foreign_key => 'id', :order => 'sequence_id' has_many :way_tags, :foreign_key => 'id' has_many :old_ways, :foreign_key => 'id', :order => 'version' @@@ -36,8 -36,8 +36,8 @@@ way.add_tag_keyval(tag['k'], tag['v']) end - pt.find('seg').each do |seg| - way.add_seg_num(seg['id']) + pt.find('nd').each do |nd| + way.add_nd_num(nd['ref']) end end rescue @@@ -53,7 -53,7 +53,7 @@@ return doc end - def to_xml_node(visible_segments = nil, user_display_name_cache = nil) + def to_xml_node(visible_nodes = nil, user_display_name_cache = nil) el1 = XML::Node.new 'way' el1['id'] = self.id.to_s el1['visible'] = self.visible.to_s @@@ -71,26 -71,26 +71,26 @@@ el1['user'] = user_display_name_cache[self.user_id] unless user_display_name_cache[self.user_id].nil? - # make sure segments are output in sequence_id order - ordered_segments = [] - self.way_segments.each do |seg| - if visible_segments - # if there is a list of visible segments then use that to weed out deleted segments - if visible_segments[seg.segment_id] - ordered_segments[seg.sequence_id] = seg.segment_id.to_s + # make sure nodes are output in sequence_id order + ordered_nodes = [] + self.way_nodes.each do |nd| + if visible_nodes + # if there is a list of visible nodes then use that to weed out deleted nodes + if visible_nodes[nd.node_id] + ordered_nodes[nd.sequence_id] = nd.node_id.to_s end else # otherwise, manually go to the db to check things - if seg.segment.visible? and seg.segment.from_node.visible? and seg.segment.to_node.visible? - ordered_segments[seg.sequence_id] = seg.segment_id.to_s + if nd.node.visible? and nd.node.visible? + ordered_nodes[nd.sequence_id] = nd.node_id.to_s end end end - ordered_segments.each do |seg_id| - if seg_id and seg_id != '0' - e = XML::Node.new 'seg' - e['id'] = seg_id + ordered_nodes.each do |nd_id| + if nd_id and nd_id != '0' + e = XML::Node.new 'nd' + e['ref'] = nd_id el1 << e end end @@@ -104,14 -104,14 +104,14 @@@ return el1 end - def segs - unless @segs - @segs = Array.new - self.way_segments.each do |seg| - @segs += [seg.segment_id] + def nds + unless @nds + @nds = Array.new + self.way_nodes.each do |nd| + @nds += [nd.node_id] end end - @segs + @nds end def tags @@@ -124,17 -124,17 +124,17 @@@ @tags end - def segs=(s) - @segs = s + def nds=(s) + @nds = s end def tags=(t) @tags = t end - def add_seg_num(n) - @segs = Array.new unless @segs - @segs << n.to_i + def add_nd_num(n) + @nds = Array.new unless @nds + @nds << n.to_i end def add_tag_keyval(k, v) @@@ -142,56 -142,54 +142,54 @@@ @tags[k] = v end - def save_with_history - begin - Way.transaction do - t = Time.now - self.timestamp = t - self.save! + def save_with_history! + t = Time.now - tags = self.tags + Way.transaction do + self.timestamp = t + self.save! + end - WayTag.delete_all(['id = ?', self.id]) + WayTag.transaction do + tags = self.tags - tags.each do |k,v| - tag = WayTag.new - tag.k = k - tag.v = v - tag.id = self.id - tag.save! - end + WayTag.delete_all(['id = ?', self.id]) - nds = self.nds + tags.each do |k,v| + tag = WayTag.new + tag.k = k + tag.v = v + tag.id = self.id + tag.save! + end + end - WayNode.delete_all(['id = ?', self.id]) - WaySegment.transaction do - segs = self.segs ++ WayNode.transaction do ++ nds = self.nds - i = 1 - nds.each do |n| - nd = WayNode.new - nd.id = self.id - nd.node_id = n - nd.sequence_id = i - nd.save! - i += 1 - end - WaySegment.delete_all(['id = ?', self.id]) ++ WayNode.delete_all(['id = ?', self.id]) - old_way = OldWay.from_way(self) - old_way.timestamp = t - old_way.save_with_dependencies! + i = 1 - segs.each do |n| - seg = WaySegment.new - seg.id = self.id - seg.segment_id = n - seg.sequence_id = i - seg.save! ++ nds.each do |n| ++ nd = WayNode.new ++ nd.id = self.id ++ nd.node_id = n ++ nd.sequence_id = i ++ nd.save! + i += 1 end - - return true - rescue => ex - puts ex - return nil end + + old_way = OldWay.from_way(self) + old_way.timestamp = t + old_way.save_with_dependencies! end def preconditions_ok? - return false if self.segs.empty? - self.segs.each do |n| - segment = Segment.find(:first, :conditions => ["id = ?", n]) - unless segment and segment.visible and segment.preconditions_ok? + return false if self.nds.empty? + self.nds.each do |n| + node = Node.find(:first, :conditions => ["id = ?", n]) + unless node and node.visible return false end end diff --combined config/environment.rb index 570cb04b5,003a7b832..3820ed294 --- a/config/environment.rb +++ b/config/environment.rb @@@ -1,5 -1,8 +1,8 @@@ # Be sure to restart your web server when you modify this file. + # Limit each rails process to a 512Mb resident set size + Process.setrlimit Process::RLIMIT_AS, 640*1024*1024, Process::RLIM_INFINITY + # Uncomment below to force Rails into production mode when # you don't control web/app server and can't set it the proper way ENV['RAILS_ENV'] ||= 'production' @@@ -14,7 -17,7 +17,7 @@@ RAILS_GEM_VERSION = '1.2.3 require File.join(File.dirname(__FILE__), 'boot') # Application constants needed for routes.rb - must go before Initializer call -API_VERSION = ENV['OSM_API_VERSION'] || '0.4' +API_VERSION = ENV['OSM_API_VERSION'] || '0.5' # Custom logger class to format messages sensibly class OSMLogger < Logger diff --combined db/migrate/006_add_relations.rb index a30642e32,000000000..a30642e32 mode 100644,000000..100644 --- a/db/migrate/006_add_relations.rb +++ b/db/migrate/006_add_relations.rb @@@ -1,83 -1,0 +1,83 @@@ +class AddRelations < ActiveRecord::Migration + def self.up + # a relation can have members much like a way can have nodes. + # differences: + # way: only nodes / relation: any kind of member + # way: ordered sequence of nodes / relation: free-form "role" string + create_table "current_relation_members", innodb_table do |t| + t.column "id", :bigint, :limit => 64, :null => false + t.column "member_type", :string, :limit => 11, :null => false + t.column "member_id", :bigint, :limit => 11, :null => false + t.column "member_role", :string + end + # enums work like strings but are more efficient + execute "alter table current_relation_members change column member_type member_type enum('node','way','relation');" + + add_primary_key "current_relation_members", ["id", "member_type", "member_id", "member_role"] + add_index "current_relation_members", ["member_type", "member_id"], :name => "current_relation_members_member_idx" + # the following is obsolete given the primary key, is it not? + # add_index "current_relation_members", ["id"], :name => "current_relation_members_id_idx" + create_table "current_relation_tags", myisam_table do |t| + t.column "id", :bigint, :limit => 64, :null => false + t.column "k", :string, :default => "", :null => false + t.column "v", :string, :default => "", :null => false + end + + add_index "current_relation_tags", ["id"], :name => "current_relation_tags_id_idx" + execute "CREATE FULLTEXT INDEX `current_relation_tags_v_idx` ON `current_relation_tags` (`v`)" + + create_table "current_relations", innodb_table do |t| + t.column "id", :bigint, :limit => 64, :null => false + t.column "user_id", :bigint, :limit => 20, :null => false + t.column "timestamp", :datetime, :null => false + t.column "visible", :boolean, :null => false + end + + add_primary_key "current_relations", ["id"] + change_column "current_relations", "id", :bigint, :limit => 64, :null => false, :options => "AUTO_INCREMENT" + + create_table "relation_members", myisam_table do |t| + t.column "id", :bigint, :limit => 64, :default => 0, :null => false + t.column "member_type", :string, :limit => 11, :null => false + t.column "member_id", :bigint, :limit => 11, :null => false + t.column "member_role", :string + t.column "version", :bigint, :limit => 20, :default => 0, :null => false + end + + execute "alter table relation_members change column member_type member_type enum('node','way','relation');" + add_primary_key "relation_members", ["id", "version", "member_type", "member_id", "member_role"] + add_index "relation_members", ["member_type", "member_id"], :name => "relation_members_member_idx" + + create_table "relation_tags", myisam_table do |t| + t.column "id", :bigint, :limit => 64, :default => 0, :null => false + t.column "k", :string, :null => false, :default => "" + t.column "v", :string, :null => false, :default => "" + t.column "version", :bigint, :limit => 20, :null => false + end + + add_index "relation_tags", ["id", "version"], :name => "relation_tags_id_version_idx" + + create_table "relations", myisam_table do |t| + t.column "id", :bigint, :limit => 64, :null => false, :default => 0 + t.column "user_id", :bigint, :limit => 20, :null => false + t.column "timestamp", :datetime, :null => false + t.column "version", :bigint, :limit => 20, :null => false + t.column "visible", :boolean, :null => false, :default => true + end + + add_primary_key "relations", ["id", "version"] + add_index "relations", ["timestamp"], :name => "relations_timestamp_idx" + + change_column "relations", "version", :bigint, :limit => 20, :null => false, :options => "AUTO_INCREMENT" + end + + + def self.down + drop_table :relations + drop_table :current_relations + drop_table :relation_tags + drop_table :current_relation_tags + drop_table :relation_members + drop_table :current_relation_members + end +end diff --combined db/migrate/007_remove_segments.rb index de9557cc9,000000000..492beda63 mode 100644,000000..100644 --- a/db/migrate/007_remove_segments.rb +++ b/db/migrate/007_remove_segments.rb @@@ -1,89 -1,0 +1,89 @@@ +require 'lib/migrate' + +class RemoveSegments < ActiveRecord::Migration + def self.up + have_segs = select_value("SELECT count(*) FROM current_segments").to_i != 0 + + if have_segs - prefix = File.join Dir.tmpdir, "006_remove_segments.#{$$}." ++ prefix = File.join Dir.tmpdir, "007_remove_segments.#{$$}." + - cmd = "db/migrate/006_remove_segments_helper" ++ cmd = "db/migrate/007_remove_segments_helper" + src = "#{cmd}.cc" + if not File.exists? cmd or File.mtime(cmd) < File.mtime(src) then + system 'c++ -O3 -Wall `mysql_config --cflags --libs` ' + + "#{src} -o #{cmd}" or fail + end + + conn_opts = ActiveRecord::Base.connection. + instance_eval { @connection_options } + args = conn_opts.map { |arg| arg.to_s } + [prefix] + fail "#{cmd} failed" unless system cmd, *args + + tempfiles = ['ways', 'way_nodes', 'way_tags', + 'relations', 'relation_members', 'relation_tags']. + map { |base| prefix + base } + ways, way_nodes, way_tags, + relations, relation_members, relation_tags = tempfiles + end + + drop_table :segments + drop_table :way_segments + create_table :way_nodes, myisam_table do |t| + t.column :id, :bigint, :limit => 64, :null => false + t.column :node_id, :bigint, :limit => 64, :null => false + t.column :version, :bigint, :limit => 20, :null => false + t.column :sequence_id, :bigint, :limit => 11, :null => false + end + add_primary_key :way_nodes, [:id, :version, :sequence_id] + + drop_table :current_segments + drop_table :current_way_segments + create_table :current_way_nodes, innodb_table do |t| + t.column :id, :bigint, :limit => 64, :null => false + t.column :node_id, :bigint, :limit => 64, :null => false + t.column :sequence_id, :bigint, :limit => 11, :null => false + end + add_primary_key :current_way_nodes, [:id, :sequence_id] + + execute "TRUNCATE way_tags" + execute "TRUNCATE ways" + execute "TRUNCATE current_way_tags" + execute "TRUNCATE current_ways" + + # now get the data back + csvopts = "FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\"' LINES TERMINATED BY '\\n'" + + tempfiles.each { |fn| File.chmod 0644, fn } if have_segs + + if have_segs + execute "LOAD DATA LOCAL INFILE '#{ways}' INTO TABLE ways #{csvopts} (id, user_id, timestamp) SET visible = 1, version = 1" + execute "LOAD DATA LOCAL INFILE '#{way_nodes}' INTO TABLE way_nodes #{csvopts} (id, node_id, sequence_id) SET version = 1" + execute "LOAD DATA LOCAL INFILE '#{way_tags}' INTO TABLE way_tags #{csvopts} (id, k, v) SET version = 1" + + execute "INSERT INTO current_ways SELECT id, user_id, timestamp, visible FROM ways" + execute "INSERT INTO current_way_nodes SELECT id, node_id, sequence_id FROM way_nodes" + execute "INSERT INTO current_way_tags SELECT id, k, v FROM way_tags" + end + + # and then readd the index + add_index :current_way_nodes, [:node_id], :name => "current_way_nodes_node_idx" + + if have_segs + execute "LOAD DATA LOCAL INFILE '#{relations}' INTO TABLE relations #{csvopts} (id, user_id, timestamp) SET visible = 1, version = 1" + execute "LOAD DATA LOCAL INFILE '#{relation_members}' INTO TABLE relation_members #{csvopts} (id, member_type, member_id, member_role) SET version = 1" + execute "LOAD DATA LOCAL INFILE '#{relation_tags}' INTO TABLE relation_tags #{csvopts} (id, k, v) SET version = 1" + + # FIXME: This will only work if there were no relations before the + # migration! + execute "INSERT INTO current_relations SELECT id, user_id, timestamp, visible FROM relations" + execute "INSERT INTO current_relation_members SELECT id, member_type, member_id, member_role FROM relation_members" + execute "INSERT INTO current_relation_tags SELECT id, k, v FROM relation_tags" + end + + tempfiles.each { |fn| File.unlink fn } if have_segs + end + + def self.down + raise IrreversibleMigration.new + end +end diff --combined db/migrate/007_remove_segments_helper.cc index dd2176a04,000000000..2234a3bb5 mode 100644,000000..100644 --- a/db/migrate/007_remove_segments_helper.cc +++ b/db/migrate/007_remove_segments_helper.cc @@@ -1,591 -1,0 +1,591 @@@ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __amd64__ + +#define F_U64 "%lu" +#define F_U32 "%u" + +#else + +#define F_U64 "%Lu" +#define F_U32 "%u" + +#endif + +using namespace std; + +template +static T parse(const char *str) { + istringstream in(str); + T t; + in >> t; + return t; +} + +static void exit_mysql_err(MYSQL *mysql) { + const char *err = mysql_error(mysql); + if (err) { - fprintf(stderr, "005_remove_segments_helper: MySQL error: %s\n", err); ++ fprintf(stderr, "007_remove_segments_helper: MySQL error: %s\n", err); + } else { - fprintf(stderr, "005_remove_segments_helper: MySQL error\n"); ++ fprintf(stderr, "007_remove_segments_helper: MySQL error\n"); + } + abort(); + exit(EXIT_FAILURE); +} + +static void exit_stmt_err(MYSQL_STMT *stmt) { + const char *err = mysql_stmt_error(stmt); + if (err) { - fprintf(stderr, "005_remove_segments_helper: MySQL stmt error: %s\n", err); ++ fprintf(stderr, "007_remove_segments_helper: MySQL stmt error: %s\n", err); + } else { - fprintf(stderr, "005_remove_segments_helper: MySQL stmt error\n"); ++ fprintf(stderr, "007_remove_segments_helper: MySQL stmt error\n"); + } + abort(); + exit(EXIT_FAILURE); +} + +struct segment { + uint32_t from, to; +}; + +struct data { + MYSQL *mysql, *mysql2; + + uint64_t seg_maxid, way_maxid; + uint64_t new_way_id; + uint64_t new_relation_id; + + size_t segs_len; + struct segment *segs; + unsigned char *rem_segs; + + FILE *ways, *way_nodes, *way_tags, + *relations, *relation_members, *relation_tags; +}; + +static uint64_t select_u64(MYSQL *mysql, const char *q) { + MYSQL_RES *res; + MYSQL_ROW row; + uint64_t ret; + + if (mysql_query(mysql, q)) + exit_mysql_err(mysql); + + res = mysql_store_result(mysql); + if (!res) exit_mysql_err(mysql); + + row = mysql_fetch_row(res); + if (!row) exit_mysql_err(mysql); + + if (row[0]) { + ret = parse(row[0]); + } else { + ret = 0; + } + + mysql_free_result(res); + + return ret; +} + +static void find_maxids(struct data *d) { + d->seg_maxid = select_u64(d->mysql, "SELECT max(id) FROM current_segments"); + d->segs_len = d->seg_maxid + 1; + d->way_maxid = select_u64(d->mysql, "SELECT max(id) FROM current_ways"); + d->new_way_id = d->way_maxid + 1; + d->new_relation_id = select_u64(d->mysql, "SELECT max(id) FROM current_relations") + 1; +} + +static void populate_segs(struct data *d) { + MYSQL_RES *res; + MYSQL_ROW row; + size_t id; + + d->segs = (segment *) malloc(sizeof(struct segment) * d->segs_len); + memset(d->segs, 0, sizeof(struct segment) * d->segs_len); + + d->rem_segs = (unsigned char *) malloc(d->segs_len); + memset(d->rem_segs, 0, d->segs_len); + + if (mysql_query(d->mysql, "SELECT id, node_a, node_b " + "FROM current_segments WHERE visible")) + exit_mysql_err(d->mysql); + + res = mysql_use_result(d->mysql); + if (!res) exit_mysql_err(d->mysql); + + while ((row = mysql_fetch_row(res))) { + id = parse(row[0]); + if (id >= d->segs_len) continue; + d->segs[id].from = parse(row[1]); + d->segs[id].to = parse(row[2]); + d->rem_segs[id] = 1; + } + if (mysql_errno(d->mysql)) exit_mysql_err(d->mysql); + + mysql_free_result(res); +} + +static void write_csv_col(FILE *f, const char *str, char end) { + char *out = (char *) malloc(2 * strlen(str) + 4); + char *o = out; + size_t len; + + *(o++) = '\"'; + for (; *str; str++) { + if (*str == '\0') { + break; + } else if (*str == '\"') { + *(o++) = '\"'; + *(o++) = '\"'; + } else { + *(o++) = *str; + } + } + *(o++) = '\"'; + *(o++) = end; + *(o++) = '\0'; + + len = strlen(out); + if (fwrite(out, len, 1, f) != 1) { + perror("fwrite"); + exit(EXIT_FAILURE); + } + + free(out); +} + +static void convert_ways(struct data *d) { + MYSQL_RES *res; + MYSQL_ROW row; + MYSQL_STMT *load_segs, *load_tags; + const char + load_segs_stmt[] = "SELECT segment_id FROM current_way_segments " + "WHERE id = ? ORDER BY sequence_id", + load_tags_stmt[] = "SELECT k, v FROM current_way_tags WHERE id = ?"; + char *k, *v; + const size_t max_tag_len = 1 << 16; + long long mysql_id, mysql_seg_id; + unsigned long res_len; + my_bool res_error; + MYSQL_BIND bind[1], seg_bind[1], tag_bind[2]; + + /* F***ing libmysql only support fixed size buffers for string results of + * prepared statements. So allocate 65k for the tag key and the tag value + * and hope it'll suffice. */ + k = (char *) malloc(max_tag_len); + v = (char *) malloc(max_tag_len); + + load_segs = mysql_stmt_init(d->mysql2); + if (!load_segs) exit_mysql_err(d->mysql2); + if (mysql_stmt_prepare(load_segs, load_segs_stmt, sizeof(load_segs_stmt))) + exit_stmt_err(load_segs); + + memset(bind, 0, sizeof(bind)); + bind[0].buffer_type = MYSQL_TYPE_LONGLONG; + bind[0].buffer = (char *) &mysql_id; + bind[0].is_null = 0; + bind[0].length = 0; + if (mysql_stmt_bind_param(load_segs, bind)) + exit_stmt_err(load_segs); + + memset(bind, 0, sizeof(seg_bind)); + seg_bind[0].buffer_type = MYSQL_TYPE_LONGLONG; + seg_bind[0].buffer = (char *) &mysql_seg_id; + seg_bind[0].is_null = 0; + seg_bind[0].length = 0; + seg_bind[0].error = &res_error; + if (mysql_stmt_bind_result(load_segs, seg_bind)) + exit_stmt_err(load_segs); + + load_tags = mysql_stmt_init(d->mysql2); + if (!load_tags) exit_mysql_err(d->mysql2); + if (mysql_stmt_prepare(load_tags, load_tags_stmt, sizeof(load_tags_stmt))) + exit_stmt_err(load_tags); + + memset(bind, 0, sizeof(bind)); + bind[0].buffer_type = MYSQL_TYPE_LONGLONG; + bind[0].buffer = (char *) &mysql_id; + bind[0].is_null = 0; + bind[0].length = 0; + + if (mysql_stmt_bind_param(load_tags, bind)) + exit_stmt_err(load_tags); + + memset(bind, 0, sizeof(tag_bind)); + tag_bind[0].buffer_type = MYSQL_TYPE_STRING; + tag_bind[0].buffer = k; + tag_bind[0].is_null = 0; + tag_bind[0].length = &res_len; + tag_bind[0].error = &res_error; + tag_bind[0].buffer_length = max_tag_len; + tag_bind[1].buffer_type = MYSQL_TYPE_STRING; + tag_bind[1].buffer = v; + tag_bind[1].is_null = 0; + tag_bind[1].length = &res_len; + tag_bind[1].error = &res_error; + tag_bind[1].buffer_length = max_tag_len; + if (mysql_stmt_bind_result(load_tags, tag_bind)) + exit_stmt_err(load_tags); + + if (mysql_query(d->mysql, "SELECT id, user_id, timestamp " + "FROM current_ways WHERE visible")) + exit_mysql_err(d->mysql); + + res = mysql_use_result(d->mysql); + if (!res) exit_mysql_err(d->mysql); + + while ((row = mysql_fetch_row(res))) { + uint64_t id; + const char *user_id, *timestamp; + + id = parse(row[0]); + user_id = row[1]; + timestamp = row[2]; + + mysql_id = (long long) id; + + if (mysql_stmt_execute(load_segs)) + exit_stmt_err(load_segs); + + if (mysql_stmt_store_result(load_segs)) + exit_stmt_err(load_segs); + + list segs; + while (!mysql_stmt_fetch(load_segs)) { + if (((uint64_t) mysql_seg_id) >= d->segs_len) continue; + segs.push_back(d->segs[mysql_seg_id]); + d->rem_segs[mysql_seg_id] = 0; + } + + list > node_lists; + while (segs.size()) { + list node_list; + node_list.push_back(segs.front().from); + node_list.push_back(segs.front().to); + segs.pop_front(); + while (true) { + bool found = false; + for (list::iterator it = segs.begin(); + it != segs.end(); ) { + if (it->from == node_list.back()) { + node_list.push_back(it->to); + segs.erase(it++); + found = true; + } else if (it->to == node_list.front()) { + node_list.insert(node_list.begin(), it->from); + segs.erase(it++); + found = true; + } else { + ++it; + } + } + if (!found) break; + } + node_lists.push_back(node_list); + } + + vector ids; ids.reserve(node_lists.size()); + bool orig_id_used = false; + for (list >::iterator it = node_lists.begin(); + it != node_lists.end(); ++it) { + uint64_t way_id; + int sid; + if (orig_id_used) { + way_id = d->new_way_id++; + } else { + way_id = id; + orig_id_used = true; + } + ids.push_back(way_id); + + fprintf(d->ways, "\"" F_U64 "\",", way_id); + write_csv_col(d->ways, user_id, ','); + write_csv_col(d->ways, timestamp, '\n'); + + sid = 1; + for (list::iterator nit = it->begin(); + nit != it->end(); ++nit) { + fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, *nit, sid++); + } + } + + if (mysql_stmt_execute(load_tags)) + exit_stmt_err(load_tags); + + if (mysql_stmt_store_result(load_tags)) + exit_stmt_err(load_tags); + + bool multiple_parts = ids.size() > 1, + create_multipolygon = false; + + while (!mysql_stmt_fetch(load_tags)) { + if (multiple_parts && !create_multipolygon) { + if (!strcmp(k, "natural")) { + if (strcmp(v, "coastline")) { + create_multipolygon = true; + } + } else if (!strcmp(k, "waterway")) { + if (!strcmp(v, "riverbank")) { + create_multipolygon = true; + } + } else if (!strcmp(k, "leisure") || !strcmp(k, "landuse") + || !strcmp(k, "sport") || !strcmp(k, "amenity") + || !strcmp(k, "tourism") || !strcmp(k, "building")) { + create_multipolygon = true; + } + } + + for (vector::iterator it = ids.begin(); + it != ids.end(); ++it) { + fprintf(d->way_tags, "\"" F_U64 "\",", *it); + write_csv_col(d->way_tags, k, ','); + write_csv_col(d->way_tags, v, '\n'); + } + } + + if (multiple_parts && create_multipolygon) { + uint64_t ent_id = d->new_relation_id++; + + fprintf(d->relations, "\"" F_U64 "\",", ent_id); + write_csv_col(d->relations, user_id, ','); + write_csv_col(d->relations, timestamp, '\n'); + + fprintf(d->relation_tags, + "\"" F_U64 "\",\"type\",\"multipolygon\"\n", ent_id); + + for (vector::iterator it = ids.begin(); + it != ids.end(); ++it) { + fprintf(d->relation_members, + "\"" F_U64 "\",\"way\",\"" F_U64 "\",\"\"\n", ent_id, *it); + } + } + } + if (mysql_errno(d->mysql)) exit_stmt_err(load_tags); + + mysql_stmt_close(load_segs); + mysql_stmt_close(load_tags); + + mysql_free_result(res); + free(k); + free(v); +} + +static int read_seg_tags(char **tags, char **k, char **v) { + if (!**tags) return 0; + char *i = strchr(*tags, ';'); + if (!i) i = *tags + strlen(*tags); + char *j = strchr(*tags, '='); + *k = *tags; + if (j && j < i) { + *v = j + 1; + } else { + *v = i; + } + *tags = *i ? i + 1 : i; + *i = '\0'; + if (j) *j = '\0'; + return 1; +} + +static void mark_tagged_segs(struct data *d) { + MYSQL_RES *res; + MYSQL_ROW row; + + if (mysql_query(d->mysql, "SELECT id, tags FROM current_segments " + "WHERE visible && tags != '' && tags != 'created_by=JOSM'")) + exit_mysql_err(d->mysql); + + res = mysql_use_result(d->mysql); + if (!res) exit_mysql_err(d->mysql); + + while ((row = mysql_fetch_row(res))) { + size_t id = parse(row[0]); + if (d->rem_segs[id]) continue; + char *tags_it = row[1], *k, *v; + while (read_seg_tags(&tags_it, &k, &v)) { + if (strcmp(k, "created_by") && + strcmp(k, "tiger:county") && + strcmp(k, "tiger:upload_uuid") && + strcmp(k, "converted_by") && + (strcmp(k, "natural") || strcmp(v, "coastline")) && + (strcmp(k, "source") || strcmp(v, "PGS"))) { + d->rem_segs[id] = 1; + break; + } + } + } + + mysql_free_result(res); +} + +static void convert_remaining_segs(struct data *d) { + MYSQL_STMT *load_seg; + MYSQL_BIND args[1], res[3]; + const size_t max_tag_len = 1 << 16; + char *tags, timestamp[100]; + char *k, *v; + int user_id; + long long mysql_id; + unsigned long res_len; + my_bool res_error; + const char load_seg_stmt[] = + "SELECT user_id, tags, CAST(timestamp AS CHAR) FROM current_segments " + "WHERE visible && id = ?"; + + tags = (char *) malloc(max_tag_len); + + load_seg = mysql_stmt_init(d->mysql); + if (!load_seg) exit_mysql_err(d->mysql); + if (mysql_stmt_prepare(load_seg, load_seg_stmt, sizeof(load_seg_stmt))) + exit_stmt_err(load_seg); + + memset(args, 0, sizeof(args)); + args[0].buffer_type = MYSQL_TYPE_LONGLONG; + args[0].buffer = (char *) &mysql_id; + args[0].is_null = 0; + args[0].length = 0; + if (mysql_stmt_bind_param(load_seg, args)) + exit_stmt_err(load_seg); + + memset(res, 0, sizeof(res)); + res[0].buffer_type = MYSQL_TYPE_LONG; + res[0].buffer = (char *) &user_id; + res[0].is_null = 0; + res[0].length = 0; + res[0].error = &res_error; + res[1].buffer_type = MYSQL_TYPE_STRING; + res[1].buffer = tags; + res[1].is_null = 0; + res[1].length = &res_len; + res[1].error = &res_error; + res[1].buffer_length = max_tag_len; + res[2].buffer_type = MYSQL_TYPE_STRING; + res[2].buffer = timestamp; + res[2].is_null = 0; + res[2].length = &res_len; + res[2].error = &res_error; + res[2].buffer_length = sizeof(timestamp); + if (mysql_stmt_bind_result(load_seg, res)) + exit_stmt_err(load_seg); + + for (size_t seg_id = 0; seg_id < d->segs_len; seg_id++) { + if (!d->rem_segs[seg_id]) continue; + segment seg = d->segs[seg_id]; + + mysql_id = seg_id; + if (mysql_stmt_execute(load_seg)) exit_stmt_err(load_seg); + if (mysql_stmt_store_result(load_seg)) exit_stmt_err(load_seg); + + while (!mysql_stmt_fetch(load_seg)) { + uint64_t way_id = d->new_way_id++; + + fprintf(d->ways, "\"" F_U64 "\",\"%i\",", way_id, user_id); + write_csv_col(d->ways, timestamp, '\n'); + + fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, seg.from, 1); + fprintf(d->way_nodes, "\"" F_U64 "\",\"" F_U32 "\",\"%i\"\n", way_id, seg.to, 2); + + char *tags_it = tags; + while (read_seg_tags(&tags_it, &k, &v)) { + fprintf(d->way_tags, "\"" F_U64 "\",", way_id); + write_csv_col(d->way_tags, k, ','); + write_csv_col(d->way_tags, v, '\n'); + } + } + } + + mysql_stmt_close(load_seg); + + free(tags); +} + +static MYSQL *connect_to_mysql(char **argv) { + MYSQL *mysql = mysql_init(NULL); + if (!mysql) exit_mysql_err(mysql); + + if (!mysql_real_connect(mysql, argv[1], argv[2], argv[3], argv[4], + argv[5][0] ? atoi(argv[5]) : 0, argv[6][0] ? argv[6] : NULL, 0)) + exit_mysql_err(mysql); + + if (mysql_set_character_set(mysql, "utf8")) + exit_mysql_err(mysql); + + return mysql; +} + +static void open_file(FILE **f, char *fn) { + *f = fopen(fn, "w+"); + if (!*f) { + perror("fopen"); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char **argv) { + struct data data; + struct data *d = &data; + size_t prefix_len; + char *tempfn; + + if (argc != 8) { - printf("Usage: 006_remove_segments_helper host user passwd database port socket prefix\n"); ++ printf("Usage: 007_remove_segments_helper host user passwd database port socket prefix\n"); + exit(EXIT_FAILURE); + } + + d->mysql = connect_to_mysql(argv); + d->mysql2 = connect_to_mysql(argv); + + prefix_len = strlen(argv[7]); + tempfn = (char *) malloc(prefix_len + 15); + strcpy(tempfn, argv[7]); + + strcpy(tempfn + prefix_len, "ways"); + open_file(&d->ways, tempfn); + + strcpy(tempfn + prefix_len, "way_nodes"); + open_file(&d->way_nodes, tempfn); + + strcpy(tempfn + prefix_len, "way_tags"); + open_file(&d->way_tags, tempfn); + + strcpy(tempfn + prefix_len, "relations"); + open_file(&d->relations, tempfn); + + strcpy(tempfn + prefix_len, "relation_members"); + open_file(&d->relation_members, tempfn); + + strcpy(tempfn + prefix_len, "relation_tags"); + open_file(&d->relation_tags, tempfn); + + free(tempfn); + + find_maxids(d); + populate_segs(d); + convert_ways(d); + mark_tagged_segs(d); + convert_remaining_segs(d); + + mysql_close(d->mysql); + mysql_close(d->mysql2); + + fclose(d->ways); + fclose(d->way_nodes); + fclose(d->way_tags); + + fclose(d->relations); + fclose(d->relation_members); + fclose(d->relation_tags); + + free(d->segs); + free(d->rem_segs); + + exit(EXIT_SUCCESS); +} diff --combined test/fixtures/current_relation_members.yml index 7921ac145,000000000..bddc8a0dd mode 100644,000000..100644 --- a/test/fixtures/current_relation_members.yml +++ b/test/fixtures/current_relation_members.yml @@@ -1,5 -1,0 +1,23 @@@ +t1: ++ id: 1 ++ member_role: "some" ++ member_type: "way" ++ member_id: 3 ++ ++t2: + id: 1 + member_role: "some" + member_type: "node" ++ member_id: 5 ++ ++t3: ++ id: 1 ++ member_role: "some" ++ member_type: "relation" + member_id: 3 ++ ++t4: ++ id: 3 ++ member_role: "some" ++ member_type: "node" ++ member_id: 5 diff --combined test/fixtures/current_relation_tags.yml index ba795b698,000000000..aaf06a397 mode 100644,000000..100644 --- a/test/fixtures/current_relation_tags.yml +++ b/test/fixtures/current_relation_tags.yml @@@ -1,9 -1,0 +1,14 @@@ +t1: + id: 1 + k: test + v: yes + +t2: + id: 2 + k: test + v: yes ++ ++t2: ++ id: 3 ++ k: test ++ v: yes diff --combined test/fixtures/current_relations.yml index 67c8ddb95,000000000..c1f77d428 mode 100644,000000..100644 --- a/test/fixtures/current_relations.yml +++ b/test/fixtures/current_relations.yml @@@ -1,11 -1,0 +1,17 @@@ +visible_relation: + id: 1 + user_id: 1 + timestamp: 2007-01-01 00:00:00 + visible: 1 + +invisible_relation: + id: 2 + user_id: 1 + timestamp: 2007-01-01 00:00:00 + visible: 0 ++ ++used_relation: ++ id: 3 ++ user_id: 1 ++ timestamp: 2007-01-01 00:00:00 ++ visible: 1 diff --combined test/fixtures/relation_members.yml index f424e6a51,000000000..27e8b533a mode 100644,000000..100644 --- a/test/fixtures/relation_members.yml +++ b/test/fixtures/relation_members.yml @@@ -1,6 -1,0 +1,24 @@@ +t1: ++ id: 1 ++ member_role: "some" ++ member_type: "way" ++ member_id: 3 ++ version: 1 ++t2: + id: 1 + member_role: "some" + member_type: "node" ++ member_id: 5 ++ version: 1 ++t3: ++ id: 1 ++ member_role: "some" ++ member_type: "relation" + member_id: 3 + version: 1 ++t4: ++ id: 3 ++ member_role: "some" ++ member_type: "node" ++ member_id: 5 ++ version: 1 diff --combined test/fixtures/relation_tags.yml index d1c69a6d4,000000000..39f4bd5de mode 100644,000000..100644 --- a/test/fixtures/relation_tags.yml +++ b/test/fixtures/relation_tags.yml @@@ -1,11 -1,0 +1,17 @@@ +t1: + id: 1 + k: test + v: yes + version: 1 + +t2: + id: 2 + k: test + v: yes + version: 1 ++ ++t3: ++ id: 3 ++ k: test ++ v: yes ++ version: 1 diff --combined test/fixtures/relations.yml index f1e4026ff,000000000..cf1d1ff56 mode 100644,000000..100644 --- a/test/fixtures/relations.yml +++ b/test/fixtures/relations.yml @@@ -1,13 -1,0 +1,20 @@@ +visible_relation: + id: 1 + user_id: 1 + timestamp: 2007-01-01 00:00:00 + visible: 1 + version: 1 + +invisible_relation: + id: 2 + user_id: 1 + timestamp: 2007-01-01 00:00:00 + visible: 0 + version: 1 ++ ++used_relation: ++ id: 3 ++ user_id: 1 ++ timestamp: 2007-01-01 00:00:00 ++ visible: 1 ++ version: 1 diff --combined test/functional/relation_controller_test.rb index 2893ba956,000000000..8f8b72770 mode 100644,000000..100644 --- a/test/functional/relation_controller_test.rb +++ b/test/functional/relation_controller_test.rb @@@ -1,209 -1,0 +1,209 @@@ +require File.dirname(__FILE__) + '/../test_helper' +require 'relation_controller' + +# Re-raise errors caught by the controller. +class RelationController; def rescue_action(e) raise e end; end + +class RelationControllerTest < Test::Unit::TestCase + api_fixtures + fixtures :relations, :current_relations, :relation_members, :current_relation_members, :relation_tags, :current_relation_tags + set_fixture_class :current_relations => :Relation + set_fixture_class :relations => :OldRelation + + def setup + @controller = RelationController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + end + + def basic_authorization(user, pass) + @request.env["HTTP_AUTHORIZATION"] = "Basic %s" % Base64.encode64("#{user}:#{pass}") + end + + def content(c) + @request.env["RAW_POST_DATA"] = c + end + + # ------------------------------------- + # Test reading relations. + # ------------------------------------- + + def test_read + # check that a visible relation is returned properly + get :read, :id => current_relations(:visible_relation).id + assert_response :success + + # check that an invisible relation is not returned + get :read, :id => current_relations(:invisible_relation).id + assert_response :gone + + # check chat a non-existent relation is not returned + get :read, :id => 0 + assert_response :not_found + + # check the "relations for node" mode - get :relations_for_node, :id => current_nodes(:used_node_1).id ++ get :relations_for_node, :id => current_nodes(:node_used_by_relationship).id + assert_response :success + # FIXME check whether this contains the stuff we want! + if $VERBOSE + print @response.body + end + + # check the "relations for way" mode + get :relations_for_way, :id => current_ways(:used_way).id + assert_response :success + # FIXME check whether this contains the stuff we want! + if $VERBOSE + print @response.body + end + + + # check the "relations for relation" mode - get :relations_for_node, :id => current_relations(:used_relation).id ++ get :relations_for_relation, :id => current_relations(:used_relation).id + assert_response :success + # FIXME check whether this contains the stuff we want! + if $VERBOSE + print @response.body + end + + # check the "full" mode - get :full, :id => current_relations(:relation_using_all).id ++ get :full, :id => current_relations(:visible_relation).id + assert_response :success + # FIXME check whether this contains the stuff we want! + if $VERBOSE + print @response.body + end + end + + # ------------------------------------- + # Test simple relation creation. + # ------------------------------------- + + def test_create + basic_authorization "test@openstreetmap.org", "test" + + # create an relation without members + content "" + put :create + # hope for success + assert_response :success, + "relation upload did not return success status" + # read id of created relation and search for it + relationid = @response.body + checkrelation = Relation.find(relationid) + assert_not_nil checkrelation, + "uploaded relation not found in data base after upload" + # compare values + assert_equal checkrelation.members.length, 0, + "saved relation contains members but should not" + assert_equal checkrelation.tags.length, 1, + "saved relation does not contain exactly one tag" + assert_equal users(:normal_user).id, checkrelation.user_id, + "saved relation does not belong to user that created it" + assert_equal true, checkrelation.visible, + "saved relation is not visible" + # ok the relation is there but can we also retrieve it? + get :read, :id => relationid + assert_response :success + + + # create an relation with a node as member + nid = current_nodes(:used_node_1).id + content "" + + "" + put :create + # hope for success + assert_response :success, + "relation upload did not return success status" + # read id of created relation and search for it + relationid = @response.body + checkrelation = Relation.find(relationid) + assert_not_nil checkrelation, + "uploaded relation not found in data base after upload" + # compare values + assert_equal checkrelation.members.length, 1, + "saved relation does not contain exactly one member" + assert_equal checkrelation.tags.length, 1, + "saved relation does not contain exactly one tag" + assert_equal users(:normal_user).id, checkrelation.user_id, + "saved relation does not belong to user that created it" + assert_equal true, checkrelation.visible, + "saved relation is not visible" + # ok the relation is there but can we also retrieve it? + + get :read, :id => relationid + assert_response :success + + # create an relation with a way and a node as members + nid = current_nodes(:used_node_1).id + wid = current_ways(:used_way).id + content "" + + "" + + "" + put :create + # hope for success + assert_response :success, + "relation upload did not return success status" + # read id of created relation and search for it + relationid = @response.body + checkrelation = Relation.find(relationid) + assert_not_nil checkrelation, + "uploaded relation not found in data base after upload" + # compare values + assert_equal checkrelation.members.length, 2, + "saved relation does not have exactly two members" + assert_equal checkrelation.tags.length, 1, + "saved relation does not contain exactly one tag" + assert_equal users(:normal_user).id, checkrelation.user_id, + "saved relation does not belong to user that created it" + assert_equal true, checkrelation.visible, + "saved relation is not visible" + # ok the relation is there but can we also retrieve it? + get :read, :id => relationid + assert_response :success + + end + + # ------------------------------------- + # Test creating some invalid relations. + # ------------------------------------- + + def test_create_invalid + basic_authorization "test@openstreetmap.org", "test" + + # create a relation with non-existing node as member + content "" + put :create + # expect failure + assert_response :precondition_failed, + "relation upload with invalid node did not return 'precondition failed'" + end + + # ------------------------------------- + # Test deleting relations. + # ------------------------------------- + + def test_delete + return true + + # first try to delete relation without auth + delete :delete, :id => current_relations(:visible_relation).id + assert_response :unauthorized + + # now set auth + basic_authorization("test@openstreetmap.org", "test"); + + # this should work + delete :delete, :id => current_relations(:visible_relation).id + assert_response :success + + # this won't work since the relation is already deleted + delete :delete, :id => current_relations(:invisible_relation).id + assert_response :gone + + # this won't work since the relation never existed + delete :delete, :id => 0 + assert_response :not_found + end + +end diff --combined test/unit/node_test.rb index b3e834370,b3e834370..95321b5cf --- a/test/unit/node_test.rb +++ b/test/unit/node_test.rb @@@ -11,7 -11,7 +11,7 @@@ class NodeTest < Test::Unit::TestCas :user_id => users(:normal_user).id, :visible => 1, :tags => "") -- assert node_template.save_with_history ++ assert node_template.save_with_history! node = Node.find(node_template.id) assert_not_nil node @@@ -44,7 -44,7 +44,7 @@@ node_template.latitude = 12.3456 node_template.longitude = 65.4321 node_template.tags = "updated=yes" -- assert node_template.save_with_history ++ assert node_template.save_with_history! node = Node.find(node_template.id) assert_not_nil node @@@ -76,7 -76,7 +76,7 @@@ assert_not_nil old_node_template node_template.visible = 0 -- assert node_template.save_with_history ++ assert node_template.save_with_history! node = Node.find(node_template.id) assert_not_nil node