CPU-агрегирующий слой строится вокруг идеи предварительной обработки данных на центральном процессоре с последующей отрисовкой уже агрегированных ячеек. В случае данного слоя исходный набор точек не передаётся напрямую в рендеринг как независимые объекты. Вместо этого пространство разбивается на регулярную сетку, после чего каждая точка попадает в соответствующую ячейку, где происходит накопление метрик: количества объектов, суммы значений, минимумы, максимумы или пользовательские показатели.
Ключевая особенность подхода заключается в том, что вычислительная нагрузка смещается в JavaScript-слой, а WebGL используется преимущественно для визуализации уже готового результата. Это делает поведение предсказуемым и упрощает реализацию сложной агрегационной логики.
Географическое пространство переводится в координатную систему слоя, после чего формируется регулярная сетка фиксированного размера. Размер ячейки задаёт уровень детализации: чем меньше ячейка, тем выше точность отображения распределения данных и тем больше вычислительная нагрузка.
Каждая точка данных проецируется в координаты сетки:
Такой подход исключает необходимость пространственных деревьев для базового случая и позволяет обрабатывать большие массивы данных линейно.
Слой поддерживает расширяемую систему агрегирования. Внутри каждой ячейки могут вычисляться:
При пользовательской агрегации каждая точка может модифицировать аккумулятор ячейки. Это позволяет реализовывать метрики плотности, взвешенные показатели и комбинированные индексы.
Типичная структура аккумулятора:
{
count: 0,
sum: 0,
min: Infinity,
max: -Infinity
}
Обновление состояния при добавлении точки:
function accumulate(cell, point) {
cell.count += 1;
cell.sum += point.value;
cell.min = Math.min(cell.min, point.value);
cell.max = Math.max(cell.max, point.value);
}
После завершения прохода по исходному набору данных формируется массив ячеек. Каждая ячейка становится отдельным примитивом для отрисовки.
Геометрия ячейки обычно представляет собой прямоугольник, соответствующий границам сетки. Высота или цвет зависят от агрегированных значений.
Часто используется нормализация:
Пример преобразования:
const normalized = (cell.sum - min) / (max - min);
const color = getColorFromGradient(normalized);
Данные представляют собой массив объектов с координатами и дополнительными атрибутами. Минимальная форма:
[
{ position: [lng, lat], value: 10 },
{ position: [lng, lat], value: 5 }
]
Координаты обязательно задаются в виде долгота–широта, так как слой опирается на географическую проекцию.
При обработке больших наборов данных важным становится отсутствие лишних преобразований внутри цикла агрегации, так как он выполняется на CPU и чувствителен к сложности операций.
Размер сетки напрямую влияет на характер визуализации. Малые ячейки дают высокую детализацию, но увеличивают количество вычисляемых элементов. Большие ячейки уменьшают нагрузку, но сглаживают распределение.
Типичная зависимость:
Выбор параметра обычно зависит от масштаба карты и плотности данных.
В некоторых реализациях размер ячейки связывается с уровнем зума, что позволяет сохранять стабильную визуальную плотность при масштабировании карты.
Основная нагрузка возникает при пересчёте сетки. Оптимизация достигается несколькими методами:
Дополнительно может применяться кэширование результатов при неизменности входного набора данных. При изменении только визуальных параметров пересчёт агрегации не требуется.
При обновлении набора данных выполняется полная переработка сетки. Алгоритм повторно:
Если обновление затрагивает только часть данных, возможна инкрементальная стратегия, при которой пересчитываются только изменённые ячейки, однако это усложняет реализацию и требует дополнительной индексации.
Отображение агрегированных значений может происходить через несколько каналов:
Часто применяется логарифмическая шкала для компенсации сильного разброса значений плотности.
const scaled = Math.log(cell.count + 1);
Такое преобразование уменьшает доминирование сверхплотных областей и делает распределение более читаемым.
После агрегации в географических координатах выполняется проекция в экранное пространство. Каждая ячейка преобразуется в два треугольника, формирующих прямоугольник.
Буферы вершин содержат:
Этот этап полностью выполняется в WebGL и обеспечивает стабильную частоту кадров даже при большом числе ячеек.
В отличие от визуализации каждого объекта отдельно, сеточный подход уменьшает количество примитивов с O(n) до O(k), где k — число ячеек.
Это приводит к следующим эффектам:
Помимо базового количества точек, ячейки могут хранить произвольные вычисляемые метрики. Это позволяет строить:
Пример раздельного накопления категорий:
if (point.type === 'A') cell.aCount += 1;
if (point.type === 'B') cell.bCount += 1;
Сеточная агрегация вводит ряд ограничений:
При необходимости более адаптивного разбиения используются альтернативные методы пространственной агрегации, однако они требуют более сложных структур данных и увеличивают вычислительную стоимость.
При изменении масштаба происходит перерасчёт проекции точек и, при динамической сетке, пересборка ячеек. В некоторых конфигурациях используется фиксированная сетка в экранных координатах, что позволяет сохранять визуальную стабильность при зуме, но изменяет семантику агрегирования.
В других вариантах сетка привязана к географическим координатам, и при зуме изменяется плотность точек внутри ячеек, что даёт более «картографическое» поведение.
Внутреннее представление обычно включает:
Хэширование ячеек выполняется через комбинацию индексов строки и столбца, что обеспечивает быстрый доступ без сложных пространственных структур.
const key = row * numCols + col;
Смещение агрегации на CPU позволяет:
При этом основным ограничением остаётся пропускная способность JavaScript-цикла при очень больших объёмах данных, где GPU-агрегация или гибридные методы могут быть более эффективными.