В JavaScript API HERE Technologies элементы управления картой строятся поверх базового контейнера UI и интегрируются в систему управления состоянием карты через UI Manager. Архитектура предполагает расширяемость: разработчик может добавлять собственные компоненты, которые ведут себя как встроенные элементы управления масштабом, слоями или навигацией, сохраняя единый стиль взаимодействия и корректную работу с жизненным циклом карты.
UI-слой в HERE Maps построен вокруг объекта H.ui.UI,
который управляет набором элементов интерфейса и их размещением
относительно карты. Каждый элемент управления наследует поведение
базового класса H.ui.Control, обеспечивающего:
Пользовательский элемент управления фактически является DOM-компонентом с логикой, интегрированной в UI Manager.
Создание собственного контроллера начинается с определения
конструктора и наследования от H.ui.Control:
function CustomControl(options) {
H.ui.Control.call(this);
this.options = options || {};
this.buildUI();
}
CustomControl.prototype = Object.create(H.ui.Control.prototype);
CustomControl.prototype.constructor = CustomControl;
Метод buildUI отвечает за создание DOM-структуры:
CustomControl.prototype.buildUI = function () {
const container = document.createElement('div');
container.className = 'custom-control';
const button = document.createElement('button');
button.innerText = 'Action';
container.appendChild(button);
this.setElement(container);
};
Ключевой момент заключается в вызове setElement, который
регистрирует DOM-узел в системе UI.
После создания контрол добавляется в интерфейс через UI Manager:
const ui = H.ui.UI.createDefault(map, defaultLayers);
const customControl = new CustomControl();
ui.addControl('custom-control', customControl, H.ui.ControlPosition.TOP_RIGHT);
Позиционирование определяется константами
H.ui.ControlPosition, которые задают область закрепления
элемента.
Элементы управления должны корректно обрабатывать пользовательские события, не нарушая поведение карты (панорамирование, зумирование, клик по объектам).
Пример обработки клика:
button.addEventListener('click', function (e) {
e.stopPropagation();
console.log('Custom action triggered');
});
Остановка всплытия событий критична, поскольку без неё взаимодействие с картой может конфликтовать с UI-слоем.
Контрол может взаимодействовать с объектом карты через замыкание или явную передачу ссылки:
function CustomControl(map) {
H.ui.Control.call(this);
this.map = map;
this.buildUI();
}
Использование карты внутри контролов позволяет:
Пример изменения масштаба:
button.addEventListener('click', () => {
const zoom = this.map.getZoom();
this.map.setZoom(zoom + 1);
});
UI-слой HERE Maps не накладывает строгих ограничений на стили, однако рекомендуется соблюдать структуру, совместимую с системным дизайном карты.
Пример CSS:
.custom-control {
background: white;
padding: 8px;
border-radius: 4px;
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
font-family: Arial, sans-serif;
}
.custom-control button {
border: none;
background: #0078ff;
color: white;
padding: 6px 10px;
cursor: pointer;
}
Важно учитывать, что карта может работать в различных контейнерах, поэтому фиксированные размеры должны использоваться аккуратно.
Вместо создания элемента с нуля можно расширять существующие
контролы, например H.ui.ZoomControl или
H.ui.SpatialLayersControl, добавляя дополнительную
функциональность.
function ExtendedZoomControl() {
H.ui.ZoomControl.call(this);
}
ExtendedZoomControl.prototype = Object.create(H.ui.ZoomControl.prototype);
ExtendedZoomControl.prototype.constructor = ExtendedZoomControl;
Дальнейшая модификация позволяет добавлять кнопки или изменять поведение стандартных действий.
Каждый контрол проходит несколько стадий:
Удаление осуществляется через UI:
ui.removeControl(customControl);
После удаления важно очищать обработчики событий и ссылки на карту, чтобы избежать утечек памяти.
Контролы могут реагировать на изменения состояния карты, подписываясь на события:
map.addEventListener('zoom', function () {
const zoomLevel = map.getZoom();
console.log('Current zoom:', zoomLevel);
});
Более сложные элементы управления синхронизируют своё состояние с картой:
map.addEventListener('mapviewchange', function () {
updateUI();
});
Для сложных интерфейсов допускается использование шаблонов:
CustomControl.prototype.buildUI = function () {
this.element = document.createElement('div');
this.element.innerHTML = `
<div class="panel">
<input type="text" placeholder="Введите запрос">
<button>Поиск</button>
</div>
`;
this.setElement(this.element);
};
Такой подход упрощает построение мини-приложений внутри карты.
Элементы управления часто выступают интерфейсом к геосервисам платформы: маршрутизации, поиску объектов и геокодированию.
Пример интеграции с поиском:
button.addEventListener('click', () => {
const geocoder = platform.getSearchService();
geocoder.geocode({ q: input.value }, result => {
const location = result.items[0].position;
map.setCenter(location);
});
});
Контрол становится точкой входа в бизнес-логику геоданных.
UI Manager поддерживает одновременное размещение нескольких пользовательских элементов. При этом важно учитывать:
Пример:
ui.addControl('search', searchControl, H.ui.ControlPosition.TOP_LEFT);
ui.addControl('layers', layersControl, H.ui.ControlPosition.TOP_RIGHT);
ui.addControl('tools', toolsControl, H.ui.ControlPosition.BOTTOM_RIGHT);
Контролы должны учитывать размеры контейнера карты. Для этого используется обработка resize-событий:
window.addEventListener('resize', () => {
map.getViewPort().resize();
});
Также возможно динамическое изменение структуры UI в зависимости от ширины экрана:
if (window.innerWidth < 600) {
ui.removeControl(largePanelControl);
}
При добавлении кастомных элементов необходимо учитывать взаимодействие с системными контролами:
Часто используется обёртка:
container.style.pointerEvents = 'auto';
container.style.zIndex = 1000;
Правильная архитектура требует отделения UI от логики:
Пример разделения:
function SearchService(platform) {
this.platform = platform;
}
SearchService.prototype.query = function (text, callback) {
this.platform.getSearchService().geocode({ q: text }, callback);
};
Контрол вызывает сервис, не реализуя бизнес-логику напрямую.
Контролы проектируются как переиспользуемые модули. Для этого:
Пример конфигурационного подхода:
const control = new CustomControl({
label: 'Поиск',
placeholder: 'Город или адрес'
});