最近的一个项目中有这么一个功能需求,在搜索一个城市的某些记录的时候后,要将该城市附近城市的类似数据也现实出来。于是上网google一下,希望有相关api能够提供输入城市名称返回附近城市的列表的功能,找了半天似乎也没有类似api,只找到一个 Urban Mapping 不过是提供neighborhoods 查找功能的(其实neighborhoods应该是更为合理的,不过似乎客户就是想显示临近的city),没办法,只能又回到了google map,既希望于能找出一些可用的api。之前应用google map的场景也不少,但是都没有接触过GDirection这个类,这次又仔细了看了一遍api,发现似乎可以利用这个类来达到我的目的。主要思想就是,首先找出你要用到的所有城市(我们这里是CA Bay Area 附近的所有城市),然后一次调用gmap的接口,发送类似 “from: ** to: ** ”这样的query 给gdirection, 然后通过在回调方法load中调用getDistance方法来获得两个城市之间的距离,这样我们就可以任意的定义”nearby”的距离,然后根据获得的distance来判断一个城市是否是另一个城市的”nearby city”.

这个解决方法的关键步骤就在于如何将所有的city两两之间的距离自动发送给gmap api,然后返回结果。一开始我也没多想,就循环呗,于是循环调用api 发送和回调代码,大概是这个样子的:

function initialize(){
...
	while(cities.length>0){
		city = cities.shift();
		index = 0
		for(c in cities){
			direction = new GDirections();
			query = "from: "+city+" to: "+cities[c]
			direction.load(query);
			listen(direction,city,cities[c]);
		}
	 }
...
}
// 回调方法
function listen(){...}

结果代码出了几个问题:

  1. 只运行了几个结果就报错跳出了
  2. browser会提示脚本运行会使browser变慢
  3. 由于连续的发放请求,被google认为使spam,会将api key停止是用一会。

经过一番尝试想到从一下几个方面解决此问题:

  1. 由于回调代码是异步的而且不能保证执行的顺序所以造成循环调用的时候报错,要解决此问题,必须保证query“一个接一个”的发送,发送一个query-执行回调代码完成-再发送下一个query。因此不能是用简单的循环来做此事,重构上述代码用递归的方式运行
  2. 将每一发送的时间间隔提高,防止被google当作spam,配合prototype的periodicalexecuter使用,效果甚佳。

下面给出完整的rhtml页面代码


	
		<%= javascript_include_tag "prototype"%>
		
		
		
	
	
Distance:
0
<%= link_to_remote "Load results", :url=>{:controller=>'home',:action=>'save_distances',:method=>"post"},:with=>"'results='+results.toJSON()" %>

PS: 这篇文章和上述解决方案有神似的地方,基本上讲的一回事.