Merge changes from trunk 7673:8632.

This commit is contained in:
Shaun McDonald 2008-07-03 13:06:24 +00:00
commit 5f8ab9e924
62 changed files with 1456 additions and 373 deletions

View file

@ -1,60 +0,0 @@
class GeoRecord < ActiveRecord::Base
before_save :update_tile
# This is a scaling factor for going between the lat and lon via the API
# and the longitude and latitude that is stored in the database
SCALE = 10000000
# Is this node within -90 <= latitude <= 90 and -180 <= longitude <= 180
# * returns true/false
def in_world?
return false if self.lat < -90 or self.lat > 90
return false if self.lon < -180 or self.lon > 180
return true
end
def self.find_by_area(minlat, minlon, maxlat, maxlon, options)
self.with_scope(:find => {:conditions => OSM.sql_for_area(minlat, minlon, maxlat, maxlon)}) do
return self.find(:all, options)
end
end
def update_tile
self.tile = QuadTile.tile_for_point(lat, lon)
end
def lat=(l)
self.latitude = (l * SCALE).round
end
def lon=(l)
self.longitude = (l * SCALE).round
end
# Return WGS84 latitude
def lat
return self.latitude.to_f / SCALE
end
# Return WGS84 longitude
def lon
return self.longitude.to_f / SCALE
end
# Potlatch projections
def lon_potlatch(baselong,masterscale)
(self.lon-baselong)*masterscale
end
def lat_potlatch(basey,masterscale)
-(lat2y(self.lat)-basey)*masterscale
end
private
def lat2y(a)
180/Math::PI * Math.log(Math.tan(Math::PI/4+a*(Math::PI/180)/2))
end
end

View file

@ -1,6 +1,8 @@
class Node < GeoRecord
class Node < ActiveRecord::Base
require 'xml/libxml'
include GeoRecord
set_table_name 'current_nodes'
validates_presence_of :user_id, :timestamp
@ -8,21 +10,21 @@ class Node < GeoRecord
validates_numericality_of :latitude, :longitude
validate :validate_position
has_many :old_nodes, :foreign_key => :id
has_many :way_nodes
has_many :node_tags, :foreign_key => :id
belongs_to :user
has_many :old_nodes, :foreign_key => :id
has_many :way_nodes
has_many :ways, :through => :way_nodes
has_many :containing_relation_members, :class_name => "RelationMember", :as => :member
has_many :containing_relations, :class_name => "Relation", :through => :containing_relation_members, :source => :relation, :extend => ObjectFinder
# Sanity check the latitude and longitude and add an error if it's broken
def validate_position
errors.add_to_base("Node is not in the world") unless in_world?
end
def in_world?
return false if self.lat < -90 or self.lat > 90
return false if self.lon < -180 or self.lon > 180
return true
end
#
# Search for nodes matching tags within bounding_box
#
@ -55,109 +57,64 @@ class Node < GeoRecord
p = XML::Parser.new
p.string = xml
doc = p.parse
node = Node.new
doc.find('//osm/node').each do |pt|
return Node.from_xml_node(pt, create)
node.lat = pt['lat'].to_f
node.lon = pt['lon'].to_f
return nil unless node.in_world?
unless create
if pt['id'] != '0'
node.id = pt['id'].to_i
end
end
node.visible = pt['visible'] and pt['visible'] == 'true'
if create
node.timestamp = Time.now
else
if pt['timestamp']
node.timestamp = Time.parse(pt['timestamp'])
end
end
tags = []
pt.find('tag').each do |tag|
tags << [tag['k'],tag['v']]
end
node.tags = Tags.join(tags)
end
rescue
return nil
end
end
def self.from_xml_node(pt, create=false)
node = Node.new
node.version = pt['version']
node.lat = pt['lat'].to_f
node.lon = pt['lon'].to_f
return nil unless node.in_world?
unless create
if pt['id'] != '0'
node.id = pt['id'].to_i
end
end
node.visible = pt['visible'] and pt['visible'] == 'true'
if create
node.timestamp = Time.now
else
if pt['timestamp']
node.timestamp = Time.parse(pt['timestamp'])
end
end
tags = []
pt.find('tag').each do |tag|
node.add_tag_key_val(tag['k'],tag['v'])
node = nil
end
return node
end
# Save this node with the appropriate OldNode object to represent it's history.
def save_with_history!
t = Time.now
Node.transaction do
self.version += 1
self.timestamp = t
self.timestamp = Time.now
self.save!
# Create a NodeTag
tags = self.tags
NodeTag.delete_all(['id = ?', self.id])
tags.each do |k,v|
tag = NodeTag.new
tag.k = k
tag.v = v
tag.id = self.id
tag.save!
end
# Create an OldNode
old_node = OldNode.from_node(self)
old_node.timestamp = t
old_node.save_with_dependencies!
old_node.save!
end
end
def delete_with_history(user)
if self.visible
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 = ?", self.id ])
raise OSM::APIPreconditionFailedError.new
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=?", self.id])
raise OSM::APIPreconditionFailedError.new
else
self.user_id = user.id
self.visible = 0
save_with_history!
end
else
raise OSM::APIAlreadyDeletedError.new
end
end
def update_from(new_node, user)
if new_node.version != version
raise OSM::APIVersionMismatchError.new(new_node.version, version)
end
self.user_id = user.id
self.latitude = new_node.latitude
self.longitude = new_node.longitude
self.tags = new_node.tags
self.visible = true
save_with_history!
end
# Turn this Node in to a complete OSM XML object with <osm> wrapper
def to_xml
doc = OSM::API.new.get_xml_doc
doc.root << to_xml_node()
return doc
end
# Turn this Node in to an XML Node without the <osm> wrapper.
def to_xml_node(user_display_name_cache = nil)
el1 = XML::Node.new 'node'
el1['id'] = self.id.to_s
@ -176,7 +133,7 @@ class Node < GeoRecord
el1['user'] = user_display_name_cache[self.user_id] unless user_display_name_cache[self.user_id].nil?
self.tags.each do |k,v|
Tags.split(self.tags) do |k,v|
el2 = XML::Node.new('tag')
el2['k'] = k.to_s
el2['v'] = v.to_s
@ -185,33 +142,15 @@ class Node < GeoRecord
el1['visible'] = self.visible.to_s
el1['timestamp'] = self.timestamp.xmlschema
el1['version'] = self.version.to_s
return el1
end
# Return the node's tags as a Hash of keys and their values
def tags_as_hash
return tags
end
def tags
unless @tags
@tags = {}
self.node_tags.each do |tag|
@tags[tag.k] = tag.v
end
hash = {}
Tags.split(self.tags) do |k,v|
hash[k] = v
end
@tags
hash
end
def tags=(t)
@tags = t
end
def add_tag_key_val(k,v)
@tags = Hash.new unless @tags
@tags[k] = v
end
end

View file

@ -1,4 +1,6 @@
class OldNode < GeoRecord
class OldNode < ActiveRecord::Base
include GeoRecord
set_table_name 'nodes'
validates_presence_of :user_id, :timestamp
@ -27,15 +29,8 @@ class OldNode < GeoRecord
old_node.timestamp = node.timestamp
old_node.user_id = node.user_id
old_node.id = node.id
old_node.version = node.version
return old_node
end
def to_xml
doc = OSM::API.new.get_xml_doc
doc.root << to_xml_node()
return doc
end
def to_xml_node
el1 = XML::Node.new 'node'
@ -44,7 +39,7 @@ class OldNode < GeoRecord
el1['lon'] = self.lon.to_s
el1['user'] = self.user.display_name if self.user.data_public?
self.tags.each do |k,v|
Tags.split(self.tags) do |k,v|
el2 = XML::Node.new('tag')
el2['k'] = k.to_s
el2['v'] = v.to_s
@ -53,41 +48,24 @@ class OldNode < GeoRecord
el1['visible'] = self.visible.to_s
el1['timestamp'] = self.timestamp.xmlschema
el1['version'] = self.version.to_s
return el1
end
def save_with_dependencies!
save!
#not sure whats going on here
clear_aggregation_cache
clear_association_cache
#ok from here
@attributes.update(OldNode.find(:first, :conditions => ['id = ? AND timestamp = ?', self.id, self.timestamp]).instance_variable_get('@attributes'))
self.tags.each do |k,v|
tag = OldNodeTag.new
tag.k = k
tag.v = v
tag.id = self.id
tag.version = self.version
tag.save!
def tags_as_hash
hash = {}
Tags.split(self.tags) do |k,v|
hash[k] = v
end
hash
end
def tags
unless @tags
@tags = Hash.new
OldNodeTag.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
# Pretend we're not in any ways
def ways
return []
end
def tags=(t)
@tags = t
end
# Pretend we're not in any relations
def containing_relation_members
return []
end
end

View file

@ -109,5 +109,20 @@ class OldRelation < ActiveRecord::Base
el1 << e
end
return el1
end
end
# Temporary method to match interface to nodes
def tags_as_hash
return self.tags
end
# Temporary method to match interface to relations
def relation_members
return self.old_members
end
# Pretend we're not in any relations
def containing_relation_members
return []
end
end

View file

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

View file

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

View file

@ -110,5 +110,20 @@ class OldWay < ActiveRecord::Base
el1 << e
end
return el1
end
end
# Temporary method to match interface to nodes
def tags_as_hash
return self.tags
end
# Temporary method to match interface to ways
def way_nodes
return self.old_nodes
end
# Pretend we're not in any relations
def containing_relation_members
return []
end
end

View file

@ -1,14 +1,17 @@
class Relation < ActiveRecord::Base
require 'xml/libxml'
set_table_name 'current_relations'
belongs_to :user
has_many :old_relations, :foreign_key => 'id', :order => 'version'
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'
has_many :containing_relation_members, :class_name => "RelationMember", :as => :member
has_many :containing_relations, :class_name => "Relation", :through => :containing_relation_members, :source => :relation, :extend => ObjectFinder
def self.from_xml(xml, create=false)
begin
@ -109,29 +112,6 @@ class Relation < ActiveRecord::Base
return el1
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...
# FIXME: rip out the fucking SQL
def self.find_for_nodes_and_ways(node_ids, way_ids)
relations = []
if node_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='node' and em.member_id in (#{node_ids.join(',')})")
end
if way_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='way' and em.member_id in (#{way_ids.join(',')})")
end
relations # if you don't do this then it returns nil and not []
end
# FIXME is this really needed?
def members
unless @members
@ -236,22 +216,49 @@ class Relation < ActiveRecord::Base
end
def preconditions_ok?
# These are hastables that store an id in the index of all
# the nodes/way/relations that have already been added.
# Once we know the id of the node/way/relation exists
# we check to see if it is already existing in the hashtable
# if it does, then we return false. Otherwise
# we add it to the relevant hash table, with the value true..
# Thus if you have nodes with the ids of 50 and 1 already in the
# relation, then the hash table nodes would contain:
# => {50=>true, 1=>true}
nodes = Hash.new
ways = Hash.new
relations = Hash.new
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
if nodes[m[1]]
return false
else
nodes[m[1]] = true
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
if ways[m[1]]
return false
else
ways[m[1]] = true
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
if relations[m[1]]
return false
else
relations[m[1]] = true
end
else
return false
end
@ -261,4 +268,8 @@ class Relation < ActiveRecord::Base
return false
end
# Temporary method to match interface to nodes
def tags_as_hash
return self.tags
end
end

View file

@ -1,30 +1,23 @@
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"
belongs_to :member, :polymorphic => true, :foreign_type => :member_class
belongs_to :relation, :foreign_key => :id
# so we define this "member" function that returns whatever it
# is.
def member()
return (member_type == "node") ? node : (member_type == "way") ? way : relation
def after_find
self[:member_class] = self.member_type.capitalize
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.
def after_initialize
self[:member_class] = self.member_type.capitalize
end
def before_save
self.member_type = self[:member_class].downcase
end
def member_type=(type)
self[:member_type] = type
self[:member_class] = type.capitalize
end
end

View file

@ -183,7 +183,7 @@ class Trace < ActiveRecord::Base
# If there are any existing points for this trace then delete
# them - we check for existing points first to avoid locking
# the table in the common case where there aren't any.
if Tracepoint.exists?(['gpx_id = ?', self.id])
if Tracepoint.find(:first, :conditions => ['gpx_id = ?', self.id])
Tracepoint.delete_all(['gpx_id = ?', self.id])
end

View file

@ -1,4 +1,6 @@
class Tracepoint < GeoRecord
class Tracepoint < ActiveRecord::Base
include GeoRecord
set_table_name 'gps_points'
validates_numericality_of :trackid, :only_integer => true

View file

@ -1,15 +1,19 @@
class Way < ActiveRecord::Base
require 'xml/libxml'
belongs_to :user
set_table_name 'current_ways'
has_many :nodes, :through => :way_nodes, :order => 'sequence_id'
has_many :way_nodes, :foreign_key => 'id', :order => 'sequence_id'
has_many :way_tags, :foreign_key => 'id'
belongs_to :user
has_many :old_ways, :foreign_key => 'id', :order => 'version'
set_table_name 'current_ways'
has_many :way_nodes, :foreign_key => 'id', :order => 'sequence_id'
has_many :nodes, :through => :way_nodes, :order => 'sequence_id'
has_many :way_tags, :foreign_key => 'id'
has_many :containing_relation_members, :class_name => "RelationMember", :as => :member
has_many :containing_relations, :class_name => "Relation", :through => :containing_relation_members, :source => :relation, :extend => ObjectFinder
def self.from_xml(xml, create=false)
begin
@ -270,4 +274,9 @@ class Way < ActiveRecord::Base
self.delete_with_history(user)
end
# Temporary method to match interface to nodes
def tags_as_hash
return self.tags
end
end