1. 程式人生 > 其它 >百度地圖基於react實現

百度地圖基於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就可以解決,但因為還沒上線
,還有待商榷