百度地图基于react实现
背景需求
在某一次需求中被要求,研发一个地图页面,能够具备搜索定位,自动定位,地图与标记拖移的功能,并且要求使用百度地图研发。
因为项目使用的是react,因为习惯性的搜索react百度地图,现网其实有成熟的组件,但是使用一遍下来,感觉有两种问题:
1: 通过文档翻阅,组件无法满足需求,例如搜索定位,拖动地图中心点标记不动
2: 组件库似乎还不完善,有时会出现无法加载地图
最终决定还是使用sdk进行原生的研发
开发流程
一、sdk引入
因为使用react,必不可少的使用组件库,包括引入sdk,这里因为不想影响其他页面,加上是ahooks的粉丝,我采用的是useExternal去动态注入js资源,这样子保证全局的js唯一性。
import { useExternal } from 'ahooks'
useExternal("//api.map.baidu.com/api?v=2.0&ak=你的密钥";
二、创建百度地图实例
在引入sdk之后,根据sdk文档我们需要创建一个地图实例。
注意我们所需要实现的需求:
1: 创建地图
2: 初始化地图能够根据当前位置进行定位并展示
3: 地图具备搜索
4: 标记位于中心不可移动,拖动地图获取标记经纬度
1:创建地图
创建地图需要注意,sdk的地图实例对象需要绑定对应id的div,且该div必须具备有宽度与高度,才能正确创建地图
<div id="container"></div>
const map = new BMapGL.Map('container'); // 创建Map实例
map.centerAndZoom(new BMapGL.Point(116.404, 39.915), 18); // 初始化地图,设置中心点坐标和地图级别
注:此时最好将该map对象存起来,后续在地图上进行其他操作时可以更便捷
2:根据当前位置进行定位并展示中心点
此时我们需要将地图的中心点设置为我们当前的位置,在此就遇到许多坑了,先贴上官方的方案
// 获取当前定位并且设置中心点
const geolocation = new BMapGL.Geolocation();
geolocation.enableSDKLocation()// 开启sdk辅助定位,适用于weiview
geolocation.getCurrentPosition((r: any) => {
// 设置当前城市用以搜索使用
setCity(r.address.city)
const { lat, lng } = r.point
map.panTo({ lat, lng }, {})// 定位至中心点
setCenterPoint({ lat, lng })
此时利用百度api的Geolocation对象,我们可以获取到当前位置的经纬度并且定位地图,但是在此会存在经纬度偏差较大的问题,后面上网查询大致原因,总结了一下
百度的定位方法会优先使用H5的navigator.geolocation.getCurrentPosition
进行原生gps定位并且内部转化为百度坐标系。这里需要注意,百度使用的是百度坐标系,其他例如原生方法,高德地图等获取的是国测局坐标系,若使用原生则需要将gps坐标调用百度的Convertor方法转化为百度坐标才可在地图中展示正常,方法如下
navigator.geolocation.getCurrentPosition((position) => {
const lat = position?.coords?.latitude;
const lng = position?.coords?.longitude;
console.log({ position, lat, lng })
const convertor = new BMap.Convertor();
const pointArr = [];
pointArr.push({ lat, lng });
convertor.translate(pointArr, 1, 5, (data) => {
const { pointArrData = [] } = data
const point = {
lat: pointArrData[0].lat,
lng: pointArrData[0].lng
}
})
}, (err) => console.log({ err }));
而原生的navigator.geolocation
,谷歌已经对非https
协议的网站禁用了该功能,因此开发环境的定位有时会使用ip定位,此时的经纬度就与当前所在位置产生偏差。这也就是为什么百度的定位方法会导致定位不准确的原因
解决方法网上说的是使用百度接口进行定位,但我自己掉了这些接口依旧是不准确的,个人觉得最优解便是试一试原生navigator.geolocatione
的经纬度转为百度坐标系
3: 地图具备搜索
这里选择最为简便的方式,百度似乎有提供接口进行信息的请求,这个后续可能回去做,而紧急需求只采用了最为简便易行的方式,但就是这种方式依旧遇到了坑,引用百度文档里的方法,将搜索对象与一个input框进行绑定,根据input里的值进行展示关联地址名称,通过Autocomplete
对象绑定确认事件可以将地址列表的某一项点击做操作,例如展示到地图中心
<div id="r-result">
<input className={styles.inputCss} type="text" id="suggestId" size={24} placeholder='请输入查询地址' />
</div>
const ac = new BMap.Autocomplete( // 建立一个自动完成的对象,suggestId为input的id,city是当前城市名称,也可以设置经纬度
{
"input": "suggestId",
"location": city
});
ac.addEventListener("onconfirm", (e: any) => { // 鼠标点击下拉列表后的事件
const { value } = e.item;
myMap.clearOverlays();
const myValue = value.province + value.city + value.district + value.street + value.business;
// 搜索调用
const myFun = (resoult: any, local: any) => {
const pp = local.getResults().getPoi(0).point; // 获取第一个智能搜索的结果
// 创建图标标记
const marker = new BMapGL.Marker(pp, {
enableDragging: true,
});
myMap.addOverlay(marker);
setCenterPoint(pp)
myMap.centerAndZoom(pp, 18);
}
const local = new BMap.LocalSearch(city)
local.search(myValue, { forceLocal: true });
local.setSearchCompleteCallback((i: any) => myFun(i, local));
});
其实主要用到的是百度的LocalSearch
对象进行搜索,但是这里会遇到一个问题,按照下面百度文档提供的方法,会存在无法取得搜索成功后点击对应地址的回调
var local = new BMap.LocalSearch(map, { //智能搜索
onSearchComplete: myFun
});
我发现这里的onSearchComplete
会无法执行,但是将该代码放在html文件中是正常的,也就是百度文档的例子,不知道是不是sdk在react中执行报错
因此改用了下面的回调调用
local.setSearchCompleteCallback((i: any) => myFun(i, local));
这样就可以成功执行回调了
4: 标记位于中心不可移动,拖动地图获取标记经纬度
其实方案就是将一个img定位放在页面中心,获取经纬度则在map对象的拖动结束事件dragend
中用百度map对象提供的map.getCenter()
获取,实际操作误差不超过10m
页面耗时1d完成,主要都在解决定位不准确的问题,其实到现在也没有很明确的方案,似乎一切问题到了生产的https就可以解决,但因为还没上线
,还有待商榷