Здравствуйте!
Хочу поделиться способом интерактивного определения вхождения указанных координат в область с заданным радиусом охвата.
Данный способ реализован с помощью карт OpenStreetMap и библиотеки Leaflet. На инфостарте встречаются десятки статей, описывающих взаимодействие данной карты с 1С. Но т.к. подобных тем я не встречал, делюсь с форумчанами.
Итак, для начала необходимо создать текстовый макет "Карта", где будет храниться выполняемы JS код. Код с комментариями привожу ниже:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/redirect.php?url=aHR0cHM6Ly91bnBrZy5jb20vbGVhZmxldEAxLjcuMS9kaXN0L2xlYWZsZXQuY3Nz"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin="" />
<link rel="stylesheet" href="/redirect.php?url=aHR0cHM6Ly91bnBrZy5jb20vbGVhZmxldC1yb3V0aW5nLW1hY2hpbmVAbGF0ZXN0L2Rpc3QvbGVhZmxldC1yb3V0aW5nLW1hY2hpbmUuY3Nz" />
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossorigin=""></script>
<script src="https://unpkg.com/leaflet-routing-machine@latest/dist/leaflet-routing-machine.js"></script>
</head>
<body>
<div id="map" class="map" style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"></div>
<script type="text/javascript">
//Установка начальных координат (широта и долгота и (13 - это зум))
var map = L.map('map').setView([53.2030600, 50.1594980], 13); // Координаты г. Самара
var markers = L.layerGroup().addTo(map);
var circles = L.layerGroup().addTo(map);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="/redirect.php?url=aHR0cHM6Ly93d3cub3BlbnN0cmVldG1hcC5vcmcvY29weXJpZ2h0">OpenStreetMap</a> contributors'
}).addTo(map);
map.on("click", onClick)
//map.on("dblclick", onClickduble)
let lat = 0;
let lng = 0;
let title = "";
//Выбор точки на карте
function onClick(e = e) {
lat = e.latlng.lat;
lng = e.latlng.lng;
refreshMap();
newMarker(lat, lng)
}
//Создает новый слой окружности на карте. Вызывается из 1С.
function createRadius(latitude, longitude, rad) {
circles.clearLayers();
let circle = new L.circle([latitude, longitude], {radius: rad});/**/
circle.addTo(circles);
SetViewOffice(latitude, longitude)
}
//Отрисовка нового маркера при нажатии на карту
function newMarker(latitude, longitude, title) {
lat = latitude;
lng = longitude;
refreshMap()
SetViewOffice(lat, lng)
let marker = new L.Marker([lat, lng]);/**/
marker.bindPopup(title).openPopup();
marker.addTo(markers);
}
//Функция вызывается из 1С. Возвращает координаты установленного маркера
function returnLatLng() {
coordinate = {
lat,
lng,
title
}
jsonString = JSON.stringify(coordinate)
return jsonString;
}
//function onClickduble(e) {
//}
//Очищаем карту
function refreshMap() {
markers.clearLayers();
}
//Установка отображение на текущем адресе
function SetViewOffice(latitude, longitude) {
map.setView([latitude, longitude], 15);
}
</script>
</body>
</html>
Затем в процедуре ПриСозданииНаСервере необходимо присвоить код html, описанный выше, переменной Карта. В данной обработке, я также заполняю ТЗ "Адреса" тестовыми данными и сразу формирую список выбора для переменной "Адрес".
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
МакетКарта = РеквизитФормыВЗначение("Объект").ПолучитьМакет("Карта");
Карта = МакетКарта.ПолучитьТекст();
///// Заполнение ТЗ "Адреса" тестовыми данными
СтрокаТЗ = Адреса.Добавить();
СтрокаТЗ.Адрес = "Самарская область, г. Самара";
СтрокаТЗ.Широта = 53.203060;
СтрокаТЗ.Долгота = 50.159498;
СтрокаТЗ = Адреса.Добавить();
СтрокаТЗ.Адрес = "Свердловская область, г. Екатеринбург";
СтрокаТЗ.Широта = 56.835095;
СтрокаТЗ.Долгота = 60.613329;
СтрокаТЗ = Адреса.Добавить();
СтрокаТЗ.Адрес = "Челябинская область, г. Челябинск";
СтрокаТЗ.Широта = 55.165798;
СтрокаТЗ.Долгота = 61.434271;
///// Заполнение списка выбора реквизита Адрес
ЗаполнитьСписокВыбораАдресов();
КонецПроцедуры
Процедура ЗаполнитьСписокВыбораАдресов()
Для каждого Стр Из Адреса Цикл
Элементы.Адрес.СписокВыбора.Добавить(Стр.Адрес);
КонецЦикла;
КонецПроцедуры
Следующим пунктом, необходимо добавить событие "ПриНажатии" реквизита Карта. Туда мы пропишем следующий код:
&НаКлиенте
Процедура КартаПриНажатии(Элемент, ДанныеСобытия, СтандартнаяОбработка)
Попытка
ДокументПервогоБраузера = Элементы.Карта.Документ;
ОкноПервогоБраузера = ДокументПервогоБраузера.defaultView;
КоординатыСозданногоМаркера = ОкноПервогоБраузера.returnLatLng();
СтруктураКоординатВыбраннойТочки = ПрочитатьJSONВСтруктуру(КоординатыСозданногоМаркера);
Широта1 = СтруктураКоординатВыбраннойТочки.lat;
Долгота1 = СтруктураКоординатВыбраннойТочки.lng;
Исключение
Сообщить("Карта еще не сформирована");
КонецПопытки;
КонецПроцедуры
Здесь как раз таки и пригодится "костыль", описанный выше. В момент нажатия переменным lat & lng уже присвоены значения, и при обращении к функции returnLatLng() мы получаем JSON со значением координат только что созданного маркера.
Теперь, когда координаты маркера получены и записаны в реквизиты формы, необходимо отрисовать область, относительно которой и будет происходить проверка вхождения созданного маркера. Для этого при нажатии на команду "ОтрисоватьНаКарте" мы будем вызывать функцию createRadius(Широта, Долгота, РадиусОхвата).
&НаКлиенте
Процедура ОтрисоватьНаКарте(Команда)
Если Не ЗначениеЗаполнено(Адрес) Тогда
ВызватьИсключение "Адрес не заполнен";
КонецЕсли;
Попытка
ДокументПервогоБраузера = Элементы.Карта.Документ;
ОкноПервогоБраузера = ДокументПервогоБраузера.defaultView;
ОкноПервогоБраузера.createRadius(Широта, Долгота, РадиусОхвата);
Исключение
Сообщить("Карта еще не сформирована");
КонецПопытки;
КонецПроцедуры
По итогу, при нажатии на карту, у вас должен создавать маркер, а при нажатии на команду "Отрисовать на карте" должна появляться выделенная область с заданным радиусом, где центром окружности является выбранный адрес.
Ну и наконец, самое важное в рамках данной статьи. Это определение вхождение маркера в заданную область.
Для этого я использовал формулу гаверсинуса (определяет расстояние по дуге между двумя точками на сфера с учетом их долготы и широты). Расчет в радианах. Расстояние в метрах.
Для команды "ОпределитьВхождение" определяем процедуру и размещаем в ней следующий код:
&НаКлиенте
Процедура ОпределитьВхождение(Команда)
Радиана = 57.29577951308;
ШиротаРад = Широта / Радиана;
Широта1Рад = Широта1 / Радиана;
ДолготаРад = Долгота / Радиана;
Долгота1Рад = Долгота1 / Радиана;
R = 6371; // Радиус планеты
sin1 = sin((ШиротаРад - Широта1Рад) / 2);
sin2 = sin((ДолготаРад - Долгота1Рад) / 2);
РасстояниеМаркераОтОкружностиКм = 2 * R * asin(sqrt(sin1*sin1+sin2*sin2*cos(ШиротаРад)*cos(Широта1Рад)));
РасстояниеМаркераОтОкружностиМ = РасстояниеМаркераОтОкружностиКм * 1000;
Если РасстояниеМаркераОтОкружностиМ <= РадиусОхвата Тогда
РезультатВхождения = "Есть вхождение";
Иначе
РезультатВхождения = "Нет вхождений";
КонецЕсли;
ОчиститьСообщения();
Сообщить(РезультатВхождения);
КонецПроцедуры
Пример работы:
P.S. Это моя первая публикация на Инфостарт. Прошу не судить строго)
Готовую обработку прикладываю (платформа 8.3.14)! Всем добра!