Support unwrapped bbox values in changeset history queries

This commit is contained in:
Anton Khorev 2025-01-05 14:10:20 +03:00
parent 8878c963f3
commit 930a8d7ac3
3 changed files with 125 additions and 11 deletions

View file

@ -59,7 +59,7 @@ OSM.History = function (map) {
const data = new URLSearchParams(); const data = new URLSearchParams();
if (location.pathname === "/history") { if (location.pathname === "/history") {
data.set("bbox", map.getBounds().wrap().toBBoxString()); data.set("bbox", map.getBounds().toBBoxString());
const feedLink = $("link[type=\"application/atom+xml\"]"), const feedLink = $("link[type=\"application/atom+xml\"]"),
feedHref = feedLink.attr("href").split("?")[0]; feedHref = feedLink.attr("href").split("?")[0];
feedLink.attr("href", feedHref + "?" + data); feedLink.attr("href", feedHref + "?" + data);

View file

@ -52,7 +52,10 @@ class ChangesetsController < ApplicationController
changesets.where("false") changesets.where("false")
end end
elsif @params[:bbox] elsif @params[:bbox]
changesets = conditions_bbox(changesets, BoundingBox.from_bbox_params(params)) bbox_array = @params[:bbox].split(",").map(&:to_f)
raise OSM::APIBadUserInput, "The parameter bbox must be of the form min_lon,min_lat,max_lon,max_lat" unless bbox_array.count == 4
changesets = conditions_bbox(changesets, *bbox_array)
elsif @params[:friends] && current_user elsif @params[:friends] && current_user
changesets = changesets.where(:user => current_user.followings.identifiable) changesets = changesets.where(:user => current_user.followings.identifiable)
elsif @params[:nearby] && current_user elsif @params[:nearby] && current_user
@ -113,21 +116,28 @@ class ChangesetsController < ApplicationController
#------------------------------------------------------------ #------------------------------------------------------------
## ##
# if a bounding box was specified do some sanity checks.
# restrict changesets to those enclosed by a bounding box # restrict changesets to those enclosed by a bounding box
def conditions_bbox(changesets, bbox) def conditions_bbox(changesets, min_lon, min_lat, max_lon, max_lat)
if bbox db_min_lat = (min_lat * GeoRecord::SCALE).to_i
bbox.check_boundaries db_max_lat = (max_lat * GeoRecord::SCALE).to_i
bbox = bbox.to_scaled db_min_lon = (wrap_lon(min_lon) * GeoRecord::SCALE).to_i
db_max_lon = (wrap_lon(max_lon) * GeoRecord::SCALE).to_i
changesets.where("min_lon < ? and max_lon > ? and min_lat < ? and max_lat > ?", changesets = changesets.where("min_lat < ? and max_lat > ?", db_max_lat, db_min_lat)
bbox.max_lon.to_i, bbox.min_lon.to_i,
bbox.max_lat.to_i, bbox.min_lat.to_i) if max_lon - min_lon >= 360
else
changesets changesets
elsif db_min_lon > db_max_lon
changesets.where("min_lon < ? or max_lon > ?", db_max_lon, db_min_lon)
else
changesets.where("min_lon < ? and max_lon > ?", db_max_lon, db_min_lon)
end end
end end
def wrap_lon(lon)
((lon + 180) % 360) - 180
end
## ##
# eliminate empty changesets (where the bbox has not been set) # eliminate empty changesets (where the bbox has not been set)
# this should be applied to all changeset list displays # this should be applied to all changeset list displays

View file

@ -114,6 +114,110 @@ class ChangesetsControllerTest < ActionDispatch::IntegrationTest
check_index_result(changesets) check_index_result(changesets)
end end
##
# Test bbox spanning across the dateline with changesets close to the dateline
def test_index_bbox_dateline
western_changeset = create(:changeset, :num_changes => 1,
:min_lat => 0 * GeoRecord::SCALE, :min_lon => 176 * GeoRecord::SCALE,
:max_lat => 1 * GeoRecord::SCALE, :max_lon => 178 * GeoRecord::SCALE)
eastern_changeset = create(:changeset, :num_changes => 1,
:min_lat => 0 * GeoRecord::SCALE, :min_lon => -178 * GeoRecord::SCALE,
:max_lat => 1 * GeoRecord::SCALE, :max_lon => -176 * GeoRecord::SCALE)
get history_path(:format => "html", :list => "1")
assert_response :success
check_index_result([eastern_changeset, western_changeset])
# negative longitudes
get history_path(:format => "html", :list => "1", :bbox => "-190,-10,-170,10")
assert_response :success
check_index_result([eastern_changeset, western_changeset])
get history_path(:format => "html", :list => "1", :bbox => "-183,-10,-177,10")
assert_response :success
check_index_result([eastern_changeset, western_changeset])
get history_path(:format => "html", :list => "1", :bbox => "-181,-10,-177,10")
assert_response :success
check_index_result([eastern_changeset])
get history_path(:format => "html", :list => "1", :bbox => "-183,-10,-179,10")
assert_response :success
check_index_result([western_changeset])
get history_path(:format => "html", :list => "1", :bbox => "-181,-10,-179,10")
assert_response :success
check_index_result([])
# positive longitudes
get history_path(:format => "html", :list => "1", :bbox => "170,-10,190,10")
assert_response :success
check_index_result([eastern_changeset, western_changeset])
get history_path(:format => "html", :list => "1", :bbox => "177,-10,183,10")
assert_response :success
check_index_result([eastern_changeset, western_changeset])
get history_path(:format => "html", :list => "1", :bbox => "177,-10,181,10")
assert_response :success
check_index_result([western_changeset])
get history_path(:format => "html", :list => "1", :bbox => "179,-10,183,10")
assert_response :success
check_index_result([eastern_changeset])
get history_path(:format => "html", :list => "1", :bbox => "179,-10,181,10")
assert_response :success
check_index_result([])
end
##
# Test bbox spanning across the dateline with changesets around the globe
def test_index_bbox_global
changeset1 = create(:changeset, :num_changes => 1,
:min_lat => 40 * GeoRecord::SCALE, :min_lon => -150 * GeoRecord::SCALE,
:max_lat => 50 * GeoRecord::SCALE, :max_lon => -140 * GeoRecord::SCALE)
changeset2 = create(:changeset, :num_changes => 1,
:min_lat => -30 * GeoRecord::SCALE, :min_lon => -30 * GeoRecord::SCALE,
:max_lat => -20 * GeoRecord::SCALE, :max_lon => -20 * GeoRecord::SCALE)
changeset3 = create(:changeset, :num_changes => 1,
:min_lat => 60 * GeoRecord::SCALE, :min_lon => 10 * GeoRecord::SCALE,
:max_lat => 70 * GeoRecord::SCALE, :max_lon => 20 * GeoRecord::SCALE)
changeset4 = create(:changeset, :num_changes => 1,
:min_lat => -60 * GeoRecord::SCALE, :min_lon => 150 * GeoRecord::SCALE,
:max_lat => -50 * GeoRecord::SCALE, :max_lon => 160 * GeoRecord::SCALE)
# no bbox, get all changesets
get history_path(:format => "html", :list => "1")
assert_response :success
check_index_result([changeset4, changeset3, changeset2, changeset1])
# large enough bbox within normal range
get history_path(:format => "html", :list => "1", :bbox => "-170,-80,170,80")
assert_response :success
check_index_result([changeset4, changeset3, changeset2, changeset1])
# bbox for [1,2] within normal range
get history_path(:format => "html", :list => "1", :bbox => "-160,-80,0,80")
assert_response :success
check_index_result([changeset2, changeset1])
# bbox for [1,4] containing dateline with negative lon
get history_path(:format => "html", :list => "1", :bbox => "-220,-80,-100,80")
assert_response :success
check_index_result([changeset4, changeset1])
# bbox for [1,4] containing dateline with positive lon
get history_path(:format => "html", :list => "1", :bbox => "100,-80,220,80")
assert_response :success
check_index_result([changeset4, changeset1])
# large enough bbox outside normal range
get history_path(:format => "html", :list => "1", :bbox => "-220,-80,220,80")
assert_response :success
check_index_result([changeset4, changeset3, changeset2, changeset1])
end
## ##
# Checks the display of the user changesets listing # Checks the display of the user changesets listing
def test_index_user def test_index_user