В современной разработке на платформе 1С:Предприятие часто возникает задача повышения юзабилити интерфейсов. Одним из отличных способов сделать работу менеджеров более комфортной является интеграция учетной системы с картографическими сервисами. Разберем практическую задачу: как при активизации строки в списке контрагентов автоматически отображать на форме мини-карту с его фактическим адресом.
В данной статье мы подробно проанализируем ситуацию, выясним причину распространенных ошибок (таких как уход маркера карты в Бразилию или другие неожиданные локации) и реализуем надежное решение с использованием API Яндекс.Карт. Мы рассмотрим два основных способа вывода: в виде статической картинки и интерактивной HTML-карты.
При работе со старыми версиями платформы 1С:Предприятие (в частности, версий 8.3.9 и ниже) разработчики часто сталкиваются с тем, что латинские адреса (например, "Moskva, Tverskaya, 7") геокодируются корректно, а при отправке запроса на русском языке (например, "Москва, Тверская, 7") Яндекс возвращает пустой результат или указывает координаты в Африке или Южной Америке.
Давайте проанализируем ситуацию. Проблема кроется в поведении функции КодироватьСтроку. В старых версиях платформы данная функция без явного указания третьего параметра кодирует строку в системную кодировку операционной системы (часто Windows-1251 для русскоязычной локали) вместо универсальной кодировки UTF-8, которую ожидает веб-сервер Яндекса. В результате этого веб-сервис получает искаженный набор байтов и пытается распознать его по собственным алгоритмам, уводя координаты в случайную точку планеты.
Для гарантированного решения этой проблемы в коде всегда необходимо явно передавать кодировку UTF-8 в качестве третьего параметра функции КодироватьСтроку:
КодированнаяСтрока = КодироватьСтроку(АдресФактический, СпособКодированияСтроки.КодировкаURL, "utf-8");
Посмотрим на пример полной реализации геокодирования и получения статического изображения карты.
Для начала нам необходимо отправить текстовый адрес контрагента в геокодер Яндекса, получить в ответ координаты (широту и долготу) в формате JSON и распарсить их средствами платформы 1С. Рассмотрим рабочий пример функции, выполняющей эту задачу на сервере.
Обратите внимание! В современных реалиях Яндекс требует обязательного использования API-ключа (параметр apikey). Для тестирования вы можете получить бесплатный ключ в Кабинете разработчика Яндекса.
&НаСервере
Функция ПолучитьКоординатыАдреса(Знач ТекстовыйАдрес) Экспорт
// Задайте ваш персональный API-ключ Геокодера
APIКлюч = "ВАШ_API_КЛЮЧ_ГЕОКОДЕРА";
// Кодируем адрес в формат URL (обязательно в UTF-8)
БезопасныйАдрес = КодироватьСтроку(ТекстовыйАдрес, СпособКодированияСтроки.КодировкаURL, "utf-8");
// Формируем путь запроса
АдресРесурса = "1.x/?apikey=" + APIКлюч + "&results=1&format=json&geocode=" + БезопасныйАдрес;
// Создаем защищенное SSL-соединение
SSLСоединение = Новый ЗащищенноеСоединениеOpenSSL;
Соединение = Новый HTTPСоединение("geocode-maps.yandex.ru", 443,,,,, SSLСоединение);
Запрос = Новый HTTPЗапрос(АдресРесурса);
Попытка
Ответ = Соединение.Получить(Запрос);
Исключение
ЗаписатьЛогСобытий("Ошибка связи с сервисом геокодирования: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
Если Ответ.КодСостояния <> 200 Тогда
ЗаписатьЛогСобытий("Сервер вернул код ошибки: " + Ответ.КодСостояния);
Возврат Неопределено;
КонецЕсли;
// Чтение и парсинг JSON-ответа
ТелоОтвета = Ответ.ПолучитьТелоКакСтроку();
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ТелоОтвета);
ДанныеJSON = ПрочитатьJSON(ЧтениеJSON, Истина);
ЧтениеJSON.Закрыть();
// Извлекаем коллекцию найденных объектов
МассивЛокаций = ДанныеJSON["response"]["GeoObjectCollection"]["featureMember"];
Если МассивЛокаций.Количество() > 0 Тогда
// Яндекс возвращает координаты в формате "Долгота Широта" через пробел
КоординатыТекст = МассивЛокаций[0]["GeoObject"]["Point"]["pos"];
// Преобразуем пробел в запятую для последующего использования в Static API
КоординатыДляКарты = СтрЗаменить(КоординатыТекст, " ", ",");
Возврат КоординатыДляКарты;
КонецЕсли;
Возврат Неопределено;
КонецФункции
Теперь, когда у нас есть точные координаты контрагента (переменная КоординатыДляКарты содержит строку вида "37.611203,55.757972"), мы можем запросить статическую картинку у сервиса Static API Яндекс.Карт. Мы скачаем бинарные данные картинки, поместим их во временное хранилище 1С и выведем на форму.
Рассмотрим подробный код процедуры получения картинки карты:
&НаСервере
Функция ПолучитьКартинкуКартыВХранилище(Знач Координаты)
Если ПустаяСтрока(Координаты) Тогда
Возврат "";
КонецЕсли;
// Формируем параметры запроса статической карты
// z=14 — масштаб карты (от 0 до 17)
// l=map — тип карты (схема)
// pt — маркер на карте в формате: Долгота,Широта,СтильМаркера
ПараметрыЗапроса = "1.x/?size=450,450&z=14&l=map&pt=" + Координаты + ",pm2rdl1";
SSLСоединение = Новый ЗащищенноеСоединениеOpenSSL;
Соединение = Новый HTTPСоединение("static-maps.yandex.ru", 443,,,,, SSLСоединение);
Запрос = Новый HTTPЗапрос(ПараметрыЗапроса);
Попытка
Ответ = Соединение.Получить(Запрос);
Исключение
Возврат "";
КонецПопытки;
Если Ответ.КодСостояния = 200 Тогда
ДвоичныеДанныеКартинки = Ответ.ПолучитьТелоКакДвоичныеДанные();
КартинкаКарты = Новый Картинка(ДвоичныеДанныеКартинки);
// Помещаем картинку во временное хранилище платформы
АдресХранилища = ПоместитьВоВременноеХранилище(КартинкаКарты, УникальныйИдентификатор);
Возврат АдресХранилища;
КонецЕсли;
Возврат "";
КонецФункции
Чтобы обновление карты происходило автоматически при выборе контрагента пользователем, задействуем обработчик события таблицы списка. Разберем по шагам логику привязки кода к управляемой форме.
АдресКартыВХранилище с типом данных Строка.ПриАктивизацииСтроки для элемента таблицы Список.Проанализируем программный код обработчика события на клиенте и сервере:
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
// Подключаем обработчик ожидания, чтобы избежать лавины HTTP-запросов при быстром пролистывании списка клавишами
ПодключитьОбработчикОжидания("ОбновитьКартуКонтрагента", 0.5, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьКартуКонтрагента()
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
АдресКартыВХранилище = "";
Возврат;
КонецЕсли;
// Вызываем серверный метод для получения карты
ОбновитьКартуНаСервере(ТекущиеДанные.Ссылка);
КонецПроцедуры
&НаСервере
Процедура ОбновитьКартуНаСервере(Знач СсылкаНаКонтрагента)
// Считываем адрес из реквизитов контрагента
АдресКонтрагента = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаКонтрагента, "АдресФактическийКонтрагента");
Если ПустаяСтрока(АдресКонтрагента) Тогда
АдресКартыВХранилище = "";
Возврат;
КонецЕсли;
// Шаг 1: Получаем координаты
Координаты = ПолучитьКоординатыАдреса(АдресКонтрагента);
// Шаг 2: Получаем картинку и записываем адрес временного хранилища в реквизит формы
АдресКартыВХранилище = ПолучитьКартинкуКартыВХранилище(Координаты);
КонецПроцедуры
Как справедливо заметили участники обсуждения в сообщении 87, форматы XML и JSON представляют собой исключительно структуру передачи текстовых данных и сами по себе не делают карту интерактивной. Статический API возвращает обычный плоский рисунок.
Если перед вами стоит задача дать пользователю возможность масштабировать карту колесиком мыши, перемещаться по ней и прокладывать маршруты прямо в интерфейсе 1С, вам потребуется использовать JavaScript API Яндекс.Карт и элемент формы Поле HTML-документа.
Давайте подробно рассмотрим, как реализовать этот современный подход:
ТекстHTML и типом данных Строка.Посмотрим на пример генерации интерактивного HTML-кода:
&НаСервере
Процедура ИнициализироватьИнтерактивнуюКарту(Знач КоординатыАдреса, Знач ИмяКлиента)
APIКлючJS = "ВАШ_API_КЛЮЧ_JS_API"; // Ключ для JS API карт (отличается от ключа Геокодера!)
Если ПустаяСтрока(КоординатыАдреса) Тогда
ТекстHTML = "<html><body><h3>Адрес не задан или не найден</h3></body></html>";
Возврат;
КонецЕсли;
// Шаблон HTML с подключением скриптов Яндекса
// Координаты Яндекса передаются как массив чисел [Широта, Долгота]
// Внимание: в pos геокодера идет сначала Долгота, а в JS API сначала Широта!
// Разобьем строку координат и поменяем их местами
ЧастиКоординат = СтрРазделить(КоординатыАдреса, ",");
Широта = ЧастиКоординат[1];
Долгота = ЧастиКоординат[0];
СтрокаШаблона =
"<!DOCTYPE html>
|<html>
|<head>
| <title>Карта расположения</title>
| <meta http-equiv=""Content-Type"" content=""text/html; charset=utf-8"" />
| <script src=""https://api-maps.yandex.ru/2.1/?apikey=[КЛЮЧ]&lang=ru_RU"" type=""text/javascript""></script>
| <style>
| html, body, #map {
| width: 100%; height: 100%; padding: 0; margin: 0;
| }
| </style>
| <script type=""text/javascript"">
| ymaps.ready(init);
| function init() {
| var myMap = new ymaps.Map(""map"", {
| center: [[ШИРОТА], [ДОЛГОТА]],
| zoom: 15
| });
| var myPlacemark = new ymaps.Placemark([[ШИРОТА], [ДОЛГОТА]], {
| balloonContent: '[ИМЯ_КЛИЕНТА]'
| });
| myMap.geoObjects.add(myPlacemark);
| }
| </script>
|</head>
|<body>
| <div id=""map""></div>
|</body>
|</html>";
// Заменяем маркеры в шаблоне на реальные данные
ТекстHTML = СтрЗаменить(СтрокаШаблона, "[КЛЮЧ]", APIКлючJS);
ТекстHTML = СтрЗаменить(ТекстHTML, "[ШИРОТА]", Широта);
ТекстHTML = СтрЗаменить(ТекстHTML, "[ДОЛГОТА]", Долгота);
ТекстHTML = СтрЗаменить(ТекстHTML, "[ИМЯ_КЛИЕНТА]", ИмяКлиента);
КонецПроцедуры
Мы подробно разобрали два отличных варианта решения задачи вывода карты расположения клиента в 1С. Подведем итоги и зафиксируем важные правила:
"utf-8" в параметрах метода КодироватьСтроку.