文章目录
前言
之前做了个课设,内容是在地图上显示停车位。但是课设有个要求是只显示2km范围的停车位,这一块网上资料较少,但其实思路很简单,我就简略写一写,当作帮助后来者。
如上图所示,一个个的锁代表着停车位,现在我们要显示离屏幕中心2km范围内的所有锁。(如果你是要只显示屏幕范围内的锁也可以做到,原理一样的)
大家可以在共享单车的平台(如下图所示的“青桔”小程序)上找到类似实现效果:当用户移动地图后(中心大头针随之移动),即用户手指刚离开屏幕时,屏幕中心周围的共享单车就会刷新一次(即向后台重新请求一次大头针半径范围内的共享单车)。
提示:以下是本篇文章正文内容,下面案例可供参考
一、思路
- 用户每次划动地图,都会获取一次屏幕中心经纬度坐标,并将坐标发送给后台。
- 后台根据坐标筛选出范围内的车位,返回给小程序。坐标可由腾讯地图工具获取。
- 小程序接收到新的车位信息后,进行车位刷新并重新展示即可。
注:后端存储的marker的地理坐标可以由腾讯地图工具获得,并不难
二、实现步骤与源代码
2.1.如何获取屏幕中心坐标
2.1.1 地图中心大头针
首先,为了方便起见,我们可以屏幕中心加个大头针.png图片。(png图片背景是透明的,适合这种上层图标)
链接: 大头针.png 百度网盘下载链接
提取码:4DEF
下载完大头针图片后,将图片插入到map组件里,修改wxml和wxss文件即可。
//.wxml文件
<map id="map" style="width:100vw; height:100vh" markers="{{markers}}"
<image class="mapCenter" src="../../images/map-center.png" ></image>
</map>
//.wxss文件
.mapCenter {
width: 48rpx;
height: 77rpx;
position: absolute; //固定大头针在屏幕中心
left: calc(50% - 24rpx);
top: calc(50% - 77rpx);
}
2.1.2 调用API,获取屏幕中心坐标
获取屏幕中心点的经纬度坐标,可以用微信官方提供的API getCenterLocation() ,success后会返回给我们中心点经纬度。
我们在.js文件中添加以下代码即可。
//.js文件
getCenterLocation: function () {
this.mpCtx.getCenterLocation({
success: function(res){
console.log(res.longitude+" "+res.latitude)
}
})
},
2.1.3 用户拖动屏幕时,小程序能自动获取屏幕中心坐标
这里需要将上述两步进行结合,即大头针移动时,小程序能自动获取中心大头针坐标。这里要用到官方提供的API叫regionchange(),作用是每当用户进行缩放或移动屏幕时,小程序都会自动调用一次该函数。
所以,只要我们在这个函数里写上获取中心坐标的代码后,每当用户移动屏幕,小程序就会自动调用该函数,从而实现自动获取屏幕中心经纬度坐标的效果。
具体代码如下所示:
在wxml中的map组件加入bindregionchange属性,并让该属性绑定.js文件中的 regionDidChange() 函数
//.wxml文件
<map id="map" style="width:100vw; height:100vh" markers="{{markers}}" bindregionchange="regionDidChange" >
<image class="mapCenter" src="../../images/map-center.png" ></image>
</map>
在js文件里写下 regionDidChange() 函数,每当用户移动屏幕时,前端会自动触发该函数,并将用户移动屏幕的具体信息传入参数e,你们可以自己console出来看看,我就不过多赘述了,注释写得很清楚。
//.js文件
regionDidChange(e) {
console.log(e)
if (e.type == 'end') //begin 代表屏幕移动操作开始,end 代表屏幕移动操作结束,我们只要移动结束的时候即可
if(e.causedBy == "drag") //scale 代表屏幕缩放操作,drag代表屏幕移动操作,我们只要屏幕移动的时候即可
{
this.getCenterLocation() //当屏幕移动结束时,我们立刻获取屏幕中心坐标
}
}
},
至此,我们就实现了用户移动屏幕,小程序自动获取中心坐标的效果。
2.2 后台筛选范围内的markers信息
获取到中心经纬度坐标后,我们就要将坐标发送给后台(服务器),让后台帮我们筛选范围内的markers。
当然,你也可以在自己的小程序写个js函数,让小程序直接执行筛选范围内车位的工作,代码思路也一样。但是这只适合markers数量很少的时候。当markers数量过多时,计算量过大会使得小程序运行速度大大降低,所以可以的话还是尽量让后台来帮我们进行计算过程。
2.2.1 如何显示后台筛选范围后的markers
我们先对上述的.js函数 getCenterLocation() 进行些微更改,添加了一个函数putArrayIntoMarkers() 用来向后台发送我们的屏幕中心经纬度坐标。
//.js文件
getCenterLocation: function () {
var that = this //success里的this并不是页面的this,故要先把页面的this保存为that
this.mpCtx.getCenterLocation({
success: function(res){
that.putArrayIntoMarkers(res.longitude,res.latitude) //向后台发送屏幕中心经纬度坐标
}
})
},
putArrayIntoMarkers() 函数实现了刷新车位信息,并装载新的车位信息效果。
//.js文件
//获取大头针2km范围内的地锁信息,并存入markers
putArrayIntoMarkers(longitude,latitude){
var that = this
var temp_array = [] //用于接收后台传来的新的车位组信息
this.setData({
markers:[] //清空当前的车位组信息
})
wx.request({
url: 'http://127.0.0.1:8888/find2kmLock',
data:{ //向后台发送屏幕中心经纬度坐标
"longitude":longitude,
"latitude":latitude
},
method:'POST',
success:(res)=>{ //后台筛选完毕后,以data数组的形式将范围内所有车位信息发给我
res.data.data.forEach(function(item, index) {//循环所有的车位信息
var temp = {id:item.id, latitude:item.latitude, longitude:item.longitude,
iconPath:temp_icon, width:that.data.width, height: that.data.height}
temp_array.push(temp) //一个temp代表一个车位,把所有车位都push到temp_array集合里
})
},
complete:()=>{ //success函数全部执行完后
that.setData({
markers:temp_array //全部完成后再将所有的车位信息装载到markers里
})
}
})
},
注: that.setData({}) 不能放在请求外面,因为请求是异步的,当小程序运行速度很快的时候,后台还没来得及给小程序返回值,temp_array就还是空的,所以得到的markers也是空的。故要等请求完全complete后再进行赋值操作。
至此,我们实现了如何让小程序给后台发送请求,并让小程序渲染后台返回的新车位的效果了。
2.2.2 后台筛选车位的思路
这里才是整个算法的关键之处。
假设我们服务器的数据库里存储了每个车位的经纬度坐标,然后现在我们要获取距离屏幕中心2km范围的车位,那我们最容易想到的思路应该是:
- 建立经纬度和km单位之间的转换方法。
- 用for循环将每个车位经纬度坐标和屏幕中心坐标进行欧氏距离计算(两点之间距离)。
- 将范围小于2km的车位记录下来,后续一同发送给小程序。
显然,这样的做法计算量过于庞大,于是在参考了网上资料后,我们有了更简便的方法:
我们本来是求半径2km范围圆内的车位,但现在我们将问题转换成求半径2km圆的外接矩形范围内的车位,下图是找到的网图,O点代表屏幕中心。
假设我们知道经纬度和km距离换算方法(假设经纬度与km之比为1:2),那我们显然可以根据O点的经纬度坐标来求得A点和B点的经纬度坐标。
如果O点经纬度坐标为(1,2),那么2km外接矩形上的B点经纬度坐标为(0,1),A点经纬度坐标为(2,3)。显然,我们现在就将问题转换成了求得落在矩形区域内的车位。
很简单,一条sql语句即可:
SELECT * FROM 车位信息表 WHERE (纬度 between 0 and 2) and (经度 between 1 and 3)
了解过数据库的应该都不难理解这句SQL的含义,这样我们就大大减少了计算量(计算欧氏距离等),加快了后台筛选速度。
2.2.3 经纬度与km之间的转换
经纬度与km之间的转换有两种方法。
第一种:较为准确的,即直接进行数学计算,网上有很多教程,很简单的高中运算知识,就不赘述了,文章后面我会贴上代码,。
第二种:较为简便也带点误差的方法。
先进入 腾讯地图位置服务官网—开发文档— 坐标拾取器网站
我们可以在这个网站上很方便的得到每一点的经纬度。
然后我们再打开腾讯地图页面,右上角有一个“尺子”的标志,即测距功能,我们测量横向2km的距离。
然后在坐标拾取器上找到对应的经纬度距离即可,这里为了方便我直接列出数据:
横向2km,方向从左到右,经度增加0.019014
纵向2km,方向从下到上,纬度增加0.017380
至此,我们就能够转换km和经纬度的关系,从而得到A和B点的坐标。
2.2.4 后台代码
后台服务器用的是第一种方法实现的经纬度转换。(这里的代码是由同组人员完成的,非本人实现,经过测试,效果还是挺准确的)
public Result findNearLock(@RequestBody RoadLock roadLock){
double longitude = roadLock.getLongitude();
double latitude = roadLock.getLatitude();
int distance = 2;
double R = 6371.393;//地球半径:千米
double r = R*Math.cos(latitude*Math.PI/180);//latitude是角度
double dlng = distance/r;//弧度
//double dlng = 2*Math.asin(Math.sin(distance/(2*R))/Math.cos(latitude*Math.PI/180));
dlng = dlng*180/Math.PI;//弧度转为角度
double dlat = distance/R;//弧长/半径=弧度
dlat = dlat*180/Math.PI;//角度
double minlatitude = latitude - dlat;//get
double maxlatitude = latitude + dlat;//get
double minlongitude = longitude - dlng;
double maxlongitude = longitude + dlng;
List<RoadLock> list = roadLockService.findByLongitudeAndLatitudeDistance(minlatitude,maxlatitude,
minlongitude,maxlongitude);
return Result.suc((Object) list, (long) list.size());
}
2.2.5 小程序源代码
wxml部分:
<map id="map" style="width:100vw; height:100vh" markers="{{markers}}"
latitude="{{latitude}}" longitude="{{longitude}}" scale="{{scale}}"
show-location="true" controls="{{controls}}" bindcontroltap="controltap"
bindmarkertap="handleMarkerTap" bindregionchange="regionDidChange" >
<image class="mapCenter" src="../../images/map-center.png" ></image>
</map>
wxss部分:
.mapCenter {
width: 48rpx;
height: 77rpx;
position: absolute; //固定大头针在屏幕中心
left: calc(50% - 24rpx);
top: calc(50% - 77rpx);
}
js部分:
//.js文件
regionDidChange(e) { //用户进行了屏幕移动操作则触发该函数
console.log(e)
if (e.type == 'end') //begin 代表屏幕移动操作开始,end 代表屏幕移动操作结束,我们只要移动结束的时候即可
if(e.causedBy == "drag") //scale 代表屏幕缩放操作,drag代表屏幕移动操作,我们只要屏幕移动的时候即可
{
this.getCenterLocation() //当屏幕移动结束时,我们立刻获取屏幕中心坐标
}
}
},
getCenterLocation: function () {
var that = this //success里的this并不是页面的this,故要先把页面的this保存为that
this.mpCtx.getCenterLocation({
success: function(res){
that.putArrayIntoMarkers(res.longitude,res.latitude) //向后台发送屏幕中心经纬度坐标
}
})
},
//获取大头针2km范围内的地锁信息,并存入markers
putArrayIntoMarkers(longitude,latitude){
var that = this
var temp_array = [] //用于接收后台传来的新的车位组信息
this.setData({
markers:[] //清空当前的车位组信息
})
wx.request({
url: 'http://127.0.0.1:8888/find2kmLock',
data:{ //向后台发送屏幕中心经纬度坐标
"longitude":longitude,
"latitude":latitude
},
method:'POST',
success:(res)=>{ //后台筛选完毕后,以data数组的形式将范围内所有车位信息发给我
res.data.data.forEach(function(item, index) {//循环所有的车位信息
var temp = {id:item.id, latitude:item.latitude, longitude:item.longitude,
iconPath:temp_icon, width:that.data.width, height: that.data.height}
temp_array.push(temp) //一个temp代表一个车位,把所有车位都push到temp_array集合里
})
},
complete:()=>{ //success函数全部执行完后
that.setData({
markers:temp_array //全部完成后再将所有的车位信息装载到markers里
})
}
})
},
三、可选
如果你是想显示屏幕范围内的markers,你可以试试调用微信官方提供的获取屏幕坐标API,比如说MapContext.fromScreenLocation(Object object),这一块我不太清楚,不过思路应该大体一致。
总结
以上就是获取固定范围内的markers方法,部分内容参考了网上的资料,如有错误,欢迎指正。