Логотип компании LOGisTON
Логотип компании
LOGisTON

Поиск точек на карте

Пример разработки Web-Сервиса работы с географической картой

Это тестовое задание на вакансию Программист 1С + web в компанию LOGisTON.

Так как HR-менеджер компании LOGisTON, Кшиванский Александр Борисович, спустя 2 месяца так и не соизволил дать хоть какой-либо ответ по поводу того, проверили они мое решение их тестового задания или нет, то я считаю себя вполне вправе опубликовать данные материалы. Тем более, что они могут быть полезны другим разработчикам, столкнувшимся в своей работе с теми же проблемами, с которыми столкнулся и я, решая данное задание.

Кстати, несмотря на всю некорректность поступка компании LOGisTON по отношению ко мне, как к соискателю, вызвавшую во мне подозрения по отношению к ним в том, что они таким образом бесплатно решают сложные "нерешаемые" задачи в своей разработке, хочу выразить им Благодарность за, пожалуй, самую интересную тестовую задачу в моей 20-летней практике.

В данной работе вы найдете

Постановка задачи

Чтобы не пересказывать своими словами поставленную мне задачу, приложу здесь оригинал документа с описанием задачи.

Файлы для скачивания

Результатом работы явилась выгрузка из двух тестовых баз:
Выгрузка тестовой базы Сервер (формата 8.3.8.2054) (137 КБ)
Выгрузка тестовой базы Клиент (формата 8.3.8.2054) (1,72 MБ)
Входить в базы нужно под пользователем "Назаренко_СВ" без пароля.
При разворачивании баз, учитывайте, что базу "Сервер" нужно будет публиковать на Web-Сервере.

Редактирование областей карты (интеграция 1С с JS)

В базе Сервер есть справочник ОбластиКарты. У него есть макет (HTML документ) РедакторПолигонов.
В этом макете сохранен "допиленный напильником" файл PoligonManager.html (6 KB) с программой на JavaScript, которая использует открытую библиотеку по работе с географическими картами Leaflet.
Кто писал эту программу и насколько она хорошо\плохо написана - обсуждать не возьмусь. Мне ее выдали в готовом виде, и одним из пунктов задачи было использовать ее для реализации интегрированного в 1С интерфейса редактирования областей на карте.
Поэтому содержимое этого макета выведено в Поле HTML документа на форме элемента справочника ОбластиКарты.

Форма редактирования области карты

И все бы ничего, если бы не "допиливание напильником". Проблема оказалась в том, что 1С в упор не хотела видеть объявленные в JS переменные и функции (через которые должно было происходить взаимодействие 1С и редактора полигонов).

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

Сама интеграция выполнена в двух процедурах модуля формы элемента справочника ОбластиКарты.

// Назаренко_СВ 2017-02-13
&НаКлиенте
Процедура РедакторПолигоновДокументСформирован(Элемент)
	ОкноБраузера = Элементы.РедакторПолигонов.Документ.defaultView;
	Если НЕ ОкноБраузера.initialized Тогда
		ОкноБраузера.initialize(ЭтотОбъект); // initialize(thisForm, thisRegion, strRectSel)
		Если НЕ ПустаяСтрока(Объект.ПолигоныСтрокой) Тогда
			ОкноБраузера.loadJSONPolygons(Объект.ПолигоныСтрокой);
		КонецЕсли;
	КонецЕсли;
КонецПроцедуры
			
Эта процедура вызывается при окончании загрузки макета РедакторПолигонов в расположенное на форме поле HTML-документа РедакторПолигонов.
Первым делом, с помощью обращения к объектной модели браузера, а именно к свойству defaultView, мы получаем связанное с загруженным документом окно браузера, через которое будем обращаться к переменным и функциям JS, описанным в HTML-документе.
Далее, мы обращаемся к определенной в нашем JS-коде переменной initialized, чтобы узнать не выполнялась ли уже инициализация ранее. Это нужно потому, что обработчик РедакторПолигоновДокументСформирован почему-то вызывается дважды (фича 1С).
И если инициализация редактора полигонов еще не выполнялась, то выполняем ее - вызовом описанного в нашем JS-коде метода ОкноБраузера.initialize(ЭтотОбъект), передавая ему ссылку на форму 1С, в которой должен быть объявлен callback-метод, вызываемый редактором полигонов при изменении полигонов на карте.
А для того, чтобы передать в редактор полигонов сохраненные в редактируемом элементе справочника полигоны, мы обращаемся к описанному в нашем JS-коде методу ОкноБраузера.loadJSONPolygons(Объект.ПолигоныСтрокой), в который передаем строку, хранящую записанный в формате JSON массив полигонов (каждый полигон представляет собой массив точек, а каждая точка представляет собой массив координат - Широта и Долгота).

Вторая из рассматриваемых процедур является callback-методом, который вызывается из редактора полигонов при изменении полигонов.

Но сначала рассмотрим как осуществляется данный вызов на стороне JS.

function action1C(id_action, param) {
	//*** call 1C
	success = false;
	errStr = "";
	try {
		parentForm._call('action1C',[id_action, param]); 
		success = true;
	} catch(e) {
		errStr += e.message;
	};
	try {
		if (!success) parentForm.action1C(id_action, param);
	} catch(e) {
		errStr += e.message;
		alert(errStr);
	};
	//*** end call
}
			
Как видим, код данной функции предельно прост - в нем выполняется попытка вызвать метод action1C у формы, сохраненной в переменной parentForm при инициализации. Причем, похоже, что редактор полигонов в первую очередь ориентирован на работу с PHP (на такие догадки наталкивает использование "волшебного" метода PHP _call), а если такой вызов не удался, то уже предпринимается попытка вызова через точку, более присущего COM-технологиям, на которых написана 1С.

При этом, в вызываемый метод action1C передаются два аргумента:

Если проследить в JS-коде места вызова функции action1C, то обнаружится, что в id_action ВСЕГДА передается единица, а в param передается строка с JSON-представлением массива полигонов.

Сам же callback-метод, описанный в форме элемента справочника ОбластиКарты, выглядит так

// Назаренко_СВ 2017-02-15
//
// Обработчик действий, вызываемый из редактора полигонов.
// (!!!) Имя и состав аргументов процедуры жестко прописаны в JavaScript-коде редактора полигонов.
//
//Параметры:
// ИДДействия – Число – Идентификатор выполняемого действия.
//                      В текущей версии редактора полигонов всегда = 1, что можно трактовать как "изменился состав полигонов области".
// ПараметрыДействия - Строка - JSON-представление массива с описанием полигонов области.
//                              ПараметрыДействия = "[Region,Region,...,Region]",
//                              где Region = "[Point,Point,...,Point]",
//                              где Point = "[lat, lng]",
//                              где lat = Широта точки, lng = Долгота точки.
//
&НаКлиенте
Процедура action1C(ИДДействия, ПараметрыДействия) Экспорт
	Если ИДДействия = 1 Тогда
		Объект.ПолигоныСтрокой = ПараметрыДействия;
	Иначе
		ВызватьИсключение "Обработчик action1C: Непредусмотренный идентификатор действия получен из редактора полигонов.";
	КонецЕсли;
КонецПроцедуры
			
Как видим, он вообще элементарный - проверяет, что идентификатор действия соответствует ожидаемому значению, и сохраняет переданную строку с JSON-представлением массива описывающих область карты полигонов, в соответствующий реквизит элемента справочника.

Поиск областей карты по входящим в них точкам (работа с объектом ГеографическаяСхема)

База Сервер предоставляет функциональность определения областей, в которые входят указанные точки.
Функциональность эта реализована в общем модуле ОбластиКартыСервер, функцией ОбластиПоТочкам(...).
Эта функция принимает в аргументах таблицу с координатами точек, а на выходе возвращает эту же таблицу, но с добавленной к ней колонкой со ссылкой на соответствующий точке элемент справочника ОбластиКарты. Если какая-либо из переданных точек попадает в несколько разных областей карты, то в выходной таблице будет соответствующее количество строк с этой точкой - по одной строке на кажду подходящую область карты.

Поиск подходящих областей реализован с использованием объекта ГеографическаяСхема.
Так как ни в официальной документации 1С, ни на дисках ИТС, ни на форумах, ни с помощью Гугла, не удалось найти более-менее внятной информации, да еще и с примерами, о том как пользоваться этим объектом, то постараюсь хотя бы частично восполнить этот пробел. Дальнейшее изложение не претендует на полноту предоставления информации об этом объекте. Это всего лишь еще один пазл в общую картину.

Итак.

// Для поиска вхождения точек в области будем использовать ГеографическуюСхему.
Гео = Новый ГеографическаяСхема;
			
Далее создаем слой, на который нужно поместить все объекты, среди которых будем выполнять поиск.
СлойОбластей = Гео.Слои.Добавить("ОбластиКарты", Тип("ПолигональныйОбъектГеографическойСхемы"));
			
Далее мы запросом получаем области карты. И каждую область добавляем новым объектом в слой областей.
НовыйОбъект = СлойОбластей.Объекты.Добавить();
НовыйОбъект.Значение = Выборка.Ссылка; // Это ссылка на соответствующий элемент справочника ОбластиКарты.
			
Каждый описывающий область полигон добавляется в объект слоя в виде контура.
НовыйКонтур = НовыйОбъект.Контуры.Добавить();
			
При этом контур описывается набором точек, его составляющих.
Для Каждого ВыборкаТочка Из ВыборкаОбластьТекущая Цикл
	НовыйКонтур.Добавить(Новый ГеографическиеКоординаты(ВыборкаТочка[0], ВыборкаТочка[1]));
КонецЦикла;
			

После того, как все объекты с областями загружены в соответствующий слой географической схемы, можно воспользоваться специальным методом объекта ГеографическаяСхема, чтобы для каждой точки определить перечень областей, в которые она попадает.

// Выполним поиск областей, в которые попадает данная точка.
НайденныеОбласти = Гео.НайтиПоРасположению(НоваяТочка, СлойОбластей.Объекты, ТипПоискаОбъектовГеографическойСхемы.Включают);
			
Вторым аргументом в этот метод передается массив объектов географической схемы, среди которых ищем. Для этой цели идеально подходит свойство Объекты слоя областей карты.

Первым же аргументом нужно передать проверяемую точку.
Но так как у объекта ТочечныйОбъектГеографическойСхемы нет конструктора (т.е. он не может быть создан непосредственно), то для создания точки приходится немного "пошаманить".
Предварительно в географическую схему нужно добавить слой, в котором будем создавать точечные объекты с искомыми точками.

СлойТочек = Гео.Слои.Добавить("ПроверяемыеТочки", Тип("ТочечныйОбъектГеографическойСхемы"));
			
После чего добавляем на этот слой точку с указанными координатами, и таким образом получаем нужный нам объект ТочечныйОбъектГеографическойСхемы.
// Добавим точку на слой точек, чтобы получить объект ТочечныйОбъектГеографическойСхемы.
НоваяТочка = СлойТочек.Объекты.Добавить();
НоваяТочка.Значение = ТочкиСтрока.ТочкаИдентификатор;
НоваяТочка.Координаты = Новый ГеографическиеКоординаты(ТочкиСтрока.ТочкаШирота, ТочкиСтрока.ТочкаДолгота);
			

Третьим аргументом в метод НайтиПоРасположению передается способ поиска объектов.
Так как мы ищем точечные объекты среди полигонов, то нам подходят значения Включают и ВключаютПолностью этого параметра, которые в данном случае являются эквивалентными. Я остановился на значении Включают.
Кстати, более-менее внятно значение этого параметра описано в соответствующем разделе ИТС.

Метод НайтиПоРасположению возвращает массив найденых объектов, согласно которому мы добавляем строки в выходную таблицу нашей функции.

Использование XDTO-пакетов для передачи непростых типов данных через Web-сервис

Рассмотрим подробнее веб-сервис MapRegions, который предоставляет база Сервер. И его единственный метод RegionsByPoints.
Он принимает один аргумент - массив точек. И возвращает таблицу с двумя колонками Точка-Область.
Передаваемые между базами Клиент и Сервер данные (точка, область, таблица, массив) являются непримитивными. Поэтому, чтобы их можно было использовать в описании веб-сервиса, нужно описать их в типах XDTO.
Соответственно, и в базе Сервер и в базе Клиент описан XDTO-пакет ОбластиКарты.
XDTO-пакет ОбластиКарты
И в той и в другой базе этот пакет почти идентичен, за исключением того, что ссылки на справочники, которые отсутствуют в соответствующей конфигурации, заменены на тип anyType (http://www.w3.org/2001/XMLSchema). И таким образом, когда на одной стороне в такое поле записывается ссылка, то на другой стороне из этого поля читается строка с соответствующим этой ссылке GUID-ом. И наоборот, записывая в такое поле на одной стороне строку с соответствующим ссылке GUID-ом, на другом конце можно получить соответствующую ссылку.

Вызов же этого Веб-серсиса осуществляется в базе Клиент, в отчете ОбластиТочек, в форме ФормаПолученияДанных, процедура ПолучитьДанныеСервер().
Более подробно механизм вызова веб-сервиса и использования СКД для отображения получаемых от него результатов, описан в соответствующем разделе далее.

А пока рассмотрим наиболее интересные моменты передачи данных при использовании веб-сервисов.

Передача массивов при вызове веб-сервисов

Для создания массива передаваемых в вызов веб-сервиса объектов XDTO, используется следующий подход.

// Получаем используемые типы объектов XDTO.
ТипТочкаКарты       = ФабрикаXDTO.Тип("http://hj.net.ua/MapRegions", "ТочкаКарты");
ТипМассивТочекКарты = ФабрикаXDTO.Тип("http://v8.1c.ru/8.1/data/core", "Array");

МассивТочекКартыXDTO = ФабрикаXDTO.Создать(ТипМассивТочекКарты);
Для Каждого ТочкиСтрока Из Отчет.Точки Цикл
	// Создаем и заполняем точку карты.
	ТочкаКартыXDTO = ФабрикаXDTO.Создать(ТипТочкаКарты);
	ТочкаКартыXDTO.Ссылка       = ТочкиСтрока.Точка;
	ТочкаКартыXDTO.Код          = ТочкиСтрока.Точка.Код;
	ТочкаКартыXDTO.Наименование = ТочкиСтрока.Точка.Наименование;
	ТочкаКартыXDTO.Широта       = ТочкиСтрока.Точка.Широта;
	ТочкаКартыXDTO.Долгота      = ТочкиСтрока.Точка.Долгота;
	// Добавляем точку в массив.
	МассивТочекКартыXDTO.Value.Добавить(ТочкаКартыXDTO);
КонецЦикла;
			
Сам массив создается с помощью фабрики XDTO как объект типа Array (http://v8.1c.ru/8.1/data/core).
Далее создается добавляемый в массив объект (в нашем случае ТочкаКарты). Заполняются его поля.
И этот объект добавляется в коллекцию Value созданного массива объектов XDTO.

Проблема повторного использования XDTO-объектов в передаваемых данных

Хотелось бы обратить внимание читателя на еще одни "грабли", которые подстерегают разработчиков, использующих XDTO при работе с веб-сервисами.
В базе Сервер, в процедуре обработки метода RegionsByPoints веб-сервиса MapRegions, можно обнаружить следующий код.

//////////////////////////////////////////
// Преобразуем результат в формат XDTO.
	
// Получаем используемые типы объектов XDTO.
ТипТочкаКарты                    = ФабрикаXDTO.Тип("http://hj.net.ua/MapRegions", "ТочкаКарты");
ТипОбластьКарты                  = ФабрикаXDTO.Тип("http://hj.net.ua/MapRegions", "ОбластьКарты");
ТипТаблицаОбластейПоТочкам       = ФабрикаXDTO.Тип("http://hj.net.ua/MapRegions", "ТаблицаОбластейПоТочкам");
ТипСтрокаТаблицыОбластейПоТочкам = ФабрикаXDTO.Тип("http://hj.net.ua/MapRegions", "СтрокаТаблицыОбластейПоТочкам");
// Создаем объект XDTO результирующей таблицы
РезультатXDTO = ФабрикаXDTO.Создать(ТипТаблицаОбластейПоТочкам);
Для Каждого ТочкаXDTO Из Points.Value Цикл
	НайденныеСтроки = Результат.НайтиСтроки(Новый Структура("ТочкаИдентификатор", ТочкаXDTO.Ссылка));
	Для Каждого НайденнаяСтрока Из НайденныеСтроки Цикл
		///////////////////////////////////////////
		// Обход бага\фичи платформы 8.3.8.2054.
		///////////////////////////////////////////
		// Суть:
		//   Один объект XDTO может использоваться только один раз (может иметь только одну ссылку на себя).
		// В результате этого:
		//   - Попытка сохранить исходную точку из входящего массива точек в поле Точка строки результирующей таблицы,
		//     приводит к удалению ее из входящего массива точек. Что в свою очередь, приводит к неправильной работе
		//     цикла "Для Каждого ТочкаXDTO Из Points.Value Цикл".
		//   - Попытка сделать копию исходной точки (перед данным циклом "Для Каждого НайденнаяСтрока Из НайденныеСтроки Цикл"),
		//     и указание в поле Точка всех добавляемых в результирующую таблицу строк для этой точки,
		//     приводит к тому, что точка оказывается указанной только в последней из этих строк.
		// Решение:
		//   Для каждого поля, в котором нужно указывать исходную точку, создавать свою отдельную копию от нее.
		ТочкаXDTOКопия = ФабрикаXDTO.Создать(ТипТочкаКарты);
		ЗаполнитьЗначенияСвойств(ТочкаXDTOКопия, ТочкаXDTO);
		//
		///////////////////////////////////////////
			
		// Создаем и заполняем область карты.
		ОбластьXDTO = ФабрикаXDTO.Создать(ТипОбластьКарты);
		ОбластьXDTO.Ссылка       = НайденнаяСтрока.ОбластьСсылка;
		ОбластьXDTO.Наименование = Строка(НайденнаяСтрока.ОбластьСсылка);
			
		// Создаем и заполняем строку результирующей таблицы.
		РезультатXDTOСтрока = ФабрикаXDTO.Создать(ТипСтрокаТаблицыОбластейПоТочкам);
		РезультатXDTOСтрока.Точка   = ТочкаXDTOКопия;
		РезультатXDTOСтрока.Область = ОбластьXDTO;
			
		// Добавляем строку в результирующую таблицу.
		РезультатXDTO.Строки.Добавить(РезультатXDTOСтрока);
	КонецЦикла;
КонецЦикла;
			
Здесь бросается в глаза огромный комментарий внутри циклов. И так как этот комментарий является самодостаточным, и полностью описывающим, как подробности возникающих проблем, так и способ их решения, то добавить к нему больше нечего.

Вызов веб-сервиса и отображение результатов

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

Для этих целей в базе Клиент существует отчет "Области точек". Но перед тем, как перейти непосредственно к рассмотрению этого отчета, настроим в базе Клиент параметры подключения к веб-сервису базы Сервер.

Настройка параметров подключения к веб-сервису

Для возможности подключения к веб-сервису базы Сервер, в базе Клиент нужно настроить параметры такого подключения.
И, так как в данном решении используется динамическое создание WS-определения, параметры которого хранятся в соответствующих константах,

////////////////////////////////////////////
// Подготовимся к подключению к ВебСервису.
ВебСервисЛогин  = Константы.ВебСервисMapRegionsЛогин.Получить();
ВебСерсисПароль = Константы.ВебСервисMapRegionsПароль.Получить();
ПутьКФайлуWSDL  = Константы.ВебСервисMapRegionsПутьКWSDLФайлу.Получить();
Попытка
	Определение = Новый WSОпределения(ПутьКФайлуWSDL, ВебСервисЛогин, ВебСерсисПароль);
Исключение
	Информация = ИнформацияОбОшибке();
	ТекстИнформации = ОписаниеОшибки();
	ТекстСообщения =	"Ошибка создания описания веб-сервиса:
						|" + ТекстИнформации + "
						|
						|Проверьте параметры подключения к веб-сервису.";
	ВызватьИсключение ТекстСообщения;
КонецПопытки;
Прокси = Новый WSПрокси(Определение,"http://hj.net.ua/MapRegions","MapRegions","MapRegionsSoap");
Прокси.Пользователь = ВебСервисЛогин;
Прокси.Пароль = ВебСерсисПароль;
			
то нужно установить значения этих констант.

Сделать это можно в форме, открываемой с помощью меню "Сервис\Параметры".
Снимок экрана с настройкой параметров подключения к веб-сервису

Описание интерфейса и функциональности отчета "Области точек"

Хоть этот отчет и реализован на механизме СКД, и поэтому имеет относительно стандартный, привычный пользователям 1С, интерфейс, вместе с тем имеет и одну особенность, которая может показаться непривычной. А именно, для того, чтобы отделить получение через веб-сервис данных для формирования отчета, от собственно его формирования и отображения результатов, на форму добавлена кнопка, которая изначально называется "Нет данных для отображения".
Снимок экрана с отчетом ОбластиТочек

Нажатие на эту кнопку октрывает форму получения данных для формирования отчета.
Снимок экрана с формой получения данных для отчета ОбластиТочек
В этой форме, с помощью кнопок "Добавить" (1) и "Подбор" (2), можно выбрать из справочника "Точки карты" в табличную часть "Точки" (3) элементы, для которых необходимо получить соответствующие области карты и отобразить это в отчете.
Нажатие на кнопку "Получить данные и закрыть" (4) выполняет обращение, посредством веб-сервиса, к базе Сервер, и получение данных об областях карты, в которые попали указанные точки. А также к закрытию формы получения данных и возврату к форме формирования отчета.
Нажатие же на кнопку "Отмена" (5) позволяет закрыть окно получения данных, не сохранив сделанных в нем изменений.

После получения данных для отображения, кнопка "Нет данных для отображения" переименуется в "Есть данные для отображения", и значит, можно отобразить результат нажатием на кнопку "Сформировать".

При этом, нажатием на кнопку "Выбрать вариант..." можно выбрать один из предопределенных вариантов отчета:

Области по точкам - группирует информацию по точкам, и для каждой точки указывает перечень областей, в которые входит данная точка.
Снимок экрана с вариантом "Области по точкам" отчета ОбластиТочек

Точки по областям - группирует информацию по областям, и для каждой области отображает перечень точек, которые в нее входят.
Снимок экрана с вариантом "Точки по областям" отчета ОбластиТочек

Также, с помощью кнопки "Настройки..." можно открыть стандартное окно более гибкой настройки структуры отчета.

Вызов веб-сервиса и получение данных

Теперь заглянем "под капот", и посмотрим как этот отчет функционирует.

Как было уже описано выше, получение данных для отображения, посредством обращения к веб-сервису, выполняется в отдельной форме отчета - ФормаПолученияДанных. А именно - в процедуре ПолучитьДанныеСервер().

Данная процедура начинается с динамического создания WS-определения, и создания, на основе него, WS-прокси, через который будет осуществляться вызов веб-сервиса.
Эта часть кода была приведена в разделе Настройка параметров подключения к веб-сервису.

Далее по табличной части Точки формируется массив XDTO-объектов типа ТочкаКарты (http://hj.net.ua/MapRegions)
Эта часть кода была рассмотрена в разделе Передача массивов при вызове веб-сервисов.

После чего производится непосредственно вызов веб-сервиса.

///////////////////////////////////////////
// Выполним обращение к Веб-сервису.
РезультатXDTO = Прокси.RegionsByPoints(МассивТочекКартыXDTO);
			
Веб-сервис возвращает XDTO-таблицу, которая переносится в табличную часть ТочкиСОбластями.
////////////////////////////////////////////////////////////////////
// Преобразуем результат к удобному для дальнейшей работы формату.
Для Каждого РезультатXDTOСтрока Из РезультатXDTO.Строки Цикл
	НоваяСтрока = Отчет.ТочкиСОбластями.Добавить();
	НоваяСтрока.Точка   = Справочники.ТочкиКарты.ПолучитьСсылку(Новый УникальныйИдентификатор(РезультатXDTOСтрока.Точка.Ссылка));
	НоваяСтрока.Область = РезультатXDTOСтрока.Область.Наименование;
КонецЦикла;
			

Использование СКД для отображения данных табличной части отчета

Использующаяся для формирования отчета СКД, описана в макете СКДОтображенияРезультата, и использует для отображения результатов "набор данных - объект", который предполагает таблицу с двумя колонками - Точка и Область.

Вся "магия" описана в обработчике ПриКомпоновкеРезультата(...) модуля объекта отчета.

// Назаренко_СВ 2017-02-19
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
	СтандартнаяОбработка = Ложь;
	
	ВнешниеНаборыДанных = Новый Структура("ТочкиСОбластями", ТочкиСОбластями);
	Настройки = КомпоновщикНастроек.ПолучитьНастройки();
	
	КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
	МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки);
	
	ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
	ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных);
	
	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
	ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
	ПроцессорВывода.Вывести(ПроцессорКомпоновки, Истина);
КонецПроцедуры
			

В нем сначала выполняется настройка использования табличной части ТочкиСОбластями в качестве внешнего источника данных, вместо указанной в СКД таблицы "ТочкиСОбластями" набора данных - объект.

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

Заключение

Если у Вас появятся какие-либо вопросы по затронутой теме, или идеи и предложения по поводу улучшения данного решения, то с удовольствием выслушаю их и\или отвечу на них по приведенным на страничке "Связь" контактам.