From: Matt Amos Date: Mon, 10 Nov 2014 16:14:06 +0000 (+0000) Subject: Merge remote-tracking branch 'upstream/master' into routing-merge X-Git-Tag: live~5362^2~13^2~4 X-Git-Url: https://git.openstreetmap.org/rails.git/commitdiff_plain/0cb475cc1ad12c82ff0a36f7b9629c2288737a4f?hp=1f7bd08f4a8a6a626a0c1d7ed60f2dcd6a6801e8 Merge remote-tracking branch 'upstream/master' into routing-merge Conflicts: app/assets/javascripts/index.js config/locales/en.yml --- diff --git a/CONFIGURE.md b/CONFIGURE.md index 36c5586d8..9b7bb90e8 100644 --- a/CONFIGURE.md +++ b/CONFIGURE.md @@ -120,7 +120,7 @@ For information on contributing changes to the codes, see [CONTRIBUTING.md](CONT If you want to deploy The Rails Port for production use, you'll need to make a few changes. -* It's not recommended to use `rails server` in production. Our recommended approach is to use [Phusion Passenger](https://www.phusionpassenger.com/). +* It's not recommended to use `rails server` in production. Our recommended approach is to use [Phusion Passenger](https://www.phusionpassenger.com/). Instructions are available for [setting it up with most web servers](https://www.phusionpassenger.com/documentation_and_support#documentation). * Passenger will, by design, use the Production environment and therefore the production database - make sure it contains the appropriate data and user accounts. * Your production database will also need the extensions and functions installed - see [INSTALL.md](INSTALL.md) * The included version of the map call is quite slow and eats a lot of memory. You should consider using [CGIMap](https://github.com/zerebubuth/openstreetmap-cgimap) instead. diff --git a/Gemfile b/Gemfile index fd8e46b9f..ac030d38d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' # Require rails -gem 'rails', '4.1.6' +gem 'rails', '4.1.7' # Require things which have moved to gems in ruby 1.9 gem 'bigdecimal', "~> 1.1.0", :platforms => :ruby_19 diff --git a/Gemfile.lock b/Gemfile.lock index 48d6013bf..3f6685175 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,40 +2,40 @@ GEM remote: https://rubygems.org/ specs: SystemTimer (1.2.3) - actionmailer (4.1.6) - actionpack (= 4.1.6) - actionview (= 4.1.6) + actionmailer (4.1.7) + actionpack (= 4.1.7) + actionview (= 4.1.7) mail (~> 2.5, >= 2.5.4) - actionpack (4.1.6) - actionview (= 4.1.6) - activesupport (= 4.1.6) + actionpack (4.1.7) + actionview (= 4.1.7) + activesupport (= 4.1.7) rack (~> 1.5.2) rack-test (~> 0.6.2) actionpack-page_caching (1.0.2) actionpack (>= 4.0.0, < 5) - actionview (4.1.6) - activesupport (= 4.1.6) + actionview (4.1.7) + activesupport (= 4.1.7) builder (~> 3.1) erubis (~> 2.7.0) - activemodel (4.1.6) - activesupport (= 4.1.6) + activemodel (4.1.7) + activesupport (= 4.1.7) builder (~> 3.1) - activerecord (4.1.6) - activemodel (= 4.1.6) - activesupport (= 4.1.6) + activerecord (4.1.7) + activemodel (= 4.1.7) + activesupport (= 4.1.7) arel (~> 5.0.0) - activesupport (4.1.6) + activesupport (4.1.7) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.1) tzinfo (~> 1.1) arel (5.0.1.20140414130214) - autoprefixer-rails (3.1.1.20141001) + autoprefixer-rails (3.1.2.20141016) execjs bigdecimal (1.1.0) builder (3.2.2) - capybara (2.4.3) + capybara (2.4.4) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) @@ -54,20 +54,20 @@ GEM execjs coffee-script-source (1.8.0) colorize (0.7.3) - composite_primary_keys (7.0.11) - activerecord (= 4.1.6) + composite_primary_keys (7.0.12) + activerecord (~> 4.1.7) crass (0.2.1) dalli (2.7.2) deadlock_retry (1.2.0) dynamic_form (1.1.4) erubis (2.7.0) - execjs (2.2.1) + execjs (2.2.2) faraday (0.9.0) multipart-post (>= 1.2, < 3) hike (1.2.3) htmlentities (4.3.2) http_accept_language (2.0.2) - httpclient (2.4.0) + httpclient (2.5.3.2) i18n (0.6.11) iconv (0.1) jquery-rails (3.1.2) @@ -81,23 +81,23 @@ GEM jsonify (< 0.4.0) jwt (1.0.0) kgio (2.9.2) - konacha (3.2.4) + konacha (3.3.0) actionpack (>= 3.1, < 5) capybara colorize railties (>= 3.1, < 5) sprockets libxml-ruby (2.7.0) - mail (2.6.1) + mail (2.6.3) mime-types (>= 1.16, < 3) - mime-types (2.4.1) - mini_portile (0.6.0) + mime-types (2.4.3) + mini_portile (0.6.1) minitest (5.4.2) multi_json (1.10.1) multi_xml (0.5.5) multipart-post (2.0.0) - nokogiri (1.6.3.1) - mini_portile (= 0.6.0) + nokogiri (1.6.4.1) + mini_portile (~> 0.6.0) nokogumbo (1.1.12) nokogiri oauth (0.4.7) @@ -136,48 +136,48 @@ GEM ruby-openid (>= 2.1.8) rack-test (0.6.2) rack (>= 1.0) - rails (4.1.6) - actionmailer (= 4.1.6) - actionpack (= 4.1.6) - actionview (= 4.1.6) - activemodel (= 4.1.6) - activerecord (= 4.1.6) - activesupport (= 4.1.6) + rails (4.1.7) + actionmailer (= 4.1.7) + actionpack (= 4.1.7) + actionview (= 4.1.7) + activemodel (= 4.1.7) + activerecord (= 4.1.7) + activesupport (= 4.1.7) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.6) + railties (= 4.1.7) sprockets-rails (~> 2.0) rails-i18n (4.0.3) i18n (~> 0.6) railties (~> 4.0) - railties (4.1.6) - actionpack (= 4.1.6) - activesupport (= 4.1.6) + railties (4.1.7) + actionpack (= 4.1.7) + activesupport (= 4.1.7) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rake (10.3.2) - redcarpet (3.1.2) + redcarpet (3.2.0) rinku (1.7.3) - ruby-openid (2.5.0) - sanitize (3.0.2) + ruby-openid (2.6.0) + sanitize (3.0.3) crass (~> 0.2.0) nokogiri (>= 1.4.4) nokogumbo (= 1.1.12) sass (3.2.19) - sass-rails (4.0.3) + sass-rails (4.0.4) railties (>= 4.0.0, < 5.0) - sass (~> 3.2.0) - sprockets (~> 2.8, <= 2.11.0) + sass (~> 3.2.2) + sprockets (~> 2.8, < 2.12) sprockets-rails (~> 2.0) soap4r-ruby1.9 (2.0.5) - sprockets (2.11.0) + sprockets (2.11.3) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.1.4) + sprockets-rails (2.2.0) actionpack (>= 3.0) activesupport (>= 3.0) - sprockets (~> 2.8) + sprockets (>= 2.8, < 4.0) thor (0.19.1) thread_safe (0.3.4) tilt (1.4.1) @@ -190,7 +190,7 @@ GEM validates_email_format_of (1.6.1) i18n vendorer (0.1.16) - websocket-driver (0.3.5) + websocket-driver (0.4.0) xpath (2.0.0) nokogiri (~> 1.3) @@ -227,7 +227,7 @@ DEPENDENCIES psych r2 rack-cors - rails (= 4.1.6) + rails (= 4.1.7) rails-i18n (~> 4.0.0) redcarpet rinku (>= 1.2.2) diff --git a/app/assets/images/browse/path.20.png b/app/assets/images/browse/path.20.png new file mode 100644 index 000000000..13a090e89 Binary files /dev/null and b/app/assets/images/browse/path.20.png differ diff --git a/app/assets/images/browse/tertiary.20.png b/app/assets/images/browse/tertiary.20.png new file mode 100644 index 000000000..3dd7528c1 Binary files /dev/null and b/app/assets/images/browse/tertiary.20.png differ diff --git a/app/assets/images/browse/track.20.png b/app/assets/images/browse/track.20.png new file mode 100644 index 000000000..36e579e20 Binary files /dev/null and b/app/assets/images/browse/track.20.png differ diff --git a/app/assets/images/browse/wall.20.png b/app/assets/images/browse/wall.20.png new file mode 100644 index 000000000..12dffce4e Binary files /dev/null and b/app/assets/images/browse/wall.20.png differ diff --git a/app/assets/images/sotm.png b/app/assets/images/sotm.png deleted file mode 100644 index 3df02876d..000000000 Binary files a/app/assets/images/sotm.png and /dev/null differ diff --git a/app/assets/images/sprite.png b/app/assets/images/sprite.png index e7490c84c..e3ed0e7f8 100644 Binary files a/app/assets/images/sprite.png and b/app/assets/images/sprite.png differ diff --git a/app/assets/images/sprite.svg b/app/assets/images/sprite.svg index b61018d13..b50b969e9 100644 --- a/app/assets/images/sprite.svg +++ b/app/assets/images/sprite.svg @@ -13,8 +13,8 @@ height="200" id="svg2" version="1.1" - inkscape:version="0.48.2 r9819" - inkscape:export-filename="/Users/tmcw/src/openstreetmap-website/app/assets/images/sprite.png" + inkscape:version="0.48.4 r9939" + inkscape:export-filename="/home/tom/rails/app/assets/images/sprite.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" sodipodi:docname="sprite.svg"> @@ -27,16 +27,16 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="4" - inkscape:cx="210.42032" - inkscape:cy="175.54808" + inkscape:zoom="16" + inkscape:cx="258.2457" + inkscape:cy="193.60262" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1436" - inkscape:window-height="856" - inkscape:window-x="4" - inkscape:window-y="0" + inkscape:window-width="1366" + inkscape:window-height="702" + inkscape:window-x="0" + inkscape:window-y="27" inkscape:window-maximized="1" showguides="true" inkscape:guide-bbox="true" @@ -113,6 +113,10 @@ orientation="1,0" position="260,195" id="guide11761" /> + @@ -265,5 +269,16 @@ inkscape:export-filename="/Users/saman/work_repos/osm-redesign/renders/share-1.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" /> + ? diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js index b89d6c71d..66dfa5b6f 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -6,6 +6,7 @@ //= require leaflet.note //= require leaflet.share //= require leaflet.polyline +//= require leaflet.query //= require index/search //= require index/browse //= require index/export @@ -15,6 +16,7 @@ //= require index/new_note //= require index/directions //= require index/changeset +//= require index/query //= require router $(document).ready(function () { @@ -126,6 +128,11 @@ $(document).ready(function () { sidebar: sidebar }).addTo(map); + L.OSM.query({ + position: position, + sidebar: sidebar + }).addTo(map); + L.control.scale() .addTo(map); @@ -157,15 +164,6 @@ $(document).ready(function () { $.cookie("_osm_location", OSM.locationCookie(map), { expires: expiry, path: "/" }); }); - if ($.cookie('_osm_sotm') == 'hide') { - $('#sotm').hide(); - } - - $('#sotm .close').on('click', function() { - $('#sotm').hide(); - $.cookie("_osm_sotm", 'hide', { expires: expiry }); - }); - if ($.cookie('_osm_welcome') == 'hide') { $('.welcome').hide(); } @@ -294,7 +292,8 @@ $(document).ready(function () { "/node/:id(/history)": OSM.Browse(map, 'node'), "/way/:id(/history)": OSM.Browse(map, 'way'), "/relation/:id(/history)": OSM.Browse(map, 'relation'), - "/changeset/:id": OSM.Changeset(map) + "/changeset/:id": OSM.Changeset(map), + "/query": OSM.Query(map) }); if (OSM.preferred_editor == "remote" && document.location.pathname == "/edit") { diff --git a/app/assets/javascripts/index/query.js b/app/assets/javascripts/index/query.js new file mode 100644 index 000000000..1f45a6872 --- /dev/null +++ b/app/assets/javascripts/index/query.js @@ -0,0 +1,337 @@ +//= require jquery.simulate + +OSM.Query = function(map) { + var protocol = document.location.protocol === "https:" ? "https:" : "http:", + url = protocol + OSM.OVERPASS_URL, + queryButton = $(".control-query .control-button"), + uninterestingTags = ['source', 'source_ref', 'source:ref', 'history', 'attribution', 'created_by', 'tiger:county', 'tiger:tlid', 'tiger:upload_uuid', 'KSJ2:curve_id', 'KSJ2:lat', 'KSJ2:lon', 'KSJ2:coordinate', 'KSJ2:filename', 'note:ja'], + marker; + + var featureStyle = { + color: "#FF6200", + weight: 4, + opacity: 1, + fillOpacity: 0.5, + clickable: false + }; + + queryButton.on("click", function (e) { + e.preventDefault(); + e.stopPropagation(); + + if (queryButton.hasClass("active")) { + disableQueryMode(); + } else if (!queryButton.hasClass("disabled")) { + enableQueryMode(); + } + }).on("disabled", function (e) { + if (queryButton.hasClass("active")) { + map.off("click", clickHandler); + $(map.getContainer()).removeClass("query-active").addClass("query-disabled"); + $(this).tooltip("show"); + } + }).on("enabled", function (e) { + if (queryButton.hasClass("active")) { + map.on("click", clickHandler); + $(map.getContainer()).removeClass("query-disabled").addClass("query-active"); + $(this).tooltip("hide"); + } + }); + + $("#sidebar_content") + .on("mouseover", ".query-results li.query-result", function () { + var geometry = $(this).data("geometry") + if (geometry) map.addLayer(geometry); + $(this).addClass("selected"); + }) + .on("mouseout", ".query-results li.query-result", function () { + var geometry = $(this).data("geometry") + if (geometry) map.removeLayer(geometry); + $(this).removeClass("selected"); + }) + .on("mousedown", ".query-results li.query-result", function (e) { + var moved = false; + $(this).one("click", function (e) { + if (!moved) { + var geometry = $(this).data("geometry") + if (geometry) map.removeLayer(geometry); + + if (!$(e.target).is('a')) { + $(this).find("a").simulate("click", e); + } + } + }).one("mousemove", function () { + moved = true; + }); + }); + + function interestingFeature(feature, origin, radius) { + if (feature.tags) { + for (var key in feature.tags) { + if (uninterestingTags.indexOf(key) < 0) { + return true; + } + } + } + + return false; + } + + function featurePrefix(feature) { + var tags = feature.tags; + var prefix = ""; + + if (tags.boundary === "administrative" && tags.admin_level) { + prefix = + I18n.t("geocoder.search_osm_nominatim.admin_levels.level" + tags.admin_level, { + defaultValue: I18n.t("geocoder.search_osm_nominatim.prefix.boundary.administrative") + }) + } else { + var prefixes = I18n.t("geocoder.search_osm_nominatim.prefix"); + + for (var key in tags) { + var value = tags[key]; + + if (prefixes[key]) { + if (prefixes[key][value]) { + return prefixes[key][value]; + } else { + var first = value.substr(0, 1).toUpperCase(), + rest = value.substr(1).replace(/_/g, " "); + + return first + rest; + } + } + } + } + + if (!prefix) { + prefix = I18n.t("javascripts.query." + feature.type); + } + + return prefix; + } + + function featureName(feature) { + var tags = feature.tags, + locales = I18n.locales.get(); + + for (var i = 0; i < locales.length; i++) { + if (tags["name:" + locales[i]]) { + return tags["name:" + locales[i]]; + } + } + + if (tags["name"]) { + return tags["name"]; + } else if (tags["ref"]) { + return tags["ref"]; + } else if (tags["addr:housename"]) { + return tags["addr:housename"]; + } else if (tags["addr:housenumber"] && tags["addr:street"]) { + return tags["addr:housenumber"] + " " + tags["addr:street"]; + } else { + return "#" + feature.id; + } + } + + function featureGeometry(feature) { + var geometry; + + if (feature.type === "node" && feature.lat && feature.lon) { + geometry = L.circleMarker([feature.lat, feature.lon], featureStyle); + } else if (feature.type === "way" && feature.geometry) { + geometry = L.polyline(feature.geometry.filter(function (point) { + return point !== null; + }).map(function (point) { + return [point.lat, point.lon]; + }), featureStyle); + } else if (feature.type === "relation" && feature.members) { + geometry = L.featureGroup(feature.members.map(featureGeometry).filter(function (geometry) { + return geometry !== undefined; + })); + } + + return geometry; + } + + function runQuery(latlng, radius, query, $section, compare) { + var $ul = $section.find("ul"); + + $ul.empty(); + $section.show(); + + $section.find(".loader").oneTime(1000, "loading", function () { + $(this).show(); + }); + + if ($section.data("ajax")) { + $section.data("ajax").abort(); + } + + $section.data("ajax", $.ajax({ + url: url, + method: "POST", + data: { + data: "[timeout:5][out:json];" + query, + }, + success: function(results) { + var elements; + + $section.find(".loader").stopTime("loading").hide(); + + if (compare) { + elements = results.elements.sort(compare); + } else { + elements = results.elements; + } + + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + + if (interestingFeature(element, latlng, radius)) { + var $li = $("
  • ") + .addClass("query-result") + .data("geometry", featureGeometry(element)) + .appendTo($ul); + var $p = $("

    ") + .text(featurePrefix(element) + " ") + .appendTo($li); + + $("") + .attr("href", "/" + element.type + "/" + element.id) + .text(featureName(element)) + .appendTo($p); + } + } + + if ($ul.find("li").length == 0) { + $("

  • ") + .text(I18n.t("javascripts.query.nothing_found")) + .appendTo($ul); + } + }, + error: function(xhr, status, error) { + $section.find(".loader").stopTime("loading").hide(); + + $("
  • ") + .text(I18n.t("javascripts.query." + status, { server: url, error: error })) + .appendTo($ul); + } + })); + } + + function compareSize(feature1, feature2) { + var width1 = feature1.bounds.maxlon - feature1.bounds.minlon, + height1 = feature1.bounds.maxlat - feature1.bounds.minlat, + area1 = width1 * height1, + width2 = feature2.bounds.maxlat - feature2.bounds.minlat, + height2 = feature2.bounds.maxlat - feature2.bounds.minlat, + area2 = width2 * height2; + + return area1 - area2; + } + + /* + * To find nearby objects we ask overpass for the union of the + * following sets: + * + * node(around:,,lng>) + * way(around:,,lng>) + * relation(around:,,lng>) + * + * to find enclosing objects we first find all the enclosing areas: + * + * is_in(,)->.a + * + * and then return the union of the following sets: + * + * relation(pivot.a) + * way(pivot.a) + * + * In both cases we then ask to retrieve tags and the geometry + * for each object. + */ + function queryOverpass(lat, lng) { + var latlng = L.latLng(lat, lng), + bounds = map.getBounds(), + bbox = bounds.getSouth() + "," + bounds.getWest() + "," + bounds.getNorth() + "," + bounds.getEast(), + radius = 10 * Math.pow(1.5, 19 - map.getZoom()), + around = "around:" + radius + "," + lat + "," + lng, + nodes = "node(" + around + ")", + ways = "way(" + around + ")", + relations = "relation(" + around + ")", + nearby = "(" + nodes + ";" + ways + ");out tags geom(" + bbox + ");" + relations + ";out geom(" + bbox + ");", + isin = "is_in(" + lat + "," + lng + ")->.a;way(pivot.a);out tags geom(" + bbox + ");relation(pivot.a);out tags bb;"; + + $("#sidebar_content .query-intro") + .hide(); + + if (marker) map.removeLayer(marker); + marker = L.circle(latlng, radius, featureStyle).addTo(map); + + $(document).everyTime(75, "fadeQueryMarker", function (i) { + if (i == 10) { + map.removeLayer(marker); + } else { + marker.setStyle({ + opacity: 1 - i * 0.1, + fillOpacity: 0.5 - i * 0.05 + }); + } + }, 10); + + runQuery(latlng, radius, nearby, $("#query-nearby")); + runQuery(latlng, radius, isin, $("#query-isin"), compareSize); + } + + function clickHandler(e) { + var precision = OSM.zoomPrecision(map.getZoom()), + lat = e.latlng.lat.toFixed(precision), + lng = e.latlng.lng.toFixed(precision); + + OSM.router.route("/query?lat=" + lat + "&lon=" + lng); + } + + function enableQueryMode() { + queryButton.addClass("active"); + map.on("click", clickHandler); + $(map.getContainer()).addClass("query-active"); + } + + function disableQueryMode() { + if (marker) map.removeLayer(marker); + $(map.getContainer()).removeClass("query-active").removeClass("query-disabled"); + map.off("click", clickHandler); + queryButton.removeClass("active"); + } + + var page = {}; + + page.pushstate = page.popstate = function(path) { + OSM.loadSidebarContent(path, function () { + page.load(path, true); + }); + }; + + page.load = function(path, noCentre) { + var params = querystring.parse(path.substring(path.indexOf('?') + 1)), + latlng = L.latLng(params.lat, params.lon); + + if (!window.location.hash && !noCentre && !map.getBounds().contains(latlng)) { + OSM.router.withoutMoveListener(function () { + map.setView(latlng, 15); + }); + } + + queryOverpass(params.lat, params.lon); + }; + + page.unload = function(sameController) { + if (!sameController) { + disableQueryMode(); + } + }; + + return page; +}; diff --git a/app/assets/javascripts/leaflet.query.js b/app/assets/javascripts/leaflet.query.js new file mode 100644 index 000000000..906487274 --- /dev/null +++ b/app/assets/javascripts/leaflet.query.js @@ -0,0 +1,38 @@ +L.OSM.query = function (options) { + var control = L.control(options); + + control.onAdd = function (map) { + var $container = $('
    ') + .attr('class', 'control-query'); + + var link = $('') + .attr('class', 'control-button') + .attr('href', '#') + .html('') + .appendTo($container); + + map.on('zoomend', update); + + update(); + + function update() { + var wasDisabled = link.hasClass('disabled'), + isDisabled = map.getZoom() < 14; + link + .toggleClass('disabled', isDisabled) + .attr('data-original-title', I18n.t(isDisabled ? + 'javascripts.site.queryfeature_disabled_tooltip' : + 'javascripts.site.queryfeature_tooltip')); + + if (isDisabled && !wasDisabled) { + link.trigger('disabled'); + } else if (wasDisabled && !isDisabled) { + link.trigger('enabled'); + } + } + + return $container[0]; + }; + + return control; +}; diff --git a/app/assets/javascripts/osm.js.erb b/app/assets/javascripts/osm.js.erb index 033b2de81..15c1682d5 100644 --- a/app/assets/javascripts/osm.js.erb +++ b/app/assets/javascripts/osm.js.erb @@ -8,6 +8,7 @@ OSM = { API_VERSION: <%= API_VERSION.to_json %>, STATUS: <%= STATUS.to_json %>, MAX_NOTE_REQUEST_AREA: <%= MAX_NOTE_REQUEST_AREA.to_json %>, + OVERPASS_URL: <%= OVERPASS_URL.to_json %>, apiUrl: function (object) { var url = "/api/" + OSM.API_VERSION + "/" + object.type + "/" + object.id; @@ -71,10 +72,6 @@ OSM = { mapParams.bounds = L.latLngBounds( [parseFloat(params.minlat), parseFloat(params.minlon)], [parseFloat(params.maxlat), parseFloat(params.maxlon)]); - } else if (params.lon && params.lat) { - mapParams.lon = parseFloat(params.lon); - mapParams.lat = parseFloat(params.lat); - mapParams.zoom = parseInt(params.zoom || 5); } else if (params.mlon && params.mlat) { mapParams.lon = parseFloat(params.mlon); mapParams.lat = parseFloat(params.mlat); @@ -174,5 +171,20 @@ OSM = { zoom = map.getZoom(), precision = OSM.zoomPrecision(zoom); return [center.lng.toFixed(precision), center.lat.toFixed(precision), zoom, map.getLayersCode()].join('|'); + }, + + distance: function(latlng1, latlng2) { + var lat1 = latlng1.lat * Math.PI / 180, + lng1 = latlng1.lng * Math.PI / 180, + lat2 = latlng2.lat * Math.PI / 180, + lng2 = latlng2.lng * Math.PI / 180, + latdiff = lat2 - lat1, + lngdiff = lng2 - lng1; + + return 6372795 * 2 * Math.asin( + Math.sqrt( + Math.pow(Math.sin(latdiff / 2), 2) + + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(lngdiff / 2), 2) + )); } }; diff --git a/app/assets/javascripts/piwik.js b/app/assets/javascripts/piwik.js index a48e903ad..d327296f6 100644 --- a/app/assets/javascripts/piwik.js +++ b/app/assets/javascripts/piwik.js @@ -10,6 +10,10 @@ if (OSM.PIWIK) { success: function () { piwikTracker = Piwik.getTracker(base + "piwik.php", OSM.PIWIK.site); + if (OSM.user) { + piwikTracker.setUserId(OSM.user); + } + piwikTracker.trackPageView(); piwikTracker.enableLinkTracking(); diff --git a/app/assets/javascripts/router.js b/app/assets/javascripts/router.js index c3f13f9df..904134fc0 100644 --- a/app/assets/javascripts/router.js +++ b/app/assets/javascripts/router.js @@ -76,6 +76,8 @@ OSM.Router = function(map, rts) { }); } + params = params.concat(Array.prototype.slice.call(arguments, 2)); + return (controller[action] || $.noop).apply(controller, params); }; @@ -101,11 +103,12 @@ OSM.Router = function(map, rts) { if (window.history && window.history.pushState) { $(window).on('popstate', function(e) { if (!e.originalEvent.state) return; // Is it a real popstate event or just a hash change? - var path = window.location.pathname + window.location.search; + var path = window.location.pathname + window.location.search, + route = routes.recognize(path); if (path === currentPath) return; - currentRoute.run('unload'); + currentRoute.run('unload', null, route === currentRoute); currentPath = path; - currentRoute = routes.recognize(currentPath); + currentRoute = route; currentRoute.run('popstate', currentPath); map.setState(e.originalEvent.state, {animate: false}); }); @@ -114,7 +117,7 @@ OSM.Router = function(map, rts) { var path = url.replace(/#.*/, ''), route = routes.recognize(path); if (!route) return false; - currentRoute.run('unload'); + currentRoute.run('unload', null, route === currentRoute); var state = OSM.parseHash(url); map.setState(state); window.history.pushState(state, document.title, url); diff --git a/app/assets/stylesheets/browse.css.scss b/app/assets/stylesheets/browse.css.scss index 0aa762a12..d30f5a8a1 100644 --- a/app/assets/stylesheets/browse.css.scss +++ b/app/assets/stylesheets/browse.css.scss @@ -110,6 +110,8 @@ .aeroway.runway::before { content: image-url('browse/runway.20.png'); } .aeroway.taxiway::before { content: image-url('browse/taxiway.20.png'); } +.barrier.wall::before { content: image-url('browse/wall.20.png'); } + .building::before { content: image-url('browse/building.png'); } .highway.bridleway::before { content: image-url('browse/bridleway.20.png'); } @@ -118,6 +120,7 @@ .highway.footway::before { content: image-url('browse/footway.20.png'); } .highway.motorway::before { content: image-url('browse/motorway.20.png'); } .highway.motorway_link::before { content: image-url('browse/motorway.20.png'); } +.highway.path::before { content: image-url('browse/path.20.png'); } .highway.pedestrian::before { content: image-url('browse/service.20.png'); } .highway.primary::before { content: image-url('browse/primary.20.png'); } .highway.primary_link::before { content: image-url('browse/primary.20.png'); } @@ -125,6 +128,8 @@ .highway.secondary::before { content: image-url('browse/secondary.20.png'); } .highway.secondary_link::before { content: image-url('browse/secondary.20.png'); } .highway.service::before { content: image-url('browse/service.20.png'); } +.highway.tertiary::before { content: image-url('browse/tertiary.20.png'); } +.highway.track::before { content: image-url('browse/track.20.png'); } .highway.trunk::before { content: image-url('browse/trunk.20.png'); } .highway.trunk_link::before { content: image-url('browse/trunk.20.png'); } .highway.unclassified::before { content: image-url('browse/unclassified.20.png'); } diff --git a/app/assets/stylesheets/common.css.scss b/app/assets/stylesheets/common.css.scss index aa729640e..95e5d8328 100644 --- a/app/assets/stylesheets/common.css.scss +++ b/app/assets/stylesheets/common.css.scss @@ -169,7 +169,7 @@ small, aside { .icon.close:hover { background-position: -200px -20px; } .icon.check { background-position: -220px 0; } .icon.note { background-position: -240px 0; } -.icon.gear { background-position: -260px 0; } +.icon.query { background-position: -260px 0; } /* Rules for links */ @@ -566,16 +566,16 @@ nav.secondary { background-color: black; } - &.active { - background-color: #9ed485; - } - &.disabled { background-color: #333; background-color: rgba(0,0,0,.5); cursor: default; } + &.active { + background-color: #9ed485; + } + .icon { margin: 10px; } @@ -680,6 +680,14 @@ nav.secondary { #map { height: 100%; overflow: hidden; + + &.query-active { + cursor: help; + } + + &.query-disabled { + cursor: not-allowed; + } } #map-ui { @@ -1180,6 +1188,34 @@ tr.turn:hover { overflow: hidden; margin: 0 0 10px 10px; } + + .query-intro p { + padding: $lineheight $lineheight $lineheight/2; + } + + .query-results { + display: none; + + h3 { + padding: $lineheight $lineheight $lineheight/2; + margin: 0; + } + + ul { + li { + padding: 15px 20px; + border-bottom: 1px solid #ddd; + + &.query-result { + cursor: pointer; + } + + &.selected { + background: #FFFFE6; + } + } + } + } } /* Rules for export sidebar */ @@ -2618,36 +2654,6 @@ input.richtext_title[type="text"] { } } -#sidebar #sotm { - padding: 10px; - min-height: 120px; - - img { - float: left; - width: 100px; - height: 100px; - } - - h2 { - margin-left: 100px; - padding: 7px 10px 6px 15px; - } - - p { - margin-left: 100px; - padding: 6px 10px 7px 15px; - } - - a { - color: $darkgrey; - } - - :hover { - text-decoration: none; - color: darken($darkgrey, 25%); - } -} - @import 'browse'; @media only screen and (max-width:960px) { diff --git a/app/controllers/changeset_controller.rb b/app/controllers/changeset_controller.rb index 646ba2fa3..adda8c20c 100644 --- a/app/controllers/changeset_controller.rb +++ b/app/controllers/changeset_controller.rb @@ -5,18 +5,18 @@ class ChangesetController < ApplicationController require 'xml/libxml' skip_before_filter :verify_authenticity_token, :except => [:list] - before_filter :authorize_web, :only => [:list, :feed] - before_filter :set_locale, :only => [:list, :feed] + before_filter :authorize_web, :only => [:list, :feed, :comments_feed] + before_filter :set_locale, :only => [:list, :feed, :comments_feed] before_filter :authorize, :only => [:create, :update, :delete, :upload, :include, :close, :comment, :subscribe, :unsubscribe, :hide_comment, :unhide_comment] before_filter :require_moderator, :only => [:hide_comment, :unhide_comment] before_filter :require_allow_write_api, :only => [:create, :update, :delete, :upload, :include, :close, :comment, :subscribe, :unsubscribe, :hide_comment, :unhide_comment] before_filter :require_public_data, :only => [:create, :update, :delete, :upload, :include, :close, :comment, :subscribe, :unsubscribe] before_filter :check_api_writable, :only => [:create, :update, :delete, :upload, :include, :comment, :subscribe, :unsubscribe, :hide_comment, :unhide_comment] before_filter :check_api_readable, :except => [:create, :update, :delete, :upload, :download, :query, :list, :feed, :comment, :subscribe, :unsubscribe, :comments_feed] - before_filter(:only => [:list, :feed]) { |c| c.check_database_readable(true) } + before_filter(:only => [:list, :feed, :comments_feed]) { |c| c.check_database_readable(true) } after_filter :compress_output - around_filter :api_call_handle_error, :except => [:list, :feed] - around_filter :web_timeout, :only => [:list, :feed] + around_filter :api_call_handle_error, :except => [:list, :feed, :comments_feed] + around_filter :web_timeout, :only => [:list, :feed, :comments_feed] # Helper methods for checking consistency include ConsistencyValidations diff --git a/app/views/browse/query.html.erb b/app/views/browse/query.html.erb new file mode 100644 index 000000000..629d84c05 --- /dev/null +++ b/app/views/browse/query.html.erb @@ -0,0 +1,22 @@ +<% set_title(t "browse.query.title") %> + +

    + + <%= t "browse.query.title" %> +

    + +
    +

    <%= t("browse.query.introduction") %>

    +
    + +
    +

    <%= t("browse.query.nearby") %>

    + <%= image_tag "searching.gif", :class => "loader" %> +
      +
      + +
      +

      <%= t("browse.query.enclosing") %>

      + <%= image_tag "searching.gif", :class => "loader" %> +
        +
        diff --git a/app/views/layouts/_head.html.erb b/app/views/layouts/_head.html.erb index 1c91e68ec..b879d90bd 100644 --- a/app/views/layouts/_head.html.erb +++ b/app/views/layouts/_head.html.erb @@ -32,9 +32,12 @@ I18n.defaultLocale = "<%= I18n.default_locale %>"; I18n.locale = "<%= I18n.locale %>"; I18n.fallbacks = true; - <% if @user and !@user.home_lon.nil? and !@user.home_lat.nil? -%> + <% if @user -%> + OSM.user = <%= @user.id.to_json.html_safe %>; + <% unless @user.home_lon.nil? or @user.home_lat.nil? -%> OSM.home = <%= { :lat => @user.home_lat, :lon => @user.home_lon }.to_json.html_safe %>; <% end -%> + <% end -%> <% if session[:location] -%> OSM.location = <%= session[:location].to_json.html_safe %>; <% end -%> diff --git a/app/views/layouts/map.html.erb b/app/views/layouts/map.html.erb index 471557053..e72214cbe 100644 --- a/app/views/layouts/map.html.erb +++ b/app/views/layouts/map.html.erb @@ -30,21 +30,6 @@

        <%= t 'layouts.osm_read_only' %>

        <% end %> - -
        <%= render :partial => "layouts/flash" %>
        diff --git a/config/example.application.yml b/config/example.application.yml index 3fbebdc76..e7915c96b 100644 --- a/config/example.application.yml +++ b/config/example.application.yml @@ -89,6 +89,8 @@ defaults: &defaults - ".*\\.googleapis\\.com/.*" - ".*\\.google\\.com/.*" - ".*\\.google\\.ru/.*" + # URL of Overpass instance to use for feature queries + overpass_url: "//overpass-api.de/api/interpreter" development: <<: *defaults diff --git a/config/example.database.yml b/config/example.database.yml index 7e1ffc07d..4c77998da 100644 --- a/config/example.database.yml +++ b/config/example.database.yml @@ -1,4 +1,4 @@ -# Using a recent release (8.3 or higher) of PostgreSQL (http://postgresql.org/) is recommended. +# Using a recent release (9.1 or higher) of PostgreSQL (http://postgresql.org/) is recommended. # See https://github.com/openstreetmap/openstreetmap-website/blob/master/INSTALL.md#database-setup for detailed setup instructions. # development: diff --git a/config/i18n-js.yml b/config/i18n-js.yml index 026ece64c..369fa340a 100644 --- a/config/i18n-js.yml +++ b/config/i18n-js.yml @@ -31,3 +31,4 @@ translations: - "*.site.sidebar.search_results" - "*.diary_entry.edit.marker_text" - "*.layouts.project_name.title" + - "*.geocoder.search_osm_nominatim.*" diff --git a/config/locales/en.yml b/config/locales/en.yml index 7548833d8..d2797511b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -204,6 +204,11 @@ en: reopened_by: "Reactivated by %{user} %{when} ago" reopened_by_anonymous: "Reactivated by anonymous %{when} ago" hidden_by: "Hidden by %{user} %{when} ago" + query: + title: "Query Features" + introduction: "Click on the map to find nearby features." + nearby: "Nearby features" + enclosing: "Enclosing features" changeset: changeset_paging_nav: showing_page: "Page %{page}" @@ -521,7 +526,7 @@ en: primary_link: "Primary Road" proposed: "Proposed Road" raceway: "Raceway" - residential: "Residential" + residential: "Residential Road" rest_area: "Rest Area" road: "Road" secondary: "Secondary Road" @@ -732,6 +737,8 @@ en: tram: "Tramway" tram_stop: "Tram Stop" yard: "Railway Yard" + route: + bus: "Bus Route" shop: alcohol: "Off License" antiques: "Antiques" @@ -937,10 +944,6 @@ en: text: Make a Donation learn_more: "Learn More" more: More - sotm_header: State of the Map 2014 - sotm_line_1: 8th Annual Conference - sotm_line_2: November 7th-9th 2014 - sotm_line_3: Buenos Aires, Argentina license_page: foreign: title: About this translation @@ -2132,6 +2135,8 @@ en: createnote_disabled_tooltip: Zoom in to add a note to the map map_notes_zoom_in_tooltip: Zoom in to see map notes map_data_zoom_in_tooltip: Zoom in to see map data + queryfeature_tooltip: Query features + queryfeature_disabled_tooltip: Zoom in to query features changesets: show: comment: "Comment" @@ -2185,6 +2190,13 @@ en: unnamed: "(unnamed)" courtesy: "Directions courtesy of %{link}" time: "Time" + query: + node: Node + way: Way + relation: Relation + nothing_found: No features found + error: "Error contacting %{server}: %{error}" + timeout: "Timeout contacting %{server}" redaction: edit: description: "Description" diff --git a/config/routes.rb b/config/routes.rb index b28b6aff4..3d47c6dcf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -157,6 +157,7 @@ OpenStreetMap::Application.routes.draw do match '/offline' => 'site#offline', :via => :get match '/key' => 'site#key', :via => :get match '/id' => 'site#id', :via => :get + match '/query' => 'browse#query', :via => :get match '/user/new' => 'user#new', :via => :get match '/user/new' => 'user#create', :via => :post match '/user/terms' => 'user#terms', :via => :get diff --git a/db/structure.sql b/db/structure.sql index c287baea3..343aec084 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -126,7 +126,7 @@ CREATE TYPE user_status_enum AS ENUM ( CREATE FUNCTION maptile_for_point(bigint, bigint, integer) RETURNS integer LANGUAGE c STRICT - AS '/home/ukasiu/repos/openstreetmap-website/db/functions/libpgosm', 'maptile_for_point'; + AS '/srv/www/overpass.osm.compton.nu/db/functions/libpgosm.so', 'maptile_for_point'; -- @@ -135,7 +135,7 @@ CREATE FUNCTION maptile_for_point(bigint, bigint, integer) RETURNS integer CREATE FUNCTION tile_for_point(integer, integer) RETURNS bigint LANGUAGE c STRICT - AS '/home/ukasiu/repos/openstreetmap-website/db/functions/libpgosm', 'tile_for_point'; + AS '/srv/www/overpass.osm.compton.nu/db/functions/libpgosm.so', 'tile_for_point'; -- @@ -143,8 +143,8 @@ CREATE FUNCTION tile_for_point(integer, integer) RETURNS bigint -- CREATE FUNCTION xid_to_int4(xid) RETURNS integer - LANGUAGE c STRICT - AS '/home/ukasiu/repos/openstreetmap-website/db/functions/libpgosm', 'xid_to_int4'; + LANGUAGE c IMMUTABLE STRICT + AS '/srv/www/overpass.osm.compton.nu/db/functions/libpgosm.so', 'xid_to_int4'; SET default_tablespace = ''; @@ -1795,13 +1795,6 @@ CREATE INDEX gpx_files_user_id_idx ON gpx_files USING btree (user_id); CREATE INDEX gpx_files_visible_visibility_idx ON gpx_files USING btree (visible, visibility); --- --- Name: index_changeset_comments_on_body; Type: INDEX; Schema: public; Owner: -; Tablespace: --- - -CREATE INDEX index_changeset_comments_on_body ON changeset_comments USING btree (body); - - -- -- Name: index_changeset_comments_on_created_at; Type: INDEX; Schema: public; Owner: -; Tablespace: -- @@ -2635,3 +2628,4 @@ INSERT INTO schema_migrations (version) VALUES ('7'); INSERT INTO schema_migrations (version) VALUES ('8'); INSERT INTO schema_migrations (version) VALUES ('9'); + diff --git a/test/javascripts/osm_test.js b/test/javascripts/osm_test.js index d7fe4a2fd..51f74fe7a 100644 --- a/test/javascripts/osm_test.js +++ b/test/javascripts/osm_test.js @@ -73,18 +73,8 @@ describe("OSM", function () { expect(params).to.have.property("bounds").deep.equal(expected); }); - it("parses lat/lon/zoom params", function () { - var params = OSM.mapParams("?lat=57.6247&lon=-3.6845"); - expect(params).to.have.property("lat", 57.6247); - expect(params).to.have.property("lon", -3.6845); - expect(params).to.have.property("zoom", 5); - - params = OSM.mapParams("?lat=57.6247&lon=-3.6845&zoom=10"); - expect(params).to.have.property("lat", 57.6247); - expect(params).to.have.property("lon", -3.6845); - expect(params).to.have.property("zoom", 10); - - params = OSM.mapParams("?mlat=57.6247&mlon=-3.6845"); + it("parses mlat/mlon/zoom params", function () { + var params = OSM.mapParams("?mlat=57.6247&mlon=-3.6845"); expect(params).to.have.property("lat", 57.6247); expect(params).to.have.property("lon", -3.6845); expect(params).to.have.property("zoom", 12); @@ -249,4 +239,14 @@ describe("OSM", function () { expect(OSM.locationCookie(map)).to.eq("-3.685|57.625|5|M"); }); }); + + describe(".distance", function () { + it("computes distance between points", function () { + var latlng1 = L.latLng(51.76712,-0.00484), + latlng2 = L.latLng(51.7675159, -0.0078329); + + expect(OSM.distance(latlng1, latlng2)).to.be.closeTo(210.664, 0.005); + expect(OSM.distance(latlng2, latlng1)).to.be.closeTo(210.664, 0.005); + }); + }); });