Как реализовать автоматический вывод карты с расположением клиента на форме в 1С:Предприятие

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В современной разработке на платформе 1С:Предприятие часто возникает задача повышения юзабилити интерфейсов. Одним из отличных способов сделать работу менеджеров более комфортной является интеграция учетной системы с картографическими сервисами. Разберем практическую задачу: как при активизации строки в списке контрагентов автоматически отображать на форме мини-карту с его фактическим адресом.

В данной статье мы подробно проанализируем ситуацию, выясним причину распространенных ошибок (таких как уход маркера карты в Бразилию или другие неожиданные локации) и реализуем надежное решение с использованием API Яндекс.Карт. Мы рассмотрим два основных способа вывода: в виде статической картинки и интерактивной HTML-карты.

Выясняем причину проблемы: почему кириллица ломает геокодирование?

При работе со старыми версиями платформы 1С:Предприятие (в частности, версий 8.3.9 и ниже) разработчики часто сталкиваются с тем, что латинские адреса (например, "Moskva, Tverskaya, 7") геокодируются корректно, а при отправке запроса на русском языке (например, "Москва, Тверская, 7") Яндекс возвращает пустой результат или указывает координаты в Африке или Южной Америке.

Давайте проанализируем ситуацию. Проблема кроется в поведении функции КодироватьСтроку. В старых версиях платформы данная функция без явного указания третьего параметра кодирует строку в системную кодировку операционной системы (часто Windows-1251 для русскоязычной локали) вместо универсальной кодировки UTF-8, которую ожидает веб-сервер Яндекса. В результате этого веб-сервис получает искаженный набор байтов и пытается распознать его по собственным алгоритмам, уводя координаты в случайную точку планеты.

Для гарантированного решения этой проблемы в коде всегда необходимо явно передавать кодировку UTF-8 в качестве третьего параметра функции КодироватьСтроку:


КодированнаяСтрока = КодироватьСтроку(АдресФактический, СпособКодированияСтроки.КодировкаURL, "utf-8");

Посмотрим на пример полной реализации геокодирования и получения статического изображения карты.

Шаг 1. Получение координат адреса через API Геокодера Яндекс

Для начала нам необходимо отправить текстовый адрес контрагента в геокодер Яндекса, получить в ответ координаты (широту и долготу) в формате 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
        КоординатыДляКарты = СтрЗаменить(КоординатыТекст, " ", ",");
        Возврат КоординатыДляКарты;
    КонецЕсли;
    
    Возврат Неопределено;
    
КонецФункции

Шаг 2. Получение статического изображения карты по координатам

Теперь, когда у нас есть точные координаты контрагента (переменная КоординатыДляКарты содержит строку вида "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 Тогда
        ДвоичныеДанныеКартинки = Ответ.ПолучитьТелоКакДвоичныеДанные();
        КартинкаКарты = Новый Картинка(ДвоичныеДанныеКартинки);
        
        // Помещаем картинку во временное хранилище платформы
        АдресХранилища = ПоместитьВоВременноеХранилище(КартинкаКарты, УникальныйИдентификатор);
        Возврат АдресХранилища;
    КонецЕсли;
    
    Возврат "";
    
КонецФункции

Шаг 3. Интеграция логики в форму списка контрагентов

Чтобы обновление карты происходило автоматически при выборе контрагента пользователем, задействуем обработчик события таблицы списка. Разберем по шагам логику привязки кода к управляемой форме.

  1. Добавим на форму списка Контрагентов новый реквизит АдресКартыВХранилище с типом данных Строка.
  2. Перетащим этот реквизит на форму в виде элемента с видом Поле картинки.
  3. Создадим обработчик события ПриАктивизацииСтроки для элемента таблицы Список.

Проанализируем программный код обработчика события на клиенте и сервере:


&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    
    // Подключаем обработчик ожидания, чтобы избежать лавины HTTP-запросов при быстром пролистывании списка клавишами
    ПодключитьОбработчикОжидания("ОбновитьКартуКонтрагента", 0.5, Истина);
    
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьКартуКонтрагента()
    
    ТекущиеДанные = Элементы.Список.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        АдресКартыВХранилище = "";
        Возврат;
    КонецЕсли;
    
    // Вызываем серверный метод для получения карты
    ОбновитьКартуНаСервере(ТекущиеДанные.Ссылка);
    
КонецПроцедуры

&НаСервере
Процедура ОбновитьКартуНаСервере(Знач СсылкаНаКонтрагента)
    
    // Считываем адрес из реквизитов контрагента
    АдресКонтрагента = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаКонтрагента, "АдресФактическийКонтрагента");
    
    Если ПустаяСтрока(АдресКонтрагента) Тогда
        АдресКартыВХранилище = "";
        Возврат;
    КонецЕсли;
    
    // Шаг 1: Получаем координаты
    Координаты = ПолучитьКоординатыАдреса(АдресКонтрагента);
    
    // Шаг 2: Получаем картинку и записываем адрес временного хранилища в реквизит формы
    АдресКартыВХранилище = ПолучитьКартинкуКартыВХранилище(Координаты);
    
КонецПроцедуры

Альтернативное решение: Интерактивная карта через JS API в Поле HTML-документа

Как справедливо заметили участники обсуждения в сообщении 87, форматы XML и JSON представляют собой исключительно структуру передачи текстовых данных и сами по себе не делают карту интерактивной. Статический API возвращает обычный плоский рисунок.

Если перед вами стоит задача дать пользователю возможность масштабировать карту колесиком мыши, перемещаться по ней и прокладывать маршруты прямо в интерфейсе 1С, вам потребуется использовать JavaScript API Яндекс.Карт и элемент формы Поле HTML-документа.

Давайте подробно рассмотрим, как реализовать этот современный подход:

  1. Создайте на управляемой форме реквизит с именем ТекстHTML и типом данных Строка.
  2. Выведите его на форму как элемент с видом Поле HTML-документа.
  3. При активизации строки динамически генерируйте 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С. Подведем итоги и зафиксируем важные правила:

← На главную