From: Tom Hughes Date: Wed, 5 Feb 2025 19:09:37 +0000 (+0000) Subject: Merge remote-tracking branch 'upstream/pull/5597' X-Git-Tag: live~41 X-Git-Url: https://git.openstreetmap.org./rails.git/commitdiff_plain/cfd2c5dddab88372905a23f7b9680df58feb9b53?hp=1e57668c7ec25382bd50e135cb44d06ecd03f0ae Merge remote-tracking branch 'upstream/pull/5597' --- diff --git a/app/abilities/api_ability.rb b/app/abilities/api_ability.rb index c74a4d099..97b461b1f 100644 --- a/app/abilities/api_ability.rb +++ b/app/abilities/api_ability.rb @@ -16,8 +16,8 @@ class ApiAbility can :read, Tracepoint can :read, User can :read, Node - can [:read, :full, :ways_for_node], Way - can [:read, :full, :relations_for_node, :relations_for_way, :relations_for_relation], Relation + can [:read, :ways_for_node], Way + can [:read, :relations_for_node, :relations_for_way, :relations_for_relation], Relation can [:history, :read], [OldNode, OldWay, OldRelation] can :read, UserBlock diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js index ad34479d3..8ef067740 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -97,9 +97,7 @@ $(document).ready(function () { var position = $("html").attr("dir") === "rtl" ? "topleft" : "topright"; function addControlGroup(controls) { - controls.forEach(function (control) { - control.addTo(map); - }); + for (const control of controls) control.addTo(map); var firstContainer = controls[0].getContainer(); $(firstContainer).find(".control-button").first() diff --git a/app/assets/javascripts/index/history.js b/app/assets/javascripts/index/history.js index ae8f027ed..3c7101c74 100644 --- a/app/assets/javascripts/index/history.js +++ b/app/assets/javascripts/index/history.js @@ -96,7 +96,7 @@ OSM.History = function (map) { function updateBounds() { group.clearLayers(); - changesets.forEach(function (changeset) { + for (const changeset of changesets) { var bottomLeft = map.project(L.latLng(changeset.bbox.minlat, changeset.bbox.minlon)), topRight = map.project(L.latLng(changeset.bbox.maxlat, changeset.bbox.maxlon)), width = topRight.x - bottomLeft.x, @@ -115,16 +115,15 @@ OSM.History = function (map) { changeset.bounds = L.latLngBounds(map.unproject(bottomLeft), map.unproject(topRight)); - }); + } changesets.sort(function (a, b) { return b.bounds.getSize() - a.bounds.getSize(); }); - for (var i = 0; i < changesets.length; ++i) { - var changeset = changesets[i], - rect = L.rectangle(changeset.bounds, - { weight: 2, color: "#FF9500", opacity: 1, fillColor: "#FFFFAF", fillOpacity: 0 }); + for (const changeset of changesets) { + const rect = L.rectangle(changeset.bounds, + { weight: 2, color: "#FF9500", opacity: 1, fillColor: "#FFFFAF", fillOpacity: 0 }); rect.id = changeset.id; rect.addTo(group); } diff --git a/app/assets/javascripts/index/layers/notes.js b/app/assets/javascripts/index/layers/notes.js index 24bf969b3..1bc3714f0 100644 --- a/app/assets/javascripts/index/layers/notes.js +++ b/app/assets/javascripts/index/layers/notes.js @@ -84,9 +84,7 @@ OSM.initializeNotesLayer = function (map) { function success(json) { var oldNotes = notes; notes = {}; - json.features.forEach(updateMarkers); - - function updateMarkers(feature) { + for (const feature of json.features) { var marker = oldNotes[feature.properties.id]; delete oldNotes[feature.properties.id]; notes[feature.properties.id] = updateMarker(marker, feature); diff --git a/app/assets/javascripts/index/query.js b/app/assets/javascripts/index/query.js index 3874dbdbb..5c8ee8876 100644 --- a/app/assets/javascripts/index/query.js +++ b/app/assets/javascripts/index/query.js @@ -109,9 +109,9 @@ OSM.Query = function (map) { var tags = feature.tags, locales = OSM.preferred_languages; - for (var i = 0; i < locales.length; i++) { - if (tags["name:" + locales[i]]) { - return tags["name:" + locales[i]]; + for (const locale of locales) { + if (tags["name:" + locale]) { + return tags["name:" + locale]; } } @@ -193,22 +193,20 @@ OSM.Query = function (map) { elements = elements.sort(compare); } - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - - if (interestingFeature(element)) { - var $li = $("
  • ") - .addClass("list-group-item list-group-item-action") - .text(featurePrefix(element) + " ") - .appendTo($ul); - - $("") - .addClass("stretched-link") - .attr("href", "/" + element.type + "/" + element.id) - .data("geometry", featureGeometry(element)) - .text(featureName(element)) - .appendTo($li); - } + for (const element of elements) { + if (!interestingFeature(element)) continue; + + var $li = $("
  • ") + .addClass("list-group-item list-group-item-action") + .text(featurePrefix(element) + " ") + .appendTo($ul); + + $("") + .addClass("stretched-link") + .attr("href", "/" + element.type + "/" + element.id) + .data("geometry", featureGeometry(element)) + .text(featureName(element)) + .appendTo($li); } if (results.remark) { diff --git a/app/assets/javascripts/leaflet.layers.js b/app/assets/javascripts/leaflet.layers.js index 3f577b532..a7a2335f3 100644 --- a/app/assets/javascripts/leaflet.layers.js +++ b/app/assets/javascripts/leaflet.layers.js @@ -60,11 +60,11 @@ L.OSM.layers = function (options) { }); input.on("click", function () { - layers.forEach(function (other) { + for (const other of layers) { if (other !== layer) { map.removeLayer(other); } - }); + } map.addLayer(layer); }); diff --git a/app/assets/javascripts/leaflet.share.js b/app/assets/javascripts/leaflet.share.js index 1eed2151a..7d8462f46 100644 --- a/app/assets/javascripts/leaflet.share.js +++ b/app/assets/javascripts/leaflet.share.js @@ -201,41 +201,31 @@ L.OSM.share = function (options) { .attr("class", "form-check-input") .bind("change", toggleFilter)))); - ["minlon", "minlat", "maxlon", "maxlat", "lat", "lon"].forEach(function (name) { + const mapnikNames = ["minlon", "minlat", "maxlon", "maxlat", "lat", "lon"]; + + for (const name of mapnikNames) { $("") .attr("id", "mapnik_" + name) .attr("name", name) .attr("type", "hidden") .appendTo($form); - }); - - $("") - .attr("id", "map_format") - .attr("name", "format") - .attr("value", "mapnik") - .attr("type", "hidden") - .appendTo($form); - - $("") - .attr("id", "map_zoom") - .attr("name", "zoom") - .attr("value", map.getZoom()) - .attr("type", "hidden") - .appendTo($form); + } - $("") - .attr("id", "map_width") - .attr("name", "width") - .attr("value", 0) - .attr("type", "hidden") - .appendTo($form); + const hiddenExportDefaults = { + format: "mapnik", + zoom: map.getZoom(), + width: 0, + height: 0 + }; - $("") - .attr("id", "map_height") - .attr("name", "height") - .attr("value", 0) - .attr("type", "hidden") - .appendTo($form); + for (const name in hiddenExportDefaults) { + $("") + .attr("id", "map_" + name) + .attr("name", name) + .attr("value", hiddenExportDefaults[name]) + .attr("type", "hidden") + .appendTo($form); + } var csrf_param = $("meta[name=csrf-param]").attr("content"), csrf_token = $("meta[name=csrf-token]").attr("content"); diff --git a/app/assets/javascripts/router.js b/app/assets/javascripts/router.js index a8659d308..30a71e0f1 100644 --- a/app/assets/javascripts/router.js +++ b/app/assets/javascripts/router.js @@ -84,14 +84,12 @@ OSM.Router = function (map, rts) { return route; } - var routes = []; - for (var r in rts) { - routes.push(new Route(r, rts[r])); - } + const routes = Object.entries(rts) + .map(([r, t]) => new Route(r, t)); routes.recognize = function (path) { - for (var i = 0; i < this.length; i++) { - if (this[i].match(path)) return this[i]; + for (const route of this) { + if (route.match(path)) return route; } }; diff --git a/app/controllers/api/relations_controller.rb b/app/controllers/api/relations_controller.rb index f712534f0..006b3e8a6 100644 --- a/app/controllers/api/relations_controller.rb +++ b/app/controllers/api/relations_controller.rb @@ -26,9 +26,64 @@ module Api end def show - @relation = Relation.find(params[:id]) - response.last_modified = @relation.timestamp - if @relation.visible + relation = Relation.find(params[:id]) + + response.last_modified = relation.timestamp unless params[:full] + + @nodes = [] + @ways = [] + @relations = [] + + if relation.visible + if params[:full] + # with parameter :full + # returns representation of one relation object plus all its + # members, plus all nodes part of member ways + + # first find the ids of nodes, ways and relations referenced by this + # relation - note that we exclude this relation just in case. + + node_ids = relation.members.select { |m| m[0] == "Node" }.pluck(1) + way_ids = relation.members.select { |m| m[0] == "Way" }.pluck(1) + relation_ids = relation.members.select { |m| m[0] == "Relation" && m[1] != relation.id }.pluck(1) + + # next load the relations and the ways. + + relations = Relation.where(:id => relation_ids).includes(:relation_tags) + ways = Way.where(:id => way_ids).includes(:way_nodes, :way_tags) + + # now additionally collect nodes referenced by ways. Note how we + # recursively evaluate ways but NOT relations. + + way_node_ids = ways.collect do |way| + way.way_nodes.collect(&:node_id) + end + node_ids += way_node_ids.flatten + nodes = Node.where(:id => node_ids.uniq).includes(:node_tags) + + @nodes = [] + nodes.each do |node| + next unless node.visible? # should be unnecessary if data is consistent. + + @nodes << node + end + + ways.each do |way| + next unless way.visible? # should be unnecessary if data is consistent. + + @ways << way + end + + relations.each do |rel| + next unless rel.visible? # should be unnecessary if data is consistent. + + @relations << rel + end + end + + # finally add self + @relations << relation + # Render the result respond_to do |format| format.xml @@ -68,74 +123,6 @@ module Api end end - # ----------------------------------------------------------------- - # full - # - # input parameters: id - # - # returns XML representation of one relation object plus all its - # members, plus all nodes part of member ways - # ----------------------------------------------------------------- - def full - relation = Relation.find(params[:id]) - - if relation.visible - - # first find the ids of nodes, ways and relations referenced by this - # relation - note that we exclude this relation just in case. - - node_ids = relation.members.select { |m| m[0] == "Node" }.pluck(1) - way_ids = relation.members.select { |m| m[0] == "Way" }.pluck(1) - relation_ids = relation.members.select { |m| m[0] == "Relation" && m[1] != relation.id }.pluck(1) - - # next load the relations and the ways. - - relations = Relation.where(:id => relation_ids).includes(:relation_tags) - ways = Way.where(:id => way_ids).includes(:way_nodes, :way_tags) - - # now additionally collect nodes referenced by ways. Note how we - # recursively evaluate ways but NOT relations. - - way_node_ids = ways.collect do |way| - way.way_nodes.collect(&:node_id) - end - node_ids += way_node_ids.flatten - nodes = Node.where(:id => node_ids.uniq).includes(:node_tags) - - @nodes = [] - nodes.each do |node| - next unless node.visible? # should be unnecessary if data is consistent. - - @nodes << node - end - - @ways = [] - ways.each do |way| - next unless way.visible? # should be unnecessary if data is consistent. - - @ways << way - end - - @relations = [] - relations.each do |rel| - next unless rel.visible? # should be unnecessary if data is consistent. - - @relations << rel - end - - # finally add self - @relations << relation - - # Render the result - respond_to do |format| - format.xml - format.json - end - else - head :gone - end - end - def relations_for_way relations_for_object("Way") end diff --git a/app/controllers/api/ways_controller.rb b/app/controllers/api/ways_controller.rb index 285ed4604..b1bc8d799 100644 --- a/app/controllers/api/ways_controller.rb +++ b/app/controllers/api/ways_controller.rb @@ -26,12 +26,21 @@ module Api end def show - @way = Way.find(params[:id]) + @way = Way + @way = @way.includes(:nodes => :node_tags) if params[:full] + @way = @way.find(params[:id]) - response.last_modified = @way.timestamp + response.last_modified = @way.timestamp unless params[:full] if @way.visible - # Render the result + if params[:full] + @nodes = [] + + @way.nodes.uniq.each do |node| + @nodes << node if node.visible + end + end + respond_to do |format| format.xml format.json @@ -72,26 +81,6 @@ module Api end end - def full - @way = Way.includes(:nodes => :node_tags).find(params[:id]) - - if @way.visible - @nodes = [] - - @way.nodes.uniq.each do |node| - @nodes << node if node.visible - end - - # Render the result - respond_to do |format| - format.xml - format.json - end - else - head :gone - end - end - ## # returns all the ways which are currently using the node given in the # :id parameter. note that this used to return deleted ways as well, but diff --git a/app/views/api/relations/full.json.jbuilder b/app/views/api/relations/full.json.jbuilder deleted file mode 100644 index 16331a89e..000000000 --- a/app/views/api/relations/full.json.jbuilder +++ /dev/null @@ -1,7 +0,0 @@ -json.partial! "api/root_attributes" - -json.elements do - json.array! @nodes, :partial => "/api/nodes/node", :as => :node - json.array! @ways, :partial => "/api/ways/way", :as => :way - json.array! @relations, :partial => "/api/relations/relation", :as => :relation -end diff --git a/app/views/api/relations/full.xml.builder b/app/views/api/relations/full.xml.builder deleted file mode 100644 index e23061727..000000000 --- a/app/views/api/relations/full.xml.builder +++ /dev/null @@ -1,7 +0,0 @@ -xml.instruct! - -xml.osm(OSM::API.new.xml_root_attributes) do |osm| - osm << (render(@nodes) || "") - osm << (render(@ways) || "") - osm << (render(@relations) || "") -end diff --git a/app/views/api/relations/show.json.jbuilder b/app/views/api/relations/show.json.jbuilder index e5da7cd3f..16331a89e 100644 --- a/app/views/api/relations/show.json.jbuilder +++ b/app/views/api/relations/show.json.jbuilder @@ -1,5 +1,7 @@ json.partial! "api/root_attributes" json.elements do - json.array! [@relation], :partial => "relation", :as => :relation + json.array! @nodes, :partial => "/api/nodes/node", :as => :node + json.array! @ways, :partial => "/api/ways/way", :as => :way + json.array! @relations, :partial => "/api/relations/relation", :as => :relation end diff --git a/app/views/api/relations/show.xml.builder b/app/views/api/relations/show.xml.builder index 555eb4db5..e23061727 100644 --- a/app/views/api/relations/show.xml.builder +++ b/app/views/api/relations/show.xml.builder @@ -1,5 +1,7 @@ xml.instruct! xml.osm(OSM::API.new.xml_root_attributes) do |osm| - osm << (render(@relation) || "") + osm << (render(@nodes) || "") + osm << (render(@ways) || "") + osm << (render(@relations) || "") end diff --git a/app/views/api/ways/full.json.jbuilder b/app/views/api/ways/full.json.jbuilder deleted file mode 100644 index 1bd127dac..000000000 --- a/app/views/api/ways/full.json.jbuilder +++ /dev/null @@ -1,6 +0,0 @@ -json.partial! "api/root_attributes" - -json.elements do - json.array! @nodes, :partial => "/api/nodes/node", :as => :node - json.array! [@way], :partial => "/api/ways/way", :as => :way -end diff --git a/app/views/api/ways/full.xml.builder b/app/views/api/ways/full.xml.builder deleted file mode 100644 index 025291638..000000000 --- a/app/views/api/ways/full.xml.builder +++ /dev/null @@ -1,6 +0,0 @@ -xml.instruct! - -xml.osm(OSM::API.new.xml_root_attributes) do |osm| - osm << (render(@nodes) || "") - osm << (render(@way) || "") -end diff --git a/app/views/api/ways/show.json.jbuilder b/app/views/api/ways/show.json.jbuilder index 92e570d86..adf8beab2 100644 --- a/app/views/api/ways/show.json.jbuilder +++ b/app/views/api/ways/show.json.jbuilder @@ -1,5 +1,6 @@ json.partial! "api/root_attributes" json.elements do + json.array! @nodes, :partial => "/api/nodes/node", :as => :node if @nodes json.array! [@way], :partial => "way", :as => :way end diff --git a/app/views/api/ways/show.xml.builder b/app/views/api/ways/show.xml.builder index d520a0844..72b22e737 100644 --- a/app/views/api/ways/show.xml.builder +++ b/app/views/api/ways/show.xml.builder @@ -1,5 +1,6 @@ xml.instruct! xml.osm(OSM::API.new.xml_root_attributes) do |osm| + osm << (render(@nodes) || "") if @nodes osm << (render(@way) || "") end diff --git a/config/routes.rb b/config/routes.rb index b562ca9f4..bc06e5f38 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -37,14 +37,12 @@ OpenStreetMap::Application.routes.draw do get "node/:id/:version" => "old_nodes#show", :as => :api_old_node, :id => /\d+/, :version => /\d+/ get "way/:id/history" => "old_ways#history", :as => :api_way_history, :id => /\d+/ - get "way/:id/full" => "ways#full", :as => :way_full, :id => /\d+/ get "way/:id/relations" => "relations#relations_for_way", :as => :way_relations, :id => /\d+/ post "way/:id/:version/redact" => "old_ways#redact", :as => :way_version_redact, :version => /\d+/, :id => /\d+/ get "way/:id/:version" => "old_ways#show", :as => :api_old_way, :id => /\d+/, :version => /\d+/ get "relation/:id/relations" => "relations#relations_for_relation", :as => :relation_relations, :id => /\d+/ get "relation/:id/history" => "old_relations#history", :as => :api_relation_history, :id => /\d+/ - get "relation/:id/full" => "relations#full", :as => :relation_full, :id => /\d+/ post "relation/:id/:version/redact" => "old_relations#redact", :as => :relation_version_redact, :version => /\d+/, :id => /\d+/ get "relation/:id/:version" => "old_relations#show", :as => :api_old_relation, :id => /\d+/, :version => /\d+/ end @@ -55,11 +53,19 @@ OpenStreetMap::Application.routes.draw do put "node/create" => "nodes#create", :as => nil resources :ways, :only => [:index, :create] - resources :ways, :path => "way", :id => /\d+/, :only => [:show, :update, :destroy] + resources :ways, :path => "way", :id => /\d+/, :only => [:show, :update, :destroy] do + member do + get :full, :action => :show, :full => true, :as => nil + end + end put "way/create" => "ways#create", :as => nil resources :relations, :only => [:index, :create] - resources :relations, :path => "relation", :id => /\d+/, :only => [:show, :update, :destroy] + resources :relations, :path => "relation", :id => /\d+/, :only => [:show, :update, :destroy] do + member do + get :full, :action => :show, :full => true, :as => nil + end + end put "relation/create" => "relations#create", :as => nil resource :map, :only => :show diff --git a/test/controllers/api/relations_controller_test.rb b/test/controllers/api/relations_controller_test.rb index 34953f7b7..803a57acf 100644 --- a/test/controllers/api/relations_controller_test.rb +++ b/test/controllers/api/relations_controller_test.rb @@ -27,11 +27,11 @@ module Api ) assert_routing( { :path => "/api/0.6/relation/1/full", :method => :get }, - { :controller => "api/relations", :action => "full", :id => "1" } + { :controller => "api/relations", :action => "show", :full => true, :id => "1" } ) assert_routing( { :path => "/api/0.6/relation/1/full.json", :method => :get }, - { :controller => "api/relations", :action => "full", :id => "1", :format => "json" } + { :controller => "api/relations", :action => "show", :full => true, :id => "1", :format => "json" } ) assert_routing( { :path => "/api/0.6/relation/1", :method => :put }, @@ -149,19 +149,19 @@ module Api end def test_full_not_found - get relation_full_path(999999) + get api_relation_path(999999, :full => true) assert_response :not_found end def test_full_deleted - get relation_full_path(create(:relation, :deleted)) + get api_relation_path(create(:relation, :deleted), :full => true) assert_response :gone end def test_full_empty relation = create(:relation) - get relation_full_path(relation) + get api_relation_path(relation, :full => true) assert_response :success assert_dom "relation", :count => 1 do @@ -174,7 +174,7 @@ module Api node = create(:node) create(:relation_member, :relation => relation, :member => node) - get relation_full_path(relation) + get api_relation_path(relation, :full => true) assert_response :success assert_dom "node", :count => 1 do @@ -190,7 +190,7 @@ module Api way = create(:way_with_nodes) create(:relation_member, :relation => relation, :member => way) - get relation_full_path(relation) + get api_relation_path(relation, :full => true) assert_response :success assert_dom "node", :count => 1 do @@ -204,6 +204,30 @@ module Api end end + def test_full_with_node_member_json + relation = create(:relation) + node = create(:node) + create(:relation_member, :relation => relation, :member => node) + + get api_relation_path(relation, :full => true, :format => "json") + + assert_response :success + js = ActiveSupport::JSON.decode(@response.body) + assert_not_nil js + assert_equal 2, js["elements"].count + + js_relations = js["elements"].filter { |e| e["type"] == "relation" } + assert_equal 1, js_relations.count + assert_equal relation.id, js_relations[0]["id"] + assert_equal 1, js_relations[0]["members"].count + assert_equal "node", js_relations[0]["members"][0]["type"] + assert_equal node.id, js_relations[0]["members"][0]["ref"] + + js_nodes = js["elements"].filter { |e| e["type"] == "node" } + assert_equal 1, js_nodes.count + assert_equal node.id, js_nodes[0]["id"] + end + ## # check that all relations containing a particular node, and no extra # relations, are returned from the relations_for_node call. diff --git a/test/controllers/api/ways_controller_test.rb b/test/controllers/api/ways_controller_test.rb index 2ff5e6f29..191f9a820 100644 --- a/test/controllers/api/ways_controller_test.rb +++ b/test/controllers/api/ways_controller_test.rb @@ -27,11 +27,11 @@ module Api ) assert_routing( { :path => "/api/0.6/way/1/full", :method => :get }, - { :controller => "api/ways", :action => "full", :id => "1" } + { :controller => "api/ways", :action => "show", :full => true, :id => "1" } ) assert_routing( { :path => "/api/0.6/way/1/full.json", :method => :get }, - { :controller => "api/ways", :action => "full", :id => "1", :format => "json" } + { :controller => "api/ways", :action => "show", :full => true, :id => "1", :format => "json" } ) assert_routing( { :path => "/api/0.6/way/1", :method => :put }, @@ -136,10 +136,10 @@ module Api ## # check the "full" mode - def test_full + def test_show_full way = create(:way_with_nodes, :nodes_count => 3) - get way_full_path(way) + get api_way_path(way, :full => true) assert_response :success @@ -154,10 +154,42 @@ module Api end end - def test_full_deleted + def test_show_full_json + way = create(:way_with_nodes, :nodes_count => 3) + + get api_way_path(way, :full => true, :format => "json") + + assert_response :success + + # Check the way is correctly returned + js = ActiveSupport::JSON.decode(@response.body) + assert_not_nil js + assert_equal 4, js["elements"].count + js_ways = js["elements"].filter { |e| e["type"] == "way" } + assert_equal 1, js_ways.count + assert_equal way.id, js_ways[0]["id"] + assert_equal 1, js_ways[0]["version"] + + # check that each node in the way appears once in the output as a + # reference and as the node element. + js_nodes = js["elements"].filter { |e| e["type"] == "node" } + assert_equal 3, js_nodes.count + + way.nodes.each_with_index do |n, i| + assert_equal n.id, js_ways[0]["nodes"][i] + js_nodes_with_id = js_nodes.filter { |e| e["id"] == n.id } + assert_equal 1, js_nodes_with_id.count + assert_equal n.id, js_nodes_with_id[0]["id"] + assert_equal 1, js_nodes_with_id[0]["version"] + assert_equal n.lat, js_nodes_with_id[0]["lat"] + assert_equal n.lon, js_nodes_with_id[0]["lon"] + end + end + + def test_show_full_deleted way = create(:way, :deleted) - get way_full_path(way) + get api_way_path(way, :full => true) assert_response :gone end