Merge branch 'overpass'
authorTom Hughes <tom@compton.nu>
Fri, 7 Nov 2014 08:40:18 +0000 (08:40 +0000)
committerTom Hughes <tom@compton.nu>
Fri, 7 Nov 2014 08:40:18 +0000 (08:40 +0000)
15 files changed:
app/assets/images/sprite.png
app/assets/images/sprite.svg
app/assets/javascripts/index.js
app/assets/javascripts/index/query.js [new file with mode: 0644]
app/assets/javascripts/leaflet.query.js [new file with mode: 0644]
app/assets/javascripts/osm.js.erb
app/assets/javascripts/router.js
app/assets/stylesheets/common.css.scss
app/views/browse/query.html.erb [new file with mode: 0644]
config/example.application.yml
config/i18n-js.yml
config/locales/en.yml
config/routes.rb
db/structure.sql
test/javascripts/osm_test.js

index e7490c84cbca1a7852d89a3011b3c1f23d541e3f..e3ed0e7f81185d93ae6d75b8aab27d20d521242a 100644 (file)
Binary files a/app/assets/images/sprite.png and b/app/assets/images/sprite.png differ
index b61018d135276d031f0fb64a7e1611ac03305ecb..b50b969e909620ce71400c28d5c54c4dc409e1f4 100644 (file)
@@ -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">
      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"
        orientation="1,0"
        position="260,195"
        id="guide11761" />
+    <sodipodi:guide
+       orientation="1,0"
+       position="280,153.875"
+       id="guide3019" />
   </sodipodi:namedview>
   <metadata
      id="metadata7">
        inkscape:export-filename="/Users/saman/work_repos/osm-redesign/renders/share-1.png"
        inkscape:export-xdpi="90"
        inkscape:export-ydpi="90" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
+       x="264.8125"
+       y="869.62622"
+       id="text3021"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3023"
+         x="264.8125"
+         y="869.62622">?</tspan></text>
   </g>
 </svg>
index 0cf08f84539f50321dcff5caa35f368a2fbcda8e..191f390bf4941363049d61981db189ea0f17313b 100644 (file)
@@ -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
@@ -13,6 +14,7 @@
 //= require index/note
 //= require index/new_note
 //= require index/changeset
+//= require index/query
 //= require router
 
 (function() {
@@ -124,6 +126,11 @@ $(document).ready(function () {
     sidebar: sidebar
   }).addTo(map);
 
+  L.OSM.query({
+    position: position,
+    sidebar: sidebar
+  }).addTo(map);
+
   L.control.scale()
     .addTo(map);
 
@@ -305,7 +312,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 (file)
index 0000000..d0e9cc2
--- /dev/null
@@ -0,0 +1,329 @@
+//= 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("disabled")) return;
+
+    if (queryButton.hasClass("active")) {
+      disableQueryMode();
+    } else {
+      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") {
+      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: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 = $("<li>")
+              .addClass("query-result")
+              .data("geometry", featureGeometry(element))
+              .appendTo($ul);
+            var $p = $("<p>")
+              .text(featurePrefix(element) + " ")
+              .appendTo($li);
+
+            $("<a>")
+              .attr("href", "/" + element.type + "/" + element.id)
+              .text(featureName(element))
+              .appendTo($p);
+          }
+        }
+
+        if ($ul.find("li").length == 0) {
+          $("<li>")
+            .text(I18n.t("javascripts.query.nothing_found"))
+            .appendTo($ul);
+        }
+      },
+      error: function(xhr, status, error) {
+        $section.find(".loader").stopTime("loading").hide();
+
+        $("<li>")
+          .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:<radius>,<lat>,lng>)
+   *   way(around:<radius>,<lat>,lng>)
+   *   relation(around:<radius>,<lat>,lng>)
+   *
+   * to find enclosing objects we first find all the enclosing areas:
+   *
+   *   is_in(<lat>,<lng>)->.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 (file)
index 0000000..9064872
--- /dev/null
@@ -0,0 +1,38 @@
+L.OSM.query = function (options) {
+  var control = L.control(options);
+
+  control.onAdd = function (map) {
+    var $container = $('<div>')
+      .attr('class', 'control-query');
+
+    var link = $('<a>')
+      .attr('class', 'control-button')
+      .attr('href', '#')
+      .html('<span class="icon query"></span>')
+      .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;
+};
index 033b2de8122e991513ab9f431ba8a504b4b2c4aa..15c1682d5f0e14e925a8c5a4496da16ff577dc02 100644 (file)
@@ -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)
+      ));
   }
 };
index 9af70c21d5e714d4ee9384c5d6e48a170b662e08..dcf8ea6a8cbad7aa45c8a2717e71f67fdf19b6e0 100644 (file)
@@ -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);
index 37a6aa7a95bae2c6d81c0f40af663ff402694740..35ec432b5306e311ddc4663264132c9852248b43 100644 (file)
@@ -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 */
 
@@ -680,6 +680,14 @@ nav.secondary {
   #map {
     height: 100%;
     overflow: hidden;
+
+    &.query-active {
+      cursor: help;
+    }
+
+    &.query-disabled {
+      cursor: not-allowed;
+    }
   }
 
   #map-ui {
@@ -1136,6 +1144,34 @@ header .search_form {
     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 */
diff --git a/app/views/browse/query.html.erb b/app/views/browse/query.html.erb
new file mode 100644 (file)
index 0000000..629d84c
--- /dev/null
@@ -0,0 +1,22 @@
+<% set_title(t "browse.query.title") %>
+
+<h2>
+  <a class="geolink" href="<%= root_path %>"><span class="icon close"></span></a>
+  <%= t "browse.query.title" %>
+</h2>
+
+<div class="query-intro">
+  <p><%= t("browse.query.introduction") %></p>
+</div>
+
+<div id="query-nearby" class="query-results">
+  <h3><%= t("browse.query.nearby") %></h3>
+  <%= image_tag "searching.gif", :class => "loader" %>
+  <ul class="query-results-list"></ul>
+</div>
+
+<div id="query-isin" class="query-results">
+  <h3><%= t("browse.query.enclosing") %></h3>
+  <%= image_tag "searching.gif", :class => "loader" %>
+  <ul class="query-results-list"></ul>
+</div>
index 3fbebdc766c5d82424682433381aeeb5246463ca..e7915c96b7699924de9f98a1a06e8479a0575c07 100644 (file)
@@ -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
index 026ece64c11053f181dc1220921a187c683e5927..369fa340a2cd6611c538e9e93706ed0a7ea40154 100644 (file)
@@ -31,3 +31,4 @@ translations:
     - "*.site.sidebar.search_results"
     - "*.diary_entry.edit.marker_text"
     - "*.layouts.project_name.title"
+    - "*.geocoder.search_osm_nominatim.*"
index 021151052ed5c13806fe4c3bfb185687e3134000..69e52e44dfd685b685cf8a9102838c0bf9af3eb3 100644 (file)
@@ -204,6 +204,11 @@ en:
       reopened_by: "Reactivated by %{user} <abbr title='%{exact_time}'>%{when} ago</abbr>"
       reopened_by_anonymous: "Reactivated by anonymous <abbr title='%{exact_time}'>%{when} ago</abbr>"
       hidden_by: "Hidden by %{user} <abbr title='%{exact_time}'>%{when} ago</abbr>"
+    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"
@@ -2126,6 +2133,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"
@@ -2145,6 +2154,13 @@ 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
+      nothing_found: No features found
+      error: "Error contacting %{server}: %{error}"
+      timeout: "Timeout contacting %{server}"
   redaction:
     edit:
       description: "Description"
index 8cbf8f900c06f2305a5067c3ea6524914fff1388..7084d1c8dac071a8ab8f4ec4546a5b3ac2280491 100644 (file)
@@ -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
index c287baea38197e16737ff0143af5866b58d14a0a..343aec084abbc9b1ac43026e1839ca5c41c5647b 100644 (file)
@@ -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');
+
index d7fe4a2fd87ab41451f4dc139554a08f2cc3d4fd..51f74fe7a56b98087fba50772169642e0797d673 100644 (file)
@@ -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);
+    });
+  });
 });