Впервые за весь свой опыт работы 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/
Добавить комментарий