From: Tom Hughes Date: Sat, 8 Mar 2014 17:40:27 +0000 (+0000) Subject: Initial work on overpass based query API X-Git-Tag: live~4298^2~32 X-Git-Url: https://git.openstreetmap.org/rails.git/commitdiff_plain/078059b76b8304368fd7ddac2dd563470e83472e?hp=0de6885a91e1740e3e2740287cb2d576de827844 Initial work on overpass based query API --- 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 dc3c93277..ddec2bffe 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -5,6 +5,7 @@ //= require leaflet.key //= require leaflet.note //= require leaflet.share +//= require leaflet.query //= require index/search //= require index/browse //= require index/export @@ -12,6 +13,7 @@ //= require index/history //= require index/note //= require index/new_note +//= require index/query //= require router (function() { @@ -123,6 +125,11 @@ $(document).ready(function () { sidebar: sidebar }).addTo(map); + L.OSM.query({ + position: position, + sidebar: sidebar + }).addTo(map); + L.control.scale() .addTo(map); @@ -294,7 +301,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.Browse(map, 'changeset') + "/changeset/:id": OSM.Browse(map, 'changeset'), + "/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..8d04efd5f --- /dev/null +++ b/app/assets/javascripts/index/query.js @@ -0,0 +1,244 @@ +OSM.Query = function(map) { + var queryButton = $(".control-query .control-button"), + uninterestingTags = ['source', 'source_ref', 'source:ref', 'history', 'attribution', 'created_by', 'tiger:county', 'tiger:tlid', 'tiger:upload_uuid'], + marker; + + queryButton.on("click", function (e) { + e.preventDefault(); + e.stopPropagation(); + + if (queryButton.hasClass("active")) { + disableQueryMode(); + + OSM.router.route("/"); + } else { + enableQueryMode(); + } + }); + + $("#sidebar_content") + .on("mouseover", ".query-results li", function () { + var geometry = $(this).data("geometry") + if (geometry) map.addLayer(geometry); + $(this).addClass("selected"); + }) + .on("mouseout", ".query-results li", function () { + var geometry = $(this).data("geometry") + if (geometry) map.removeLayer(geometry); + $(this).removeClass("selected"); + }); + + function interestingFeature(feature) { + 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") { + prefix = I18n.t("geocoder.search_osm_nominatim.admin_levels.level" + tags.admin_level) + } 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; + + if (tags["name"]) { + return tags["name"]; + } else if (tags["ref"]) { + return tags["ref"]; + } else if (tags["addr:housenumber"] && tags["addr:street"]) { + return tags["addr:housenumber"] + " " + tags["addr:street"]; + } else { + return "#" + feature.id; + } + } + + function featureLink(feature) { + if (feature.type === "area") { + if (feature.id >= 3600000000) { + var id = feature.id - 3600000000; + + return "/browse/relation/" + id; + } else if (feature.id >= 2400000000) { + var id = feature.id - 2400000000; + + return "/browse/way/" + id; + } else { + return "/browse/node/" + feature.id; + } + } else { + return "/browse/" + feature.type + "/" + feature.id; + } + } + + function featureGeometry(feature, nodes) { + var geometry; + + if (feature.type === "node") { + geometry = L.circleMarker([feature.lat, feature.lon]); + } else if (feature.type === "way") { + geometry = L.polyline(feature.nodes.map(function (node) { + return nodes[node]; + })); + } + + return geometry; + } + + function runQuery(query, $section) { + var $ul = $section.find("ul"); + + $ul.empty(); + $section.show(); + + $section.find(".loader").oneTime(1000, "loading", function () { + $(this).show(); + }); + + $.ajax({ + url: "http://overpass-api.de/api/interpreter", + method: "GET", + data: { + data: "[timeout:5][out:json];" + query, + }, + success: function(results) { + var nodes = {}; + + $section.find(".loader").stopTime("loading").hide(); + + results.elements.forEach(function (element) { + if (element.type === "node") { + nodes[element.id] = [element.lat, element.lon]; + } + }); + + for (var i = 0; i < results.elements.length; i++) { + var element = results.elements[i]; + + if (interestingFeature(element)) { + var $li = $("
  • ") + .data("geometry", featureGeometry(element, nodes)) + .appendTo($ul); + var $p = $("

    ") + .addClass("inner12 search_results_entry clearfix") + .text(featurePrefix(element) + " ") + .appendTo($li); + + $("") + .attr("href", featureLink(element)) + .text(featureName(element)) + .appendTo($p); + } + } + } + }); + } + + function queryOverpass(lat, lng) { + var latlng = L.latLng(lat, lng), + around = "around:10.0," + lat + "," + lng, + features = "(node(" + around + ");way(" + around + ");relation(" + around + "))", + nearby = "((" + features + ";way(bn));node(w));out;", + isin = "(is_in(" + lat + "," + lng + ");>);out;"; + + $("#sidebar_content .query-intro") + .hide(); + + if (marker) { + marker.setLatLng(latlng).addTo(map); + } else { + marker = L.circle(latlng, 10, { clickable: false }).addTo(map); + } + + $(document).everyTime(75, "fadeQueryMarker", function (i) { + if (i == 10) { + map.removeLayer(marker); + } else { + marker.setStyle({ + opacity: 0.5 - i * 0.05, + fillOpacity: 0.2 - i * 0.02 + }); + } + }, 10); + + runQuery(nearby, $("#query-nearby")); + runQuery(isin, $("#query-isin")); + } + + 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"); + map.off("click", clickHandler); + queryButton.removeClass("active"); + } + + var page = {}; + + page.pushstate = page.popstate = function(path) { + OSM.loadSidebarContent(path, function () { + page.load(path); + }); + }; + + page.load = function(path) { + var params = querystring.parse(path.substring(path.indexOf('?') + 1)); + + queryOverpass(params.lat, params.lon); + enableQueryMode(); + + return map.getState(); + }; + + page.unload = function() { + 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..3eab9054d --- /dev/null +++ b/app/assets/javascripts/leaflet.query.js @@ -0,0 +1,19 @@ +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', '#') + .attr('data-original-title', I18n.t('javascripts.site.queryfeature_tooltip')) + .html('') + .appendTo($container); + + return $container[0]; + }; + + return control; +}; diff --git a/app/assets/stylesheets/common.css.scss b/app/assets/stylesheets/common.css.scss index 533e91c69..c088e9429 100644 --- a/app/assets/stylesheets/common.css.scss +++ b/app/assets/stylesheets/common.css.scss @@ -171,7 +171,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 */ @@ -683,6 +683,10 @@ nav.secondary { #map { height: 100%; overflow: hidden; + + &.query-active { + cursor: help; + } } #map-ui { @@ -1119,6 +1123,14 @@ header .search_form { overflow: hidden; margin: 0 0 10px 10px; } + + .query-results { + display: none; + + ul.results-list li.selected { + background: #FFFFE6; + } + } } /* Rules for export sidebar */ diff --git a/app/views/browse/query.html.erb b/app/views/browse/query.html.erb new file mode 100644 index 000000000..62ab94efd --- /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/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 94537c1fb..4db668b38 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -197,6 +197,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}" @@ -507,7 +512,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" @@ -718,6 +723,8 @@ en: tram: "Tramway" tram_stop: "Tram Stop" yard: "Railway Yard" + route: + bus: "Bus Route" shop: alcohol: "Off License" antiques: "Antiques" @@ -2101,6 +2108,7 @@ 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 notes: new: intro: "Spotted a mistake or something missing? Let other mappers know so we can fix it. Move the marker to the correct position and type a note to explain the problem. (Please don't enter personal information here.)" @@ -2113,6 +2121,10 @@ en: comment_and_resolve: Comment & Resolve comment: Comment edit_help: Move the map and zoom in on a location you want to edit, then click here. + query: + node: Node + way: Way + relation: Relation redaction: edit: description: "Description" diff --git a/config/routes.rb b/config/routes.rb index e9f593d92..e03c5d632 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -150,6 +150,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