iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0
JavaScript

PM說: RD大大,這個功能要怎麼寫啊?系列 第 4

PM 說: 要怎麼寫外送平台的移動地圖抓地址?

  • 分享至 

  • xImage
  •  

foodpanda

前言

今天我們來思考在外送平台常見的
拖曳地圖抓地址的功能怎麼做?

拆解問題

  1. 怎麼抓取地圖中心的的經緯度?
  2. 怎麼將經緯度轉成地址?
  3. 怎麼在移動時更改 tooltip 的文字?

當以上問題解完其實就能寫出來了

重點

A1

抓目前題圖的中心點 經緯度

map.getCenter();
//{
//     "lat": 24.153940200805668,
//     "lng": 120.6743698120117
// }

A2

透過免費的 API 將經緯度轉地址名稱
可以到以下官網試試看:
https://nominatim.openstreetmap.org/ui/reverse.html

我們可以用"address"或是"display_name"的資訊
要特別注意"display_name"對我們來說是反向的

// "display_name": "210號, 五權一街, 大忠里, 西區, 田心, 臺中市, 403, 臺灣",
// "address": {
//     "house_number": "210號",
//     "road": "五權一街",
//     "neighbourhood": "大忠里",
//     "suburb": "西區",
//     "village": "田心",
//     "city": "臺中市",
//     "ISO3166-2-lvl4": "TW-TXG",
//     "postcode": "403",
//     "country": "臺灣",
//     "country_code": "tw"
// },

A3

//地圖開始移動:隱藏 tooltip
map.on("movestart", () => {
  this.centerMarker.closeTooltip();
});
//地圖移動中:更改 marker 位置
map.on("move", () => {
  const center = map.getCenter();
  this.centerMarker.setLatLng(center);
});
//地圖移動完成:更改文字後,開啟 tooltip
map.on("moveend", () => {
  updateMarker();
  this.centerMarker.openTooltip();
});
// 先取得 marker 的經緯度再轉換成地址
const updateMarker = () => {
  const latlng = this.centerMarker.getLatLng();

  updateAddress(latlng, this.centerMarker);
};

//call api 轉換後將 marker 文字更改
function updateAddress({ lat, lng }, centerMarker) {
  centerMarker.setTooltipContent("Loading...");
  const url = `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}`;
  fetch(url)
    .then((response) => response.json())
    .then((data) => {
      const address = data.display_name || "No address found";
      const reverseAddress = address.split(",").reverse().join("");
      centerMarker.setTooltipContent(reverseAddress);
    })
    .catch(() => {
      centerMarker.setTooltipContent("Unable to load address");
    });
}

成果

其實最早我完成的是這個
demo2
手動移動 marker 來更新地圖
但使用後發現不好用XD

  1. user不一定知道marker可以移動
  2. 當距離很遠時要操作很多次才能抓到想要的地址

果然外送平台的做法UX還是比較讚!
移動地圖抓中心點非常直覺~

有興趣可以實際操作看看最後的成果:
demo1

demo1

完成後的程式碼

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>DAY-4 移動地圖抓中心點地址 Example</title>
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.css"
    />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.js"></script>
    <style>
      body {
        background-color: black;
        padding: 0;
        margin: 0;
      }
      .wrapper {
        width: 99dvw;
        height: 99dvh;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      #map {
        height: 100%;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div class="wrapper">
      <div id="map"></div>
    </div>

    <script>
      const myMap = {
        map: null,
        centerMarker: null,
        init() {
          this.map = L.map("map").setView(
            [24.153940200805668, 120.6743698120117],
            14
          );
          //圖層
          L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
            attribution: "© OpenStreetMap contributors",
          }).addTo(this.map);
        },
        generateMarker() {
          const map = this.map;
          // 在地圖中心放一個可移動的 marker
          this.centerMarker = L.marker(map.getCenter(), {
            draggable: true,
          }).addTo(map);

          // 初始化 Tooltip
          this.centerMarker
            .bindTooltip("Loading address...", {
              permanent: true,
              direction: "top",
            })
            .openTooltip();
        },
        main() {
          // 產生marker
          this.generateMarker();

          // 更新 marker 的位置和地址
          const updateMarker = () => {
            const latlng = this.centerMarker.getLatLng();
            updateAddress(latlng, this.centerMarker);
          };

          // 初次載入地圖時更新一次 marker 位置和地址
          updateMarker();

          const map = this.map;
          // 當地圖拖動結束時更新 marker 位置和地址
          map.on("movestart", () => {
            this.centerMarker.closeTooltip();
          });
          map.on("move", () => {
            const center = map.getCenter();
            this.centerMarker.setLatLng(center);
          });
          map.on("moveend", () => {
            updateMarker();
            this.centerMarker.openTooltip();
          });
        },
      };

      myMap.init();
      myMap.main();

      // call api 更新地點的地址
      function updateAddress({ lat, lng }, centerMarker) {
        centerMarker.setTooltipContent("Loading...");
        const url = `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}`;
        fetch(url)
          .then((response) => response.json())
          .then((data) => {
            const address = data.display_name || "No address found";
            const reverseAddress = address.split(",").reverse().join("");
            centerMarker.setTooltipContent(reverseAddress);
          })
          .catch(() => {
            centerMarker.setTooltipContent("Unable to load address");
          });
      }
    </script>
  </body>
</html>

上一篇
PM說: 要怎麼寫出公車路線圖(網頁地圖)?
下一篇
PM 說: 客戶要做出"反向雷達圖" Σ ヽ(゚ Д ゚; )ノ
系列文
PM說: RD大大,這個功能要怎麼寫啊?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言