API Яндекс.Карты – геокодирование и маршрутизация на JavaScript

Яндекс.Карты - замечательный, популярный инструмент, демонстрирующий отличные результаты геокодирования и построения маршрутов.

Но предположим, что перед пользователем стоит задача обработать большой список адресов/координат, а именно, посчитать расстояния между точками по автомобильным дорогам и определить координаты внушительного списка объектов. Тогда-то он и столкнется с отсутствием возможности пакетной обработки в пользовательском интерфейсе сервиса. Однако, Яндекс об этом позаботился, предоставив в условно-бесплатное пользование JavaScript API.

Разработчики на JavaScript API получают техническую поддержку, подробную документацию, инструментарий для тестирования кода в “песочнице” на сайте проекта https://yandex.ru/dev/maps/jsapi/

Итак, я попробую воспользоваться этим инструментом для решения задачи геокодирования и построения маршрутов. Мне понадобится учетная запись Яндекс и API ключ для сервиса “JavaScript API и HTTP Геокодер”, который можно создать в Кабинете разработчика https://developer.tech.yandex.ru. Не буду подробно останавливаться на этом моменте, в сети достаточно информации, чтобы самостоятельно разобраться.

JavaScript API работает только в браузере, поэтому для выполнения кода нужно разработать web-страничку. В моем случае я могу обойтись статичным html-файлом, т.е. читать исходные данные и записывать результат буду в html-элементы, т.е. для упрощения обойдусь без использования серверной части. Итак, запускаю текстовый редактор и создаю заготовку будущей web-страницы.

<!DOCTYPE html> <html> <head> <title>JavaScript API Yndex.Map</title> <meta charset="utf-8" /> <script src="https://api-maps.yandex.ru/2.1/?apikey=<ваш API-ключ >=ru_RU" type="text/javascript"></script> <script type="text/javascript"></script> </head> <body style="font-family: Arial, Helvetica, sans-serif;"> <textarea id="data" class="area" rows=5 ></textarea> <div><button id="process_button" class="button process" onclick="start_process()">Start</button></div> <textarea id="target" class="area" rows=5 ></textarea> <div><button id="clear_button" class="button clear" onclick="clear_result()">Clear</button></div> </body> </html>

Тут будет два многострочных текстовых поля, первое для ввода исходных данных (data) и второе - для записи результата (target), и две кнопки для запуска процесса обработки и очистки поля с результатом.

Обратите внимание, в заголовке страницы в элементе script подключается так необходимый мне API от Яндекса, а в параметрах url атрибута src необходимо указать тот самый секретный api-ключ, полученный в кабинете разработчика.

После загрузки страницы, в глобальном контексте выполнения JavaScript станет доступен объект ymaps, через который я получаю доступ к API, в том числе и к необходимым мне функциям геокодирования и маршрутизации. Напишу две функции “router” и “geocoder”, которые, как не сложно догадаться, реализуют построение маршрута и выполняют геокодирование. Конечно же JavaScript API реализуют значительно больше картографических сервисов и даже получаемые тут данные о маршруте и местоположении являются далеко не полными. Полное описание используемых методов доступно по ссылке, указанной в начале этого материала. Но вернемся к поставленной задаче и разберемся сначала с функцией “router”:

function router(from, to){ return ymaps.route([from,to], {multiRoute: false, routingMode: "auto"}) .then(r => {return {from: from, to: to, dist: r.getLength()}} ) .catch(e => {console.log(e); return {from: from, to: to, dist: 'route error'}}); }

В качестве аргументов она принимает пункты отправления и назначения, которыми могут быть как строками с адресом, так и массивами [широта, долгота]. Эти аргументы передаются в метод route объекта ymaps вместе с параметрами построения маршрута, в моем случае routingMode: “auto” – это указание строить именно автомобильный маршрут. Метод возвращает promise-объект, содержащий в том числе протяженность маршрута в метрах, который затем и возвращает функция.

Очередь функции “geocoder”:

function geocoder(place){ return ymaps.geocode(place, {results: 1}).then( r => { let geo = r.geoObjects.toArray()[0]; let data = geo.properties.get('metaDataProperty').GeocoderMetaData; let point = geo.geometry.getCoordinates() let result = {point: point, place: place, kind: data.kind, precision: data.precision, text: data.text}; return result;} ).catch(e => {console.log(e); return {place: place, point: 'geocoding error', text: null, kind:null, precision: null}}) }

Функция принимает адрес строкой или массив [широта, долгота]. Этот параметр передается в метод geocode объекта ymaps вместе с параметрами геокодирования, в моем случае results: 1 – указание вернуть только один, наиболее точный, результат. Функция вернет promise-объект, содержащий координаты, тип, наименование, точность геокодирования.

API Яндекс.Карты – геокодирование и маршрутизация на JavaScript

Ну что ж, уже можно пользоваться. Например, если вызвать следующий код

(async ()=>{ console.log(await geocoder(“Москва”)); console.log(await router(“Москва”,”Санкт-Петербург”)); })()

, то в консоль браузера будут записаны результат геокодирования объекта “Москва” и информация о протяженности автомобильного маршрута “Москва -> Санкт-Петербург”

Остается только реализовать построчную обработку содержимого элемента “data” и запись результата в элемент “target”. Ниже приводится итоговое содержимое html-файла, в котором это реализуется функцией “start_process”.

<!DOCTYPE html> <html> <head> <title>JavaScript API Yndex.Map</title> <meta charset="utf-8" /> <link rel="stylesheet" type="text/css" href="css/style.css"> <script src="https://api-maps.yandex.ru/2.1/?apikey=<ваш API-ключ >&lang=ru_RU" type="text/javascript"></script> <script type="text/javascript"> function clear_result() { document.getElementById("target").value= ""; } async function start_process() { let data = document.getElementById("data").value.split("\n") let target = document.getElementById("target") for (row of data) { await new Promise(r => setTimeout(r, 50)); let arr = row.split(";") let result = ( arr.length > 1 ? await (()=>{ let from = iscoord(arr[0])? arr[0].split(",",2): arr[0] ; let to = iscoord(arr[1])? arr[1].split(",",2): arr[1] ; return router(from,to).then(r => { return `${r.from};${r.to};${r.dist}`; })})() : await (()=>{ let place = iscoord(arr[0]) ? arr[0].split(",",2) : arr[0]; return geocoder(place).then(r => { return `${r.place};${r.point};${r.text}`; })})() ) target.value += result + "\n" } } function router(from, to){ return ymaps.route([from,to], {multiRoute: false, routingMode: "auto"}) .then(r => {return {from: from, to: to, dist: r.getLength()}} ) .catch(e => {console.log(e); return {from: from, to: to, dist: 'route error'}}); } function geocoder(place){ return ymaps.geocode(place, {results: 1}).then( r => { let geo = r.geoObjects.toArray()[0]; let data = geo.properties.get('metaDataProperty').GeocoderMetaData; let point = geo.geometry.getCoordinates() let result = {point: point, place: place, kind: data.kind, precision: data.precision, text: data.text}; return result;} ).catch(e => {console.log(e); return {place: place, point: 'geocoding error', text: null, kind:null, precision: null}}) } function iscoord(value) { let regexp = /\d+(?:\.\d+)*,\d+(?:\.\d+)*/ ; return regexp.test(value) } </script> </head> <body style="font-family: Arial, Helvetica, sans-serif;"> <textarea id="data" class="area" rows=5 ></textarea> <div><button id="process_button" class="button process" onclick="start_process()">Start</button></div> <textarea id="target" class="area" rows=5 ></textarea> <div><button id="clear_button" class="button clear" onclick="clear_result()">Clear</button></div> </body> </html>

Страница в браузере с результатами обработки выглядит примерно следующим образом:

API Яндекс.Карты – геокодирование и маршрутизация на JavaScript

Верхнее текстовое поле для вставки исходных данных, в нижнее пишется полученный результат. Если в строке исходных данных встречается разделитель (разделителем между пунктом отправления и прибытия является “;”), то осуществляется расчет маршрута, в противном случае выполняется геокодирование сроки (в примере на картинке выше “Лондон”). Также регулярным выражением определяется, являются ли исходные данные географическими координатами (широта и долгота разделены запятой) или строкой адреса, при этом адреса и координаты можно комбинировать при построении маршрута.

Внимательному читателю наверняка сразу бросилась в глаза вот эта конструкция в листинге кода:

await new Promise(r => setTimeout(r, 50));

Тут я делаю короткую (50 мсек) паузу перед обработкой следующей строки, т.е. искусственно устанавливаю предел максимальной скорости обработки в 20 строк в секунду. Спросите, зачем? И тут настало время упомянуть о технических лимитах бесплатного использования JavaScript API от Яндекса. А их два, но они довольно «вкусные»:

- Суточное ограничение – до 25000 запросов к API (каждый вызов метода geocode или route считается за 1). Информация о расходовании суточного лимита доступна в Кабинете разработчика.

- Кол-во запросов в секунду – не более 50

Т.е. пауза между вызовами API нужна, чтобы не превысить «скоростной лимит», установленный Яндексом.

На этом, пожалуй, всё, “за кадром” осталась css-таблица стилей, а также разработка backend на Node.js, но это - уже совсем другая история.

55
3 комментария

Здравствуйте! Скажите, пожалуйста, можно ли получить данную наработку?

Ответить
Автор

Здравствуйте, исходный код представлен в материалах публикации, более подробную информацию, к сожалению, не сможем выложить, т.к. задача решалась в рамках рабочего проекта. Спасибо за интерес!

Ответить