Тепловые карты

Тепловая карта (Heatmap) — это способ визуализации большого количества точечных данных, при котором отдельные объекты отображаются не в виде маркеров, а как области различной интенсивности. Чем выше концентрация объектов или больше их вес, тем ярче и насыщеннее становится соответствующий участок карты.

В OpenLayers тепловые карты используются для отображения:

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

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


Принцип работы Heatmap Layer

В OpenLayers тепловая карта реализована через класс Heatmap.

Импорт:

import Heatmap from 'ol/layer/Heatmap.js';

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

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

Точки → Векторный источник → Heatmap → Растеризация → Карта

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


Создание простой тепловой карты

Минимальный пример:

import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/Tile.js';
import OSM from 'ol/source/OSM.js';

import Heatmap from 'ol/layer/Heatmap.js';
import VectorSource from 'ol/source/Vector.js';
import Feature from 'ol/Feature.js';
import Point from 'ol/geom/Point.js';

const source = new VectorSource();

source.addFeature(
    new Feature({
        geometry: new Point([4180000, 7500000])
    })
);

source.addFeature(
    new Feature({
        geometry: new Point([4200000, 7520000])
    })
);

const heatmapLayer = new Heatmap({
    source: source
});

const map = new Map({
    target: 'map',
    layers: [
        new TileLayer({
            source: new OSM()
        }),
        heatmapLayer
    ],
    view: new View({
        center: [4190000, 7510000],
        zoom: 8
    })
});

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


Параметр blur

Свойство blur определяет степень размытия каждой точки.

const heatmapLayer = new Heatmap({
    source: source,
    blur: 20
});

Влияние параметра:

Значение Результат
5 Резкие области
10 Небольшое размытие
20 Стандартный вариант
30 Мягкие переходы
50+ Очень широкое распределение

Пример:

const heatmapLayer = new Heatmap({
    source: source,
    blur: 40
});

Большие значения позволяют визуализировать общие тенденции, а небольшие — локальные скопления объектов.


Параметр radius

Радиус определяет область влияния каждой точки.

const heatmapLayer = new Heatmap({
    source: source,
    radius: 15
});

Сравнение значений:

radius: 5

Компактные пятна.

radius: 20

Средняя зона воздействия.

radius: 50

Широкое распределение интенсивности.

Часто параметры radius и blur используются совместно:

const heatmapLayer = new Heatmap({
    source: source,
    radius: 25,
    blur: 30
});

Вес точек (Weight)

Не все точки имеют одинаковую значимость.

Например:

  • один магазин обслуживает 100 клиентов;
  • другой обслуживает 5000 клиентов.

В таком случае каждая точка должна иметь собственный вес.

Создание объектов:

const feature = new Feature({
    geometry: new Point(coordinates)
});

feature.set('weight', 0.8);

source.addFeature(feature);

Настройка тепловой карты:

const heatmapLayer = new Heatmap({
    source: source,
    weight: function(feature) {
        return feature.get('weight');
    }
});

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

0.0 → 1.0

Где:

0.0 = минимальное влияние
1.0 = максимальное влияние

Автоматическое вычисление веса

Часто исходные данные содержат реальные значения:

{
    count: 350
}

Необходимо преобразовать их в диапазон от 0 до 1.

Пример:

const maxValue = 1000;

const heatmapLayer = new Heatmap({
    source: source,
    weight: function(feature) {
        return feature.get('count') / maxValue;
    }
});

Более безопасный вариант:

const heatmapLayer = new Heatmap({
    source: source,
    weight: function(feature) {

        const value = feature.get('count') / 1000;

        return Math.min(value, 1);
    }
});

Использование GeoJSON

На практике данные обычно загружаются из GeoJSON.

Пример:

import GeoJSON from 'ol/format/GeoJSON.js';
import VectorSource from 'ol/source/Vector.js';

const source = new VectorSource({
    url: '/data/points.geojson',
    format: new GeoJSON()
});

Создание слоя:

const heatmapLayer = new Heatmap({
    source: source,
    radius: 20,
    blur: 15
});

Структура GeoJSON:

{
  "type": "Feature",
  "properties": {
    "weight": 0.7
  },
  "geometry": {
    "type": "Point",
    "coordinates": [37.62, 55.75]
  }
}

Использование свойства:

const heatmapLayer = new Heatmap({
    source: source,
    weight: feature => feature.get('weight')
});

Динамическое изменение параметров

Параметры можно изменять после создания слоя.

Изменение радиуса:

heatmapLayer.setRadius(40);

Изменение размытия:

heatmapLayer.setBlur(25);

Пример со слайдером:

<input type="range" id="radius" min="1" max="100">
document
    .getElementById('radius')
    .addEventListener('input', function() {

        heatmapLayer.setRadius(
            Number(this.value)
        );

    });

Такой подход позволяет создавать интерактивные панели настройки визуализации.


Настройка градиента

По умолчанию OpenLayers использует стандартную цветовую палитру.

Возможна установка собственного градиента:

const heatmapLayer = new Heatmap({
    source: source,
    gradient: [
        '#0000ff',
        '#00ffff',
        '#00ff00',
        '#ffff00',
        '#ff0000'
    ]
});

Интерпретация:

Синий       → низкая плотность
Голубой     → умеренная плотность
Зелёный     → средняя плотность
Жёлтый      → высокая плотность
Красный     → максимальная плотность

Создание монохромной карты

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

Пример:

const heatmapLayer = new Heatmap({
    source: source,
    gradient: [
        'rgba(255,255,255,0)',
        'rgba(0,128,255,1)'
    ]
});

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


Использование нескольких цветовых схем

Холодная палитра

gradient: [
    '#001f3f',
    '#0074D9',
    '#7FDBFF'
]

Тёплая палитра

gradient: [
    '#ffffcc',
    '#ffeda0',
    '#feb24c',
    '#fd8d3c',
    '#f03b20',
    '#bd0026'
]

Экологическая палитра

gradient: [
    '#edf8e9',
    '#bae4b3',
    '#74c476',
    '#31a354',
    '#006d2c'
]

Выбор цветовой схемы зависит от характера отображаемых данных.


Обновление данных в реальном времени

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

Добавление новой точки:

function addPoint(x, y, weight) {

    const feature = new Feature({
        geometry: new Point([x, y])
    });

    feature.set('weight', weight);

    source.addFeature(feature);
}

Периодическое обновление:

setInterval(() => {

    addPoint(
        randomX(),
        randomY(),
        Math.random()
    );

}, 1000);

После добавления объектов слой автоматически перерисовывается.


Удаление старых точек

Для визуализации текущей активности часто требуется ограничивать историю данных.

Пример:

setInterval(() => {

    const features = source.getFeatures();

    if (features.length > 5000) {
        source.removeFeature(features[0]);
    }

}, 1000);

Подобный механизм используется для отображения:

  • перемещения транспорта;
  • сетевой активности;
  • телеметрии устройств;
  • посещаемости объектов.

Производительность

Тепловые карты способны отображать десятки тысяч точек, однако существуют ограничения.

Основные рекомендации:

Использовать минимально необходимый радиус

Большой радиус увеличивает вычислительную нагрузку.

Плохо:

radius: 100

Лучше:

radius: 15

Ограничивать количество объектов

Вместо:

100000 точек

целесообразно использовать:

10000–30000 точек

при отображении в браузере.

Кластеризовать данные заранее

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


Комбинирование с обычными слоями

Тепловая карта может использоваться вместе с векторными слоями.

Пример:

layers: [
    osmLayer,
    heatmapLayer,
    vectorLayer
]

Распространённая схема:

OSM
↓
Heatmap
↓
Маркеры объектов

При таком подходе пользователь одновременно видит:

  • общую статистическую картину;
  • точное положение отдельных объектов.

Отображение разных категорий данных

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

Создание отдельных источников:

const accidentsSource = new VectorSource();
const trafficSource = new VectorSource();

Создание слоёв:

const accidentsHeatmap = new Heatmap({
    source: accidentsSource
});

const trafficHeatmap = new Heatmap({
    source: trafficSource
});

Для каждого слоя можно использовать собственную палитру:

gradient: [
    '#ffff00',
    '#ff0000'
]

и

gradient: [
    '#00ffff',
    '#0000ff'
]

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


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

Допустим, имеется набор координат пользователей интернет-магазина.

Каждая запись содержит:

{
    longitude: 37.62,
    latitude: 55.75,
    visits: 250
}

Создание объекта:

const feature = new Feature({
    geometry: new Point(
        fromLonLat([
            item.longitude,
            item.latitude
        ])
    )
});

feature.set(
    'weight',
    item.visits / 1000
);

Настройка слоя:

const heatmapLayer = new Heatmap({
    source: source,
    radius: 25,
    blur: 20,
    weight: feature =>
        Math.min(
            feature.get('weight'),
            1
        )
});

Результат позволяет быстро определить:

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

Ограничения тепловых карт

Несмотря на высокую наглядность, тепловые карты имеют ряд особенностей:

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

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