From 6b5a3536bc586b8b67686b51e19209c0b738db98 Mon Sep 17 00:00:00 2001 From: Anton Khorev Date: Mon, 31 Mar 2025 05:02:29 +0300 Subject: [PATCH] Create nominatim search query resource --- app/abilities/ability.rb | 2 +- app/controllers/geocoder_controller.rb | 60 +----------------- .../searches/nominatim_queries_controller.rb | 61 +++++++++++++++++++ config/routes.rb | 2 +- test/abilities/abilities_test.rb | 2 +- test/controllers/geocoder_controller_test.rb | 37 ----------- .../nominatim_queries_controller_test.rb | 45 ++++++++++++++ 7 files changed, 110 insertions(+), 99 deletions(-) create mode 100644 app/controllers/searches/nominatim_queries_controller.rb create mode 100644 test/controllers/searches/nominatim_queries_controller_test.rb diff --git a/app/abilities/ability.rb b/app/abilities/ability.rb index 8c943b880..2aae3f4ae 100644 --- a/app/abilities/ability.rb +++ b/app/abilities/ability.rb @@ -10,7 +10,7 @@ class Ability can :search, :direction can [:index, :permalink, :edit, :help, :fixthemap, :offline, :export, :about, :communities, :preview, :copyright, :id], :site can [:finish, :embed], :export - can [:create, :search, :search_osm_nominatim, :search_osm_nominatim_reverse], :geocoder + can [:create, :search, :search_osm_nominatim_reverse], :geocoder if Settings.status != "database_offline" can [:read, :feed], Changeset diff --git a/app/controllers/geocoder_controller.rb b/app/controllers/geocoder_controller.rb index 7f5416e8d..c8d3c35cd 100644 --- a/app/controllers/geocoder_controller.rb +++ b/app/controllers/geocoder_controller.rb @@ -21,7 +21,7 @@ class GeocoderController < ApplicationController :fetch_url => url_for(params.permit(:lat, :lon, :zoom).merge(:action => "search_osm_nominatim_reverse"))) elsif params[:query] @sources.push(:name => "osm_nominatim", :url => nominatim_query_url(:format => "html"), - :fetch_url => url_for(params.permit(:query, :minlat, :minlon, :maxlat, :maxlon).merge(:action => "search_osm_nominatim"))) + :fetch_url => search_nominatim_query_path(params.permit(:query, :minlat, :minlon, :maxlat, :maxlon))) end if @sources.empty? @@ -31,64 +31,6 @@ class GeocoderController < ApplicationController end end - def search_osm_nominatim - # ask nominatim - response = fetch_xml(nominatim_query_url(:format => "xml")) - - # extract the results from the response - results = response.elements["searchresults"] - - # create result array - @results = [] - - # create parameter hash for "more results" link - @more_params = params - .permit(:query, :minlon, :minlat, :maxlon, :maxlat, :exclude) - .merge(:exclude => results.attributes["exclude_place_ids"]) - - # parse the response - results.elements.each("place") do |place| - lat = place.attributes["lat"] - lon = place.attributes["lon"] - klass = place.attributes["class"] - type = place.attributes["type"] - name = place.attributes["display_name"] - min_lat, max_lat, min_lon, max_lon = place.attributes["boundingbox"].split(",") - prefix_name = if type.empty? - "" - else - t "geocoder.search_osm_nominatim.prefix.#{klass}.#{type}", :default => type.tr("_", " ").capitalize - end - if klass == "boundary" && type == "administrative" - rank = (place.attributes["address_rank"].to_i + 1) / 2 - prefix_name = t "geocoder.search_osm_nominatim.admin_levels.level#{rank}", :default => prefix_name - border_type = nil - place_type = nil - place_tags = %w[linked_place place] - place.elements["extratags"].elements.each("tag") do |extratag| - border_type = t "geocoder.search_osm_nominatim.border_types.#{extratag.attributes['value']}", :default => border_type if extratag.attributes["key"] == "border_type" - place_type = t "geocoder.search_osm_nominatim.prefix.place.#{extratag.attributes['value']}", :default => place_type if place_tags.include?(extratag.attributes["key"]) - end - prefix_name = place_type || border_type || prefix_name - end - prefix = t ".prefix_format", :name => prefix_name - object_type = place.attributes["osm_type"] - object_id = place.attributes["osm_id"] - - @results.push(:lat => lat, :lon => lon, - :min_lat => min_lat, :max_lat => max_lat, - :min_lon => min_lon, :max_lon => max_lon, - :prefix => prefix, :name => name, - :type => object_type, :id => object_id) - end - - render "searches/queries/create" - rescue StandardError => e - host = URI(Settings.nominatim_url).host - @error = "Error contacting #{host}: #{e}" - render "searches/queries/error" - end - def search_osm_nominatim_reverse # get query parameters zoom = params[:zoom] diff --git a/app/controllers/searches/nominatim_queries_controller.rb b/app/controllers/searches/nominatim_queries_controller.rb new file mode 100644 index 000000000..f5d27ad82 --- /dev/null +++ b/app/controllers/searches/nominatim_queries_controller.rb @@ -0,0 +1,61 @@ +module Searches + class NominatimQueriesController < QueriesController + include NominatimMethods + + def create + # ask nominatim + response = fetch_xml(nominatim_query_url(:format => "xml")) + + # extract the results from the response + results = response.elements["searchresults"] + + # create result array + @results = [] + + # create parameter hash for "more results" link + @more_params = params + .permit(:query, :minlon, :minlat, :maxlon, :maxlat, :exclude) + .merge(:exclude => results.attributes["exclude_place_ids"]) + + # parse the response + results.elements.each("place") do |place| + lat = place.attributes["lat"] + lon = place.attributes["lon"] + klass = place.attributes["class"] + type = place.attributes["type"] + name = place.attributes["display_name"] + min_lat, max_lat, min_lon, max_lon = place.attributes["boundingbox"].split(",") + prefix_name = if type.empty? + "" + else + t "geocoder.search_osm_nominatim.prefix.#{klass}.#{type}", :default => type.tr("_", " ").capitalize + end + if klass == "boundary" && type == "administrative" + rank = (place.attributes["address_rank"].to_i + 1) / 2 + prefix_name = t "geocoder.search_osm_nominatim.admin_levels.level#{rank}", :default => prefix_name + border_type = nil + place_type = nil + place_tags = %w[linked_place place] + place.elements["extratags"].elements.each("tag") do |extratag| + border_type = t "geocoder.search_osm_nominatim.border_types.#{extratag.attributes['value']}", :default => border_type if extratag.attributes["key"] == "border_type" + place_type = t "geocoder.search_osm_nominatim.prefix.place.#{extratag.attributes['value']}", :default => place_type if place_tags.include?(extratag.attributes["key"]) + end + prefix_name = place_type || border_type || prefix_name + end + prefix = t "geocoder.search_osm_nominatim.prefix_format", :name => prefix_name + object_type = place.attributes["osm_type"] + object_id = place.attributes["osm_id"] + + @results.push(:lat => lat, :lon => lon, + :min_lat => min_lat, :max_lat => max_lat, + :min_lon => min_lon, :max_lon => max_lon, + :prefix => prefix, :name => name, + :type => object_type, :id => object_id) + end + rescue StandardError => e + host = URI(Settings.nominatim_url).host + @error = "Error contacting #{host}: #{e}" + render :action => "error" + end + end +end diff --git a/config/routes.rb b/config/routes.rb index c947a7a4d..b09c07c7c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -334,11 +334,11 @@ OpenStreetMap::Application.routes.draw do # geocoder get "/search" => "geocoder#search" - post "/geocoder/search_osm_nominatim" => "geocoder#search_osm_nominatim" post "/geocoder/search_osm_nominatim_reverse" => "geocoder#search_osm_nominatim_reverse" resource :search, :only => [] do scope :module => :searches do resource :latlon_query, :only => :create + resource :nominatim_query, :only => :create end end diff --git a/test/abilities/abilities_test.rb b/test/abilities/abilities_test.rb index f6cba39e2..8273cd3e5 100644 --- a/test/abilities/abilities_test.rb +++ b/test/abilities/abilities_test.rb @@ -9,7 +9,7 @@ class GuestAbilityTest < AbilityTest test "geocoder permission for a guest" do ability = Ability.new nil - [:create, :search, :search_osm_nominatim, + [:create, :search, :search_osm_nominatim_reverse].each do |action| assert ability.can?(action, :geocoder), "should be able to #{action} geocoder" end diff --git a/test/controllers/geocoder_controller_test.rb b/test/controllers/geocoder_controller_test.rb index 3cca57db0..fe0b46f1b 100644 --- a/test/controllers/geocoder_controller_test.rb +++ b/test/controllers/geocoder_controller_test.rb @@ -8,10 +8,6 @@ class GeocoderControllerTest < ActionDispatch::IntegrationTest { :path => "/search", :method => :get }, { :controller => "geocoder", :action => "search" } ) - assert_routing( - { :path => "/geocoder/search_osm_nominatim", :method => :post }, - { :controller => "geocoder", :action => "search_osm_nominatim" } - ) assert_routing( { :path => "/geocoder/search_osm_nominatim_reverse", :method => :post }, { :controller => "geocoder", :action => "search_osm_nominatim_reverse" } @@ -319,39 +315,6 @@ class GeocoderControllerTest < ActionDispatch::IntegrationTest search_check "foo bar baz", %w[osm_nominatim] end - ## - # Test the nominatim forward search - def test_search_osm_nominatim - with_http_stubs "nominatim" do - post geocoder_search_osm_nominatim_path(:query => "Hoddesdon", :zoom => 10, - :minlon => -0.559, :minlat => 51.217, - :maxlon => 0.836, :maxlat => 51.766), :xhr => true - results_check "name" => "Hoddesdon, Hertfordshire, East of England, England, United Kingdom", - "min-lat" => 51.7216709, "max-lat" => 51.8016709, - "min-lon" => -0.0512898, "max-lon" => 0.0287102, - "type" => "node", "id" => 18007599 - - post geocoder_search_osm_nominatim_path(:query => "Broxbourne", :zoom => 10, - :minlon => -0.559, :minlat => 51.217, - :maxlon => 0.836, :maxlat => 51.766), :xhr => true - results_check({ "prefix" => "Suburb", - "name" => "Broxbourne, Hertfordshire, East of England, England, United Kingdom", - "min-lat" => 51.7265723, "max-lat" => 51.7665723, - "min-lon" => -0.0390782, "max-lon" => 0.0009218, - "type" => "node", "id" => 28825933 }, - { "prefix" => "Village", - "name" => "Broxbourne, Hertfordshire, East of England, England, United Kingdom", - "min-lat" => 51.6808751, "max-lat" => 51.7806237, - "min-lon" => -0.114204, "max-lon" => 0.0145267, - "type" => "relation", "id" => 2677978 }, - { "prefix" => "Railway Station", - "name" => "Broxbourne, Stafford Drive, Broxbourne, Hertfordshire, East of England, England, United Kingdom", - "min-lat" => 51.7418469, "max-lat" => 51.7518469, - "min-lon" => -0.0156773, "max-lon" => -0.0056773, - "type" => "node", "id" => 17044599 }) - end - end - ## # Test the nominatim reverse search def test_search_osm_nominatim_reverse diff --git a/test/controllers/searches/nominatim_queries_controller_test.rb b/test/controllers/searches/nominatim_queries_controller_test.rb new file mode 100644 index 000000000..e7ede3dc0 --- /dev/null +++ b/test/controllers/searches/nominatim_queries_controller_test.rb @@ -0,0 +1,45 @@ +require_relative "queries_controller_test" + +module Searches + class NominatimQueriesControllerTest < QueriesControllerTest + ## + # test all routes which lead to this controller + def test_routes + assert_routing( + { :path => "/search/nominatim_query", :method => :post }, + { :controller => "searches/nominatim_queries", :action => "create" } + ) + end + + def test_create + with_http_stubs "nominatim" do + post search_nominatim_query_path(:query => "Hoddesdon", :zoom => 10, + :minlon => -0.559, :minlat => 51.217, + :maxlon => 0.836, :maxlat => 51.766), :xhr => true + results_check "name" => "Hoddesdon, Hertfordshire, East of England, England, United Kingdom", + "min-lat" => 51.7216709, "max-lat" => 51.8016709, + "min-lon" => -0.0512898, "max-lon" => 0.0287102, + "type" => "node", "id" => 18007599 + + post search_nominatim_query_path(:query => "Broxbourne", :zoom => 10, + :minlon => -0.559, :minlat => 51.217, + :maxlon => 0.836, :maxlat => 51.766), :xhr => true + results_check({ "prefix" => "Suburb", + "name" => "Broxbourne, Hertfordshire, East of England, England, United Kingdom", + "min-lat" => 51.7265723, "max-lat" => 51.7665723, + "min-lon" => -0.0390782, "max-lon" => 0.0009218, + "type" => "node", "id" => 28825933 }, + { "prefix" => "Village", + "name" => "Broxbourne, Hertfordshire, East of England, England, United Kingdom", + "min-lat" => 51.6808751, "max-lat" => 51.7806237, + "min-lon" => -0.114204, "max-lon" => 0.0145267, + "type" => "relation", "id" => 2677978 }, + { "prefix" => "Railway Station", + "name" => "Broxbourne, Stafford Drive, Broxbourne, Hertfordshire, East of England, England, United Kingdom", + "min-lat" => 51.7418469, "max-lat" => 51.7518469, + "min-lon" => -0.0156773, "max-lon" => -0.0056773, + "type" => "node", "id" => 17044599 }) + end + end + end +end -- 2.39.5