# 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
end
if node_ids.length == 0
- render :text => "<osm version='0.4'></osm>", :content_type => "text/xml"
+ render :text => "<osm version='0.5'></osm>", :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 = {}
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
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;
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
+++ /dev/null
-class OldSegmentController < ApplicationController
- require 'xml/libxml'
-
- session :off
- before_filter :check_read_availability
- after_filter :compress_output
-
- def history
- begin
- segment = Segment.find(params[:id])
-
- doc = OSM::API.new.get_xml_doc
-
- segment.old_segments.each do |old_segment|
- doc.root << old_segment.to_xml_node
- end
-
- render :text => doc.to_s, :content_type => "text/xml"
- rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
- rescue
- render :nothing => true, :status => :internal_server_error
- end
- end
-end
--- /dev/null
+class OldWayNodeController < ApplicationController
+end
+++ /dev/null
-class OldWaySegmentController < ApplicationController
-end
--- /dev/null
+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!
+
+ 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!
+
+ 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!
+
+ 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
--- /dev/null
+class RelationMemberController < ApplicationController
+
+
+
+end
--- /dev/null
+class RelationTagController < ApplicationController
+ layout 'site'
+
+ def search
+ @tags = RelationTag.find(:all, :limit => 11, :conditions => ["match(v) against (?)", params[:query][:query].to_s] )
+ end
+
+
+end
class SearchController < ApplicationController
- # Support searching for nodes, segments, ways, or all
+ # Support searching for nodes, ways, or all
# Can search by tag k, v, or both (type->k,value->v)
# Can search by name (k=name,v=....)
def search_ways
do_search(true,false,false)
end
- def search_segments
+ def search_nodes
do_search(false,true,false)
end
- def search_nodes
+ def search_relations
do_search(false,false,true)
end
-
- def do_search(do_ways,do_segments,do_nodes)
+ def do_search(do_ways,do_nodes,do_relations)
type = params['type']
value = params['value']
unless type or value
way_ids = Array.new
ways = Array.new
- segments = Array.new
nodes = Array.new
+ relations = Array.new
# Matching for tags table
- cond_tbl = Array.new
+ cond_way = Array.new
sql = 'id IN (SELECT id FROM current_way_tags WHERE 1=1'
if type
sql += ' AND k=?'
- cond_tbl += [type]
+ cond_way += [type]
end
if value
sql += ' AND v=?'
- cond_tbl += [value]
+ cond_way += [value]
end
sql += ')'
- cond_tbl = [sql] + cond_tbl
+ cond_way = [sql] + cond_way
+
+ # Matching for tags table
+ cond_rel = Array.new
+ sql = 'id IN (SELECT id FROM current_relation_tags WHERE 1=1'
+ if type
+ sql += ' AND k=?'
+ cond_rel += [type]
+ end
+ if value
+ sql += ' AND v=?'
+ cond_rel += [value]
+ end
+ sql += ')'
+ cond_rel = [sql] + cond_rel
# Matching for tags column
if type and value
cond_tags = ['1=1']
end
-
- # First up, look for the ways we want
- if do_ways
- ways = Way.find(:all, :conditions => cond_tbl, :limit => 100)
+ # First up, look for the relations we want
+ if do_relations
+ relations = Relation.find(:all, :conditions => cond_rel, :limit => 100)
end
- # Now, segments matching
- if do_segments
- segments = Segment.find(:all, :conditions => cond_tags, :limit => 500)
+ # then ways
+ if do_ways
+ ways = Way.find(:all, :conditions => cond_way, :limit => 100)
end
# Now, nodes
nodes = Node.find(:all, :conditions => cond_tags, :limit => 2000)
end
- # Fetch any segments needed for our ways (only have matching segments so far)
- segments += Segment.find(ways.collect { |w| w.segs }.uniq)
-
- # Fetch any nodes needed for our segments (only have matching nodes so far)
- nodes += Node.find(segments.collect { |s| [s.node_a, s.node_b] }.flatten.uniq)
+ # Fetch any node needed for our ways (only have matching nodes so far)
+ nodes += Node.find(ways.collect { |w| w.nds }.uniq)
# Print
user_display_name_cache = {}
doc.root << node.to_xml_node(user_display_name_cache)
end
- segments.each do |segment|
- doc.root << segment.to_xml_node(user_display_name_cache)
- end
-
ways.each do |way|
doc.root << way.to_xml_node(user_display_name_cache)
end
+ relations.each do |rel|
+ doc.root << rel.to_xml_node(user_display_name_cache)
+ end
render :text => doc.to_s, :content_type => "text/xml"
end
end
+++ /dev/null
-class SegmentController < ApplicationController
- require 'xml/libxml'
-
- session :off
- before_filter :authorize, :only => [:create, :update, :delete]
- before_filter :check_write_availability, :only => [:create, :update, :delete]
- before_filter :check_read_availability, :except => [:create, :update, :delete]
- after_filter :compress_output
-
- def create
- if request.put?
- segment = Segment.from_xml(request.raw_post, true)
-
- if segment
- if segment.node_a == segment.node_b
- render :nothing => true, :status => :expectation_failed
- elsif !segment.preconditions_ok?
- render :nothing => true, :status => :precondition_failed
- else
- segment.user_id = @user.id
- segment.from_node = Node.find(segment.node_a.to_i)
- segment.to_node = Node.find(segment.node_b.to_i)
- segment.save_with_history!
-
- render :text => segment.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
- segment = Segment.find(params[:id])
-
- if segment.visible
- render :text => segment.to_xml.to_s, :content_type => "text/xml"
- else
- render :nothing => true, :status => :gone
- end
- rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
- end
- end
-
- def update
- begin
- segment = Segment.find(params[:id])
-
- if segment.visible
- new_segment = Segment.from_xml(request.raw_post)
-
- if new_segment and new_segment.id == segment.id
- if new_segment.node_a == new_segment.node_b
- render :nothing => true, :status => :expectation_failed
- elsif !new_segment.preconditions_ok?
- render :nothing => true, :status => :precondition_failed
- else
- segment.user_id = @user.id
- segment.node_a = new_segment.node_a
- segment.node_b = new_segment.node_b
- segment.tags = new_segment.tags
- segment.visible = new_segment.visible
- segment.save_with_history!
-
- 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
- end
- end
-
- def delete
- begin
- segment = Segment.find(params[:id])
-
- if segment.visible
- if WaySegment.find(:first, :joins => "INNER JOIN current_ways ON current_ways.id = current_way_segments.id", :conditions => [ "current_ways.visible = 1 AND current_way_segments.segment_id = ?", segment.id ])
- render :nothing => true, :status => :precondition_failed
- else
- segment.user_id = @user.id
- segment.visible = 0
- segment.save_with_history!
-
- render :nothing => true
- end
- else
- render :nothing => true, :status => :gone
- end
- rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
- end
- end
-
- def segments
- ids = params['segments'].split(',').collect { |s| s.to_i }
-
- if ids.length > 0
- doc = OSM::API.new.get_xml_doc
-
- Segment.find(ids).each do |segment|
- doc.root << segment.to_xml_node
- end
-
- render :text => doc.to_s, :content_type => "text/xml"
- else
- render :nothing => true, :status => :bad_request
- end
- end
-
- def segments_for_node
- segmentids = Segment.find(:all, :conditions => ['node_a = ? OR node_b = ?', params[:id], params[:id]]).collect { |s| s.id }.uniq
-
- if segmentids.length > 0
- doc = OSM::API.new.get_xml_doc
-
- Segment.find(segmentids).each do |segment|
- doc.root << segment.to_xml_node
- end
-
- render :text => doc.to_s, :content_type => "text/xml"
- else
- render :nothing => true, :status => :bad_request
- end
- end
-end
way = Way.find(params[:id])
begin
- node = way.way_segments.first.segment.from_node
+ node = way.way_nodes.first.node
redirect_to :controller => 'site', :action => 'index', :lat => node.latitude, :lon => node.longitude, :zoom => 6
rescue
redirect_to :back
sql="SELECT cn1.latitude*0.0000001 AS lat1,cn1.longitude*0.0000001 AS lon1,"+
" cn2.latitude*0.0000001 AS lat2,cn2.longitude*0.0000001 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 "+OSM.sql_for_area(ymin,xmin,ymax,xmax,"cn1.")+
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!
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!
- render :nothing => true
+ render :nothing => true
+ end
else
render :nothing => true, :status => :gone
end
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :not_found
+ rescue => ex
+ puts ex
end
end
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"
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
--- /dev/null
+class WayNodeController < ApplicationController
+
+
+
+end
+++ /dev/null
-class WaySegmentController < ApplicationController
-
-
-
-end
+++ /dev/null
-module OldSegmentHelper
-end
--- /dev/null
+module OldWayNodeHelper
+end
+++ /dev/null
-module OldWaySegmentHelper
-end
+++ /dev/null
-module SegmentHelper
-end
--- /dev/null
+module WayNodeHelper
+end
+++ /dev/null
-module WaySegmentHelper
-end
--- /dev/null
+class OldRelation < ActiveRecord::Base
+ set_table_name 'relations'
+
+ belongs_to :user
+
+ def self.from_relation(relation)
+ old_relation = OldRelation.new
+ old_relation.visible = relation.visible
+ old_relation.user_id = relation.user_id
+ old_relation.timestamp = relation.timestamp
+ old_relation.id = relation.id
+ old_relation.members = relation.members
+ old_relation.tags = relation.tags
+ return old_relation
+ end
+
+ def save_with_dependencies!
+
+ # see comment in old_way.rb ;-)
+ save!
+ clear_aggregation_cache
+ clear_association_cache
+ @attributes.update(OldRelation.find(:first, :conditions => ['id = ? AND timestamp = ?', self.id, self.timestamp]).instance_variable_get('@attributes'))
+
+ # ok, you can touch from here on
+
+ self.tags.each do |k,v|
+ tag = OldRelationTag.new
+ tag.k = k
+ tag.v = v
+ tag.id = self.id
+ tag.version = self.version
+ tag.save!
+ end
+
+ i = 1
+ self.members.each do |m|
+ member = OldRelationMember.new
+ member.id = self.id
+ member.member_type = m[0]
+ member.member_id = m[1]
+ member.member_role = m[2]
+ member.version = self.version
+ member.save!
+ end
+ end
+
+ def members
+ unless @members
+ @members = Array.new
+ OldRelationMember.find(:all, :conditions => ["id = ? AND version = ?", self.id, self.version]).each do |m|
+ @members += [[m.type,m.id,m.role]]
+ end
+ end
+ @members
+ end
+
+ def tags
+ unless @tags
+ @tags = Hash.new
+ OldRelationTag.find(:all, :conditions => ["id = ? AND version = ?", self.id, self.version]).each do |tag|
+ @tags[tag.k] = tag.v
+ end
+ end
+ @tags = Hash.new unless @tags
+ @tags
+ end
+
+ def members=(s)
+ @members = s
+ end
+
+ def tags=(t)
+ @tags = t
+ end
+
+# has_many :relation_segments, :class_name => 'OldRelationSegment', :foreign_key => 'id'
+# has_many :relation_tags, :class_name => 'OldRelationTag', :foreign_key => 'id'
+
+ def old_members
+ OldRelationMember.find(:all, :conditions => ['id = ? AND version = ?', self.id, self.version])
+ end
+
+ def old_tags
+ OldRelationTag.find(:all, :conditions => ['id = ? AND version = ?', self.id, self.version])
+ end
+
+ def to_xml_node
+ el1 = XML::Node.new 'relation'
+ el1['id'] = self.id.to_s
+ el1['visible'] = self.visible.to_s
+ el1['timestamp'] = self.timestamp.xmlschema
+ el1['user'] = self.user.display_name if self.user.data_public?
+
+ self.old_members.each do |member|
+ e = XML::Node.new 'member'
+ e['type'] = member.member_type.to_s
+ e['ref'] = member.member_id.to_s # "id" is considered uncool here as it should be unique in XML
+ e['role'] = member.member_role.to_s
+ el1 << e
+ end
+
+ self.old_tags.each do |tag|
+ e = XML::Node.new 'tag'
+ e['k'] = tag.k
+ e['v'] = tag.v
+ el1 << e
+ end
+ return el1
+ end
+end
--- /dev/null
+class OldRelationMember < ActiveRecord::Base
+ belongs_to :user
+
+ set_table_name 'relation_members'
+
+end
--- /dev/null
+class OldRelationTag < ActiveRecord::Base
+ belongs_to :user
+
+ set_table_name 'relation_tags'
+
+end
+++ /dev/null
-class OldSegment < ActiveRecord::Base
- set_table_name 'segments'
-
- validates_presence_of :user_id, :timestamp
- validates_inclusion_of :visible, :in => [ true, false ]
- validates_numericality_of :node_a, :node_b
-
- belongs_to :user
-
- def self.from_segment(segment)
- old_segment = OldSegment.new
- old_segment.node_a = segment.node_a
- old_segment.node_b = segment.node_b
- old_segment.visible = segment.visible
- old_segment.tags = segment.tags
- old_segment.timestamp = segment.timestamp
- old_segment.user_id = segment.user_id
- old_segment.id = segment.id
- return old_segment
- end
-
- def to_xml_node
- el1 = XML::Node.new 'segment'
- el1['id'] = self.id.to_s
- el1['from'] = self.node_a.to_s
- el1['to'] = self.node_b.to_s
- el1['user'] = self.user.display_name if self.user.data_public?
- Segment.split_tags(el1, self.tags)
- el1['visible'] = self.visible.to_s
- el1['timestamp'] = self.timestamp.xmlschema
- return el1
- end
-end
old_way.user_id = way.user_id
old_way.timestamp = way.timestamp
old_way.id = way.id
- old_way.segs = way.segs
+ old_way.nds = way.nds
old_way.tags = way.tags
return old_way
end
end
i = 1
- self.segs.each do |n|
- seg = OldWaySegment.new
- seg.id = self.id
- seg.segment_id = n
- seg.version = self.version
- seg.save!
+ self.nds.each do |n|
+ nd = OldWayNode.new
+ nd.id = self.id
+ nd.node_id = n
+ nd.sequence_id = i
+ nd.version = self.version
+ nd.save!
+ i += 1
end
end
- def segs
- unless @segs
- @segs = Array.new
- OldWaySegment.find(:all, :conditions => ["id = ? AND version = ?", self.id, self.version], :order => "sequence_id").each do |seg|
- @segs += [seg.segment_id]
+ def nds
+ unless @nds
+ @nds = Array.new
+ OldWayNode.find(:all, :conditions => ["id = ? AND version = ?", self.id, self.version], :order => "sequence_id").each do |nd|
+ @nds += [nd.node_id]
end
end
- @segs
+ @nds
end
def tags
@tags
end
- def segs=(s)
- @segs = s
+ def nds=(s)
+ @nds = s
end
def tags=(t)
@tags = t
end
-# has_many :way_segments, :class_name => 'OldWaySegment', :foreign_key => 'id'
+# has_many :way_nodes, :class_name => 'OldWayNode', :foreign_key => 'id'
# has_many :way_tags, :class_name => 'OldWayTag', :foreign_key => 'id'
- def old_segments
- OldWaySegment.find(:all, :conditions => ['id = ? AND version = ?', self.id, self.version])
+ def old_nodes
+ OldWayNode.find(:all, :conditions => ['id = ? AND version = ?', self.id, self.version])
end
def old_tags
el1['timestamp'] = self.timestamp.xmlschema
el1['user'] = self.user.display_name if self.user.data_public?
- self.old_segments.each do |seg| # FIXME need to make sure they come back in the right order
- e = XML::Node.new 'seg'
- e['id'] = seg.segment_id.to_s
+ self.old_nodes.each do |nd| # FIXME need to make sure they come back in the right order
+ e = XML::Node.new 'nd'
+ e['id'] = nd.node_id.to_s
el1 << e
end
--- /dev/null
+class OldWayNode < ActiveRecord::Base
+ belongs_to :user
+
+ set_table_name 'way_nodes'
+
+end
+++ /dev/null
-class OldWaySegment < ActiveRecord::Base
- belongs_to :user
-
- set_table_name 'way_segments'
-
-end
--- /dev/null
+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!
+ 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
+
+ RelationMember.delete_all(['id = ?', self.id])
+
+ 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
+
+ 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
--- /dev/null
+class RelationMember < ActiveRecord::Base
+ set_table_name 'current_relation_members'
+
+ # problem with RelationMember is that it may link to any one
+ # object (a node, a way, another relation), and belongs_to is
+ # not flexible enough for that. So we do this, which is ugly,
+ # but fortunately rails won't actually run the SQL behind that
+ # unless someone really accesses .node, .way, or
+ # .relation - which is what we do below based on member_type.
+ # (and no: the :condition on belongs_to doesn't work here as
+ # it is a condition on the *referenced* object not the
+ # *referencing* object!)
+
+ belongs_to :node, :foreign_key => "member_id"
+ belongs_to :way, :foreign_key => "member_id"
+ belongs_to :relation, :foreign_key => "member_id"
+
+ # so we define this "member" function that returns whatever it
+ # is.
+
+ def member()
+ return (member_type == "node") ? node : (member_type == "way") ? way : relation
+ end
+
+ # NOTE - relations are SUBJECTS of memberships. The fact that nodes,
+ # ways, and relations can be the OBJECT of a membership,
+ # i.e. a node/way/relation can be referenced throgh a
+ # RelationMember object, is NOT modelled in rails, i.e. these links
+ # have to be resolved manually, on demand.
+end
--- /dev/null
+class RelationTag < ActiveRecord::Base
+ set_table_name 'current_relation_tags'
+
+ belongs_to :relation, :foreign_key => 'id'
+
+end
+++ /dev/null
-class Segment < ActiveRecord::Base
- require 'xml/libxml'
- set_table_name 'current_segments'
-
- validates_presence_of :user_id, :timestamp
- validates_inclusion_of :visible, :in => [ true, false ]
- validates_numericality_of :node_a, :node_b
-
- has_many :old_segments, :foreign_key => :id
- belongs_to :user
-
- # using belongs_to :foreign_key = 'node_*', since if use has_one :foreign_key = 'id', segment preconditions? fails checking for segment id in node table
- belongs_to :from_node, :class_name => 'Node', :foreign_key => 'node_a'
- belongs_to :to_node, :class_name => 'Node', :foreign_key => 'node_b'
-
- def self.from_xml(xml, create=false)
- begin
- p = XML::Parser.new
- p.string = xml
- doc = p.parse
-
- segment = Segment.new
-
- doc.find('//osm/segment').each do |pt|
- segment.node_a = pt['from'].to_i
- segment.node_b = pt['to'].to_i
-
- unless create
- if pt['id'] != '0'
- segment.id = pt['id'].to_i
- end
- end
-
- segment.visible = true
-
- if create
- segment.timestamp = Time.now
- else
- if pt['timestamp']
- segment.timestamp = Time.parse(pt['timestamp'])
- end
- end
-
- tags = []
-
- pt.find('tag').each do |tag|
- tags << [tag['k'],tag['v']]
- end
-
- tags = tags.collect { |k,v| "#{k}=#{v}" }.join(';')
- tags = '' if tags.nil?
-
- segment.tags = tags
- end
- rescue
- segment = nil
- end
-
- return segment
- end
-
- def save_with_history!
- Segment.transaction do
- self.timestamp = Time.now
- self.save!
- old_segment = OldSegment.from_segment(self)
- old_segment.save!
- end
- 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 'segment'
- el1['id'] = self.id.to_s
- el1['from'] = self.node_a.to_s
- el1['to'] = self.node_b.to_s
-
- 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?
-
- Segment.split_tags(el1, self.tags)
- el1['visible'] = self.visible.to_s
- el1['timestamp'] = self.timestamp.xmlschema
- return el1
- end
-
- def self.split_tags(el, tags)
- tags.split(';').each do |tag|
- parts = tag.split('=')
- key = ''
- val = ''
- key = parts[0].strip unless parts[0].nil?
- val = parts[1].strip unless parts[1].nil?
- if key != '' && val != ''
- el2 = XML::Node.new('tag')
- el2['k'] = key.to_s
- el2['v'] = val.to_s
- el << el2
- end
- end
- end
-
- def preconditions_ok?
- from_node and from_node.visible and to_node and to_node.visible
- end
-
-end
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'
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
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
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
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
@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)
end
end
- WaySegment.transaction do
- segs = self.segs
+ WayNode.transaction do
+ nds = self.nds
- WaySegment.delete_all(['id = ?', self.id])
+ WayNode.delete_all(['id = ?', self.id])
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
end
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
--- /dev/null
+class WayNode < ActiveRecord::Base
+ set_table_name 'current_way_nodes'
+
+ belongs_to :node
+end
+++ /dev/null
-class WaySegment < ActiveRecord::Base
- set_table_name 'current_way_segments'
-
- belongs_to :segment
-end
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
# API
map.connect "api/#{API_VERSION}/node/create", :controller => 'node', :action => 'create'
- map.connect "api/#{API_VERSION}/node/:id/segments", :controller => 'segment', :action => 'segments_for_node', :id => /\d+/
+ map.connect "api/#{API_VERSION}/node/:id/ways", :controller => 'way', :action => 'ways_for_node', :id => /\d+/
+ map.connect "api/#{API_VERSION}/node/:id/relations", :controller => 'relation', :action => 'relations_for_node', :id => /\d+/
map.connect "api/#{API_VERSION}/node/:id/history", :controller => 'old_node', :action => 'history', :id => /\d+/
map.connect "api/#{API_VERSION}/node/:id", :controller => 'node', :action => 'read', :id => /\d+/, :conditions => { :method => :get }
map.connect "api/#{API_VERSION}/node/:id", :controller => 'node', :action => 'update', :id => /\d+/, :conditions => { :method => :put }
map.connect "api/#{API_VERSION}/node/:id", :controller => 'node', :action => 'delete', :id => /\d+/, :conditions => { :method => :delete }
map.connect "api/#{API_VERSION}/nodes", :controller => 'node', :action => 'nodes', :id => nil
- map.connect "api/#{API_VERSION}/segment/create", :controller => 'segment', :action => 'create'
- map.connect "api/#{API_VERSION}/segment/:id/ways", :controller => 'way', :action => 'ways_for_segment', :id => /\d+/
- map.connect "api/#{API_VERSION}/segment/:id/history", :controller => 'old_segment', :action => 'history', :id => /\d+/
- map.connect "api/#{API_VERSION}/segment/:id", :controller => 'segment', :action => 'read', :id => /\d+/, :conditions => { :method => :get }
- map.connect "api/#{API_VERSION}/segment/:id", :controller => 'segment', :action => 'update', :id => /\d+/, :conditions => { :method => :put }
- map.connect "api/#{API_VERSION}/segment/:id", :controller => 'segment', :action => 'delete', :id => /\d+/, :conditions => { :method => :delete }
- map.connect "api/#{API_VERSION}/segments", :controller => 'segment', :action => 'segments', :id => nil
-
map.connect "api/#{API_VERSION}/way/create", :controller => 'way', :action => 'create'
map.connect "api/#{API_VERSION}/way/:id/history", :controller => 'old_way', :action => 'history', :id => /\d+/
map.connect "api/#{API_VERSION}/way/:id/full", :controller => 'way', :action => 'full', :id => /\d+/
+ map.connect "api/#{API_VERSION}/way/:id/relations", :controller => 'relation', :action => 'relations_for_way', :id => /\d+/
map.connect "api/#{API_VERSION}/way/:id", :controller => 'way', :action => 'read', :id => /\d+/, :conditions => { :method => :get }
map.connect "api/#{API_VERSION}/way/:id", :controller => 'way', :action => 'update', :id => /\d+/, :conditions => { :method => :put }
map.connect "api/#{API_VERSION}/way/:id", :controller => 'way', :action => 'delete', :id => /\d+/, :conditions => { :method => :delete }
map.connect "api/#{API_VERSION}/ways", :controller => 'way', :action => 'ways', :id => nil
map.connect "api/#{API_VERSION}/capabilities", :controller => 'api', :action => 'capabilities'
+ map.connect "api/#{API_VERSION}/relation/create", :controller => 'relation', :action => 'create'
+ map.connect "api/#{API_VERSION}/relation/:id/relations", :controller => 'relation', :action => 'relations_for_relation', :id => /\d+/
+ map.connect "api/#{API_VERSION}/relation/:id/history", :controller => 'old_relation', :action => 'history', :id => /\d+/
+ map.connect "api/#{API_VERSION}/relation/:id/full", :controller => 'relation', :action => 'full', :id => /\d+/
+ map.connect "api/#{API_VERSION}/relation/:id", :controller => 'relation', :action => 'read', :id => /\d+/, :conditions => { :method => :get }
+ map.connect "api/#{API_VERSION}/relation/:id", :controller => 'relation', :action => 'update', :id => /\d+/, :conditions => { :method => :put }
+ map.connect "api/#{API_VERSION}/relation/:id", :controller => 'relation', :action => 'delete', :id => /\d+/, :conditions => { :method => :delete }
+ map.connect "api/#{API_VERSION}/relations", :controller => 'relation', :action => 'relations', :id => nil
map.connect "api/#{API_VERSION}/map", :controller => 'api', :action => 'map'
map.connect "api/#{API_VERSION}/search", :controller => 'search', :action => 'search_all'
map.connect "api/#{API_VERSION}/ways/search", :controller => 'search', :action => 'search_ways'
- map.connect "api/#{API_VERSION}/segments/search", :controller => 'search', :action => 'search_segments'
+ map.connect "api/#{API_VERSION}/relations/search", :controller => 'search', :action => 'search_relations'
map.connect "api/#{API_VERSION}/nodes/search", :controller => 'search', :action => 'search_nodes'
map.connect "api/#{API_VERSION}/user/details", :controller => 'user', :action => 'api_details'
--- /dev/null
+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
--- /dev/null
+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, "008_remove_segments.#{$$}."
+
+ cmd = "db/migrate/008_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]
+ add_index :current_way_nodes, [:node_id], :name => "current_way_nodes_node_idx"
+
+ 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 INFILE '#{ways}' INTO TABLE ways #{csvopts} (id, user_id, timestamp) SET visible = 1, version = 1"
+ execute "LOAD DATA INFILE '#{way_nodes}' INTO TABLE way_nodes #{csvopts} (id, node_id, sequence_id) SET version = 1"
+ execute "LOAD DATA 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
+
+ if have_segs
+ execute "LOAD DATA INFILE '#{relations}' INTO TABLE relations #{csvopts} (id, user_id, timestamp) SET visible = 1, version = 1"
+ execute "LOAD DATA INFILE '#{relation_members}' INTO TABLE relation_members #{csvopts} (id, member_type, member_id, member_role) SET version = 1"
+ execute "LOAD DATA 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
--- /dev/null
+#include <mysql.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vector>
+#include <list>
+#include <sstream>
+#include <map>
+#include <string>
+
+#ifdef __amd64__
+
+#define F_U64 "%lu"
+#define F_U32 "%u"
+
+#else
+
+#define F_U64 "%Lu"
+#define F_U32 "%u"
+
+#endif
+
+using namespace std;
+
+template <typename T>
+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, "008_remove_segments_helper: MySQL error: %s\n", err);
+ } else {
+ fprintf(stderr, "008_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, "008_remove_segments_helper: MySQL stmt error: %s\n", err);
+ } else {
+ fprintf(stderr, "008_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;
+ unsigned char *tgd_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<uint64_t>(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);
+ d->tgd_segs = (unsigned char *) malloc(d->segs_len);
+ memset(d->rem_segs, 0, d->segs_len);
+ memset(d->tgd_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<size_t>(row[0]);
+ if (id >= d->segs_len) continue;
+ d->segs[id].from = parse<uint32_t>(row[1]);
+ d->segs[id].to = parse<uint32_t>(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(seg_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(tag_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<uint64_t>(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<segment> 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<list<uint32_t> > node_lists;
+ while (segs.size()) {
+ list<uint32_t> 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<segment>::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<uint64_t> ids; ids.reserve(node_lists.size());
+ bool orig_id_used = false;
+ for (list<list<uint32_t> >::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<uint32_t>::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<uint64_t>::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<uint64_t>::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;
+ MYSQL_STMT *way_tags;
+ const char
+ way_tags_stmt[] = "SELECT k, v FROM current_way_segments INNER JOIN "
+ "current_way_tags ON current_way_segments.id = "
+ "current_way_tags.id WHERE segment_id = ?";
+ char *wk, *wv;
+ const size_t max_tag_len = 1 << 16;
+ long long mysql_seg_id;
+ unsigned long res_len;
+ my_bool res_error;
+ MYSQL_BIND in_bind[1], out_bind[1];
+
+ /* 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. */
+ wk = (char *) malloc(max_tag_len);
+ wv = (char *) malloc(max_tag_len);
+
+ way_tags = mysql_stmt_init(d->mysql2);
+ if (!way_tags) exit_mysql_err(d->mysql2);
+ if (mysql_stmt_prepare(way_tags, way_tags_stmt, sizeof(way_tags_stmt)))
+ exit_stmt_err(way_tags);
+
+ memset(in_bind, 0, sizeof(in_bind));
+ in_bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
+ in_bind[0].buffer = (char *) &mysql_seg_id;
+ in_bind[0].is_null = 0;
+ in_bind[0].length = 0;
+
+ if (mysql_stmt_bind_param(way_tags, in_bind))
+ exit_stmt_err(way_tags);
+
+ memset(out_bind, 0, sizeof(out_bind));
+ out_bind[0].buffer_type = MYSQL_TYPE_STRING;
+ out_bind[0].buffer = wk;
+ out_bind[0].is_null = 0;
+ out_bind[0].length = &res_len;
+ out_bind[0].error = &res_error;
+ out_bind[0].buffer_length = max_tag_len;
+ out_bind[1].buffer_type = MYSQL_TYPE_STRING;
+ out_bind[1].buffer = wv;
+ out_bind[1].is_null = 0;
+ out_bind[1].length = &res_len;
+ out_bind[1].error = &res_error;
+ out_bind[1].buffer_length = max_tag_len;
+ if (mysql_stmt_bind_result(way_tags, out_bind))
+ exit_stmt_err(way_tags);
+
+ 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<size_t>(row[0]);
+ if (d->rem_segs[id]) continue;
+
+ map<string, string> interesting_tags;
+
+ 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, "width") || strcmp(v, "4")) &&
+ (strcmp(k, "natural") || strcmp(v, "coastline")) &&
+ (strcmp(k, "source") || strncmp(v, "PGS", 3))) {
+ interesting_tags.insert(make_pair(string(k), string(v)));
+ }
+ }
+
+ if (interesting_tags.size() == 0) continue;
+
+ mysql_seg_id = id;
+
+ if (mysql_stmt_execute(way_tags))
+ exit_stmt_err(way_tags);
+
+ if (mysql_stmt_store_result(way_tags))
+ exit_stmt_err(way_tags);
+
+ while (!mysql_stmt_fetch(way_tags)) {
+ for (map<string, string>::iterator it = interesting_tags.find(wk);
+ it != interesting_tags.end() && it->first == wk; ++it) {
+ if (it->second == wv) {
+ interesting_tags.erase(it);
+ break;
+ }
+ }
+ }
+
+ if (interesting_tags.size() > 0) {
+ d->rem_segs[id] = 1;
+ d->tgd_segs[id] = 1;
+ }
+ }
+
+ mysql_free_result(res);
+
+ mysql_stmt_close(way_tags);
+ free(wk);
+ free(wv);
+}
+
+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;
+ char notetmp[1024];
+ 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;
+ const char *what = d->tgd_segs[seg_id] ? "tagged" : "unwayed";
+ 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;
+ bool note = false;
+ while (read_seg_tags(&tags_it, &k, &v)) {
+ fprintf(d->way_tags, "\"" F_U64 "\",", way_id);
+ write_csv_col(d->way_tags, k, ',');
+ if(!strcmp(k,"note")) {
+ snprintf(notetmp, sizeof(notetmp), "%s; FIXME previously %s segment", v, what);
+ note = true;
+ write_csv_col(d->way_tags, notetmp, '\n');
+ } else {
+ write_csv_col(d->way_tags, v, '\n');
+ }
+ }
+ if (!note) {
+ sprintf(notetmp, "FIXME previously %s segment", what);
+ fprintf(d->way_tags, "\"" F_U64 "\",", way_id);
+ write_csv_col(d->way_tags, "note", ',');
+ write_csv_col(d->way_tags, notetmp, '\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: 008_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);
+ free(d->tgd_segs);
+
+ exit(EXIT_SUCCESS);
+}
user_count = User.count(:conditions => "active = true")
tracepoint_count = Tracepoint.count()
node_count = Node.count(:conditions => "visible = true")
- segment_count = Segment.count(:conditions => "visible = true")
way_count = Way.count(:conditions => "visible = true")
tagged_way_count = Way.count(:conditions => "visible = true AND EXISTS (SELECT * FROM current_way_tags WHERE id = current_ways.id AND k <> 'created_by')")
puts "<tr><td>Number of users</td><td>#{user_count}</td></tr>"
puts "<tr><td>Number of uploaded GPS points</td><td>#{tracepoint_count}</td></tr>"
puts "<tr><td>Number of nodes</td><td>#{node_count}</td></tr>"
- puts "<tr><td>Number of segments</td><td>#{segment_count}</td></tr>"
puts "<tr><td>Number of ways</td><td>#{way_count}</td></tr>"
puts "<tr><td>Number of ways with tags</td><td>#{tagged_way_count}</td></tr>"
puts "</table>"
--- /dev/null
+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
--- /dev/null
+t1:
+ id: 1
+ k: test
+ v: yes
+
+t2:
+ id: 2
+ k: test
+ v: yes
+
+t2:
+ id: 3
+ k: test
+ v: yes
--- /dev/null
+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
+++ /dev/null
-# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-visible_segment:
- id: 1
- node_a: 3
- node_b: 4
- user_id: 1
- visible: 1
- tags: test=yes
- timestamp: 2007-01-01 00:00:00
-
-invisible_segment:
- id: 2
- node_a: 3
- node_b: 4
- user_id: 1
- visible: 0
- tags: test=yes
- timestamp: 2007-01-01 00:00:00
-
-used_segment:
- id: 3
- node_a: 3
- node_b: 4
- user_id: 1
- visible: 1
- tags: test=yes
- timestamp: 2007-01-01 00:00:00
--- /dev/null
+t1:
+ id: 1
+ node_id: 3
+ sequence_id: 1
+
+t2:
+ id: 2
+ node_id: 3
+ sequence_id: 1
+
+t3:
+ id: 3
+ node_id: 3
+ sequence_id: 1
+++ /dev/null
-t1:
- id: 1
- segment_id: 3
- sequence_id: 1
-
-t2:
- id: 2
- segment_id: 3
- sequence_id: 1
k: test
v: yes
-t1:
+t2:
id: 2
k: test
v: yes
+
+t3:
+ id: 3
+ k: test
+ v: yes
+
user_id: 1
timestamp: 2007-01-01 00:00:00
visible: 0
+
+used_way:
+ id: 3
+ user_id: 1
+ timestamp: 2007-01-01 00:00:00
+ visible: 1
+
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
+++ /dev/null
-# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-visible_segment:
- id: 1
- node_a: 3
- node_b: 4
- user_id: 1
- visible: 1
- tags: test=yes
- timestamp: 2007-01-01 00:00:00
-
-invisible_segment:
- id: 2
- node_a: 3
- node_b: 4
- user_id: 1
- visible: 0
- tags: test=yes
- timestamp: 2007-01-01 00:00:00
-
-used_segment:
- id: 3
- node_a: 3
- node_b: 4
- user_id: 1
- visible: 1
- tags: test=yes
- timestamp: 2007-01-01 00:00:00
t1:
id: 1
- segment_id: 3
+ node_id: 3
sequence_id: 1
version: 1
t2:
id: 2
- segment_id: 3
+ node_id: 3
+ sequence_id: 1
+ version: 1
+
+t3:
+ id: 3
+ node_id: 3
sequence_id: 1
version: 1
k: test
v: yes
version: 1
+
+t3:
+ id: 3
+ k: test
+ v: yes
+ version: 1
timestamp: 2007-01-01 00:00:00
visible: 0
version: 1
+
+used_way:
+ id: 3
+ user_id: 1
+ timestamp: 2007-01-01 00:00:00
+ visible: 0
+ version: 1
+
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+require 'api_controller'
+
+# Re-raise errors caught by the controller.
+class ApiController; def rescue_action(e) raise e end; end
+
+class ApiControllerTest < Test::Unit::TestCase
+ api_fixtures
+
+ def setup
+ @controller = ApiController.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
+
+ # -------------------------------------
+ # Test reading a bounding box.
+ # -------------------------------------
+
+ def test_map
+ node = current_nodes(:used_node_1)
+ bbox = "#{node.latitude-0.1},#{node.longitude-0.1},#{node.latitude+0.1},#{node.longitude+0.1}"
+ get :map, :bbox => bbox
+ if $VERBOSE
+ print @response.body
+ end
+ assert_response :success
+ end
+
+end
--- /dev/null
+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(: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_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(: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 "<osm><relation><tag k='test' v='yes' /></relation></osm>"
+ 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 "<osm><relation><member type='node' ref='#{nid}' role='some'/>" +
+ "<tag k='test' v='yes' /></relation></osm>"
+ 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 "<osm><relation><member type='node' ref='#{nid}' role='some'/>" +
+ "<member type='way' ref='#{wid}' role='other'/>" +
+ "<tag k='test' v='yes' /></relation></osm>"
+ 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 "<osm><relation><member type='node' ref='0'/><tag k='test' v='yes' /></relation></osm>"
+ 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
+++ /dev/null
-require File.dirname(__FILE__) + '/../test_helper'
-require 'segment_controller'
-
-# Re-raise errors caught by the controller.
-class SegmentController; def rescue_action(e) raise e end; end
-
-class SegmentControllerTest < Test::Unit::TestCase
- api_fixtures
-
- def setup
- @controller = SegmentController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- end
-
- def test_create
- # cannot read password from fixture as it is stored as MD5 digest
- basic_authorization("test@openstreetmap.org", "test");
- na = current_nodes(:used_node_1).id
- nb = current_nodes(:used_node_2).id
- content("<osm><segment from='#{na}' to='#{nb}' /></osm>")
- put :create
- # hope for success
- assert_response :success, "segment upload did not return success status"
- # read id of created segment and search for it
- segmentid = @response.body
- checksegment = Segment.find(segmentid)
- assert_not_nil checksegment, "uploaded segment not found in data base after upload"
- # compare values
- assert_equal na, checksegment.node_a, "saved segment does not match requested from-node"
- assert_equal nb, checksegment.node_b, "saved segment does not match requested to-node"
- assert_equal users(:normal_user).id, checksegment.user_id, "saved segment does not belong to user that created it"
- assert_equal true, checksegment.visible, "saved segment is not visible"
- end
-
- def test_create_invalid
- basic_authorization("test@openstreetmap.org", "test");
- # create a segment with one invalid node
- na = current_nodes(:used_node_1).id
- nb = 0
- content("<osm><segment from='#{na}' to='#{nb}' /></osm>")
- put :create
- # expect failure
- assert_response :precondition_failed, "upload of invalid segment did not return 'precondition failed'"
- end
-
- def test_read
- # check that a visible segment is returned properly
- get :read, :id => current_segments(:visible_segment).id
- assert_response :success
- # TODO: check for <segment> tag in return data
-
- # check that an invisible segment is not returned
- get :read, :id => current_segments(:invisible_segment).id
- assert_response :gone
-
- # check chat a non-existent segment is not returned
- get :read, :id => 0
- assert_response :not_found
- end
-
- # this tests deletion restrictions - basic deletion is tested in the unit
- # tests for segment!
- def test_delete
-
- # first try to delete segment without auth
- delete :delete, :id => current_segments(:visible_segment).id
- assert_response :unauthorized
-
- # now set auth
- basic_authorization("test@openstreetmap.org", "test");
-
- # this should work
- delete :delete, :id => current_segments(:visible_segment).id
- assert_response :success
-
- # this won't work since the segment is already deleted
- delete :delete, :id => current_segments(:invisible_segment).id
- assert_response :gone
-
- # this won't work since the segment never existed
- delete :delete, :id => 0
- assert_response :not_found
-
- # this won't work since the segment is in use
- delete :delete, :id => current_segments(:used_segment).id
- assert_response :precondition_failed
- 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
-end
class WayController; def rescue_action(e) raise e end; end
class WayControllerTest < Test::Unit::TestCase
- fixtures :current_nodes, :nodes, :users, :current_segments, :segments, :ways, :current_ways, :way_tags, :current_way_tags, :way_segments, :current_way_segments
- set_fixture_class :current_ways => :Way
- set_fixture_class :ways => :OldWay
- set_fixture_class :current_segments => :Segment
- set_fixture_class :segments => :OldSegment
+ api_fixtures
def setup
@controller = WayController.new
# check chat a non-existent way is not returned
get :read, :id => 0
assert_response :not_found
+
+ # check the "ways for node" mode
+ get :ways_for_node, :id => current_nodes(:used_node_1).id
+ assert_response :success
+ # FIXME check whether this contains the stuff we want!
+ print @response.body
+
+ # check the "full" mode
+ get :full, :id => current_ways(:visible_way).id
+ assert_response :success
+ # FIXME check whether this contains the stuff we want!
+ print @response.body
end
# -------------------------------------
# -------------------------------------
def test_create
- sid = current_segments(:used_segment).id
+ nid1 = current_nodes(:used_node_1).id
+ nid2 = current_nodes(:used_node_2).id
basic_authorization "test@openstreetmap.org", "test"
- # create a way with pre-existing segment
- content "<osm><way><seg id='#{sid}'/><tag k='test' v='yes' /></way></osm>"
+ # create a way with pre-existing nodes
+ content "<osm><way><nd ref='#{nid1}'/><nd ref='#{nid2}'/><tag k='test' v='yes' /></way></osm>"
put :create
# hope for success
assert_response :success,
assert_not_nil checkway,
"uploaded way not found in data base after upload"
# compare values
- assert_equal checkway.segs.length, 1,
- "saved way does not contain exactly one segment"
- assert_equal checkway.segs[0], sid,
- "saved way does not contain the right segment"
+ assert_equal checkway.nds.length, 2,
+ "saved way does not contain exactly one node"
+ assert_equal checkway.nds[0], nid1,
+ "saved way does not contain the right node on pos 0"
+ assert_equal checkway.nds[1], nid2,
+ "saved way does not contain the right node on pos 1"
assert_equal users(:normal_user).id, checkway.user_id,
"saved way does not belong to user that created it"
assert_equal true, checkway.visible,
def test_create_invalid
basic_authorization "test@openstreetmap.org", "test"
- # create a way with non-existing segment
- content "<osm><way><seg id='0'/><tag k='test' v='yes' /></way></osm>"
+ # create a way with non-existing node
+ content "<osm><way><nd ref='0'/><tag k='test' v='yes' /></way></osm>"
put :create
# expect failure
assert_response :precondition_failed,
- "way upload with invalid segment did not return 'precondition failed'"
+ "way upload with invalid node did not return 'precondition failed'"
- # create a way with no segments
+ # create a way with no nodes
content "<osm><way><tag k='test' v='yes' /></way></osm>"
put :create
# expect failure
assert_response :precondition_failed,
- "way upload with no segments did not return 'precondition failed'"
-
- # create a way that has the same segment, twice
- # (commented out - this is currently allowed!)
- #sid = current_segments(:used_segment).id
- #content "<osm><way><seg id='#{sid}'/><seg id='#{sid}'/><tag k='test' v='yes' /></way></osm>"
- #put :create
- #assert_response :internal_server_error,
- # "way upload with double segment did not return 'internal server error'"
+ "way upload with no node did not return 'precondition failed'"
end
# -------------------------------------
set_fixture_class :current_nodes => :Node
set_fixture_class :nodes => :OldNode
- fixtures :current_segments, :segments
- set_fixture_class :current_segments => :Segment
- set_fixture_class :segments => :OldSegment
-
- fixtures :current_ways, :current_way_segments, :current_way_tags
+ fixtures :current_ways, :current_way_nodes, :current_way_tags
set_fixture_class :current_ways => :Way
- set_fixture_class :current_way_segments => :WaySegment
+ set_fixture_class :current_way_nodes => :WayNode
set_fixture_class :current_way_tags => :WayTag
- fixtures :ways, :way_segments, :way_tags
+ fixtures :ways, :way_nodes, :way_tags
set_fixture_class :ways => :OldWay
- set_fixture_class :way_segments => :OldWaySegment
+ set_fixture_class :way_nodes => :OldWayNode
set_fixture_class :way_tags => :OldWayTag
end
: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
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
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
+++ /dev/null
-require File.dirname(__FILE__) + '/../test_helper'
-
-class SegmentTest < Test::Unit::TestCase
- fixtures :current_nodes, :nodes, :current_segments, :segments, :users
- set_fixture_class :current_segments => :Segment
- set_fixture_class :segments => :OldSegment
- set_fixture_class :current_nodes => :Node
- set_fixture_class :nodes => :OldNode
-
- def test_create
-
- segment_template = Segment.new(:node_a => nodes(:used_node_1).id,
- :node_b => nodes(:used_node_2).id,
- :user_id => users(:normal_user).id,
- :visible => 1,
- :tags => "")
- assert segment_template.save_with_history
-
- segment = Segment.find(segment_template.id)
- assert_not_nil segment
- assert_equal segment_template.node_a, segment.node_a
- assert_equal segment_template.node_b, segment.node_b
- assert_equal segment_template.user_id, segment.user_id
- assert_equal segment_template.visible, segment.visible
- assert_equal segment_template.tags, segment.tags
- assert_equal segment_template.timestamp.to_i, segment.timestamp.to_i
-
- assert_equal OldSegment.find(:all, :conditions => [ "id = ?", segment_template.id ]).length, 1
- old_segment = OldSegment.find(:first, :conditions => [ "id = ?", segment_template.id ])
- assert_not_nil old_segment
- assert_equal segment_template.node_a, old_segment.node_a
- assert_equal segment_template.node_b, old_segment.node_b
- assert_equal segment_template.user_id, old_segment.user_id
- assert_equal segment_template.visible, old_segment.visible
- assert_equal segment_template.tags, old_segment.tags
- assert_equal segment_template.timestamp.to_i, old_segment.timestamp.to_i
- end
-
- def test_update
- segment_template = Segment.find(1)
- assert_not_nil segment_template
-
- assert_equal OldSegment.find(:all, :conditions => [ "id = ?", segment_template.id ]).length, 1
- old_segment_template = OldSegment.find(:first, :conditions => [ "id = ?", segment_template.id ])
- assert_not_nil old_segment_template
-
- segment_template.node_a = nodes(:used_node_2).id
- segment_template.node_b = nodes(:used_node_1).id
- segment_template.tags = "updated=yes"
- assert segment_template.save_with_history
-
- segment = Segment.find(segment_template.id)
- assert_not_nil segment
- assert_equal segment_template.node_a, segment.node_a
- assert_equal segment_template.node_b, segment.node_b
- assert_equal segment_template.user_id, segment.user_id
- assert_equal segment_template.visible, segment.visible
- assert_equal segment_template.tags, segment.tags
- assert_equal segment_template.timestamp.to_i, segment.timestamp.to_i
-
- assert_equal OldSegment.find(:all, :conditions => [ "id = ?", segment_template.id ]).length, 2
- assert_equal OldSegment.find(:all, :conditions => [ "id = ? and timestamp = ?", segment_template.id, segment_template.timestamp ]).length, 1
- old_segment = OldSegment.find(:first, :conditions => [ "id = ? and timestamp = ?", segment_template.id, segment_template.timestamp ])
- assert_not_nil old_segment
- assert_equal segment_template.node_a, old_segment.node_a
- assert_equal segment_template.node_b, old_segment.node_b
- assert_equal segment_template.user_id, old_segment.user_id
- assert_equal segment_template.visible, old_segment.visible
- assert_equal segment_template.tags, old_segment.tags
- assert_equal segment_template.timestamp.to_i, old_segment.timestamp.to_i
- end
-
- def test_delete
- segment_template = Segment.find(1)
- assert_not_nil segment_template
-
- assert_equal OldSegment.find(:all, :conditions => [ "id = ?", segment_template.id ]).length, 1
- old_segment_template = OldSegment.find(:first, :conditions => [ "id = ?", segment_template.id ])
- assert_not_nil old_segment_template
-
- segment_template.visible = 0
- assert segment_template.save_with_history
-
- segment = Segment.find(segment_template.id)
- assert_not_nil segment
- assert_equal segment_template.node_a, segment.node_a
- assert_equal segment_template.node_b, segment.node_b
- assert_equal segment_template.user_id, segment.user_id
- assert_equal segment_template.visible, segment.visible
- assert_equal segment_template.tags, segment.tags
- assert_equal segment_template.timestamp.to_i, segment.timestamp.to_i
-
- assert_equal OldSegment.find(:all, :conditions => [ "id = ?", segment_template.id ]).length, 2
- assert_equal OldSegment.find(:all, :conditions => [ "id = ? and timestamp = ?", segment_template.id, segment_template.timestamp ]).length, 1
- old_segment = OldSegment.find(:first, :conditions => [ "id = ? and timestamp = ?", segment_template.id, segment_template.timestamp ])
- assert_not_nil old_segment
- assert_equal segment_template.node_a, old_segment.node_a
- assert_equal segment_template.node_b, old_segment.node_b
- assert_equal segment_template.user_id, old_segment.user_id
- assert_equal segment_template.visible, old_segment.visible
- assert_equal segment_template.tags, old_segment.tags
- assert_equal segment_template.timestamp.to_i, old_segment.timestamp.to_i
- end
-end