Работа с GeoJSON

GeoJSON представляет собой стандарт обмена геопространственными данными, основанный на JSON-структуре. В контексте визуализации карт он используется как основной формат для описания точек, линий и полигонов, а также их атрибутов. В Mapbox GL JS GeoJSON является ключевым источником данных для слоёв, фильтрации, стилизации и интерактивности.

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

Пример базовой структуры:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [69.5901, 42.3417]
      },
      "properties": {
        "title": "Точка интереса",
        "category": "poi"
      }
    }
  ]
}

Источники GeoJSON в Mapbox GL JS

GeoJSON может поступать в карту несколькими способами:

Статический объект

Наиболее простой вариант — передача объекта напрямую в источник данных:

map.on('load', () => {
  map.addSource('points', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: []
    }
  });
});

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

Внешний файл

GeoJSON может загружаться по URL:

map.addSource('cities', {
  type: 'geojson',
  data: 'https://example.com/cities.geojson'
});

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

Динамическое обновление

GeoJSON-источник может обновляться на лету:

map.getSource('cities').setData(updatedGeojson);

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

Использование GeoJSON как слоя данных

После добавления источника создаются слои (layers), которые визуализируют данные.

Точечные данные (circle layer)

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

Каждая точка GeoJSON отображается как круг с заданными стилями.

Линейные данные (line layer)

map.addLayer({
  id: 'routes',
  type: 'line',
  source: 'routes',
  paint: {
    'line-color': '#3b82f6',
    'line-width': 3
  }
});

Используется для маршрутов, дорог, траекторий.

Полигональные данные (fill layer)

map.addLayer({
  id: 'areas',
  type: 'fill',
  source: 'areas',
  paint: {
    'fill-color': '#22c55e',
    'fill-opacity': 0.4
  }
});

Применяется для отображения границ, зон покрытия, административных областей.

Доступ к атрибутам properties

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

В Mapbox GL JS свойства используются для data-driven styling:

map.addLayer({
  id: 'points',
  type: 'circle',
  source: 'points',
  paint: {
    'circle-radius': [
      'case',
      ['==', ['get', 'category'], 'large'], 10,
      5
    ],
    'circle-color': [
      'match',
      ['get', 'category'],
      'restaurant', '#e11d48',
      'school', '#2563eb',
      '#9ca3af'
    ]
  }
});

Оператор get извлекает значения из properties, а выражения case и match позволяют строить условную визуализацию без дополнительного кода.

Фильтрация GeoJSON-данных

Mapbox GL JS поддерживает фильтрацию на уровне слоя:

map.setFilter('points', [
  '==',
  ['get', 'category'],
  'restaurant'
]);

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

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

Для больших наборов точек GeoJSON используется кластеризация:

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

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

Отображение кластеров:

map.addLayer({
  id: 'clusters',
  type: 'circle',
  source: 'earthquakes',
  filter: ['has', 'point_count'],
  paint: {
    'circle-radius': 15,
    'circle-color': '#f59e0b'
  }
});

Отдельно можно отображать одиночные точки:

map.addLayer({
  id: 'unclustered',
  type: 'circle',
  source: 'earthquakes',
  filter: ['!', ['has', 'point_count']],
  paint: {
    'circle-radius': 6,
    'circle-color': '#ef4444'
  }
});

Обработка событий на GeoJSON-слоях

GeoJSON-слои интегрируются с системой событий карты:

map.on('click', 'points', (e) => {
  const feature = e.features[0];
  console.log(feature.properties.title);
});

Для корректной работы необходимо указать queryRenderedFeatures через идентификатор слоя.

Также можно использовать hover-эффекты:

map.on('mouseenter', 'points', () => {
  map.getCanvas().style.cursor = 'pointer';
});

Обновление GeoJSON в реальном времени

GeoJSON-источники в Mapbox GL JS позволяют реализовать потоковые сценарии:

setInterval(() => {
  fetch('/api/positions')
    .then(res => res.json())
    .then(data => {
      map.getSource('vehicles').setData(data);
    });
}, 5000);

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

  • отслеживания транспорта
  • мониторинга пользователей
  • обновления сенсорных данных

Оптимизация работы с GeoJSON

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

Уменьшение геометрической сложности

Сложные полигоны следует упрощать до загрузки:

  • снижение количества координат
  • использование инструментов simplification

Разделение источников

Разделение GeoJSON на несколько источников снижает нагрузку на фильтрацию и рендеринг.

Ограничение кластеризации

Слишком большие радиусы кластеров могут снижать точность визуализации.

Преобразование GeoJSON и подготовка данных

GeoJSON часто формируется на серверной стороне или в ETL-процессах. Основные преобразования включают:

  • конвертацию CSV → GeoJSON
  • агрегацию точек
  • добавление категорий и тегов
  • генерацию идентификаторов

Пример формирования Feature:

const feature = {
  type: 'Feature',
  geometry: {
    type: 'Point',
    coordinates: [longitude, latitude]
  },
  properties: {
    id: 1,
    status: 'active'
  }
};

Использование GeoJSON в комбинированных слоях

Один источник GeoJSON может использоваться несколькими слоями одновременно:

  • точки (circle)
  • подписи (symbol)
  • тепловые карты (heatmap)

Пример heatmap:

map.addLayer({
  id: 'heat',
  type: 'heatmap',
  source: 'points',
  paint: {
    'heatmap-intensity': 1,
    'heatmap-radius': 20
  }
});

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

Связь GeoJSON с выражениями и стилями

В Mapbox GL JS выражения позволяют превращать GeoJSON в основу визуальной логики:

  • вычисление размеров объектов
  • цветовая сегментация
  • динамическое скрытие элементов
  • масштабозависимая визуализация

Пример масштабозависимого отображения:

'circle-radius': [
  'interpolate',
  ['linear'],
  ['zoom'],
  5, 2,
  10, 8,
  15, 20
]

Интеграция GeoJSON с пользовательскими интерфейсами

GeoJSON часто служит связующим звеном между картой и интерфейсом приложения:

  • списки объектов синхронизируются с картой
  • выбор элемента в UI подсвечивает Feature
  • события карты обновляют состояние приложения

Такая интеграция строится вокруг идентификаторов в properties и реактивного обновления источника данных.

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

При росте данных стандартный GeoJSON может стать узким местом. В таких случаях используются:

  • тайловые GeoJSON-источники
  • серверная агрегация
  • векторные тайлы (vector tiles)
  • потоковая подача данных

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