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
# 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] }
# 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]
# ====================================================================
# 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
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
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={}
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
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
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
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
# 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])
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
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
# 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
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
#------------------------------------------------------------
##
- # 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
#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"
@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']
# 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
##
# 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
##
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
# 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 })
#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
# 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
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)
<% 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>
});
<% 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();
<% @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);
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
class BoundingBox
- 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]
- end
-
- def self.from_s(s)
- BoundingBox.new(*s.split(/,/))
- end
-
- def min_lon
- @bbox[0]
- end
-
- def min_lon=(min_lon)
- @bbox[0] = min_lon
- end
-
- def min_lat
- @bbox[1]
- end
-
- def min_lat=(min_lat)
- @bbox[1] = min_lat
- end
+ attr_reader :min_lon, :min_lat, :max_lon, :max_lat
- def max_lon
- @bbox[2]
- end
-
- def max_lon=(max_lon)
- @bbox[2] = max_lon
- end
+private
+ LON_LIMIT = 180.0
+ LAT_LIMIT = 90.0
+ SCALED_LON_LIMIT = LON_LIMIT * GeoRecord::SCALE
+ SCALED_LAT_LIMIT = LAT_LIMIT * GeoRecord::SCALE
- def max_lat
- @bbox[3]
+public
+ def initialize(min_lon, min_lat, max_lon, max_lat)
+ @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 max_lat=(max_lat)
- @bbox[3] = max_lat
+ def self.from_s(s)
+ if s.count(',') == 3
+ BoundingBox.new(*s.split(/,/))
+ else
+ nil
+ end
+ end
+
+ 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 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 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 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 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
+
+ ##
+ # 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 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)
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
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
+++ /dev/null
-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
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
-
- return "#{tilesql} AND #{prefix}latitude BETWEEN #{minlat} AND #{maxlat} AND #{prefix}longitude BETWEEN #{minlon} AND #{maxlon}"
+ 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 #{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
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|
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|
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
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;
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)
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;
}
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
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
# 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.
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
--- /dev/null
+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
: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