最近一直在做微信公众号的开发,其中涉及到定位商家地理位置,我方所用的地图控件是腾讯地图,通过地址定位 CityService获取商家的位置信息和经纬度,这里的经纬度是腾讯系经纬度。(当然也是可以使用高德地图或者其他地图的),获取到经纬度lat和lon。
而通过微信接口获取实时地理位置的经纬度,实质上得到的是GPS坐标,并不是(高德、百度、google系)经纬度。如果直接用之前保存好的商家经纬度和微信接口获得到的经纬度(gps坐标)进行距离计算,会产生大概500m的差距,打个比方说,就是两个数据单位不统一。通过微信接口获取到的经纬度(gps坐标)还需要一步处理,将GPS坐标转化为经纬度,将转化后的经纬度和商家经纬度通过计算可以获得正常的距离(有误差,但是误差比较小)。
以下是部分代码(供参考):
1. 微信接口获取经纬度(GPS坐标)
后端:
//微信获取地理位置
HttpServletRequest request = ThreadContextHolder.getRequest();
String url = request.getRequestURL().toString();
if (request.getQueryString() !=null) {
url += "?" + request.getQueryString();
}
String jsapi_ticket = WeixinConfig.getJSAPITicketInstance();
Map<String, String> map = SignUtil.sign(jsapi_ticket, url);
this.putData("appId", WeixinConfig.getAppId());
this.putData("timestamp", map.get("timestamp"));
this.putData("nonceStr", map.get("nonceStr"));
this.putData("signature", map.get("signature"));
附:
SignUtil
public class SignUtil {
@SuppressWarnings("rawtypes")
public static void main(String[] args) {
String jsapi_ticket = "jsapi_ticket";
// 注意 URL 一定要动态获取,不能 hardcode
String url = "http://example.com";
Map<String, String> ret = sign(jsapi_ticket, url);
for (Map.Entry entry : ret.entrySet()) {
System.out.println(entry.getKey() +", " + entry.getValue());
}
};
public static Map<String, String> sign(String jsapi_ticket, String url) {
Map<String, String> ret = new HashMap<String, String>();
String nonce_str = CommonUtil.create_nonce_str();
String timestamp = CommonUtil.create_timestamp();
String string1;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsapi_ticket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
// System.out.println("string1:"+string1);
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
ret.put("url", url);
ret.put("jsapi_ticket", jsapi_ticket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
return ret;
}
private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
public static String create_timestamp() {
Date date = new Date();
DateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
return format.format(date);
//return Long.toString(System.currentTimeMillis() / 1000);
}
}
public static StringgetJSAPITicketInstance(){
if(js_api_ticket ==null ||js_api_ticket.equals("")){
String result = WeixinUtil.getJSApiTicket(getTokenInstance());
JsAPITicket jsApiTicket = JSONObject.parseObject(result, JsAPITicket.class);
js_api_ticket = jsApiTicket.getTicket();
int expiresIn = jsApiTicket.getExpires_in() * 1000 - 300;
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
js_api_ticket = null;
}
};
Timer timer = new Timer();
timer.schedule(timerTask, expiresIn);
}
return js_api_ticket;
}
public static StringgetJSApiTicket(String accessToken) {
String requestUrl = jsapi_ticket_url.replace("ACCESS_TOKEN",
accessToken);
JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
// 如果请求成功
if (null != jsonObject) {
try {
jsonObject.getString("ticket");
} catch (JSONException e) {
// 获取token失败
log.error("获取ticket失败 errcode:{} errmsg:{}",
jsonObject.getInt("errcode"),
jsonObject.getString("errmsg"));
jsonObject = new JSONObject();
}
}
return jsonObject.toString();
}
前端:
<script src="js/jquery-1.8.0.min.js"></script>
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript">
var userLatitude = 0;
var userLongitude = 0;
//圆周率
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '${appId }', // 必填,公众号的唯一标识
timestamp: '${timestamp }', // 必填,生成签名的时间戳
nonceStr: '${nonceStr }', // 必填,生成签名的随机串
signature: '${signature }',// 必填,签名,见附录1
jsApiList: ['checkJsApi', 'onMenuShareTimeline',
'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo',
'hideMenuItems', 'showMenuItems', 'hideAllNonBaseMenuItem',
'showAllNonBaseMenuItem', 'translateVoice', 'startRecord',
'stopRecord', 'onRecordEnd', 'playVoice', 'pauseVoice',
'stopVoice', 'uploadVoice', 'downloadVoice', 'chooseImage',
'previewImage', 'uploadImage', 'downloadImage',
'getNetworkType', 'openLocation', 'getLocation',
'hideOptionMenu', 'showOptionMenu', 'closeWindow',
'scanQRCode', 'chooseWXPay', 'openProductSpecificView',
'addCard', 'chooseCard', 'openCard'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.ready(function(){
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
wx.getLocation({
success: function (res) {
userLatitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
userLongitude= res.longitude; // 经度,浮点数,范围为180 ~ -180。
var speed = res.speed;// 速度,以米/每秒计
var accuracy = res.accuracy;// 位置精度
}
});
});
wx.error(function(res){
alert(location.href.split('#')[0]);
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
alert(res.errMsg);
});
</script>
附:
对微信接口获取经纬度(GPS坐标)的监听
function lisner(){
if(userLatitude && userLongitude){
//获取到GPS坐标后,停止监听事件+调用方法
clearInterval(lisnerID);
getPosition(userLongitude,userLatitude);
}
}
var lisnerID = setInterval(lisner,100);
将GPS坐标转化为高德系经纬度
function getPosition(yy,xx) {
var url = "http://restapi.amap.com/v3/assistant/coordinate/convert?locations="
+yy+","+xx+"&coordsys=gps&output=json&key=你的key";
$.ajax({
url: url,
success: function(data,status,xhr) {
realuserLatitude = data.locations.split(",")[1];
realuserLongitude = data.locations.split(",")[0];
},
dataType: 'jsonp'
});
}
function getBaiduPosition(yy,xx) {
var url ="http://api.map.baidu.com/geoconv/v1/?coords="+yy+","+xx+"&from=1&to=5&ak=PCPa5VCFfVj7yXR40WWQnSKUPF3W96y8";
$.ajax({
url: url,
success: function(data,status,xhr) {
realuserLatitude = data.result[0].y;
realuserLongitude = data.result[0].x;
},
dataType: 'jsonp'
});
}
realuserLatitude 和realuserLongitude 为转化后的经纬度。
高德key获取:
参考链接:
https://www.cnblogs.com/zsy/p/5492799.html
http://www.storyday.com/wp-content/uploads/2008/09/latlung_dis.html(经纬度计算小工具)