Как определить текущее местоположение пользователя на сайте

от автора

Впервые за весь свой опыт работы frontend разработчиком я столкнулся с задачей определения города (решение применимо не только для города, но и страны, улицы и т.д.) текущего пользователя и, порыскав в интернете, находил лишь кусочки того, что мне нужно, поэтому, чтобы сэкономить время тех, кто столкнулся с похожей задачей решил опубликовать данную статью.

Прежде чем писать код, хотелось бы отметить, что моё решение не претендует на «чистое» и единственно-верное, поэтому если есть более гармоничное и красивое решение — используйте его (буду только рад если поделитесь им).

Если кому интересно, задача была реализована на Options API, фреймворка Vue, но сам код написан на чистом JS. Это может быть полезно и тем, кто пишет на React и т.д.

1. С чего начать

Начать нужно с выбора, с помощью чего мы будет определять местоположение, с помощью IP-адреса или же координат latitude и longitude (широта и долгота)? В случае с IP-адресом диапазон удачного нахождения составляет около 60-80%, что в целом тоже не плохо, но хотелось бы поточней. Поэтому я выбрал второй вариант с координатами.

2. Как получить координаты пользователя (lat и lon)

Для того, чтобы получить координаты пользователя, нам потребуется разрешение на получение его геоданных, с этим нам поможет браузерное api — Geolocation API, с помощью метода getCurrentPosition() этого api мы инициируем всплытие разрешение на получение геоданных, после чего получаем информацию, в случае, если пользователь даст разрешение. Выглядит сама функция следующим образом:

const getCurrentGeolocation = () => { return new Promise((resolve, reject) => { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition((position) => { const lat = position.coords.latitude; const lon = position.coords.longitude;                resolve({lat, lon}); return {lat, lon}; }, (error) => {                 console.error(`Я гнусная ошибка ${error} ?`); resolve({lat: null, lon: null}); return {lat: null, lon: null}; }); } else {             console.warn('Я слишком стар для этого ?'); resolve({lat: null, lon: null}); return {lat: null, lon: null}; } }); }

Не забываем, что функция getCurrentPosition() вызывает асинхронный запрос, поэтому возвращаем Promise. Конкретно в моем случае, вызов reject мне был не столь важен, поэтому его я благополучно вырезал (чего делать вам не советую).
Условие if (navigator.geolocation) проверяет поддерживается ли Geolocation браузером или нет, а последующий вызов Callback функции — на самом деле вызов errorCallback — функции для обработки ошибок (например пользователь запретил получение геоданных и т.д.).

Вот как выглядит вызов самой функции:
const {lat, lon} = await getCurrentGeolocation(); , где lat и lon — ширина и долгота.

3. Что делать с полученными координатами

К сожалению, способы как получить название страны, города, улицы и т.д. на основе полученных координат с помощью встроенных API я не нашел, поэтому остается прибегнуть к сторонним сервисам, их на самом деле куча, но я отмечу всего парочку, это сервис DaData и всеми известное YandexAPI, что из них лучше зависит от конкретного случая, но yandex конечно будет помасштабнее. В нашей задаче от этих сервисов нас интересует графа «Обратное геокодирование» — получение адреса по координатам.

4. Сервис DaData

В моем случае, я пробовал оба и к сожалению dadata не хотел возвращать мне название города, хотя делал всё по инструкции (но это не точно, мои ошибки никто не отменял ?), выглядело это следующим образом:

const getCityByLatAndLon = async ({lat, lon}) => {   if (!lat || !lon) return '';      const query = {lat, lon};   const url = "https://suggestions.dadata.ru/suggestions/api/4_1/rs/geolocate/address";   const options = {     method: "POST",     mode: "cors",     headers: {       "Content-Type": "application/json",       "Accept": "application/json",       "Authorization": `Token ${process.env.VUE_APP_DADATA_API_KEY}`     },     body: JSON.stringify(query)   };   try   {     const response = await fetch(url, options);     if (!response.ok) return '';     const data = await response.json();          console.log(data, 'Массив найденых адресов');   } catch (error)   {     console.error('Ошибка при получении города от DaData:', error);   } }, 

*P.s. на момент написания статьи, как и говорилось ранее, вся проблема оказалось в моей ошибке ?, так что с сервисом dadata всё прекрасно, как оказалось я просто обернул query в body: JSON.stringify(query) лишними фигурными скобками, конечно сервис возвращал мне пустой массив…? выглядело это следующим образом body: JSON.stringify({query}) , убрал их и всё прекрасно заработало ?!

5. Сервис YandexApi

С сервисом yandex будет чуть посложней, а конкретнее с его документацией, но как говорится нужно только захотеть (или же должны гореть дедлайны?). В общем для успешной отправки запроса нам потребуется функцияgeocode , которая обрабатывает запросы геокодирования, подробнее читать тут (чтобы облегчить рысканья в доке). После чего в моем случае используем метод getLocalities() для возврата населённого пункта и, опционально, образование внутри населённого пункта, которым принадлежит топоним (в простонародье город), подробнее про методы читать тут. Выглядит код следующим образом:

const getCityByLatAndLon = async ({lat, lon}) => { if (!lat || !lon) return '';    const {geoObjects} = await ymaps.geocode([lat, lon], {kind: "locality"}); return geoObjects.get(0).getLocalities()?.[0] || '' },

*P.s. в случае текущего проекта для инициализацииymaps используется старая версия библиотеки vue-yandex-maps и выглядит она следующим образом:

await loadYmap({ apiKey: process.env.VUE_APP_YA_API_KEY, lang: 'ru_RU', coordorder: 'latlong', version: '2.1' });

Но для инициализации ymaps в обычном JS проекте достаточно вставить в тег head следующую строку с вашим API ключем (подробнее тут):

<head>     <script src="https://api-maps.yandex.ru/2.1/?apikey=ваш API-ключ&lang=ru_RU" type="text/javascript">     </script> </head>

В конце мы получаем следующую картину:

const {lat, lon} = await getCurrentGeolocation(); const myCityName = await getCityByLatAndLon({lat, lon}); console.log(`Мы находимся в городе под названием ${myCityName} ?`);

Подведем итоги

В целом данная задача не является сложной, но к сожалению нету актуальной информации по этой теме, поэтому приходилось искать все по кусочкам, и чтобы сэкономить ваше время была создана данная статья, за ясность которой я не отвечаю, т.к. она была моей первой и надеюсь не последней ?.

❗️ Имейте ввиду, что бесплатная версия сервиса DaData предоставляет 10000 запросов в сутки, а Yandex в свою очередь всего 2000 (на сколько я помню).

Всем мира и добра!???


ссылка на оригинал статьи https://habr.com/ru/articles/821049/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *