谷歌地图的导轨 - 更新通过AJAX的标记,只有当搜索结果的变化 [英] Google Maps For Rails - updating markers via ajax only when search result changes

查看:187
本文介绍了谷歌地图的导轨 - 更新通过AJAX的标记,只有当搜索结果的变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力通过书面很大的帮助,从计算器一个小应用程序。基本premise很简单,我已经看到了这种功能在整个网络上:我想要绘制的位置列表上可搜索/ pannable谷歌地图。的位置存储在后端,并且所述控制器馈送这些位置的图。 AJAX是参与,因为我不想重新加载整个页面。下面是情景:a)用户搜索,通过压缩code =>地图加载新位置,位置搜索得到的是发送到服务器的地图加载任何标记,如果有任何一组半径范围内,地图设置一个默认缩放水平;二)用户平底锅/缩放周围=>地图停留无论该用户已离开它,与视口边框搜索被发送到服务器,结果被映射。该地图将默认为西雅图的初始加载,它尝试的第一件事就是给大地定位用户...

I have been working through writing a small app with a lot of help from stackoverflow. The basic premise is simple, and I've seen this sort of functionality all over the web: I am trying to plot a list of locations onto a searchable/pannable google map. The locations are stored on the backend, and the controller feeds these locations to the view. AJAX is involved because I don't want to reload the entire page. Here are the scenarios: a) User searches for a location via zipcode => map loads new location, a search get is sent to server and map loads any markers if there are any within set radius, map sets a default zoom level; b) User pans/zooms around => map stays wherever the user has left it, a search with the viewport bounding box is sent to the server and the results are mapped. The map will default to Seattle on initial load and the first thing it tries is to geolocate the user...

使用gmaps4ails wiki和大多来自这个问题的答案的修改版本:<一href="http://stackoverflow.com/questions/28456641/google-maps-for-rails-update-markers-with-ajax">Google地图导轨 - 更新标记与AJAX 我已经变得非常接近。它的工作原理,其实,只是一个顺利。这里是什么样子:

Using the gmaps4ails wiki and mostly a modified version of the answer from this question: Google Maps for Rails - update markers with AJAX I have gotten very close. It works, actually, just with a hitch. Here is what it looks like:

  def search
    if params[:lat]
      @ll = [params[:lat].to_f, params[:lng].to_f]
      @sightings = Sighting.within(5, origin: @ll).order('created_at DESC')
      @remap = true
    elsif search_params = params[:zipcode]
      geocode = Geokit::Geocoders::GoogleGeocoder.geocode(search_params)
      @ll = [geocode.lat, geocode.lng]
      @sightings = Sighting.within(5, origin: @ll).order('created_at DESC')
      @remap = true
    elsif params[:bounds]
      boundarray = params[:bounds].split(',')
      bounds = [[boundarray[0].to_f, boundarray[1].to_f], [boundarray[2].to_f, boundarray[3].to_f]]
      @ll = [params[:center].split(',')[0].to_f, params[:center].split(',')[1].to_f]
      @sightings = Sighting.in_bounds(bounds, origin: @ll).order('created_at DESC')
      @remap = false
    else
      search_params = '98101'
      geocode = Geokit::Geocoders::GoogleGeocoder.geocode(search_params)
      @ll = [geocode.lat, geocode.lng]
      @sightings = Sighting.within(5, origin: @ll).order('created_at DESC')
      @remap = true
    end
    @hash = Gmaps4rails.build_markers(@sightings) do |sighting, marker|
      marker.lat sighting.latitude
      marker.lng sighting.longitude
      marker.name sighting.title
      marker.infowindow view_context.link_to("sighting", sighting)
    end
    respond_to do |format|
      format.html
      format.js
    end
  end

search.html.haml

= form_tag search_sightings_path, method: "get", id: "zipform", role: "form", remote: true do
  = text_field_tag :zipcode, params[:zipcode], size: 5, maxlength: 5, placeholder: "zipcode", id: "zipsearch"
  = button_tag "Search", name: "button"
  %input{type: "button", value: "Current Location", onclick: "getUserLocation()"}
#locationData

.sightings_map_container
 .sightings_map_canvas#sightings_map_canvas
   #sightings_container

- content_for :javascript do
  %script{src: "//maps.google.com/maps/api/js?v=3.13&amp;sensor=false&amp;libraries=geometry", type: "text/javascript"}
  %script{src: "//google-maps-utility-library-v3.googlecode.com/svn/tags/markerclustererplus/2.0.14/src/markerclusterer_packed.js", type: "text/javascript"}

  :javascript
    function getUserLocation() {
      //check if the geolocation object is supported, if so get position
      if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(setLocation);
      }
      else {
        document.getElementById("locationData").innerHTML = "Sorry - your browser doesn't support geolocation!";
      }
    }

    function setLocation(position) {
      //build text string including co-ordinate data passed in parameter
      var displayText = "Latitude: " + position.coords.latitude + ", Longitude: " + position.coords.longitude;

      //display the string for demonstration
      document.getElementById("locationData").innerHTML = displayText;
      //submit the lat/lng coordinates of current location
      $.get('/sightings/search.js',{lat: position.coords.latitude, lng: position.coords.longitude});
    }
    // build maps via Gmaps4rails
    handler = Gmaps.build('Google');
    handler.buildMap({
      provider: {
      },
      internal: {
      id: 'sightings_map_canvas'
      }
    },

    function() {
      if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(setLocation);
      }

      var json_array = #{raw @hash.to_json};
      var latlng = #{raw @ll};

      resetMarkers(handler, json_array);
      resetMap(handler, latlng);

      // listen for pan/zoom and submit new coordinates
      (function gmaps4rails_callback() {
        google.maps.event.addListener(handler.getMap(), 'idle', function() {
          var bounds = handler.getMap().getBounds().toUrlValue();
          var center = handler.getMap().getCenter().toUrlValue();
          $.get('/sightings/search.js',{bounds: bounds, center: center, old_hash: #{raw @hash.to_json}});
        })
      })();
    });

search.js.erb

(function() {
  var json_array = <%= raw @hash.to_json %>;
  if (<%= @remap %>) {
    var latlng = <%= raw @ll %>;
    resetMarkers(handler, json_array);
    resetMap(handler, latlng);
  }
  else {
    resetMarkers(handler, json_array);
  }
})();

map.js

(function() {

  function createSidebarLi(json) {
    return ("<li><a>" + json.name + "</a></li>");
  };

  function bindLiToMarker($li, marker) {
    $li.on('click', function() {
      handler.getMap().setZoom(18);
      marker.setMap(handler.getMap()); //because clusterer removes map property from marker
      google.maps.event.trigger(marker.getServiceObject(), 'click');
    })
  };

  function createSidebar(json_array) {
    _.each(json_array, function(json) {
      var $li = $( createSidebarLi(json) );
      $li.appendTo('#sightings_container');
      bindLiToMarker($li, json.marker);
    });
  };

  function clearSidebar() {
    $('#sightings_container').empty();
  };

  function clearZipcode() {
    $('#zipform')[0].reset();
  };

  /* __markers will hold a reference to all markers currently shown
  on the map, as GMaps4Rails won't do it for you.
  This won't pollute the global window object because we're nested
  in a "self-executed" anonymous function */

  var __markers;

  function resetMarkers(handler, json_array) {
    handler.removeMarkers(__markers);
    clearSidebar();
    clearZipcode();
    if (json_array.length > 0) {
      __markers = handler.addMarkers(json_array);
      _.each(json_array, function(json, index){
        json.marker = __markers[index];
      });
      createSidebar(json_array);
    }
  };

  function resetMap(handler, latlng) {
    handler.bounds.extendWith(__markers);
    handler.fitMapToBounds();
    handler.getMap().setZoom(12);
    handler.map.centerOn({
      lat: latlng[0],
      lng: latlng[1]
    });
  }

// "Publish" our method on window. You should probably have your own namespace
  window.resetMarkers = resetMarkers;
  window.resetMap = resetMap;

})();

这里的问题,这既是这个具体的例子做的如何JavaScript的(我是新来的吧)变量的工作我似乎误解。当用户的平移和缩放,但是搜索结果是一样的,我想preFER不是召resetMarkers功能,而宁愿只留下地图孤单。该地图将现在总是resetMarkers /侧边栏/等,这将导致在屏幕上标记一个闪烁的点点。

Here's the problem, and it is as much to do with this specific example as my seeming misunderstanding of how javascript (I'm new to it) variables work. When the user pans and zooms, but the search result is the same, I would prefer NOT to call the "resetMarkers" function, but would rather just leave the map alone. The map will currently always resetMarkers/sidebar/etc and this causes a little bit of a flicker of markers on the screen.

我试过几个不同的版本这一点,但不起作用。在map.js:

I've tried several different versions of this, but doesn't work. In map.js:

var __markers;
var __oldmarkers;
function resetMarkers(handler, json_array) {
  if(!(_.isEqual(__oldmarkers, __markers))) {
    handler.removeMarkers(__markers);
    clearSidebar();
    clearZipcode();
    if (json_array.length > 0) {
      __markers = handler.addMarkers(json_array);
      _.each(json_array, function(json, index){
        json.marker = __markers[index];
      });
      createSidebar(json_array);
    }
    __oldmarkers = __markers.slice(0);
  }
};

由于__markers似乎通过页面(我们用它来删除旧的标记之前,我们设定新的)的使用寿命,保持其价值,我想我可以简单地创建另一个变量来对证。然而,它总是假的,甚至当我觉得应该是真的。

Since __markers seems to hold its value through the life of the page (we use it to remove the old markers before we set new ones), I thought I could simply create another variable to check against it. However, it's always false even when I think it should be true.

另一件事我已经试过是再次提交旧的哈希值与每个搜索请求中的参数,然后设置一个标志,但是这个看似复杂和字符串/散列/数组操作变得如此混乱,我放弃了。我真的不认为这将是最好的方法,但也许我应该做的呀?

Another thing I've tried is to resubmit the old hash as a parameter with every search request and then set a flag, but this seems complicated, and the string/hash/array manipulation got so confusing I gave up. I don't really think this would be the best approach, but perhaps I should do it that way?

或者,是否有我丢失的东西完全和应该做的呢?

Or, is there something I am missing completely and should be doing instead?

推荐答案

您的问题在于比较标记的两个列表来决定你是否应该更新与否。

Your problem lies in comparing both lists of markers to decide whether you should update or not.

的事情是,虽然 _。isEqual:方法(__ oldmarkers,__markers)并进行了深刻的对比,可能会有东西在你的列表中标记实例的改变即使对于相同点(一个id,时间戳,...)。
也许这仅仅是因为在一开始,双方 __标记 __ oldMarkers ,从而平等的,这意味着你永远不会得到如果块内。

The thing is, although _.isEqual(__oldmarkers, __markers) does perform a deep comparison, there might be things in Marker instances within your list that change even for identical points (an id, timestamps, ...).
Or perhaps it's simply because at the start, both __markers and __oldMarkers are null, thus equal, meaning you never get inside the ifblock.

总之,我觉得在这里深深的比较可能变得过于昂贵。我会怎么做,而不是,是比较的东西都是比较的轻松,就像坐标为每个组标记的平面列表。

Anyway, I think deep comparison here could become too costly. What I'd do instead, is compare things that are comparable easily, like the flat list of coordinates for each set of markers.

事情是这样的:

var __markers, __coordinates = [];
function resetMarkers(handler, json_array) 
{
  var coordinates = _.map(json_array, function(marker) {
    return String(marker.lat) + ',' + String(marker.lng);
  });

  if(_.isEqual(__coordinates.sort(), coordinates.sort()))
  {
    handler.removeMarkers(__markers);
    clearSidebar();
    clearZipcode();
    if (json_array.length > 0) 
    {
      __markers = handler.addMarkers(json_array);
      _.each(json_array, function(json, index){
        json.marker = __markers[index];
      });
      createSidebar(json_array);
    }
    __coordinates = coordinates;
  }
};

下面 __坐标坐标是字符串只是平阵列,应迅速进行比较,得到预期的结果,
在下令使用 _。isEqual:方法进行比较,两个数组是预先排序。

Here __coordinates and coordinates are just flat arrays of String, which should be compared quickly and give you the expected results.
In ordered to be compared using _.isEqual, both arrays are sorted beforehand.

注:旧code使用_.difference但是这是不正确的(见注释中讨论)
(请注意,我用 _。区别虽然可能更昂贵比 _。isEqual:方法但被独立返回的标记顺序的奖金。)

NB: Old code used _.difference but that wasn't correct (see discussion in comments)
(Note that I'm using _.difference though, probably more costly than _.isEqual but with the bonus of being independent of the returned markers order.)

编辑:哦,当然,你可以停止发送的oldHash,在搜索查询现在PARAMS;)

edit: Oh and of course you can stop sending that "oldHash" in the search query params now ;)

这篇关于谷歌地图的导轨 - 更新通过AJAX的标记,只有当搜索结果的变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆