原创声明:本文来源于本人另一博客【微信JS-SDK之地理位置的获取,集成百度地图实现在线地图搜索】原创作品,绝非他处摘取,转载请联系博主
本次讲解微信开发第三篇:获取用户地址位置信息,是非常常用的功能,特别是服务行业公众号,尤为需要该功能,本次讲解的就是如何调用微信JS-SDK接口,获取用户位置信息,并结合百度地铁,实现在线地图搜索,与在线导航。
官方文档地址:https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
在这粘贴上二篇博文链接,方便大家访问:
微信公众号开发《一》OAuth2.0网页授权认证获取用户的详细信息,实现自动登陆
微信公众号开发《二》发送模板消息实现消息业务实时通知
1.何为JS-SDK:微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。简单来说:就是在自己公众平台后台配置后,可直接调用的功能接口。
那如何配置呢?下面讲解下配置步骤:示例讲解是基于测试公众号,如何使用测试公众号,可以参考第一篇文章
1.在公众号后台绑定域名:测试公众号登录就可看见如下图。正式公众号配置位置:“公众号设置”的“功能设置”里填写“JS接口安全域名”
2.页面中引入接口JS文件,下载地址:http://res.wx.qq.com/open/js/jweixin-1.2.0.js
注意:JS最新版本为1.2.0,为了适配IOS开发,最好使用最新版本号JS,1.1.0版本可能会造成有些功能调用失败,有兴趣的可以看看官方文档:https://mp.weixin.qq.com/advanced/wiki?t=t=resource/res_main&id=mp1483682025_enmey
3.获取必须参数:
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [
'openLocation',
'getLocation'
] // 必填,需要使用的JS接口列表,更多接口可看官方文档
});
看调用接口,可知,现在缺timestamp,signature,nonceStr。不用想的太复杂,还是那个原则,没什么获取什么,
获取signature签名,生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket。
写个工具类封装获取方法
public class SHA1 { //sha算法
private final int[] abcde = {
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
};
// 摘要数据存储数组
private int[] digestInt = new int[5];
// 计算过程中的临时数据存储数组
private int[] tmpData = new int[80];
// 计算sha-1摘要
private int process_input_bytes(byte[] bytedata) {
// 初试化常量
System.arraycopy(abcde, 0, digestInt, 0, abcde.length);
// 格式化输入字节数组,补10及长度数据
byte[] newbyte = byteArrayFormatData(bytedata);
// 获取数据摘要计算的数据单元个数
int MCount = newbyte.length / 64;
// 循环对每个数据单元进行摘要计算
for (int pos = 0; pos < MCount; pos++) {
// 将每个单元的数据转换成16个整型数据,并保存到tmpData的前16个数组元素中
for (int j = 0; j < 16; j++) {
tmpData[j] = byteArrayToInt(newbyte, (pos * 64) + (j * 4));
}
// 摘要计算函数
encrypt();
}
return 20;
}
// 格式化输入字节数组格式
private byte[] byteArrayFormatData(byte[] bytedata) {
// 补0数量
int zeros = 0;
// 补位后总位数
int size = 0;
// 原始数据长度
int n = bytedata.length;
// 模64后的剩余位数
int m = n % 64;
// 计算添加0的个数以及添加10后的总长度
if (m < 56) {
zeros = 55 - m;
size = n - m + 64;
} else if (m == 56) {
zeros = 63;
size = n + 8 + 64;
} else {
zeros = 63 - m + 56;
size = (n + 64) - m + 64;
}
// 补位后生成的新数组内容
byte[] newbyte = new byte[size];
// 复制数组的前面部分
System.arraycopy(bytedata, 0, newbyte, 0, n);
// 获得数组Append数据元素的位置
int l = n;
// 补1操作
newbyte[l++] = (byte) 0x80;
// 补0操作
for (int i = 0; i < zeros; i++) {
newbyte[l++] = (byte) 0x00;
}
// 计算数据长度,补数据长度位共8字节,长整型
long N = (long) n * 8;
byte h8 = (byte) (N & 0xFF);
byte h7 = (byte) ((N >> 8) & 0xFF);
byte h6 = (byte) ((N >> 16) & 0xFF);
byte h5 = (byte) ((N >> 24) & 0xFF);
byte h4 = (byte) ((N >> 32) & 0xFF);
byte h3 = (byte) ((N >> 40) & 0xFF);
byte h2 = (byte) ((N >> 48) & 0xFF);
byte h1 = (byte) (N >> 56);
newbyte[l++] = h1;
newbyte[l++] = h2;
newbyte[l++] = h3;
newbyte[l++] = h4;
newbyte[l++] = h5;
newbyte[l++] = h6;
newbyte[l++] = h7;
newbyte[l++] = h8;
return newbyte;
}
private int f1(int x, int y, int z) {
return (x & y) | (~x & z);
}
private int f2(int x, int y, int z) {
return x ^ y ^ z;
}
private int f3(int x, int y, int z) {
return (x & y) | (x & z) | (y & z);
}
private int f4(int x, int y) {
return (x << y) | x >>> (32 - y);
}
// 单元摘要计算函数
private void encrypt() {
for (int i = 16; i <= 79; i++) {
tmpData[i] = f4(tmpData[i - 3] ^ tmpData[i - 8] ^ tmpData[i - 14] ^
tmpData[i - 16], 1);
}
int[] tmpabcde = new int[5];
for (int i1 = 0; i1 < tmpabcde.length; i1++) {
tmpabcde[i1] = digestInt[i1];
}
for (int j = 0; j <= 19; j++) {
int tmp = f4(tmpabcde[0], 5) +
f1(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] +
tmpData[j] + 0x5a827999;
tmpabcde[4] = tmpabcde[3];
tmpabcde[3] = tmpabcde[2];
tmpabcde[2] = f4(tmpabcde[1], 30);
tmpabcde[1] = tmpabcde[0];
tmpabcde[0] = tmp;
}
for (int k = 20; k <= 39; k++) {
int tmp = f4(tmpabcde[0], 5) +
f2(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] +
tmpData[k] + 0x6ed9eba1;
tmpabcde[4] = tmpabcde[3];
tmpabcde[3] = tmpabcde[2];
tmpabcde[2] = f4(tmpabcde[1], 30);
tmpabcde[1] = tmpabcde[0];
tmpabcde[0] = tmp;
}
for (int l = 40; l <= 59; l++) {
int tmp = f4(tmpabcde[0], 5) +
f3(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] +
tmpData[l] + 0x8f1bbcdc;
tmpabcde[4] = tmpabcde[3];
tmpabcde[3] = tmpabcde[2];
tmpabcde[2] = f4(tmpabcde[1], 30);
tmpabcde[1] = tmpabcde[0];
tmpabcde[0] = tmp;
}
for (int m = 60; m <= 79; m++) {
int tmp = f4(tmpabcde[0], 5) +
f2(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] +
tmpData[m] + 0xca62c1d6;
tmpabcde[4] = tmpabcde[3];
tmpabcde[3] = tmpabcde[2];
tmpabcde[2] = f4(tmpabcde[1], 30);
tmpabcde[1] = tmpabcde[0];
tmpabcde[0] = tmp;
}
for (int i2 = 0; i2 < tmpabcde.length; i2++) {
digestInt[i2] = digestInt[i2] + tmpabcde[i2];
}
for (int n = 0; n < tmpData.length; n++) {
tmpData[n] = 0;
}
}
// 4字节数组转换为整数
private int byteArrayToInt(byte[] bytedata, int i) {
return ((bytedata[i] & 0xff) << 24) | ((bytedata[i + 1] & 0xff) << 16) |
((bytedata[i + 2] & 0xff) << 8) | (bytedata[i + 3] & 0xff);
}
// 整数转换为4字节数组
private void intToByteArray(int intValue, byte[] byteData, int i) {
byteData[i] = (byte) (intValue >>> 24);
byteData[i + 1] = (byte) (intValue >>> 16);
byteData[i + 2] = (byte) (intValue >>> 8);
byteData[i + 3] = (byte) intValue;
}
// 将字节转换为十六进制字符串
private static String byteToHexString(byte ib) {
char[] Digit = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
'D', 'E', 'F'
};
char[] ob = new char[2];
ob[0] = Digit[(ib >>> 4) & 0X0F];
ob[1] = Digit[ib & 0X0F];
String s = new String(ob);
return s;
}
// 将字节数组转换为十六进制字符串
private static String byteArrayToHexString(byte[] bytearray) {
String strDigest = "";
for (int i = 0; i < bytearray.length; i++) {
strDigest += byteToHexString(bytearray[i]);
}
return strDigest;
}
// 计算sha-1摘要,返回相应的字节数组
public byte[] getDigestOfBytes(byte[] byteData) {
process_input_bytes(byteData);
byte[] digest = new byte[20];
for (int i = 0; i < digestInt.length; i++) {
intToByteArray(digestInt[i], digest, i * 4);
}
return digest;
}
// 计算sha-1摘要,返回相应的十六进制字符串
public String getDigestOfString(byte[] byteData) {
return byteArrayToHexString(getDigestOfBytes(byteData));
}
public static void main(String[] args) {
String data = "123456";
System.out.println(data);
String digest = new SHA1().getDigestOfString(data.getBytes());
System.out.println(digest);
// System.out.println( ToMD5.convertSHA1(data).toUpperCase());
}
}
/**
* Ticket封装类
* @author lh
*/
public class Ticket {
private String errcode;
private String errmsg;
private String ticket;
private String expires_in;
//省略get,set方法
}
/**
* Signature封装类
* @author lh
*/
public class SignatureInfo {
private String signature;
private String timestamp;
private String noncestr;
private String url;
//省略get,set方法
}
获取ticket
/**
* 获取ticket
* @param accessToken
* @return
*/
public static Ticket getTicket(String accessToken){
Ticket ticket = new Ticket();
String getTicket = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
String url = getTicket.replace("ACCESS_TOKEN",accessToken);
JSONObject jsonObject = httpRequest(url, "POST", null);
if (null != jsonObject) {
if (0 != jsonObject.getInt("errcode")) {
log.error("获取ticket失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
}else{
ticket.setErrcode(jsonObject.getString("errcode"));
ticket.setErrmsg(jsonObject.getString("errmsg"));
ticket.setExpires_in(jsonObject.getString("expires_in"));
ticket.setTicket(jsonObject.getString("ticket"));
}
}
return ticket;
}
获取Signature方法
/**
* 签名算法
* @param ticket
* @return
*/
public static SignatureInfo getSignature(SignatureInfo sign,Ticket ticket){
String data = "jsapi_ticket="+ticket.getTicket()+"&noncestr="+sign.getNoncestr()+"×tamp="+sign.getTimestamp()+"&url="+sign.getUrl();
String signature = new SHA1().getDigestOfString(data.getBytes());
sign.setSignature(signature);
log.info("signature="+sign.getSignature());
return sign;
}
最后综合上面方法,封装主函数
public String jssdk_demo(HttpServletRequest request,HttpServletResponse response){
String param = request.getQueryString();//获取请求参数
String url = request.getServletPath();//获取请求路径(不带参数)
if(param!=null){
url = url+"?"+param;//组合成完整请求URL
}
String projectnameP = request.getContextPath();
String projectName = projectnameP.substring(projectnameP.lastIndexOf('/')+1,projectnameP.length()); //获取工程名,如testW
if(!"".equals(projectName)){
projectName ="/"+projectName;
}
String port = String.valueOf(request.getServerPort());//获取端口号
if(!"80".equals(port)){//不是80端口时需加端口号
port = ":"+port;
}else{
port = "";
}
String strBackUrl = "http://" + request.getServerName()+port+projectName+url;//完整的请求路径http://192.168.1.117/testW/+路径
AccessToken token = null;
if(TimedTask.accessToken==null || TimedTask.accessToken.getToken()==""){//token失效,重新获取,获取方法参考第二篇博文,在这由于篇幅问题暂不列出
token = WeixinUtil.getAccessToken(TimedTask.appid, TimedTask.appsecret);/
}else{
token = TimedTask.accessToken;
}
Ticket ticket = null;
if(TimedTask.ticket ==null || TimedTask.ticket.getTicket()==""){
ticket = WeixinUtil.getTicket(token.getToken());//获取ticket
}else{
ticket = TimedTask.ticket;
}
SignatureInfo siInfo = new SignatureInfo();
siInfo.setNoncestr(RandomStringUtils.randomAlphanumeric(20));//随机字符串
siInfo.setTimestamp(String.valueOf(System.currentTimeMillis()));//随机时间截
siInfo.setUrl(strBackUrl);
siInfo = WeixinUtil.getSignature(siInfo, ticket);
request.setAttribute("siInfo",siInfo);
return "weixin/jssdk_demo";
}
到此需要准备的数据就已经全部完成了,现在我们看看前台jssdk_demo.jsp页面是如何调用的
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
jssdk_demo
//百度地图没有密匙的可以去官网申请
mapLocation.js,为封装好的百度地图的方法,具体看各自需求可进行修改:
function getHtml5Location() {
if(navigator.geolocation) {
// navigator.geolocation.watchPosition(updateLocation, handleLocationError, {
navigator.geolocation.getCurrentPosition(updateLocation, handleLocationError,{
// 指示浏览器获取高精度的位置,默认为false
enableHighAcuracy: true,
// 指定获取地理位置的超时时间,默认不限时,单位为毫秒
//timeout: 5000,
// 最长有效期,在重复获取地理位置时,此参数指定多久再次获取位置。
maximumAge: 20000
});
}else{
$("#wdialogContent2").text("无法获取您当前地理位置");
$("#wdialog2").show();
}
}
function updateLocation(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
var accuracy = position.coords.accuracy;
// 如果accuracy的值太大,我们认为它不准确,不用它计算距离
if (accuracy >= 1000) {
return;
}
var pt = new BMap.Point(longitude,latitude);
setTimeout(function(){
BMap.Convertor.translate(pt,0,translateCallback); //真实经纬度转成百度坐标
}, 100);
translateCallback = function (point){
createBaiduMap(point.lat,point.lng,1);
};
}
function handleLocationError(error) {
switch (error.code) {
case 0:
$("#wdialogContent2").text("尝试获取您的位置信息时发生错误:"+ error.message);
$("#wdialog2").show();
break;
case 1:
$("#wdialogContent2").text("用户拒绝了获取位置信息请求");
$("#wdialog2").show();
break;
case 2:
$("#wdialogContent2").text("浏览器无法获取您的位置信息:"+ error.message);
$("#wdialog2").show();
break;
case 3:
$("#wdialogContent2").text("获取您位置信息超时");
$("#wdialog2").show();
break;
}
}
/**
* 传人坐标获得详细地址
* @param lat
* @param lon
*/
function getAddressInfo(lon,lat,type) {
var myGeo = new BMap.Geocoder();
var pt = new BMap.Point(lon,lat);
translateCallback2 = function (point){
myGeo.getLocation(point, function(rs) {
var addComp = rs.addressComponents;
//rs.surroundingPois;//附近地址
var addr = addComp.province+ addComp.city + addComp.district+ addComp.street+ addComp.streetNumber;
searchMap(addr);
});
};
setTimeout(function(){
if(type==1){
BMap.Convertor.translate(pt,0,translateCallback2); //真实经纬度转成百度坐标
}else{
translateCallback2(pt);
}
}, 100);
}
var map = null;
function setMapEvent(){
map.enableDragging();//启用地图拖拽事件,默认启用(可不写)
map.enableScrollWheelZoom();//启用地图滚轮放大缩小
map.enableDoubleClickZoom();//启用鼠标双击放大,默认启用(可不写)
map.enableKeyboard();//启用键盘上下左右键移动地图
}
var marker = null;
function createBaiduMap(longitude, latitude,type) {
if(map==null){
map =new BMap.Map("container");
}
setMapEvent();
mSearchManager.clear();
var pt= new BMap.Point(longitude,latitude);
translateCallback = function (point){
var point= new BMap.Point(point.lng, point.lat);
map.setCenter(point);
map.centerAndZoom(point, 16);
// 添加带有定位的导航控件
var navigationControl = new BMap.NavigationControl({
// 靠左上角位置
anchor: BMAP_ANCHOR_TOP_LEFT,
// LARGE类型
type: BMAP_NAVIGATION_CONTROL_LARGE,
// 启用显示定位
enableGeolocation: true
});
map.addControl(navigationControl);
marker = new BMap.Marker(point); //标注
marker.enableDragging();
marker.addEventListener("dragend",getAttr);
function getAttr(){
var p = marker.getPosition(); //获取marker的位置
getAddressInfo(p.lng,p.lat);
}
map.clearOverlays();
map.addOverlay(marker);
map.addEventListener("click", function(e){
var gc = new BMap.Geocoder();
var pt = new BMap.Point(e.point.lng,e.point.lat);
document.getElementById(longiText).value = e.point.lng;
document.getElementById(latiText).value = e.point.lat;
window.map.removeOverlay(marker);
marker = new BMap.Marker(e.point);//创建一个覆盖物
map.addOverlay(marker);//增加一个标示到地图上
var dress = gc.getLocation(pt, function(rs){
var addComp = rs.addressComponents;
var address = addComp.province+addComp.city+addComp.district+addComp.street+addComp.streetNumber;
if(typeof(addId)!="undefined" && addId !=null){
$("#"+addId).text(address);
}
searchMap(address);
});
});
// 添加定位控件
// var geolocationControl = new BMap.GeolocationControl();
// geolocationControl.addEventListener("locationSuccess", function(e){
// window.map.removeOverlay(marker);
// marker = new BMap.Marker(e.point);//创建一个覆盖物
// map.addOverlay(marker);//增加一个标示到地图上
// map.panTo(e.point);
// 定位成功事件
// var address = '';
// address += e.addressComponent.province;
// address += e.addressComponent.city;
// address += e.addressComponent.district;
// address += e.addressComponent.street;
// address += e.addressComponent.streetNumber;
// searchMap(address);
//getLocation();
// });
// geolocationControl.addEventListener("locationError",function(e){
// // 定位失败事件
// alert(e.message);
// });
//地图拖动事件
/*map.addEventListener("dragging", function(evt){
var offsetPoint = new BMap.Pixel(evt.offsetX, evt.offsetY);
});*/
// map.addControl(geolocationControl);
// 添加带有定位的导航控件
};
setTimeout(function(){
if(type==1){
translateCallback(pt);
}else{
BMap.Convertor.translate(pt,0,translateCallback); //真实经纬度转成百度坐标
}
}, 100);
}
var mSearchManager = new SearchManager();
var isR = false;
function SearchManager(){
this.SearchResultList = new Array();
this.showSearchResult = function(poi){
var index = this.SearchResultList.length;
var marker = new BMap.Marker(poi.point);
var a=document.createElement("a");
var p=document.createElement("P");
this.SearchResultList[index] = new KzSearchResult( marker , poi , a , p);
var address = "";
if(this.SearchResultList[index].poi.province!=undefined && this.SearchResultList[index].poi.province!=null
&& this.SearchResultList[index].poi.province !=""){
address = this.SearchResultList[index].poi.province;
}
if(this.SearchResultList[index].poi.city!=undefined && this.SearchResultList[index].poi.city!=null
&& this.SearchResultList[index].poi.city !=""){
address +=this.SearchResultList[index].poi.city;
}
var div = document.getElementById("search-result");
a.href="javascript:mSearchManager.zoomto("+index+")";
var $a = $("");
var section = $("");
var div1 = $(""+poi.title+"");
var div2 = $("");
var div3 = $(""+address+poi.address+"");
$a.append(div1).append(div2).append(div3);
section.append($a);
$("#search-result").append(section);
};
this.clear = function(){
var div = $("#search-result section");
div.remove();
window.map.removeOverlay(marker);
this.SearchResultList.length = 0;
};
this.clear2 = function(){
var div = $("#search-result section");
div.remove();
this.SearchResultList.length = 0;
};
this.zoomto = function (index){
$(".time").each(function(i,val){
if(i==index){
$(this).text("当前位置");
}else{
$(this).text("");
}
});
window.map.removeOverlay(marker);
marker = new BMap.Marker(this.SearchResultList[index].poi.point);
window.map.addOverlay(marker);
window.map.centerAndZoom(this.SearchResultList[index].poi.point, 16);
document.getElementById(longiText).value = this.SearchResultList[index].poi.point.lng;
document.getElementById(latiText).value = this.SearchResultList[index].poi.point.lat;
if(typeof(addId)!="undefined" && addId !=null){
document.getElementById(addId).innerHTML = this.SearchResultList[index].poi.title;
}
};
}
function KzSearchResult(m , b ,a , p){
this.marker = m;
this.poi = b;
this.a = a;
this.p = p;
}
function searchMap(area) {
if(map==null){
map =new BMap.Map("container");
}
var ls = new BMap.LocalSearch(map);
ls.setSearchCompleteCallback(function(rs) {
mSearchManager.clear2();
if(ls.getStatus() == BMAP_STATUS_SUCCESS) {
for(var index=0;index
到此该篇博文已经讲解完毕,如有问题,欢迎大家指出,一起探讨