Управление геометрией

В основе работы с геометрией в Mapbox GL JS лежит модель данных, основанная на GeoJSON и векторных тайлах. Геометрия в рамках карты не существует как отдельные графические примитивы; она всегда привязана к источнику данных (source) и отображается через слой (layer). Такой подход разделяет хранение данных и их визуализацию, обеспечивая гибкость управления пространственными объектами.

Геометрия в Mapbox GL JS представляется тремя основными типами:

  • Point — точечные объекты (маршруты, маркеры, события)
  • LineString — линейные объекты (дороги, маршруты)
  • Polygon — площадные объекты (здания, зоны, территории)

Каждый объект оборачивается в GeoJSON Feature, что позволяет добавлять свойства (properties), используемые для стилизации и логики отображения.


Источники данных GeoJSON и векторные тайлы

Геометрия всегда загружается через источник данных (source). Наиболее универсальный вариант — GeoJSON source, позволяющий динамически изменять геометрию в рантайме.

Типичный источник данных:

map.addSource('cities', {
  type: 'geojson',
  data: {
    type: 'FeatureCollection',
    features: []
  }
});

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

map.addLayer({
  id: 'cities-layer',
  type: 'circle',
  source: 'cities',
  paint: {
    'circle-radius': 6,
    'circle-color': '#ff0000'
  }
});

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


Добавление и обновление геометрии через setData

Основной механизм управления геометрией в GeoJSON-источниках — метод setData. Он полностью заменяет текущий набор данных источника новым объектом FeatureCollection.

map.getSource('cities').setData({
  type: 'FeatureCollection',
  features: [
    {
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: [71.4304, 51.1281]
      },
      properties: {
        name: 'City A'
      }
    }
  ]
});

Особенность этого механизма заключается в том, что обновление происходит атомарно: старая геометрия полностью заменяется новой. Это упрощает модель, но требует осторожности при больших наборах данных, поскольку повторная передача всего FeatureCollection может быть затратной.

При частых обновлениях используется стратегия частичного пересчёта данных на стороне приложения, чтобы минимизировать объем передаваемой геометрии.


Управление состоянием объектов feature-state

Для более тонкого управления геометрией применяется механизм feature-state. Он позволяет изменять визуальное состояние отдельных объектов без пересоздания всего источника данных.

map.setFeatureState(
  { source: 'cities', id: 1 },
  { selected: true }
);

Состояние может использоваться в стилях слоя:

'circle-color': [
  'case',
  ['boolean', ['feature-state', 'selected'], false],
  '#00ff00',
  '#ff0000'
]

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


Трансформация координат: project / unproject

Управление геометрией часто требует преобразования координат между географической системой (долгота/широта) и экранной системой координат (пиксели).

const point = map.project([71.4304, 51.1281]);

Обратное преобразование:

const lngLat = map.unproject([x, y]);

Эти операции используются при интерактивном редактировании геометрии, когда пользователь взаимодействует с элементами карты через мышь или касания.

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


Динамическое редактирование геометрии на лету

Изменение геометрии в реальном времени строится на комбинации getSource, setData и преобразований координат.

Типичный сценарий — перемещение точки:

const source = map.getSource('cities');

const data = source._data;
data.features[0].geometry.coordinates = [71.44, 51.13];

source.setData(data);

Хотя прямое обращение к внутренним данным источника возможно, корректнее формировать новый объект данных и передавать его через setData.

Для линий и полигонов обновление включает перерасчёт массива координат:

geometry: {
  type: 'LineString',
  coordinates: [
    [71.1, 51.1],
    [71.5, 51.4]
  ]
}

При интерактивном редактировании важно минимизировать частоту вызовов setData, так как каждый вызов инициирует перерасчёт рендеринга слоя.


Кластеризация и агрегация точек

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

map.addSource('points', {
  type: 'geojson',
  data: pointsData,
  cluster: true,
  clusterMaxZoom: 14,
  clusterRadius: 50
});

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

map.addLayer({
  id: 'clusters',
  type: 'circle',
  source: 'points',
  filter: ['has', 'point_count']
});

Индивидуальные точки внутри кластеров доступны через фильтрацию:

filter: ['!', ['has', 'point_count']]

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


Оптимизация обновлений геометрии и производительность

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

  • размер FeatureCollection
  • частота вызовов setData
  • сложность геометрии (количество вершин)
  • количество активных слоёв

При частых изменениях рекомендуется:

  • минимизировать глубину вложенности объектов
  • избегать пересоздания неизменённых геометрий
  • разделять данные на несколько источников
  • использовать feature-state вместо полного обновления данных

Большие полигоны особенно чувствительны к частым пересчётам, так как каждая вершина участвует в триангуляции и рендеринге.


Работа с вычисляемой геометрией и пространственными операциями

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

Обычно для этого применяется внешняя библиотека, например Turf.js:

const buffered = turf.buffer(point, 1, { units: 'kilometers' });

Результат таких операций затем передаётся в источник данных:

map.getSource('areas').setData(buffered);

Такой подход позволяет динамически генерировать геометрию на основе пользовательского ввода или состояния системы.


Контроль видимости и фильтрация геометрии через слои

Отображение геометрии управляется на уровне слоёв через фильтры и условия выражений.

Фильтрация по свойствам:

filter: ['==', ['get', 'type'], 'urban']

Фильтрация по геометрии:

filter: ['==', '$type', 'Polygon']

Слои позволяют не изменять саму геометрию, а управлять её представлением. Это ключевой принцип разделения данных и визуализации.

Дополнительно используется управление прозрачностью, масштабной зависимостью и условиями отображения:

minzoom: 5,
maxzoom: 12

Такая модель обеспечивает контроль над тем, как геометрия проявляется на разных уровнях детализации карты.