fitBounds: подгонка под границы

Leaflet предоставляет механизм автоматической подгонки карты под заданные географические границы. Основной инструмент для этого — метод fitBounds, позволяющий масштабировать и центрировать карту так, чтобы все точки или геометрия оказались в пределах видимой области.

Метод работает с объектом LatLngBounds, который описывает прямоугольную область на карте через две точки: юго-западную и северо-восточную границы.


LatLngBounds как основа fitBounds

Границы создаются на основе набора координат или вручную:

const bounds = L.latLngBounds([
  [55.75, 37.61],
  [55.80, 37.70]
]);

Также границы могут быть вычислены из набора маркеров:

const marker1 = L.marker([55.75, 37.61]);
const marker2 = L.marker([55.80, 37.70]);

const bounds = L.latLngBounds([
  marker1.getLatLng(),
  marker2.getLatLng()
]);

В практических задачах чаще используется автоматическое построение границ из слоёв:

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

Базовое использование fitBounds

Метод применяется к экземпляру карты:

map.fitBounds(bounds);

Результатом является автоматическое вычисление:

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

В отличие от setView, где задаются фиксированные координаты и zoom, fitBounds рассчитывает параметры динамически.


Параметры fitBounds

Метод поддерживает дополнительную настройку поведения:

map.fitBounds(bounds, {
  padding: [20, 20],
  maxZoom: 15,
  animate: true
});

padding

Добавляет отступы от границ карты:

padding: [topBottom, leftRight]

или более детально:

paddingTopLeft: [20, 20],
paddingBottomRight: [20, 20]

Используется для предотвращения «прилипания» объектов к краям экрана.


maxZoom

Ограничивает максимальный уровень приближения:

maxZoom: 12

Применяется, когда область слишком мала, и автоматический zoom может оказаться чрезмерным.


animate

Управляет анимацией перехода:

animate: true

При включении происходит плавное перемещение и масштабирование карты. Без анимации переход выполняется мгновенно.


duration и easing

Для анимации могут использоваться дополнительные параметры:

map.fitBounds(bounds, {
  animate: true,
  duration: 0.5,
  easeLinearity: 0.25
});
  • duration — длительность анимации
  • easeLinearity — характер ускорения/замедления

fitBounds и GeoJSON

При работе с GeoJSON границы вычисляются напрямую из слоя:

const geoJsonLayer = L.geoJSON(data);
map.fitBounds(geoJsonLayer.getBounds());

Метод getBounds() агрегирует все координаты объектов, включая:

  • Point
  • LineString
  • Polygon
  • MultiPolygon

fitBounds для FeatureGroup

При группировке объектов используется FeatureGroup:

const group = L.featureGroup([marker1, marker2, polygon]);
map.fitBounds(group.getBounds());

Такой подход применяется для:

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

Поведение при одной точке

Если границы содержат только одну координату, Leaflet не может вычислить прямоугольник стандартным способом. В этом случае:

  • zoom выбирается автоматически;
  • может применяться maxZoom;
  • карта центрируется на точке.
const bounds = L.latLngBounds([[55.75, 37.61]]);
map.fitBounds(bounds);

Отличие fitBounds от setView

setView

map.setView([55.75, 37.61], 10);
  • фиксированные координаты;
  • фиксированный zoom;
  • отсутствие адаптации под объекты.

fitBounds

map.fitBounds(bounds);
  • автоматический центр;
  • автоматический zoom;
  • адаптация под геометрию.

fitBounds применяется при неизвестном заранее масштабе данных.


Ограничение минимального и максимального zoom

Дополнительно учитываются глобальные настройки карты:

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

При вызове fitBounds итоговый zoom всегда приводится к этим ограничениям.


Внутренний алгоритм вычисления

При вызове fitBounds выполняется:

  1. вычисление ширины и высоты bounds;
  2. сопоставление с размером контейнера карты;
  3. выбор zoom, при котором bounds полностью помещается;
  4. вычисление центра прямоугольника;
  5. применение transform (pan + zoom).

Оптимизация поведения fitBounds

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

map.fitBounds(bounds, {
  animate: false,
  reset: true
});

Для больших наборов объектов предпочтительно:

  • предварительно вычислять bounds;
  • не пересоздавать их на каждый рендер;
  • использовать requestAnimationFrame при потоковых обновлениях.

Особенности работы с пустыми bounds

Если bounds не содержат координат, поведение неопределено:

  • карта не изменяет состояние;
  • возможны ошибки при попытке вычисления.

Проверка:

if (bounds.isValid()) {
  map.fitBounds(bounds);
}

fitBounds и ограниченные области карты

При использовании maxBounds:

const map = L.map('map', {
  maxBounds: L.latLngBounds([[50, 30], [60, 40]])
});

fitBounds не выходит за пределы этой области, даже если данные требуют большего охвата.


Работа с мультислоями

При комбинировании нескольких источников данных:

const group = L.featureGroup();

geoJsonLayer.addTo(group);
markerLayer.addTo(group);

map.fitBounds(group.getBounds());

Общий bounds автоматически агрегирует все вложенные элементы.


Поведение при изменении размеров контейнера

После resize контейнера может потребоваться повторный вызов:

map.invalidateSize();
map.fitBounds(bounds);

invalidateSize обновляет внутренние вычисления размеров, без чего fitBounds может дать некорректный результат.


Использование fitBounds в динамических сценариях

При обновлении данных (например, фильтрация):

layer.clearLayers();
layer.addData(filteredData);
map.fitBounds(layer.getBounds());

При потоковой загрузке:

data.forEach(point => layer.addLayer(L.marker(point)));
map.fitBounds(layer.getBounds());

Типичные ошибки поведения

  • вызов fitBounds до инициализации карты;
  • использование невалидных координат;
  • отсутствие проверки isValid();
  • попытка применять к пустому FeatureGroup;
  • конфликт с maxBounds.

Сценарии применения

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