Управление viewport

Viewport — это видимая область карты, отображаемая внутри контейнера. Управление viewport является одной из ключевых задач при разработке картографических интерфейсов. Именно viewport определяет:

  • текущий центр карты;
  • масштаб отображения;
  • границы видимой области;
  • анимацию перемещения;
  • реакцию карты на действия пользователя и программного кода.

В Leaflet управление viewport осуществляется через объект карты (L.Map) и набор специализированных методов.


Центрирование карты

При создании карты обычно задаются начальный центр и масштаб:

const map = L.map('map').setView([55.751244, 37.618423], 10);

Здесь:

  • 55.751244 — широта;
  • 37.618423 — долгота;
  • 10 — уровень масштабирования.

Метод setView() одновременно изменяет центр карты и масштаб.


Получение текущего центра

Для получения координат центра используется метод getCenter():

const center = map.getCenter();

console.log(center.lat);
console.log(center.lng);

Результат представляет собой объект типа LatLng:

{
    lat: 55.751244,
    lng: 37.618423
}

Подобная информация часто применяется для:

  • сохранения положения карты;
  • загрузки данных в текущей области;
  • синхронизации нескольких карт.

Изменение центра карты

Для перемещения карты без изменения масштаба применяется метод panTo():

map.panTo([59.9386, 30.3141]);

Карта плавно переместится к указанным координатам.

Пример перехода к Санкт-Петербургу:

map.panTo([59.9386, 30.3141]);

Преимущества метода:

  • плавная анимация;
  • сохранение текущего масштаба;
  • естественное визуальное восприятие перемещения.

Мгновенное изменение позиции

Если анимация не требуется, используется setView():

map.setView([59.9386, 30.3141], map.getZoom());

Либо можно изменить только центр:

map.setView(
    [59.9386, 30.3141],
    map.getZoom(),
    {
        animate: false
    }
);

Такой способ часто применяется:

  • при восстановлении состояния карты;
  • при первоначальной загрузке страницы;
  • при работе с большими объёмами данных.

Масштабирование карты

Установка масштаба

Для изменения уровня масштабирования используется метод setZoom():

map.setZoom(12);

Карта останется в том же центре, но изменит масштаб.


Получение текущего масштаба

const zoom = map.getZoom();

console.log(zoom);

Например:

if (map.getZoom() > 15) {
    console.log('Детальный режим');
}

Увеличение масштаба

map.zoomIn();

На несколько уровней:

map.zoomIn(3);

Уменьшение масштаба

map.zoomOut();

На несколько уровней:

map.zoomOut(2);

Одновременное изменение центра и масштаба

Наиболее универсальный способ управления viewport:

map.setView(
    [48.8566, 2.3522],
    13
);

Переход к Парижу с масштабом 13.

Также доступны дополнительные параметры:

map.setView(
    [48.8566, 2.3522],
    13,
    {
        animate: true,
        duration: 2
    }
);

Анимированные перелёты

Leaflet поддерживает эффект плавного перелёта через метод flyTo().

map.flyTo(
    [40.7128, -74.0060],
    12
);

Результат напоминает навигацию в геоинформационных сервисах.


Настройка анимации

map.flyTo(
    [40.7128, -74.0060],
    12,
    {
        duration: 3
    }
);

Параметр duration задаёт длительность анимации в секундах.


Практический пример

Переход к маркеру после клика:

marker.on('click', () => {
    map.flyTo(
        marker.getLatLng(),
        15
    );
});

Такой подход широко используется в:

  • каталогах объектов;
  • туристических картах;
  • системах мониторинга транспорта.

Работа с границами карты

Viewport может определяться не только центром, но и прямоугольной областью.

Для этого используется объект LatLngBounds.

Создание границ:

const bounds = L.latLngBounds(
    [55.5, 37.2],
    [56.0, 38.0]
);

Отображение заданной области

Метод fitBounds() автоматически подбирает центр и масштаб:

map.fitBounds(bounds);

Leaflet вычислит оптимальный viewport таким образом, чтобы вся область была видна.


Пример с набором маркеров

const group = L.featureGroup([
    marker1,
    marker2,
    marker3
]);

map.fitBounds(
    group.getBounds()
);

Это один из самых распространённых сценариев работы.

Преимущества:

  • автоматический выбор масштаба;
  • отсутствие ручных вычислений;
  • корректная работа при любом количестве объектов.

Добавление внутренних отступов

Иногда объекты не должны касаться краёв карты.

Для этого используются внутренние поля:

map.fitBounds(
    bounds,
    {
        padding: [50, 50]
    }
);

Или разные значения по осям:

map.fitBounds(
    bounds,
    {
        paddingTopLeft: [20, 50],
        paddingBottomRight: [20, 50]
    }
);

Подобный подход особенно полезен при наличии боковых панелей интерфейса.


Получение текущих границ viewport

Метод:

const bounds = map.getBounds();

Получение координат:

console.log(bounds.getNorth());
console.log(bounds.getSouth());
console.log(bounds.getEast());
console.log(bounds.getWest());

Результат может выглядеть следующим образом:

North: 56.21
South: 55.31
East: 38.44
West: 36.72

Проверка попадания точки в viewport

const visible = map.getBounds().contains(
    [55.75, 37.61]
);

console.log(visible);

Результат:

true

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

  • виртуализации объектов;
  • динамической загрузке данных;
  • фильтрации маркеров.

Ограничение перемещения карты

Иногда требуется запретить пользователю покидать определённую территорию.

Создаются границы:

const bounds = L.latLngBounds(
    [54.0, 36.0],
    [57.0, 39.0]
);

Затем задаётся ограничение:

map.setMaxBounds(bounds);

Теперь карта не сможет быть перемещена за пределы области.


Ограничение масштаба

Минимальный и максимальный уровни задаются при создании карты:

const map = L.map('map', {
    minZoom: 5,
    maxZoom: 18
});

Либо динамически:

map.setMinZoom(7);
map.setMaxZoom(16);

Это позволяет контролировать детализацию отображения данных.


Перемещение на заданное количество пикселей

Иногда требуется сдвинуть карту относительно текущего положения.

Для этого используется panBy():

map.panBy([200, 100]);

Где:

  • 200 — смещение по горизонтали;
  • 100 — смещение по вертикали.

Отрицательные значения перемещают карту в противоположную сторону:

map.panBy([-150, 0]);

Получение размеров viewport

Размер видимой области можно узнать через:

const size = map.getSize();

console.log(size.x);
console.log(size.y);

Пример:

{
    x: 1200,
    y: 800
}

Информация полезна при:

  • создании пользовательских оверлеев;
  • вычислении координат интерфейсных элементов;
  • адаптивной вёрстке.

Преобразование координат в пиксели

Географическая точка может быть переведена в координаты экрана.

const point = map.latLngToContainerPoint(
    [55.75, 37.61]
);

console.log(point);

Результат:

{
    x: 450,
    y: 300
}

Это позволяет размещать собственные HTML-элементы поверх карты.


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

Обратная операция:

const latlng = map.containerPointToLatLng(
    [450, 300]
);

console.log(latlng);

Подобное преобразование используется в инструментах рисования и пользовательских интерфейсах.


События изменения viewport

Перемещение карты

Событие move вызывается во время движения:

map.on('move', () => {
    console.log('Карта движется');
});

Завершение перемещения

map.on('moveend', () => {
    console.log('Перемещение завершено');
});

Это наиболее популярное событие для загрузки данных.

Пример:

map.on('moveend', () => {
    loadObjects(
        map.getBounds()
    );
});

Изменение масштаба

Во время изменения:

map.on('zoom', () => {
    console.log(map.getZoom());
});

После завершения:

map.on('zoomend', () => {
    console.log('Масштаб изменён');
});

Автоматическая загрузка данных по текущему viewport

Распространённый паттерн:

function updateData() {
    const bounds = map.getBounds();

    fetch(`/api/objects?bbox=${
        bounds.getWest()
    },${
        bounds.getSouth()
    },${
        bounds.getEast()
    },${
        bounds.getNorth()
    }`);
}

map.on('moveend', updateData);

Сервер получает только данные, находящиеся внутри видимой области карты.

Преимущества подхода:

  • снижение нагрузки на сеть;
  • уменьшение объёма данных;
  • высокая производительность при работе с большими наборами объектов.

Восстановление состояния viewport

Сохранение состояния:

const state = {
    center: map.getCenter(),
    zoom: map.getZoom()
};

Восстановление:

map.setView(
    state.center,
    state.zoom
);

Для хранения между сессиями можно использовать localStorage:

localStorage.setItem(
    'mapState',
    JSON.stringify({
        center: map.getCenter(),
        zoom: map.getZoom()
    })
);

Загрузка:

const state = JSON.parse(
    localStorage.getItem('mapState')
);

if (state) {
    map.setView(
        state.center,
        state.zoom
    );
}

Основные методы управления viewport

Метод Назначение
setView() Установка центра и масштаба
getCenter() Получение центра карты
panTo() Плавное перемещение
panBy() Смещение на заданное количество пикселей
flyTo() Анимированный перелёт
setZoom() Установка масштаба
zoomIn() Увеличение масштаба
zoomOut() Уменьшение масштаба
fitBounds() Отобразить область целиком
getBounds() Получить видимые границы
setMaxBounds() Ограничить перемещение
getSize() Размер viewport
latLngToContainerPoint() Координаты → пиксели
containerPointToLatLng() Пиксели → координаты

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