# انتخاب آدرس روی نقشه در تسویه‌حساب

<span id="checkout-address-picker"></span>

کاربر نقطه‌ای را روی نقشه انتخاب می‌کند و برنامه، آدرس آن را برای تأیید نشان می‌دهد. درخواست تبدیل نقطه به آدرس از سرور برنامه فرستاده می‌شود تا کلید خصوصی در مرورگر قرار نگیرد.

## پیش‌نیازها

- یک نقشهٔ MapLibre با کلید عمومی؛ [نمایش اولین نقشه](/dashboard/docs/quick-start-map) را ببینید.
- یک سرور Express و یک کلید خصوصی با دسترسی «تبدیل نقطه به آدرس» در متغیر محیطی `BALAD_API_KEY`.
- یک فرم با ورودی آدرس و محل نمایش خطا؛ شناسه‌های لازم در نمونهٔ مرورگر آمده‌اند.

## مراحل

1. نقشه را با یک مرکز اولیه باز کنید و نقطهٔ انتخاب‌شده را به‌صورت نشانگر نمایش دهید.
2. بعد از کلیک روی نقشه، مختصات را به مسیر داخلی `/api/reverse-geocode` بفرستید.
3. درخواست قبلی را با `AbortController` لغو کنید تا پاسخ قدیمی، آدرس جدید را بازنویسی نکند.
4. مختصات و متن آدرس را با تأیید کاربر ذخیره کنید؛ تبدیل مختصات به آدرس، جای تأیید انسانی پلاک و واحد را نمی‌گیرد.

## تبدیل مختصات به آدرس روی سرور برنامه

```javascript
app.get("/api/reverse-geocode", async (req, res) => {
  const lng = Number(req.query.lng);
  const lat = Number(req.query.lat);
  if (!Number.isFinite(lng) || lng < -180 || lng > 180 ||
      !Number.isFinite(lat) || lat < -90 || lat > 90) return res.sendStatus(400);

  const url = new URL("https://reverse-geocoding.services.balad.ir/v1/");
  url.searchParams.set("location", lng + "," + lat);
  const response = await fetch(url, {
    headers: { Authorization: "Bearer " + process.env.BALAD_API_KEY }
  });
  if (!response.ok) return res.sendStatus(response.status);

  const data = await response.json();
  res.json({ formattedAddress: data.formatted_address, components: data.components });
});
```

## انتخاب نقطه و دریافت آدرس با MapLibre

```javascript
// متغیر map نقشهٔ ساخته‌شده با MapLibre و فایل style بلد است.
// در فرم HTML، این دو عنصر را قرار دهید:
// <input id="formatted-address" aria-label="آدرس انتخاب‌شده">
// <p id="address-error" role="status"></p>

const marker = new maplibregl.Marker();
let activeRequest;

async function updateAddress({ lat, lng }) {
  marker.setLngLat([lng, lat]).addTo(map);
  activeRequest?.abort();
  const request = new AbortController();
  activeRequest = request;
  try {
    const response = await fetch(
      "/api/reverse-geocode?lat=" + lat + "&lng=" + lng,
      { signal: request.signal },
    );
    if (request.signal.aborted) return;
    if (!response.ok) {
      document.querySelector("#address-error").textContent = "برای این نقطه آدرسی پیدا نشد.";
      return;
    }
    const data = await response.json();
    if (request.signal.aborted) return;
    document.querySelector("#address-error").textContent = "";
    document.querySelector("#formatted-address").value = data.formattedAddress;
  } catch (error) {
    if (request.signal.aborted) return;
    document.querySelector("#address-error").textContent = "دریافت آدرس ناموفق بود؛ دوباره تلاش کنید.";
  }
}

map.on("click", event => void updateAddress(event.lngLat));
```

## نتیجهٔ مورد انتظار

با کلیک روی نقشه، نشانگر جابه‌جا می‌شود و آدرس دریافتی در فیلد فرم قرار می‌گیرد. کاربر می‌تواند آدرس را پیش از ثبت سفارش بررسی و اصلاح کند.

[![نقشهٔ واقعی بلد با نشانگر نقطهٔ انتخاب‌شده و نمونه‌ای از آدرس در فرم تحویل](/dashboard/docs/assets/checkout-example.webp)](/dashboard/docs/assets/checkout-example.webp)

نقشه در این تصویر واقعی است؛ متن آدرس برای نمایش فرم به‌صورت نمونه قرار داده شده است.

## نکات

- مسیر `/api/reverse-geocode` نمونه‌ای از سرور برنامهٔ شماست و بخشی از API بلد نیست؛ نمونهٔ سرور از Express و متغیر محیطی `BALAD_API_KEY` برای کلید خصوصی استفاده می‌کند.
- خطای شبکه و حالت بدون نتیجه را کنار فیلد آدرس نمایش دهید و امکان ورود دستی را نگه دارید.
- برای درخواست‌های متوالی با فاصلهٔ زمانی کوتاه، اجرای درخواست را تا پایان انتخاب کاربر به تأخیر بیندازید؛ این روش با نام debounce شناخته می‌شود. نمونهٔ بالا درخواست قبلی را نیز لغو می‌کند.

[مرجع تبدیل نقطه به آدرس](/dashboard/docs/reverse-geocoding) · [همهٔ آموزش‌های کاربردی](/dashboard/docs/guides)
