Интеграция картографического функционала в Angular-приложения через обёртку над Leaflet позволяет использовать декларативный подход к работе с картами, сохраняя при этом всю мощь нативной библиотеки. Библиотека ngx-leaflet выступает связующим слоем между императивным API Leaflet и реактивной архитектурой Angular.
В основе подхода лежит разделение ответственности: Leaflet управляет отрисовкой карты, слоями, событиями и геометрией, а Angular отвечает за состояние приложения, жизненный цикл компонентов и реактивные потоки данных. ngx-leaflet предоставляет директиву, которая связывает DOM-элемент с экземпляром карты Leaflet.
Ключевой принцип заключается в том, что карта не создаётся напрямую через конструктор Leaflet внутри компонента, а декларативно описывается через биндинги Angular.
Основные пакеты:
Установка:
npm install leaflet
npm install @asymmetrik/ngx-leaflet
Дополнительно требуется подключение типов:
npm install --save-dev @types/leaflet
Без корректных стилей карта не будет отображаться правильно. Подключение осуществляется глобально:
"styles": [
"node_modules/leaflet/dist/leaflet.css"
]
Альтернативно через styles.scss:
@import "leaflet/dist/leaflet.css";
Перед использованием директив необходимо подключить модуль:
import { LeafletModule } from '@asymmetrik/ngx-leaflet';
@NgModule({
imports: [
LeafletModule
]
})
export class AppModule {}
В компоненте создаётся объект конфигурации карты:
import { latLng, tileLayer, MapOptions } from 'leaflet';
export class MapComponent {
options: MapOptions = {
layers: [
tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'OpenStreetMap'
})
],
zoom: 10,
center: latLng(51.505, -0.09)
};
}
Шаблон компонента:
<div
leaflet
[leafletOptions]="options">
</div>
Директива leaflet создаёт экземпляр карты и связывает
его с DOM-элементом, а leafletOptions задаёт
конфигурацию.
Для получения доступа к нативному объекту карты используется событие инициализации:
<div
leaflet
[leafletOptions]="options"
(leafletMapReady)="onMapReady($event)">
</div>
import { Map } from 'leaflet';
export class MapComponent {
map!: Map;
onMapReady(map: Map): void {
this.map = map;
}
}
Нативный объект позволяет использовать весь API Leaflet без ограничений.
Leaflet использует систему слоёв для отображения данных. Основной слой — тайловый:
tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: 'OpenStreetMap'
})
Слои можно динамически менять, комбинировать и обновлять:
this.map.removeLayer(oldLayer);
this.map.addLayer(newLayer);
Маркеры создаются через класс Marker:
import { marker } from 'leaflet';
const m = marker([51.5, -0.09]);
m.addTo(this.map);
С ngx-leaflet часто используется декларативный подход через массив слоёв:
import { Marker } from 'leaflet';
layers = [
new Marker([51.5, -0.09])
];
Иконки задаются через Icon:
import { icon, Marker } from 'leaflet';
const customIcon = icon({
iconUrl: 'assets/marker.png',
iconSize: [25, 41],
iconAnchor: [12, 41]
});
const m = new Marker([51.5, -0.09], { icon: customIcon });
В Angular-подходе изменения состояния часто привязаны к потокам данных. Карта обновляется через замену массива слоёв:
layers: any[] = [];
updateMarkers(data: any[]): void {
this.layers = data.map(item =>
marker([item.lat, item.lng])
);
}
Изменение ссылки на массив вызывает обновление визуализации.
Leaflet предоставляет богатую систему событий:
this.map.on('click', (e) => {
console.log(e.latlng);
});
В Angular-интеграции события могут обрабатываться через сам экземпляр карты:
onMapReady(map: Map): void {
map.on('zoomend', () => {
console.log(map.getZoom());
});
}
Для отображения маршрутов и областей используются геометрические слои:
import { polyline, polygon } from 'leaflet';
const line = polyline([
[51.5, -0.1],
[51.51, -0.12]
]);
const area = polygon([
[51.52, -0.1],
[51.53, -0.12],
[51.51, -0.13]
]);
Leaflet не предоставляет встроенной геоаналитики, но поддерживает визуализацию результатов вычислений, выполненных отдельно. Например, маршрутизация может строиться через внешние сервисы, а затем отображаться через polyline.
Для управления видимостью слоёв применяется LayerGroup:
import { layerGroup } from 'leaflet';
const group = layerGroup([marker1, marker2]);
group.addTo(this.map);
Это позволяет включать и отключать наборы объектов:
group.remove();
group.addTo(this.map);
Важно учитывать жизненный цикл компонентов:
ngAfterViewInitngOnDestroyngOnDestroy(): void {
this.map.remove();
}
Это предотвращает утечки памяти.
Leaflet полностью поддерживает TypeScript. ngx-leaflet усиливает типизацию через интерфейсы:
import { MapOptions, Layer } from 'leaflet';
options: MapOptions;
layers: Layer[];
Типизация критична при масштабировании приложений с большим количеством географических объектов.
При отображении тысяч объектов необходимо учитывать:
Кластеризация часто реализуется через дополнительные плагины Leaflet и интегрируется в Angular через ngx-leaflet так же, как обычные слои.
Карта часто выступает визуализацией API:
this.http.get('/api/points').subscribe(data => {
this.layers = data.map(p => marker([p.lat, p.lng]));
});
Обновление происходит реактивно при изменении данных.
Карта может программно изменять положение:
this.map.setView([52.52, 13.405], 12);
Также доступно плавное перемещение:
this.map.flyTo([52.52, 13.405], 10);
Leaflet позволяет добавлять UI-элементы:
import { control } from 'leaflet';
control.zoom({
position: 'topright'
}).addTo(this.map);
Angular-интеграция не ограничивает использование нативных контролов.
Типичный сценарий — переключение между спутниковой и стандартной картой:
const streets = tileLayer('url1');
const satellite = tileLayer('url2');
Управление:
this.map.addLayer(streets);
this.map.removeLayer(satellite);
Leaflet поддерживает определение местоположения:
this.map.locate({ setView: true, maxZoom: 16 });
Обработка события:
this.map.on('locationfound', (e) => {
console.log(e.latlng);
});
При росте приложения выделяются отдельные слои абстракции:
Такой подход снижает связанность компонентов и упрощает тестирование.
Часто применяются:
Leaflet экосистема включает плагины для:
ngx-leaflet не ограничивает подключение расширений, так как работает поверх нативного API.
При работе с динамическими данными критично:
Карта может быть частью глобального состояния:
this.store.select('mapState').subscribe(state => {
this.map.setView(state.center, state.zoom);
});
Это позволяет интегрировать Leaflet в архитектуры NgRx или аналогичные state management решения.