Refactor bounding box code

Moved duplicated code into the BoundingBox class, and pass around
BoundingBox objects instead of lists of bounds.
This commit is contained in:
Peter Gray 2011-10-24 10:45:56 +01:00 committed by Tom Hughes
parent d70fd8ab1a
commit 95d899786a
21 changed files with 581 additions and 247 deletions

View file

@ -38,9 +38,6 @@
class AmfController < ApplicationController
include Potlatch
# Help methods for checking boundary sanity and area size
include MapBoundary
skip_before_filter :verify_authenticity_token
before_filter :check_api_writable
@ -265,15 +262,17 @@ class AmfController < ApplicationController
# check boundary is sane and area within defined
# see /config/application.yml
check_boundaries(xmin, ymin, xmax, ymax)
bbox = BoundingBox.new(xmin, ymin, xmax, ymax)
bbox.check_boundaries
bbox.check_size
if POTLATCH_USE_SQL then
ways = sql_find_ways_in_area(xmin, ymin, xmax, ymax)
points = sql_find_pois_in_area(xmin, ymin, xmax, ymax)
relations = sql_find_relations_in_area_and_ways(xmin, ymin, xmax, ymax, ways.collect {|x| x[0]})
ways = sql_find_ways_in_area(bbox)
points = sql_find_pois_in_area(bbox)
relations = sql_find_relations_in_area_and_ways(bbox, ways.collect {|x| x[0]})
else
# find the way ids in an area
nodes_in_area = Node.bbox(ymin, xmin, ymax, xmax).visible.includes(:ways)
nodes_in_area = Node.bbox(bbox).visible.includes(:ways)
ways = nodes_in_area.inject([]) { |sum, node|
visible_ways = node.ways.select { |w| w.visible? }
sum + visible_ways.collect { |w| [w.id,w.version] }
@ -305,9 +304,11 @@ class AmfController < ApplicationController
# check boundary is sane and area within defined
# see /config/application.yml
check_boundaries(xmin, ymin, xmax, ymax)
bbox = BoundingBox.new(xmin, ymin, xmax, ymax)
bbox.check_boundaries
bbox.check_size
nodes_in_area = Node.bbox(ymin, xmin, ymax, xmax).joins(:ways_via_history).where(:current_ways => { :visible => false })
nodes_in_area = Node.bbox(bbox).joins(:ways_via_history).where(:current_ways => { :visible => false })
way_ids = nodes_in_area.collect { |node| node.ways_via_history.invisible.collect { |way| way.id } }.flatten.uniq
[0,'',way_ids]
@ -904,7 +905,7 @@ class AmfController < ApplicationController
# ====================================================================
# Alternative SQL queries for getway/whichways
def sql_find_ways_in_area(xmin,ymin,xmax,ymax)
def sql_find_ways_in_area(bbox)
sql=<<-EOF
SELECT DISTINCT current_ways.id AS wayid,current_ways.version AS version
FROM current_way_nodes
@ -912,12 +913,12 @@ class AmfController < ApplicationController
INNER JOIN current_ways ON current_ways.id =current_way_nodes.id
WHERE current_nodes.visible=TRUE
AND current_ways.visible=TRUE
AND #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "current_nodes.")}
AND #{OSM.sql_for_area(bbox, "current_nodes.")}
EOF
return ActiveRecord::Base.connection.select_all(sql).collect { |a| [a['wayid'].to_i,a['version'].to_i] }
end
def sql_find_pois_in_area(xmin,ymin,xmax,ymax)
def sql_find_pois_in_area(bbox)
pois=[]
sql=<<-EOF
SELECT current_nodes.id,current_nodes.latitude*0.0000001 AS lat,current_nodes.longitude*0.0000001 AS lon,current_nodes.version
@ -925,7 +926,7 @@ class AmfController < ApplicationController
LEFT OUTER JOIN current_way_nodes cwn ON cwn.node_id=current_nodes.id
WHERE current_nodes.visible=TRUE
AND cwn.id IS NULL
AND #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "current_nodes.")}
AND #{OSM.sql_for_area(bbox, "current_nodes.")}
EOF
ActiveRecord::Base.connection.select_all(sql).each do |row|
poitags={}
@ -937,7 +938,7 @@ class AmfController < ApplicationController
pois
end
def sql_find_relations_in_area_and_ways(xmin,ymin,xmax,ymax,way_ids)
def sql_find_relations_in_area_and_ways(bbox,way_ids)
# ** It would be more Potlatchy to get relations for nodes within ways
# during 'getway', not here
sql=<<-EOF
@ -945,7 +946,7 @@ class AmfController < ApplicationController
FROM current_relations cr
INNER JOIN current_relation_members crm ON crm.id=cr.id
INNER JOIN current_nodes cn ON crm.member_id=cn.id AND crm.member_type='Node'
WHERE #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "cn.")}
WHERE #{OSM.sql_for_area(bbox, "cn.")}
EOF
unless way_ids.empty?
sql+=<<-EOF

View file

@ -5,9 +5,6 @@ class ApiController < ApplicationController
after_filter :compress_output
around_filter :api_call_handle_error, :api_call_timeout
# Help methods for checking boundary sanity and area size
include MapBoundary
# Get an XML response containing a list of tracepoints that have been uploaded
# within the specified bounding box, and in the specified page.
def trackpoints
@ -22,25 +19,19 @@ class ApiController < ApplicationController
offset = page * TRACEPOINTS_PER_PAGE
# Figure out the bbox
bbox = params['bbox']
unless bbox and bbox.count(',') == 3
report_error("The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat")
return
end
bbox = bbox.split(',')
min_lon, min_lat, max_lon, max_lat = sanitise_boundaries(bbox)
# check boundary is sane and area within defined
# see /config/application.yml
begin
check_boundaries(min_lon, min_lat, max_lon, max_lat)
bbox = BoundingBox.from_bbox_params(params)
bbox.check_boundaries
bbox.check_size
rescue Exception => err
report_error(err.message)
return
end
# get all the points
points = Tracepoint.bbox(min_lat, min_lon, max_lat, max_lon).offset(offset).limit(TRACEPOINTS_PER_PAGE).order("gpx_id DESC, trackid ASC, timestamp ASC")
points = Tracepoint.bbox(bbox).offset(offset).limit(TRACEPOINTS_PER_PAGE).order("gpx_id DESC, trackid ASC, timestamp ASC")
doc = XML::Document.new
doc.encoding = XML::Encoding::UTF_8
@ -124,29 +115,18 @@ class ApiController < ApplicationController
# fetched. Finally all the xml is returned.
def map
# Figure out the bbox
bbox = params['bbox']
unless bbox and bbox.count(',') == 3
# alternatively: report_error(TEXT['boundary_parameter_required']
report_error("The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat")
return
end
bbox = bbox.split(',')
min_lon, min_lat, max_lon, max_lat = sanitise_boundaries(bbox)
# check boundary is sane and area within defined
# see /config/application.yml
begin
check_boundaries(min_lon, min_lat, max_lon, max_lat)
bbox = BoundingBox.from_bbox_params(params)
bbox.check_boundaries
bbox.check_size
rescue Exception => err
report_error(err.message)
return
end
# FIXME um why is this area using a different order for the lat/lon from above???
@nodes = Node.bbox(min_lat, min_lon, max_lat, max_lon).where(:visible => true).includes(:node_tags).limit(MAX_NUMBER_OF_NODES+1)
@nodes = Node.bbox(bbox).where(:visible => true).includes(:node_tags).limit(MAX_NUMBER_OF_NODES+1)
# get all the nodes, by tag not yet working, waiting for change from NickB
# need to be @nodes (instance var) so tests in /spec can be performed
#@nodes = Node.search(bbox, params[:tag])
@ -164,12 +144,7 @@ class ApiController < ApplicationController
doc = OSM::API.new.get_xml_doc
# add bounds
bounds = XML::Node.new 'bounds'
bounds['minlat'] = min_lat.to_s
bounds['minlon'] = min_lon.to_s
bounds['maxlat'] = max_lat.to_s
bounds['maxlon'] = max_lon.to_s
doc.root << bounds
doc.root << bbox.add_bounds_to(XML::Node.new 'bounds')
# get ways
# find which ways are needed

View file

@ -17,9 +17,6 @@ class ChangesetController < ApplicationController
around_filter :api_call_handle_error, :except => [:list]
around_filter :web_timeout, :only => [:list]
# Help methods for checking boundary sanity and area size
include MapBoundary
# Helper methods for checking consistency
include ConsistencyValidations
@ -202,11 +199,11 @@ class ChangesetController < ApplicationController
# create the conditions that the user asked for. some or all of
# these may be nil.
changesets = Changeset.scoped
changesets = conditions_bbox(changesets, params['bbox'])
changesets = conditions_user(changesets, params['user'], params['display_name'])
changesets = conditions_time(changesets, params['time'])
changesets = conditions_open(changesets, params['open'])
changesets = conditions_closed(changesets, params['closed'])
changesets, bbox = conditions_bbox(changesets, params)
changesets = conditions_user(changesets, params['user'], params['display_name'])
changesets = conditions_time(changesets, params['time'])
changesets = conditions_open(changesets, params['open'])
changesets = conditions_closed(changesets, params['closed'])
# create the results document
results = OSM::API.new.get_xml_doc
@ -270,15 +267,9 @@ class ChangesetController < ApplicationController
end
end
if params[:bbox]
bbox = params[:bbox]
elsif params[:minlon] and params[:minlat] and params[:maxlon] and params[:maxlat]
bbox = params[:minlon] + ',' + params[:minlat] + ',' + params[:maxlon] + ',' + params[:maxlat]
end
changesets, bbox = conditions_bbox(changesets, params)
if bbox
changesets = conditions_bbox(changesets, bbox)
bbox = BoundingBox.from_s(bbox)
bbox_link = render_to_string :partial => "bbox", :object => bbox
end
@ -319,22 +310,24 @@ private
#------------------------------------------------------------
##
# if a bounding box was specified then parse it and do some sanity
# checks. this is mostly the same as the map call, but without the
# area restriction.
def conditions_bbox(changesets, bbox)
unless bbox.nil?
raise OSM::APIBadUserInput.new("Bounding box should be min_lon,min_lat,max_lon,max_lat") unless bbox.count(',') == 3
bbox = sanitise_boundaries(bbox.split(/,/))
raise OSM::APIBadUserInput.new("Minimum longitude should be less than maximum.") unless bbox[0] <= bbox[2]
raise OSM::APIBadUserInput.new("Minimum latitude should be less than maximum.") unless bbox[1] <= bbox[3]
# if a bounding box was specified do some sanity checks.
# restrict changesets to those enclosed by a bounding box
# we need to return both the changesets and the bounding box
def conditions_bbox(changesets, params)
if params[:bbox]
bbox = BoundingBox.from_bbox_params(params)
elsif params[:minlon] and params[:minlat] and params[:maxlon] and params[:maxlat]
bbox = BoundingBox.from_lon_lat_params(params)
end
if bbox
bbox.check_boundaries
bbox = bbox.to_scaled
return changesets.where("min_lon < ? and max_lon > ? and min_lat < ? and max_lat > ?",
(bbox[2] * GeoRecord::SCALE).to_i,
(bbox[0] * GeoRecord::SCALE).to_i,
(bbox[3] * GeoRecord::SCALE).to_i,
(bbox[1] * GeoRecord::SCALE).to_i)
bbox.max_lon.to_i, bbox.min_lon.to_i,
bbox.max_lat.to_i, bbox.min_lat.to_i),
bbox
else
return changesets
return changesets, bbox
end
end

View file

@ -8,7 +8,7 @@ class ExportController < ApplicationController
#When the user clicks 'Export' we redirect to a URL which generates the export download
def finish
bbox = BoundingBox.new(params[:minlon], params[:minlat], params[:maxlon], params[:maxlat])
bbox = BoundingBox.from_lon_lat_params(params)
format = params[:format]
if format == "osm"

View file

@ -49,15 +49,16 @@ class SiteController < ApplicationController
@zoom = params['zoom'].to_i
elsif params['bbox']
bbox = params['bbox'].split(",")
bbox = BoundingBox.from_bbox_params(params)
@lon = ( bbox[0].to_f + bbox[2].to_f ) / 2.0
@lat = ( bbox[1].to_f + bbox[3].to_f ) / 2.0
@lon = bbox.centre_lon
@lat = bbox.centre_lat
@zoom = 16
elsif params['minlon'] and params['minlat'] and params['maxlon'] and params['maxlat']
@lon = ( params['maxlon'].to_f + params['minlon'].to_f ) / 2.0
@lat = ( params['maxlat'].to_f + params['minlat'].to_f ) / 2.0
bbox = BoundingBox.from_lon_lat_params(params)
@lon = bbox.centre_lon
@lat = bbox.centre_lat
@zoom = 16
elsif params['gpx']

View file

@ -90,21 +90,11 @@ class Changeset < ActiveRecord::Base
# returns the bounding box of the changeset. it is possible that some
# or all of the values will be nil, indicating that they are undefined.
def bbox
@bbox ||= [ min_lon, min_lat, max_lon, max_lat ]
@bbox ||= BoundingBox.new(min_lon, min_lat, max_lon, max_lat)
end
def has_valid_bbox?
not bbox.include? nil
end
##
# returns area of the changset bbox as a rough comparitive quantity for use of changset displays
def area
if has_valid_bbox?
(max_lon - min_lon) * (max_lat - min_lat)
else
0
end
bbox.complete?
end
##
@ -112,26 +102,12 @@ class Changeset < ActiveRecord::Base
# expand a little bit more in the direction of the expansion, so that
# further expansions may be unnecessary. this is an optimisation
# suggested on the wiki page by kleptog.
def update_bbox!(array)
# ensure that bbox is cached and has no nils in it. if there are any
# nils, just use the bounding box update to write over them.
@bbox = bbox.zip(array).collect { |a, b| a.nil? ? b : a }
# only try to update the bbox if there is a value for every coordinate
# which there will be from the previous line as long as both array and
# bbox are all non-nil.
if has_valid_bbox? and array.all?
# FIXME - this looks nasty and violates DRY... is there any prettier
# way to do this?
@bbox[0] = [-180 * GeoRecord::SCALE, array[0] + EXPAND * (@bbox[0] - @bbox[2])].max if array[0] < @bbox[0]
@bbox[1] = [ -90 * GeoRecord::SCALE, array[1] + EXPAND * (@bbox[1] - @bbox[3])].max if array[1] < @bbox[1]
@bbox[2] = [ 180 * GeoRecord::SCALE, array[2] + EXPAND * (@bbox[2] - @bbox[0])].min if array[2] > @bbox[2]
@bbox[3] = [ 90 * GeoRecord::SCALE, array[3] + EXPAND * (@bbox[3] - @bbox[1])].min if array[3] > @bbox[3]
def update_bbox!(bbox_update)
bbox.expand!(bbox_update, EXPAND)
# update active record. rails 2.1's dirty handling should take care of
# whether this object needs saving or not.
self.min_lon, self.min_lat, self.max_lon, self.max_lat = @bbox
end
# update active record. rails 2.1's dirty handling should take care of
# whether this object needs saving or not.
self.min_lon, self.min_lat, self.max_lon, self.max_lat = @bbox.to_a if bbox.complete?
end
##
@ -237,10 +213,9 @@ class Changeset < ActiveRecord::Base
el1['closed_at'] = self.closed_at.xmlschema unless is_open?
el1['open'] = is_open?.to_s
el1['min_lon'] = (bbox[0].to_f / GeoRecord::SCALE).to_s unless bbox[0].nil?
el1['min_lat'] = (bbox[1].to_f / GeoRecord::SCALE).to_s unless bbox[1].nil?
el1['max_lon'] = (bbox[2].to_f / GeoRecord::SCALE).to_s unless bbox[2].nil?
el1['max_lat'] = (bbox[3].to_f / GeoRecord::SCALE).to_s unless bbox[3].nil?
if bbox.complete?
bbox.to_unscaled.add_bounds_to(el1, '_')
end
# NOTE: changesets don't include the XML of the changes within them,
# they are just structures for tagging. to get the osmChange of a

View file

@ -44,7 +44,6 @@ class Node < ActiveRecord::Base
# Also adheres to limitations such as within max_number_of_nodes
#
def self.search(bounding_box, tags = {})
min_lon, min_lat, max_lon, max_lat = *bounding_box
# @fixme a bit of a hack to search for only visible nodes
# couldn't think of another to add to tags condition
#conditions_hash = tags.merge({ 'visible' => 1 })
@ -59,9 +58,8 @@ class Node < ActiveRecord::Base
#end
#conditions = keys.join(' AND ')
find_by_area(min_lat, min_lon, max_lat, max_lon,
:conditions => {:visible => true},
:limit => MAX_NUMBER_OF_NODES+1)
find_by_area(bounding_box, :conditions => {:visible => true},
:limit => MAX_NUMBER_OF_NODES+1)
end
# Read in xml as text and return it's Node object representation
@ -121,7 +119,7 @@ class Node < ActiveRecord::Base
# the bounding box around a node, which is used for determining the changeset's
# bounding box
def bbox
[ longitude, latitude, longitude, latitude ]
BoundingBox.new(longitude, latitude, longitude, latitude)
end
# Should probably be renamed delete_from to come in line with update

View file

@ -205,7 +205,7 @@ class Way < ActiveRecord::Base
def bbox
lons = nodes.collect { |n| n.longitude }
lats = nodes.collect { |n| n.latitude }
[ lons.min, lats.min, lons.max, lats.max ]
BoundingBox.new(lons.min, lats.min, lons.max, lats.max)
end
def update_from(new_way, user)

View file

@ -24,23 +24,21 @@
<% unless changeset_details.has_valid_bbox? %>
<td><%= t 'browse.changeset_details.no_bounding_box' %></td>
<% else
minlon = changeset_details.min_lon/GeoRecord::SCALE.to_f
minlat = changeset_details.min_lat/GeoRecord::SCALE.to_f
maxlon = changeset_details.max_lon/GeoRecord::SCALE.to_f
maxlat = changeset_details.max_lat/GeoRecord::SCALE.to_f
bbox = changeset_details.bbox.to_unscaled
%>
<td>
<table>
<tr>
<td colspan="3" style="text-align:center"><%=maxlat -%></td>
<td colspan="3" style="text-align:center"><%=bbox.max_lat -%></td>
</tr>
<tr>
<td><%=minlon -%></td>
<td>(<a href='/?minlon=<%= minlon %>&minlat=<%= minlat %>&maxlon=<%= maxlon %>&maxlat=<%= maxlat %>&box=yes' title='<%= t 'browse.changeset_details.show_area_box' %>'><%= t 'browse.changeset_details.box' %></a>)</td>
<td><%=maxlon -%></td>
<td><%=bbox.min_lon -%></td>
<td>(<a href='/?minlon=<%= bbox.min_lon %>&minlat=<%= bbox.min_lat %>&maxlon=<%= bbox.max_lon %>&maxlat=<%= bbox.max_lat %>
&box=yes' title='<%= t 'browse.changeset_details.show_area_box' %>'><%= t 'browse.changeset_details.box' %></a>)</td>
<td><%=bbox.max_lon -%></td>
</tr>
<tr>
<td colspan="3" style="text-align:center"><%= minlat -%></td>
<td colspan="3" style="text-align:center"><%= bbox.min_lat -%></td>
</tr>
</table>
</td>

View file

@ -72,10 +72,11 @@
});
<% if map.instance_of? Changeset %>
var minlon = <%= map.min_lon / GeoRecord::SCALE.to_f %>;
var minlat = <%= map.min_lat / GeoRecord::SCALE.to_f %>;
var maxlon = <%= map.max_lon / GeoRecord::SCALE.to_f %>;
var maxlat = <%= map.max_lat / GeoRecord::SCALE.to_f %>;
<% bbox = map.bbox.to_unscaled %>
var minlon = <%= bbox.min_lon %>;
var minlat = <%= bbox.min_lat %>;
var maxlon = <%= bbox.max_lon %>;
var maxlat = <%= bbox.max_lat %>;
var bbox = new OpenLayers.Bounds(minlon, minlat, maxlon, maxlat);
var centre = bbox.getCenterLonLat();

View file

@ -51,10 +51,11 @@
<% @edits.each do |edit| %>
<% if edit.has_valid_bbox? %>
var minlon = <%= edit.min_lon / GeoRecord::SCALE.to_f %>;
var minlat = <%= edit.min_lat / GeoRecord::SCALE.to_f %>;
var maxlon = <%= edit.max_lon / GeoRecord::SCALE.to_f %>;
var maxlat = <%= edit.max_lat / GeoRecord::SCALE.to_f %>;
<% bbox = edit.bbox.to_unscaled %>
var minlon = <%= bbox.min_lon %>;
var minlat = <%= bbox.min_lat %>;
var maxlon = <%= bbox.max_lon %>;
var maxlat = <%= bbox.max_lat %>;
var bbox = new OpenLayers.Bounds(minlon, minlat, maxlon, maxlat);
bounds.extend(bbox);

View file

@ -78,15 +78,12 @@ atom_feed(:language => I18n.locale, :schema_date => 2009,
end
end
unless changeset.min_lat.nil?
minlon = changeset.min_lon/GeoRecord::SCALE.to_f
minlat = changeset.min_lat/GeoRecord::SCALE.to_f
maxlon = changeset.max_lon/GeoRecord::SCALE.to_f
maxlat = changeset.max_lat/GeoRecord::SCALE.to_f
if changeset.has_valid_bbox?
bbox = changeset.bbox.to_unscaled
# See http://georss.org/Encodings#Geometry
lower_corner = "#{minlat} #{minlon}"
upper_corner = "#{maxlat} #{maxlon}"
lower_corner = "#{bbox.min_lat} #{bbox.min_lon}"
upper_corner = "#{bbox.max_lat} #{bbox.max_lon}"
feed.georss :box, lower_corner + " " + upper_corner
end

View file

@ -1,58 +1,113 @@
class BoundingBox
attr_reader :min_lon, :min_lat, :max_lon, :max_lat
private
LON_LIMIT = 180.0
LAT_LIMIT = 90.0
SCALED_LON_LIMIT = LON_LIMIT * GeoRecord::SCALE
SCALED_LAT_LIMIT = LAT_LIMIT * GeoRecord::SCALE
public
def initialize(min_lon, min_lat, max_lon, max_lat)
@bbox = [min_lon.to_f, min_lat.to_f, max_lon.to_f, max_lat.to_f]
@min_lon = min_lon.to_f unless min_lon.nil?
@min_lat = min_lat.to_f unless min_lat.nil?
@max_lon = max_lon.to_f unless max_lon.nil?
@max_lat = max_lat.to_f unless max_lat.nil?
end
def self.from_s(s)
BoundingBox.new(*s.split(/,/))
if s.count(',') == 3
BoundingBox.new(*s.split(/,/))
else
nil
end
end
def min_lon
@bbox[0]
def self.from_bbox_params(params)
if params[:bbox] and params[:bbox].count(',') == 3
bbox_array = params[:bbox].split(',')
end
from_bbox_array(bbox_array)
end
def min_lon=(min_lon)
@bbox[0] = min_lon
def self.from_lon_lat_params(params)
if params[:minlon] and params[:minlat] and params[:maxlon] and params[:maxlat]
bbox_array = [params[:minlon], params[:minlat], params[:maxlon], params[:maxlat]]
end
from_bbox_array(bbox_array)
end
def min_lat
@bbox[1]
def expand!(bbox, margin = 0)
update!(bbox) unless complete?
# only try to expand the bbox if there is a value for every coordinate
# which there will be from the previous line as long as array does not contain a nil
if bbox.complete?
@min_lon = [-SCALED_LON_LIMIT,
bbox.min_lon + margin * (min_lon - max_lon)].max if bbox.min_lon < min_lon
@min_lat = [-SCALED_LAT_LIMIT,
bbox.min_lat + margin * (min_lat - max_lat)].max if bbox.min_lat < min_lat
@max_lon = [+SCALED_LON_LIMIT,
bbox.max_lon + margin * (max_lon - min_lon)].min if bbox.max_lon > max_lon
@max_lat = [+SCALED_LAT_LIMIT,
bbox.max_lat + margin * (max_lat - min_lat)].min if bbox.max_lat > max_lat
end
self
end
def min_lat=(min_lat)
@bbox[1] = min_lat
def check_boundaries
# check the bbox is sane
if min_lon > max_lon
raise OSM::APIBadBoundingBox.new(
"The minimum longitude must be less than the maximum longitude, but it wasn't")
end
if min_lat > max_lat
raise OSM::APIBadBoundingBox.new(
"The minimum latitude must be less than the maximum latitude, but it wasn't")
end
if min_lon < -LON_LIMIT || min_lat < -LAT_LIMIT || max_lon > +LON_LIMIT || max_lat > +LAT_LIMIT
raise OSM::APIBadBoundingBox.new("The latitudes must be between #{-LAT_LIMIT} and #{LAT_LIMIT}," +
" and longitudes between #{-LON_LIMIT} and #{LON_LIMIT}")
end
self
end
def max_lon
@bbox[2]
def check_size
# check the bbox isn't too large
if area > MAX_REQUEST_AREA
raise OSM::APIBadBoundingBox.new("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")
end
self
end
def max_lon=(max_lon)
@bbox[2] = max_lon
##
# returns area of the bbox as a rough comparative quantity
def area
if complete?
(max_lon - min_lon) * (max_lat - min_lat)
else
0
end
end
def max_lat
@bbox[3]
end
def max_lat=(max_lat)
@bbox[3] = max_lat
def complete?
not to_a.include?(nil)
end
def centre_lon
(@bbox[0] + @bbox[2]) / 2.0
(min_lon + max_lon) / 2.0
end
def centre_lat
(@bbox[1] + @bbox[3]) / 2.0
(min_lat + max_lat) / 2.0
end
def width
@bbox[2] - @bbox[0]
max_lon - min_lon
end
def height
@bbox[3] - @bbox[1]
max_lat - min_lat
end
def slippy_width(zoom)
@ -63,10 +118,64 @@ class BoundingBox
min = min_lat * Math::PI / 180.0
max = max_lat * Math::PI / 180.0
Math.log((Math.tan(max) + 1.0 / Math.cos(max)) / (Math.tan(min) + 1.0 / Math.cos(min))) * 128.0 * 2.0 ** zoom / Math::PI
Math.log((Math.tan(max) + 1.0 / Math.cos(max)) /
(Math.tan(min) + 1.0 / Math.cos(min))) *
(128.0 * 2.0 ** zoom / Math::PI)
end
# there are two forms used for bounds with and without an underscore,
# cater for both forms eg minlon and min_lon
def add_bounds_to(hash, underscore = "")
hash["min#{underscore}lat"] = min_lat.to_s
hash["min#{underscore}lon"] = min_lon.to_s
hash["max#{underscore}lat"] = max_lat.to_s
hash["max#{underscore}lon"] = max_lon.to_s
hash
end
def to_scaled
BoundingBox.new((min_lon * GeoRecord::SCALE),
(min_lat * GeoRecord::SCALE),
(max_lon * GeoRecord::SCALE),
(max_lat * GeoRecord::SCALE))
end
def to_unscaled
BoundingBox.new((min_lon / GeoRecord::SCALE),
(min_lat / GeoRecord::SCALE),
(max_lon / GeoRecord::SCALE),
(max_lat / GeoRecord::SCALE))
end
def to_a
[min_lon, min_lat, max_lon, max_lat]
end
def to_s
return @bbox.join(",")
"#{min_lon},#{min_lat},#{max_lon},#{max_lat}"
end
private
def self.from_bbox_array(bbox_array)
unless bbox_array
raise OSM::APIBadUserInput.new(
"The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat")
end
# Take an array of length 4, create a bounding box with min_lon, min_lat, max_lon and
# max_lat within their respective boundaries.
min_lon = [[bbox_array[0].to_f, -LON_LIMIT].max, +LON_LIMIT].min
min_lat = [[bbox_array[1].to_f, -LAT_LIMIT].max, +LAT_LIMIT].min
max_lon = [[bbox_array[2].to_f, +LON_LIMIT].min, -LON_LIMIT].max
max_lat = [[bbox_array[3].to_f, +LAT_LIMIT].min, -LAT_LIMIT].max
BoundingBox.new(min_lon, min_lat, max_lon, max_lat)
end
def update!(bbox)
# ensure that bbox has no nils in it. if there are any
# nils, just use the bounding box update to write over them.
@min_lon = bbox.min_lon if min_lon.nil?
@min_lat = bbox.min_lat if min_lat.nil?
@max_lon = bbox.max_lon if max_lon.nil?
@max_lat = bbox.max_lat if max_lat.nil?
end
end

View file

@ -5,7 +5,7 @@ module GeoRecord
SCALE = 10000000
def self.included(base)
base.scope :bbox, lambda { |minlat,minlon,maxlat,maxlon| base.where(OSM.sql_for_area(minlat, minlon, maxlat, maxlon)) }
base.scope :bbox, lambda { |bbox| base.where(OSM.sql_for_area(bbox)) }
base.before_save :update_tile
end

View file

@ -1,32 +0,0 @@
module MapBoundary
# Take an array of length 4, and return the min_lon, min_lat, max_lon and
# max_lat within their respective boundaries.
def sanitise_boundaries(bbox)
min_lon = [[bbox[0].to_f,-180].max,180].min
min_lat = [[bbox[1].to_f,-90].max,90].min
max_lon = [[bbox[2].to_f,+180].min,-180].max
max_lat = [[bbox[3].to_f,+90].min,-90].max
return min_lon, min_lat, max_lon, max_lat
end
def check_boundaries(min_lon, min_lat, max_lon, max_lat)
# check the bbox is sane
unless min_lon <= max_lon
raise OSM::APIBadBoundingBox.new("The minimum longitude must be less than the maximum longitude, but it wasn't")
end
unless min_lat <= max_lat
raise OSM::APIBadBoundingBox.new("The minimum latitude must be less than the maximum latitude, but it wasn't")
end
unless min_lon >= -180 && min_lat >= -90 && max_lon <= 180 && max_lat <= 90
# Due to sanitize_boundaries, it is highly unlikely we'll actually get here
raise OSM::APIBadBoundingBox.new("The latitudes must be between -90 and 90, and longitudes between -180 and 180")
end
# check the bbox isn't too large
requested_area = (max_lat-min_lat)*(max_lon-min_lon)
if requested_area > MAX_REQUEST_AREA
raise OSM::APIBadBoundingBox.new("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")
end
end
end

View file

@ -497,14 +497,12 @@ module OSM
end
# Return an SQL fragment to select a given area of the globe
def self.sql_for_area(minlat, minlon, maxlat, maxlon, prefix = nil)
tilesql = QuadTile.sql_for_area(minlat, minlon, maxlat, maxlon, prefix)
minlat = (minlat * 10000000).round
minlon = (minlon * 10000000).round
maxlat = (maxlat * 10000000).round
maxlon = (maxlon * 10000000).round
def self.sql_for_area(bbox, prefix = nil)
tilesql = QuadTile.sql_for_area(bbox, prefix)
bbox = bbox.to_scaled
return "#{tilesql} AND #{prefix}latitude BETWEEN #{minlat} AND #{maxlat} AND #{prefix}longitude BETWEEN #{minlon} AND #{maxlon}"
return "#{tilesql} AND #{prefix}latitude BETWEEN #{bbox.min_lat} AND #{bbox.max_lat} " +
"AND #{prefix}longitude BETWEEN #{bbox.min_lon} AND #{bbox.max_lon}"
end
# Return a spam score for a chunk of text

View file

@ -9,11 +9,11 @@ module QuadTile
return tile_for_xy(x, y)
end
def self.tiles_for_area(minlat, minlon, maxlat, maxlon)
minx = ((minlon + 180) * 65535 / 360).round
maxx = ((maxlon + 180) * 65535 / 360).round
miny = ((minlat + 90) * 65535 / 180).round
maxy = ((maxlat + 90) * 65535 / 180).round
def self.tiles_for_area(bbox)
minx = ((bbox.min_lon + 180) * 65535 / 360).round
maxx = ((bbox.max_lon + 180) * 65535 / 360).round
miny = ((bbox.min_lat + 90) * 65535 / 180).round
maxy = ((bbox.max_lat + 90) * 65535 / 180).round
tiles = []
minx.upto(maxx) do |x|
@ -40,8 +40,8 @@ module QuadTile
return t
end
def self.iterate_tiles_for_area(minlat, minlon, maxlat, maxlon)
tiles = tiles_for_area(minlat, minlon, maxlat, maxlon)
def self.iterate_tiles_for_area(bbox)
tiles = tiles_for_area(bbox)
first = last = nil
tiles.sort.each do |tile|
@ -60,11 +60,11 @@ module QuadTile
end
end
def self.sql_for_area(minlat, minlon, maxlat, maxlon, prefix)
def self.sql_for_area(bbox, prefix)
sql = Array.new
single = Array.new
iterate_tiles_for_area(minlat, minlon, maxlat, maxlon) do |first,last|
iterate_tiles_for_area(bbox) do |first,last|
if first == last
single.push(first)
else

View file

@ -63,12 +63,12 @@ static VALUE tile_for_point(VALUE self, VALUE lat, VALUE lon)
return UINT2NUM(xy2tile(x, y));
}
static VALUE tiles_for_area(VALUE self, VALUE minlat, VALUE minlon, VALUE maxlat, VALUE maxlon)
static VALUE tiles_for_area(VALUE self, VALUE bbox)
{
unsigned int minx = lon2x(NUM2DBL(minlon));
unsigned int maxx = lon2x(NUM2DBL(maxlon));
unsigned int miny = lat2y(NUM2DBL(minlat));
unsigned int maxy = lat2y(NUM2DBL(maxlat));
unsigned int minx = lon2x(NUM2DBL(rb_iv_get(bbox, "@min_lon")));
unsigned int maxx = lon2x(NUM2DBL(rb_iv_get(bbox, "@max_lon")));
unsigned int miny = lat2y(NUM2DBL(rb_iv_get(bbox, "@min_lat")));
unsigned int maxy = lat2y(NUM2DBL(rb_iv_get(bbox, "@max_lat")));
tilelist_t tl = tilelist_for_area(minx, miny, maxx, maxy);
VALUE tiles = rb_ary_new();
unsigned int t;
@ -88,12 +88,12 @@ static VALUE tile_for_xy(VALUE self, VALUE x, VALUE y)
return UINT2NUM(xy2tile(NUM2UINT(x), NUM2UINT(y)));
}
static VALUE iterate_tiles_for_area(VALUE self, VALUE minlat, VALUE minlon, VALUE maxlat, VALUE maxlon)
static VALUE iterate_tiles_for_area(VALUE self, VALUE bbox)
{
unsigned int minx = lon2x(NUM2DBL(minlon));
unsigned int maxx = lon2x(NUM2DBL(maxlon));
unsigned int miny = lat2y(NUM2DBL(minlat));
unsigned int maxy = lat2y(NUM2DBL(maxlat));
unsigned int minx = lon2x(NUM2DBL(rb_iv_get(bbox, "@min_lon")));
unsigned int maxx = lon2x(NUM2DBL(rb_iv_get(bbox, "@max_lon")));
unsigned int miny = lat2y(NUM2DBL(rb_iv_get(bbox, "@min_lat")));
unsigned int maxy = lat2y(NUM2DBL(rb_iv_get(bbox, "@max_lat")));
tilelist_t tl = tilelist_for_area(minx, miny, maxx, maxy);
if (tl.tilec > 0)
@ -140,9 +140,9 @@ void Init_quad_tile_so(void)
VALUE m = rb_define_module("QuadTile");
rb_define_module_function(m, "tile_for_point", tile_for_point, 2);
rb_define_module_function(m, "tiles_for_area", tiles_for_area, 4);
rb_define_module_function(m, "tiles_for_area", tiles_for_area, 1);
rb_define_module_function(m, "tile_for_xy", tile_for_xy, 2);
rb_define_module_function(m, "iterate_tiles_for_area", iterate_tiles_for_area, 4);
rb_define_module_function(m, "iterate_tiles_for_area", iterate_tiles_for_area, 1);
return;
}

View file

@ -506,7 +506,7 @@ class RelationControllerTest < ActionController::TestCase
def test_tag_modify_bounding_box
# in current fixtures, relation 5 contains nodes 3 and 5 (node 3
# indirectly via way 3), so the bbox should be [3,3,5,5].
check_changeset_modify([3,3,5,5]) do |changeset_id|
check_changeset_modify(BoundingBox.new(3,3,5,5)) do |changeset_id|
# add a tag to an existing relation
relation_xml = current_relations(:visible_relation).to_xml
relation_element = relation_xml.find("//osm/relation").first
@ -536,7 +536,7 @@ class RelationControllerTest < ActionController::TestCase
current_ways(:used_way),
current_ways(:way_with_versions)
].each_with_index do |element, version|
bbox = element.bbox.collect { |x| x / SCALE }
bbox = element.bbox.to_unscaled
check_changeset_modify(bbox) do |changeset_id|
relation_xml = Relation.find(relation_id).to_xml
relation_element = relation_xml.find("//osm/relation").first
@ -566,7 +566,7 @@ class RelationControllerTest < ActionController::TestCase
# remove a member from a relation and check the bounding box is
# only that element.
def test_remove_member_bounding_box
check_changeset_modify([5,5,5,5]) do |changeset_id|
check_changeset_modify(BoundingBox.new(5,5,5,5)) do |changeset_id|
# remove node 5 (5,5) from an existing relation
relation_xml = current_relations(:visible_relation).to_xml
relation_xml.
@ -769,10 +769,10 @@ OSM
assert_response :success, "can't re-read changeset for modify test"
assert_select "osm>changeset", 1, "Changeset element doesn't exist in #{@response.body}"
assert_select "osm>changeset[id=#{changeset_id}]", 1, "Changeset id=#{changeset_id} doesn't exist in #{@response.body}"
assert_select "osm>changeset[min_lon=#{bbox[0].to_f}]", 1, "Changeset min_lon wrong in #{@response.body}"
assert_select "osm>changeset[min_lat=#{bbox[1].to_f}]", 1, "Changeset min_lat wrong in #{@response.body}"
assert_select "osm>changeset[max_lon=#{bbox[2].to_f}]", 1, "Changeset max_lon wrong in #{@response.body}"
assert_select "osm>changeset[max_lat=#{bbox[3].to_f}]", 1, "Changeset max_lat wrong in #{@response.body}"
assert_select "osm>changeset[min_lon=#{bbox.min_lon}]", 1, "Changeset min_lon wrong in #{@response.body}"
assert_select "osm>changeset[min_lat=#{bbox.min_lat}]", 1, "Changeset min_lat wrong in #{@response.body}"
assert_select "osm>changeset[max_lon=#{bbox.max_lon}]", 1, "Changeset max_lon wrong in #{@response.body}"
assert_select "osm>changeset[max_lat=#{bbox.max_lat}]", 1, "Changeset max_lat wrong in #{@response.body}"
end
end

View file

@ -0,0 +1,316 @@
require File.dirname(__FILE__) + '/../test_helper'
class BoundingBoxTest < ActiveSupport::TestCase
def setup
@size_error_message = "The maximum bbox size is 0.25, and your request was too large. Either request a smaller area, or use planet.osm"
@malformed_error_message = "The parameter bbox is required, and must be of the form min_lon,min_lat,max_lon,max_lat"
@lon_order_error_message = "The minimum longitude must be less than the maximum longitude, but it wasn't"
@lat_order_error_message = "The minimum latitude must be less than the maximum latitude, but it wasn't"
@bbox_out_of_limits_error_message = "The latitudes must be between -90.0 and 90.0, and longitudes between -180.0 and 180.0"
@nil_error_message = "Should not contain nil"
@bbox_from_nils = BoundingBox.new(nil, nil, nil, nil)
@bbox_expand = BoundingBox.new(0,0,0,0)
@bbox_expand_ten = BoundingBox.new(10,10,10,10)
@bbox_expand_minus_two = BoundingBox.new(-2,-2,-2,-2)
@bbox_from_string = BoundingBox.from_s("1,2,3,4")
@min_lon = 1.0
@min_lat = 2.0
@max_lon = 3.0
@max_lat = 4.0
@bad_positive_boundary_bbox = %w{ 181,91,0,0 0,0,181,91 }
@bad_negative_boundary_bbox = %w{ -181,-91,0,0 0,0,-181,-91 }
@bad_big_bbox = %w{ -0.1,-0.1,1.1,1.1 10,10,11,11 }
@bad_malformed_bbox = %w{ -0.1 hello 10N2W10.1N2.1W }
@bad_lat_mixed_bbox = %w{ 0,0.1,0.1,0 -0.1,80,0.1,70 0.24,54.34,0.25,54.33 }
@bad_lon_mixed_bbox = %w{ 80,-0.1,70,0.1 54.34,0.24,54.33,0.25 }
@bad_limit_bbox = %w{ -180.1,-90,180,90 -180,-90.1,180,90 -180,-90,180.1,90 -180,-90,180,90.1}
@good_bbox = %w{ -0.1,-0.1,0.1,0.1 51.1,-0.1,51.2,0 -0.1,%20-0.1,%200.1,%200.1
-0.1edcd,-0.1d,0.1,0.1 -0.1E,-0.1E,0.1S,0.1N S0.1,W0.1,N0.1,E0.1}
@expand_min_lon_array = %w{ 2,10,10,10 1,10,10,10 0,10,10,10 -1,10,10,10 -2,10,10,10 -8,10,10,10 }
@expand_min_lat_array = %w{ 10,2,10,10 10,1,10,10 10,0,10,10 10,-1,10,10 10,-2,10,10 10,-8,10,10 }
@expand_max_lon_array = %w{ -2,-2,-1,-2 -2,-2,0,-2 -2,-2,1,-2 -2,-2,2,-2 }
@expand_max_lat_array = %w{ -2,-2,-2,-1 -2,-2,-2,0 -2,-2,-2,1 -2,-2,-2,2 }
@expand_min_lon_margin_response = [[2,10,10,10], [-7,10,10,10], [-7,10,10,10], [-7,10,10,10], [-7,10,10,10], [-25,10,10,10]]
@expand_min_lat_margin_response = [[10,2,10,10], [10,-7,10,10], [10,-7,10,10], [10,-7,10,10], [10,-7,10,10], [10,-25,10,10]]
@expand_max_lon_margin_response = [[-2,-2,-1,-2], [-2,-2,1,-2], [-2,-2,1,-2], [-2,-2,5,-2]]
@expand_max_lat_margin_response = [[-2,-2,-2,-1], [-2,-2,-2,1], [-2,-2,-2,1], [-2,-2,-2,5]]
end
def test_good_bbox_from_string
@good_bbox.each do |string|
bbox = BoundingBox.from_s(string)
array = string.split(',').collect {|s| s.to_f}
check_bbox(bbox, array)
end
end
def test_bbox_from_s_malformed
@bad_malformed_bbox.each do |bbox_string|
bbox = BoundingBox.from_s(bbox_string)
assert_nil bbox
end
end
def test_good_bbox_from_params
@good_bbox.each do |string|
bbox = BoundingBox.from_bbox_params({:bbox => string})
array = string.split(',').collect {|s| s.to_f}
check_bbox(bbox, array)
end
end
def test_good_bbox_from_lon_lat_params
@good_bbox.each do |string|
array = string.split(',')
bbox = BoundingBox.from_lon_lat_params({:minlon => array[0], :minlat => array[1], :maxlon => array[2], :maxlat => array[3]})
check_bbox(bbox, array.collect {|s| s.to_f})
end
end
def test_bbox_from_params_malformed
@bad_malformed_bbox.each do |bbox_string|
exception = assert_raise(OSM::APIBadUserInput) {BoundingBox.from_bbox_params({:bbox => bbox_string})}
assert_equal(@malformed_error_message, exception.message)
end
end
def test_good_bbox_from_new
@good_bbox.each do |string|
array = string.split(',')
bbox = BoundingBox.new(array[0], array[1], array[2], array[3])
check_bbox(bbox, array.collect {|s| s.to_f})
end
end
def test_creation_from_new_with_nils
check_bbox(@bbox_from_nils, [nil, nil, nil, nil])
end
def test_expand_min_lon_boundary
@bbox_expand.expand!(BoundingBox.new(-1810000000,0,0,0))
check_expand(@bbox_expand, "-1800000000,0,0,0")
end
def test_expand_min_lat_boundary
@bbox_expand.expand!(BoundingBox.new(0,-910000000,0,0))
check_expand(@bbox_expand, "0,-900000000,0,0")
end
def test_expand_max_lon_boundary
@bbox_expand.expand!(BoundingBox.new(0,0,1810000000,0))
check_expand(@bbox_expand, "0,0,1800000000,0")
end
def test_expand_max_lat_boundary
@bbox_expand.expand!(BoundingBox.new(0,0,0,910000000))
check_expand(@bbox_expand, "0,0,0,900000000")
end
def test_expand_min_lon_without_margin
@expand_min_lon_array.each {|array_string| check_expand(@bbox_expand_ten, array_string)}
end
def test_expand_min_lon_with_margin
@expand_min_lon_array.each_with_index do |array_string, index|
check_expand(@bbox_expand_ten, array_string, 1, @expand_min_lon_margin_response[index])
end
end
def test_expand_min_lat_without_margin
@expand_min_lat_array.each {|array_string| check_expand(@bbox_expand_ten, array_string)}
end
def test_expand_min_lat_with_margin
@expand_min_lat_array.each_with_index do |array_string, index|
check_expand(@bbox_expand_ten, array_string, 1, @expand_min_lat_margin_response[index])
end
end
def test_expand_max_lon_without_margin
@expand_max_lon_array.each {|array_string| check_expand(@bbox_expand_minus_two, array_string)}
end
def test_expand_max_lon_with_margin
@expand_max_lon_array.each_with_index do |array_string, index|
check_expand(@bbox_expand_minus_two, array_string, 1, @expand_max_lon_margin_response[index])
end
end
def test_expand_max_lat_without_margin
@expand_max_lat_array.each {|array_string| check_expand(@bbox_expand_minus_two, array_string)}
end
def test_expand_max_lat_with_margin
bbox = @bbox_expand
@expand_max_lat_array.each_with_index do |array_string, index|
check_expand(@bbox_expand_minus_two, array_string, 1, @expand_max_lat_margin_response[index])
end
end
def test_good_bbox_boundaries
@good_bbox.each do |bbox_string|
assert_nothing_raised(OSM::APIBadBoundingBox) {BoundingBox.from_s(bbox_string).check_boundaries}
end
end
def test_from_params_with_positive_out_of_boundary
@bad_positive_boundary_bbox.each do |bbox_string|
bbox = BoundingBox.from_bbox_params({:bbox => bbox_string})
array = bbox.to_a
assert_equal 180, [array[0], array[2]].max
assert_equal 90, [array[1], array[3]].max
end
end
def test_from_params_with_negative_out_of_boundary
@bad_negative_boundary_bbox.each do |bbox_string|
bbox = BoundingBox.from_bbox_params({:bbox => bbox_string})
array = bbox.to_a
assert_equal -180, [array[0], array[2]].min
assert_equal -90, [array[1], array[3]].min
end
end
def test_boundaries_mixed_lon
@bad_lon_mixed_bbox.each do |bbox_string|
exception = assert_raise(OSM::APIBadBoundingBox) {BoundingBox.from_s(bbox_string).check_boundaries}
assert_equal(@lon_order_error_message, exception.message)
end
end
def test_boundaries_mixed_lat
@bad_lat_mixed_bbox.each do |bbox_string|
exception = assert_raise(OSM::APIBadBoundingBox) {BoundingBox.from_s(bbox_string).check_boundaries}
assert_equal(@lat_order_error_message, exception.message)
end
end
def test_boundaries_out_of_limits
@bad_limit_bbox.each do |bbox_string|
exception = assert_raise(OSM::APIBadBoundingBox) {BoundingBox.from_s(bbox_string).check_boundaries}
assert_equal(@bbox_out_of_limits_error_message, exception.message)
end
end
def test_good_bbox_size
@good_bbox.each do |bbox_string|
assert_nothing_raised(OSM::APIBadBoundingBox) {BoundingBox.from_s(bbox_string).check_size}
end
end
def test_size_to_big
@bad_big_bbox.each do |bbox_string|
bbox = nil
assert_nothing_raised(OSM::APIBadBoundingBox) {bbox = BoundingBox.from_bbox_params({:bbox => bbox_string}).check_boundaries}
exception = assert_raise(OSM::APIBadBoundingBox) {bbox.check_size}
assert_equal(@size_error_message, exception.message)
end
end
def test_bbox_area
@good_bbox.each do |string|
bbox = BoundingBox.from_s(string)
array = string.split(',')
assert_equal ((array[2].to_f - array[0].to_f) * (array[3].to_f - array[1].to_f)), bbox.area
end
end
def test_complete
assert !@bbox_from_nils.complete?, "should contain a nil"
assert @bbox_from_string.complete?, "should not contain a nil"
end
def test_centre_lon
@good_bbox.each do |bbox_string|
array = bbox_string.split(',')
assert_equal ((array[0].to_f + array[2].to_f) / 2.0), BoundingBox.from_s(bbox_string).centre_lon
end
end
def test_centre_lat
@good_bbox.each do |bbox_string|
array = bbox_string.split(',')
assert_equal ((array[1].to_f + array[3].to_f) / 2.0), BoundingBox.from_s(bbox_string).centre_lat
end
end
def test_width
@good_bbox.each do |bbox_string|
array = bbox_string.split(',')
assert_equal (array[2].to_f - array[0].to_f), BoundingBox.from_s(bbox_string).width
end
end
def test_height
@good_bbox.each do |bbox_string|
array = bbox_string.split(',')
assert_equal (array[3].to_f - array[1].to_f), BoundingBox.from_s(bbox_string).height
end
end
def test_slippy_width
assert_in_delta 5.68888888888889, @bbox_from_string.slippy_width(2), 0.000000000000001
end
def test_slippy_height
assert_in_delta 5.69698684268433, @bbox_from_string.slippy_height(2), 0.000000000000001
end
def test_add_bounds_to_no_underscore
bounds = @bbox_from_string.add_bounds_to({})
assert_equal 4, bounds.size
assert_equal @min_lon.to_s, bounds['minlon']
assert_equal @min_lat.to_s, bounds['minlat']
assert_equal @max_lon.to_s, bounds['maxlon']
assert_equal @max_lat.to_s, bounds['maxlat']
end
def test_add_bounds_to_with_underscore
bounds = @bbox_from_string.add_bounds_to({}, '_')
assert_equal 4, bounds.size
assert_equal @min_lon.to_s, bounds['min_lon']
assert_equal @min_lat.to_s, bounds['min_lat']
assert_equal @max_lon.to_s, bounds['max_lon']
assert_equal @max_lat.to_s, bounds['max_lat']
end
def test_to_scaled
bbox = @bbox_from_string.to_scaled
assert_equal @min_lon * GeoRecord::SCALE, bbox.min_lon
assert_equal @min_lat * GeoRecord::SCALE, bbox.min_lat
assert_equal @max_lon * GeoRecord::SCALE, bbox.max_lon
assert_equal @max_lat * GeoRecord::SCALE, bbox.max_lat
end
def test_to_unscaled
scale = GeoRecord::SCALE
bbox = BoundingBox.new(1.0 * scale, 2.0 * scale, 3.0 * scale, 4.0 * scale).to_unscaled
check_bbox(bbox, [@min_lon, @min_lat, @max_lon, @max_lat])
end
def test_to_a
assert_equal [1.0,2.0,3.0,4.0], @bbox_from_string.to_a
end
def test_to_string
assert_equal "#{@min_lon},#{@min_lat},#{@max_lon},#{@max_lat}", @bbox_from_string.to_s
end
private
def check_expand(bbox, array_string, margin = 0, result = nil)
array = array_string.split(',').collect {|s| s.to_f}
result = array unless result
bbox.expand!(BoundingBox.new(array[0], array[1], array[2], array[3]), margin)
check_bbox(bbox, result)
end
def check_bbox(bbox, result)
assert_equal result[0], bbox.min_lon, 'min_lon'
assert_equal result[1], bbox.min_lat, 'min_lat'
assert_equal result[2], bbox.max_lon, 'max_lon'
assert_equal result[3], bbox.max_lat, 'max_lat'
end
end

View file

@ -15,7 +15,10 @@ class WayTest < ActiveSupport::TestCase
:invisible_way,
:used_way ].each do |way_symbol|
way = current_ways(way_symbol)
assert_equal node.bbox, way.bbox
assert_equal node.bbox.min_lon, way.bbox.min_lon, 'min_lon'
assert_equal node.bbox.min_lat, way.bbox.min_lat, 'min_lat'
assert_equal node.bbox.max_lon, way.bbox.max_lon, 'max_lon'
assert_equal node.bbox.max_lat, way.bbox.max_lat, 'max_lat'
end
end