preliminary commit 0.5 API with relations / untested after entity-relation rename, some changes still in queue

This commit is contained in:
Frederik Ramm 2007-08-29 22:00:26 +00:00
parent 02451061ce
commit 78b440ffc1
46 changed files with 1841 additions and 231 deletions

View file

@ -195,12 +195,12 @@ EOF
RAILS_DEFAULT_LOGGER.info(" Message: whichways, bbox=#{xmin},#{ymin},#{xmax},#{ymax}")
waylist=WaySegment.find_by_sql("SELECT DISTINCT current_way_segments.id AS wayid"+
" FROM current_way_segments,current_segments,current_nodes,current_ways "+
waylist=WayNode.find_by_sql("SELECT DISTINCT current_way_nodes.id AS wayid"+
" FROM current_way_nodes,current_segments,current_nodes,current_ways "+
" WHERE segment_id=current_segments.id "+
" AND current_segments.visible=1 "+
" AND node_a=current_nodes.id "+
" AND current_ways.id=current_way_segments.id "+
" AND current_ways.id=current_way_nodes.id "+
" AND current_ways.visible=1 "+
" AND (latitude BETWEEN "+ymin.to_s+" AND "+ymax.to_s+") "+
" AND (longitude BETWEEN "+xmin.to_s+" AND "+xmax.to_s+")")
@ -403,7 +403,7 @@ EOF
ActiveRecord::Base.connection.execute("DROP TABLE #{db_uqs}")
ActiveRecord::Base.connection.execute("DROP TABLE #{db_uqn}")
# insert new version of route into way_segments
# insert new version of route into way_nodes
insertsql =''
currentsql=''
@ -417,9 +417,9 @@ EOF
sequence +=1
end
ActiveRecord::Base.connection.execute("DELETE FROM current_way_segments WHERE id=#{way}");
ActiveRecord::Base.connection.insert("INSERT INTO way_segments (id,segment_id,version ) VALUES #{insertsql}");
ActiveRecord::Base.connection.insert("INSERT INTO current_way_segments (id,segment_id,sequence_id) VALUES #{currentsql}");
ActiveRecord::Base.connection.execute("DELETE FROM current_way_nodes WHERE id=#{way}");
ActiveRecord::Base.connection.insert("INSERT INTO way_nodes (id,segment_id,version ) VALUES #{insertsql}");
ActiveRecord::Base.connection.insert("INSERT INTO current_way_nodes (id,segment_id,sequence_id) VALUES #{currentsql}");
# -- 7. insert new way tags
@ -503,7 +503,7 @@ EOF
ActiveRecord::Base.connection.insert("INSERT INTO ways (id,user_id,timestamp,visible) VALUES (#{way},#{uid},#{db_now},0)")
ActiveRecord::Base.connection.update("UPDATE current_ways SET user_id=#{uid},timestamp=#{db_now},visible=0 WHERE id=#{way}")
ActiveRecord::Base.connection.execute("DELETE FROM current_way_segments WHERE id=#{way}")
ActiveRecord::Base.connection.execute("DELETE FROM current_way_nodes WHERE id=#{way}")
ActiveRecord::Base.connection.execute("DELETE FROM current_way_tags WHERE id=#{way}")
way
@ -537,7 +537,7 @@ def makeway(args)
FROM current_nodes AS cn1,
current_nodes AS cn2,
current_segments AS cs
LEFT OUTER JOIN current_way_segments ON segment_id=cs.id
LEFT OUTER JOIN current_way_nodes ON segment_id=cs.id
WHERE (cn1.longitude BETWEEN #{xs1} AND #{xs2})
AND (cn1.latitude BETWEEN #{ys1} AND #{ys2})
AND segment_id IS NULL
@ -603,7 +603,7 @@ def findconnect(id,nodesused,lookfor,toreverse,baselong,basey,masterscale)
FROM current_nodes AS cn1,
current_nodes AS cn2,
current_segments AS cs
LEFT OUTER JOIN current_way_segments ON segment_id=cs.id
LEFT OUTER JOIN current_way_nodes ON segment_id=cs.id
WHERE segment_id IS NULL
AND cs.visible=1
AND cn1.id=node_a AND cn1.visible=1
@ -615,7 +615,7 @@ def findconnect(id,nodesused,lookfor,toreverse,baselong,basey,masterscale)
FROM current_nodes AS cn1,
current_nodes AS cn2,
current_segments AS cs
LEFT OUTER JOIN current_way_segments ON segment_id=cs.id
LEFT OUTER JOIN current_way_nodes ON segment_id=cs.id
WHERE segment_id IS NULL
AND cs.visible=1
AND cn1.id=node_a AND cn1.visible=1
@ -663,8 +663,8 @@ end
def readwayquery(id)
ActiveRecord::Base.connection.select_all "SELECT n1.latitude AS lat1,n1.longitude AS long1,n1.id AS id1,n1.tags as tags1, "+
" n2.latitude AS lat2,n2.longitude AS long2,n2.id AS id2,n2.tags as tags2,segment_id "+
" FROM current_way_segments,current_segments,current_nodes AS n1,current_nodes AS n2 "+
" WHERE current_way_segments.id=#{id} "+
" FROM current_way_nodes,current_segments,current_nodes AS n1,current_nodes AS n2 "+
" WHERE current_way_nodes.id=#{id} "+
" AND segment_id=current_segments.id "+
" AND current_segments.visible=1 "+
" AND n1.id=node_a and n2.id=node_b "+
@ -677,9 +677,9 @@ def createuniquesegments(way,uqs_name,seglist)
sql=<<-EOF
CREATE TEMPORARY TABLE #{uqs_name}
SELECT a.segment_id
FROM (SELECT DISTINCT segment_id FROM current_way_segments
FROM (SELECT DISTINCT segment_id FROM current_way_nodes
WHERE id = #{way}) a
LEFT JOIN current_way_segments b
LEFT JOIN current_way_nodes b
ON b.segment_id = a.segment_id
AND b.id != #{way}
WHERE b.segment_id IS NULL

View file

@ -136,12 +136,14 @@ class ApiController < ApplicationController
# check the bbox isn't too large
requested_area = (max_lat-min_lat)*(max_lon-min_lon)
if requested_area > MAX_REQUEST_AREA
report_error("The maximum bbox size is " + MAX_REQUEST_AREA.to_s + ", and your request was too large. Either request a smaller area, or use planet.osm")
report_error("The maximum bbox size is " + MAX_REQUEST_AREA.to_s +
", and your request was too large. Either request a smaller area, or use planet.osm")
return
end
# get all the nodes
nodes = Node.find(:all, :conditions => ['latitude > ? AND longitude > ? AND latitude < ? AND longitude < ? AND visible = 1', min_lat, min_lon, max_lat, max_lon])
nodes = Node.find(:all, :conditions =>
['latitude > ? AND longitude > ? AND latitude < ? AND longitude < ? AND visible = 1', min_lat, min_lon, max_lat, max_lon])
node_ids = nodes.collect {|node| node.id }
@ -151,68 +153,38 @@ class ApiController < ApplicationController
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.
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)
# seg_ids = way_segments.collect {|way_segment| way_segment.segment_id }
list_of_way_nodes = ways.collect { |way|
way.way_nodes.collect { |way_node| way_node.node_id }
}
list_of_way_nodes.flatten!
list_of_way_segs = ways.collect {|way| way.way_segments}
list_of_way_segs.flatten!
list_of_way_segments = list_of_way_segs.collect { |way_seg| way_seg.segment_id }
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 = {}
@ -223,18 +195,40 @@ class ApiController < ApplicationController
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
way_ids = Array.new
ways.each do |way|
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
ways.each do |way|
doc.root << way.to_xml_node(visible_segments, user_display_name_cache) if way.visible?
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"
@ -251,8 +245,8 @@ class ApiController < ApplicationController
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;

View file

@ -79,7 +79,9 @@ class NodeController < ApplicationController
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
else if 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

View file

@ -0,0 +1,2 @@
class OldWayNodeController < ApplicationController
end

View file

@ -0,0 +1,181 @@
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
if relation.save_with_history
render :text => relation.id.to_s, :content_type => "text/plain"
else
print "save error\n";
render :nothing => true, :status => :internal_server_error
end
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
if relation.save_with_history
render :nothing => true
else
render :nothing => true, :status => :internal_server_error
end
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
if relation.save_with_history
render :nothing => true
else
render :nothing => true, :status => :internal_server_error
end
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 full
begin
relation = Relation.find(params[:id])
if relation.visible
# In future, we might want to do all the data fetch in one step
seg_ids = relation.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(',')})")
# 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 << relation.to_xml_node()
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_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 => :bad_request
end
end
end

View file

@ -0,0 +1,5 @@
class RelationMemberController < ApplicationController
end

View file

@ -0,0 +1,9 @@
class RelationTagController < ApplicationController
layout 'site'
def search
@tags = RelationTag.find(:all, :limit => 11, :conditions => ["match(v) against (?)", params[:query][:query].to_s] )
end
end

View file

@ -1,26 +1,23 @@
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=....)
after_filter :compress_output
def search_all
do_search(true,true,true)
do_search(true,true)
end
def search_ways
do_search(true,false,false)
end
def search_segments
do_search(false,true,false)
do_search(true,false)
end
def search_nodes
do_search(false,false,true)
do_search(false,true)
end
def do_search(do_ways,do_segments,do_nodes)
def do_search(do_ways,do_nodes)
type = params['type']
value = params['value']
unless type or value
@ -33,7 +30,6 @@ class SearchController < ApplicationController
way_ids = Array.new
ways = Array.new
segments = Array.new
nodes = Array.new
# Matching for tags table
@ -75,21 +71,13 @@ class SearchController < ApplicationController
ways = Way.find(:all, :conditions => cond_tbl, :limit => 100)
end
# Now, segments matching
if do_segments
segments = Segment.find(:all, :conditions => cond_tags, :limit => 500)
end
# Now, nodes
if do_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 = {}
@ -98,10 +86,6 @@ class SearchController < ApplicationController
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

View file

@ -6,7 +6,7 @@ class SiteController < ApplicationController
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

View file

@ -94,7 +94,7 @@ class SwfController < ApplicationController
sql="SELECT cn1.latitude AS lat1,cn1.longitude AS lon1,"+
" cn2.latitude AS lat2,cn2.longitude AS lon2 "+
" FROM current_segments "+
" LEFT OUTER JOIN current_way_segments"+
" LEFT OUTER JOIN current_way_nodes"+
" ON segment_id=current_segments.id,"+
" current_nodes AS cn1,current_nodes AS cn2"+
" WHERE (cn1.longitude BETWEEN #{xmin} AND #{xmax})"+

View file

@ -59,7 +59,7 @@ class WayController < ApplicationController
else
way.user_id = @user.id
way.tags = new_way.tags
way.segs = new_way.segs
way.nds = new_way.nds
way.visible = true
if way.save_with_history
@ -86,15 +86,19 @@ class WayController < ApplicationController
way = Way.find(params[:id])
if way.visible
way.user_id = @user.id
way.tags = []
way.segs = []
way.visible = false
if way.save_with_history
render :nothing => true
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
render :nothing => true, :status => :internal_server_error
way.user_id = @user.id
way.tags = []
way.nds = []
way.visible = false
if way.save_with_history
render :nothing => true
else
render :nothing => true, :status => :internal_server_error
end
end
else
render :nothing => true, :status => :gone
@ -111,23 +115,14 @@ class WayController < ApplicationController
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"
@ -161,8 +156,8 @@ class WayController < ApplicationController
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

View file

@ -0,0 +1,5 @@
class WayNodeController < ApplicationController
end

View file

@ -0,0 +1,2 @@
module OldWayNodeHelper
end

View file

@ -0,0 +1,2 @@
module WayNodeHelper
end

111
app/models/old_relation.rb Normal file
View file

@ -0,0 +1,111 @@
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

View file

@ -0,0 +1,6 @@
class OldRelationMember < ActiveRecord::Base
belongs_to :user
set_table_name 'relation_members'
end

View file

@ -0,0 +1,6 @@
class OldRelationTag < ActiveRecord::Base
belongs_to :user
set_table_name 'relation_tags'
end

View file

@ -9,7 +9,7 @@ class OldWay < ActiveRecord::Base
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
@ -39,23 +39,25 @@ class OldWay < ActiveRecord::Base
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
@ -69,19 +71,19 @@ class OldWay < ActiveRecord::Base
@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
@ -95,9 +97,9 @@ class OldWay < ActiveRecord::Base
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

View file

@ -0,0 +1,6 @@
class OldWayNode < ActiveRecord::Base
belongs_to :user
set_table_name 'way_nodes'
end

213
app/models/relation.rb Normal file
View file

@ -0,0 +1,213 @@
class Relation < ActiveRecord::Base
require 'xml/libxml'
belongs_to :user
has_many :relation_members, :foreign_key => 'id'
has_many :relation_tags, :foreign_key => 'id'
has_many :old_relations, :foreign_key => 'id', :order => 'version'
set_table_name 'current_relations'
def self.from_xml(xml, create=false)
begin
p = XML::Parser.new
p.string = xml
doc = p.parse
relation = Relation.new
doc.find('//osm/relation').each do |pt|
if !create and pt['id'] != '0'
relation.id = pt['id'].to_i
end
if create
relation.timestamp = Time.now
relation.visible = true
else
if pt['timestamp']
relation.timestamp = Time.parse(pt['timestamp'])
end
end
pt.find('tag').each do |tag|
relation.add_tag_keyval(tag['k'], tag['v'])
end
pt.find('member').each do |member|
relation.add_member(member['type'], member['ref'], member['role'])
end
end
rescue
relation = nil
end
return relation
end
def to_xml
doc = OSM::API.new.get_xml_doc
doc.root << to_xml_node()
return doc
end
def to_xml_node(user_display_name_cache = nil)
el1 = XML::Node.new 'relation'
el1['id'] = self.id.to_s
el1['visible'] = self.visible.to_s
el1['timestamp'] = self.timestamp.xmlschema
user_display_name_cache = {} if user_display_name_cache.nil?
if user_display_name_cache and user_display_name_cache.key?(self.user_id)
# use the cache if available
elsif self.user.data_public?
user_display_name_cache[self.user_id] = self.user.display_name
else
user_display_name_cache[self.user_id] = nil
end
el1['user'] = user_display_name_cache[self.user_id] unless user_display_name_cache[self.user_id].nil?
self.relation_members.each do |member|
p=0
#if visible_members
# # if there is a list of visible members then use that to weed out deleted segments
# if visible_members[member.member_type][member.member_id]
# p=1
# end
#else
# otherwise, manually go to the db to check things
if member.member.visible?
p=1
end
#end
if p
e = XML::Node.new 'member'
e['type'] = member.member_type
e['ref'] = member.member_id.to_s
e['role'] = member.member_role
el1 << e
end
end
self.relation_tags.each do |tag|
e = XML::Node.new 'tag'
e['k'] = tag.k
e['v'] = tag.v
el1 << e
end
return el1
end
# FIXME is this really needed?
def members
unless @members
@members = Array.new
self.relation_members.each do |member|
@members += [[member.member_type,member.member_id,member.member_role]]
end
end
@members
end
def tags
unless @tags
@tags = Hash.new
self.relation_tags.each do |tag|
@tags[tag.k] = tag.v
end
end
@tags
end
def members=(m)
@members = m
end
def tags=(t)
@tags = t
end
def add_member(type,id,role)
@members = Array.new unless @members
@members += [[type,id,role]]
end
def add_tag_keyval(k, v)
@tags = Hash.new unless @tags
@tags[k] = v
end
def save_with_history
begin
Relation.transaction do
t = Time.now
self.timestamp = t
self.save!
tags = self.tags
RelationTag.delete_all(['id = ?', self.id])
tags.each do |k,v|
tag = RelationTag.new
tag.k = k
tag.v = v
tag.id = self.id
tag.save!
end
members = self.members
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
return true
rescue Exception => ex
return nil
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

View file

@ -0,0 +1,30 @@
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

View file

@ -0,0 +1,6 @@
class RelationTag < ActiveRecord::Base
set_table_name 'current_relation_tags'
belongs_to :relation, :foreign_key => 'id'
end

View file

@ -3,7 +3,7 @@ class Way < ActiveRecord::Base
belongs_to :user
has_many :way_segments, :foreign_key => 'id', :order => 'sequence_id'
has_many :way_nodes, :foreign_key => 'id', :order => 'sequence_id'
has_many :way_tags, :foreign_key => 'id'
has_many :old_ways, :foreign_key => 'id', :order => 'version'
@ -36,8 +36,8 @@ class Way < ActiveRecord::Base
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['id'])
end
end
rescue
@ -53,7 +53,7 @@ class Way < ActiveRecord::Base
return doc
end
def to_xml_node(visible_segments = nil, user_display_name_cache = nil)
def to_xml_node(visible_nodes = nil, user_display_name_cache = nil)
el1 = XML::Node.new 'way'
el1['id'] = self.id.to_s
el1['visible'] = self.visible.to_s
@ -71,26 +71,26 @@ class Way < ActiveRecord::Base
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['id'] = nd_id
el1 << e
end
end
@ -104,14 +104,14 @@ class Way < ActiveRecord::Base
return el1
end
def segs
unless @segs
@segs = Array.new
self.way_segments.each do |seg|
@segs += [seg.segment_id]
def nds
unless @nds
@nds = Array.new
self.way_nodes.each do |nd|
@nds += [nd.node_id]
end
end
@segs
@nds
end
def tags
@ -124,17 +124,17 @@ class Way < ActiveRecord::Base
@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)
@ -161,17 +161,17 @@ class Way < ActiveRecord::Base
tag.save!
end
segs = self.segs
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
@ -181,16 +181,17 @@ class Way < ActiveRecord::Base
end
return true
rescue
rescue => ex
puts ex
return nil
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

5
app/models/way_node.rb Normal file
View file

@ -0,0 +1,5 @@
class WayNode < ActiveRecord::Base
set_table_name 'current_way_nodes'
belongs_to :node
end