24 марта 2025 в 21.30 Александра Реквисова / «Зеркало»
В прошедшие выходные на выездах из Беларуси у границ с Польшей и Литвой снова выросли очереди из легковых автомобилей (а кое-где — и автобусов). «Зеркало» проанализировало официальные данные и нашло дни и часы, в которые шанс «застрять» на границе самый большой, а также периоды, когда можно проскочить быстрее всего.
Изображение носит иллюстративный характер. Проверка легкового автомобиля на пункте пропуска в Мядининкае, Литва. Фото: Reuters
Как мы считали?
Госпогранкомитет Беларуси каждые два часа публикует данные о том, сколько автомобилей ожидает выезда из страны. Мы проанализировали информацию ГПК за последние четыре недели по каждому из четырех пунктов пропуска, открытых для легковушек и автобусов:
«Брест» — на границе с Польшей,
«Каменный Лог» и «Бенякони» — на границе с Литвой,
«Григоровщина» — на границе с Латвией.
Для «Григоровщины» анализ получился самым коротким: за последние недели в данных ГПК значились только нули. Похожая ситуация с автобусами на пунктах пропуска в Литву — почти все время очереди для них не фиксируются.
«Брест»
«Брест» — пункт пропуска с самой напряженной ситуацией, очередь на выезд там есть почти всегда. С октября 2024 года в «Бресте» действует новая система: водители легковых автомобилей не ждут на дороге, а могут уехать после регистрации и вернуться, когда подойдет пора пересекать границу. Следить за положением автомобиля в виртуальной очереди можно через специальный сайт (и там же — оценить, сколько примерно осталось ждать).
Судя по данным ГПК, последние четыре недели очереди из легковых авто были не такими большими, а в какие-то моменты их даже не было (например, вечером 8 марта и утром 9-го). Однако в прошедшие несколько дней они только росли.
На основании данных за последние четыре недели мы подсчитали самое «горячее» время. Это вечер воскресенья , особенно после 18 часов. В среднем в эти часы проезда ожидает по 500−600 легковых автомобилей.
Самые «легкие» дни для выезда из Беларуси через «Брест» — пятница и суббота . В эти периоды в очереди обычно около 200 авто.
Администраторы зоны ожидания делятся информацией о том, сколько авто оформлено за сутки. Так можно пересчитать количество машин в часы ожидания. К примеру, на 20.00 проезда ждут 1130 легковушек, а за последние сутки оформили 464. Значит, если регистрироваться сейчас, то на проезд уйдет примерно 2,4 суток.
Очереди из автобусов на выезде через «Брест» есть не всегда. «Худшее» время для них — все воскресенье . В среднем на протяжении этого дня в очереди постоянно стоит около 10 автобусов. Час пик — вечер, особенно после 22 часов.
А вот лучшие часы — в будние дни. Например, в 8 утра во вторник, среду или четверг особого ажиотажа в автобусной очереди не наблюдается.
«Каменный Лог»
Больше всего желающих выехать из Беларуси на легковых авто через «Каменный Лог» — вечером воскресенья . После 18 часов на выезд скапливается обычно 40−50 авто.
Еще один пик числа выезжающих — вторая половина четверга , очереди тоже растягиваются, как правило, на 40−50 легковушек. А вот наименее нагруженный день на этом пропускном пункте — суббота, почти весь день очереди нулевые.
Очереди из автобусов в «Каменном Логе», напомним, почти не фиксируются. Судя по отчетам путешественников в тематических чатах, автобусы сейчас проходят границу через «Каменный Лог» примерно за час-два.
«Бенякони»
В этом пункте пропуска очереди легковушек сравнительно небольшие. «Худшее» время — это снова же вечер воскресенья , когда в среднем выезда ожидает около 20−25 авто. Небольшой пик очередей также есть в четверг во второй половине дня , тогда ожидают выезда по 10−15 автомобилей.
Лучшее время — вся суббота , а также все утренние часы — есть шанс, что ждать в принципе не придется.
Очереди из автобусов в пункте пропуска «Бенякони» также не фиксируются. Путешественники в тематических чатах делятся, что пройти досмотры получается примерно за 2−3 часа.
Время: ${d.date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}
В очереди: ${d.count}`
};
// Объединяем настройки по умолчанию с пользовательскими
const cfg = { ...defaultConfig, ...config };
// Находим контейнер
const container = d3.select(`#${containerId}`);
container.html(''); // Очищаем контейнер
// Добавляем заголовок
container.append('div')
.attr('class', 'heatmap-title')
.style('font-size', '18px')
.style('font-weight', 'bold')
.style('margin-bottom', '10px')
.style('text-align', 'center')
.text(cfg.title);
// Определяем размеры в зависимости от ширины экрана
const containerWidth = container.node().getBoundingClientRect().width;
const isMobile = containerWidth <= cfg.mobileBreakpoint;
const width = containerWidth - 30; // Учитываем внутренние отступы контейнера
const height = cfg.height;
// Создаем SVG элемент
const svg = container.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', `translate(${cfg.margin.left},${cfg.margin.top})`);
// Задаем размеры графика
const chartWidth = width - cfg.margin.left - cfg.margin.right;
const chartHeight = height - cfg.margin.top - cfg.margin.bottom;
// Создаем шкалы
const x = d3.scaleTime()
.domain(d3.extent(data, d => d.date))
.range([0, chartWidth]);
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.count) * 1.1]) // Добавляем 10% сверху для наглядности
.range([chartHeight, 0]);
// Добавляем оси
svg.append('g')
.attr('transform', `translate(0,${chartHeight})`)
.style('font-size', isMobile ? '10px' : '12px')
.call(d3.axisBottom(x)
.ticks(isMobile ? 5 : 10)
.tickFormat(d => {
const formatDay = d3.timeFormat('%d.%m');
return formatDay(d);
}));
svg.append('g')
.style('font-size', isMobile ? '10px' : '12px')
.call(d3.axisLeft(y));
// Добавляем сетку
svg.append('g')
.attr('class', 'grid')
.attr('transform', `translate(0,${chartHeight})`)
.style('stroke', cfg.gridColor)
.style('stroke-opacity', 0.3)
.style('shape-rendering', 'crispEdges')
.call(d3.axisBottom(x)
.ticks(10)
.tickSize(-chartHeight)
.tickFormat(''));
svg.append('g')
.attr('class', 'grid')
.style('stroke', cfg.gridColor)
.style('stroke-opacity', 0.3)
.style('shape-rendering', 'crispEdges')
.call(d3.axisLeft(y)
.ticks(10)
.tickSize(-chartWidth)
.tickFormat(''));
// Создаем всплывающую подсказку (tooltip)
const tooltip = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('position', 'absolute')
.style('background-color', 'rgba(255, 255, 255, 0.9)')
.style('border', '1px solid #ddd')
.style('padding', '10px')
.style('border-radius', '4px')
.style('pointer-events', 'none')
.style('font-size', '14px')
.style('box-shadow', '0 2px 4px rgba(0, 0, 0, 0.1)')
.style('z-index', '10')
.style('max-width', '200px')
.style('display', 'none');
// Создаем линию
const line = d3.line()
.x(d => x(d.date))
.y(d => y(d.count))
.curve(d3.curveMonotoneX); // Сглаживаем линию
// Добавляем область под линией
svg.append('path')
.datum(data)
.attr('fill', cfg.lineColor)
.attr('fill-opacity', 0.1)
.attr('d', d3.area()
.x(d => x(d.date))
.y0(chartHeight)
.y1(d => y(d.count))
.curve(d3.curveMonotoneX));
// Добавляем линию
svg.append('path')
.datum(data)
.attr('fill', 'none')
.attr('stroke', cfg.lineColor)
.attr('stroke-width', 2)
.attr('d', line);
// Добавляем точки для интерактивности
const dots = svg.selectAll('.dot')
.data(data)
.enter()
.append('circle')
.attr('class', 'dot')
.attr('cx', d => x(d.date))
.attr('cy', d => y(d.count))
.attr('r', 3)
.attr('fill', cfg.lineColor)
.style('opacity', 0) // Скрываем точки по умолчанию
.on('mouseover', function(event, d) {
d3.select(this)
.transition()
.duration(100)
.attr('r', 5)
.style('opacity', 1);
tooltip
.style('display', 'block')
.html(cfg.tooltipFormat(d))
.style('left', (event.pageX + 10) + 'px')
.style('top', (event.pageY - 28) + 'px');
})
.on('mousemove', function(event, d) {
tooltip
.style('left', (event.pageX + 10) + 'px')
.style('top', (event.pageY - 28) + 'px');
})
.on('mouseout', function() {
d3.select(this)
.transition()
.duration(100)
.attr('r', 3)
.style('opacity', 0);
tooltip
.style('display', 'none');
});
// Добавляем область для отслеживания мыши по всему графику
svg.append('rect')
.attr('width', chartWidth)
.attr('height', chartHeight)
.style('fill', 'none')
.style('pointer-events', 'all')
.on('mousemove', function(event) {
const [mouseX] = d3.pointer(event);
const x0 = x.invert(mouseX);
// Находим ближайшую точку
let i = d3.bisector(d => d.date).left(data, x0, 1);
const d0 = data[i - 1];
const d1 = data[i];
if (!d0 || !d1) return;
const d = x0 - d0.date > d1.date - x0 ? d1 : d0;
// Отображаем точку и тултип
dots.style('opacity', 0);
dots.filter(dot => dot === d)
.style('opacity', 1)
.attr('r', 5);
tooltip
.style('display', 'block')
.html(cfg.tooltipFormat(d))
.style('left', (event.pageX + 10) + 'px')
.style('top', (event.pageY - 28) + 'px');
})
.on('mouseout', function() {
dots.style('opacity', 0).attr('r', 3);
tooltip.style('display', 'none');
});
// Добавляем подписи осей
svg.append('text')
.attr('transform', `translate(${chartWidth / 2}, ${chartHeight + 40})`)
.style('text-anchor', 'middle')
.style('font-size', isMobile ? '12px' : '14px')
.text('Дата');
svg.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', -40)
.attr('x', -chartHeight / 2)
.style('text-anchor', 'middle')
.style('font-size', isMobile ? '12px' : '14px')
.text('Количество автомобилей в очереди');
// Функция для обновления размеров при изменении размера окна
function updateDimensions() {
const newContainerWidth = container.node().getBoundingClientRect().width;
const newIsMobile = newContainerWidth <= cfg.mobileBreakpoint;
if (newIsMobile !== isMobile || Math.abs(newContainerWidth - containerWidth) > 50) {
// Если изменился тип устройства или значительно изменилась ширина, перерисовываем график
createLineChart(containerId, data, config);
}
}
// Слушаем изменение размера окна
window.addEventListener('resize', updateDimensions);
}
// Создаем линейный график после загрузки страницы
document.addEventListener('DOMContentLoaded', function() {
createLineChart('linechart-container', brestLineData, {
title: 'Динамика очередей в пункте пропуска «Брест» (февраль-март 2025 г.)',
lineColor: '#2a5a8a',
tooltipFormat: (d) => {
const dateFormat = new Intl.DateTimeFormat('ru', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
});
const timeFormat = new Intl.DateTimeFormat('ru', {
hour: '2-digit',
minute: '2-digit'
});
return `Дата: ${dateFormat.format(d.date)}
Время: ${timeFormat.format(d.date)}
В очереди: ${d.count} авто`;
}
});
});
// Глобальный объект для хранения всех тултипов
const tooltips = {};
// Функция для создания тепловой карты
function createHeatmap(containerId, data, config) {
// Настройки по умолчанию
const defaultConfig = {
title: 'Тепловая карта',
width: 720,
mobileBreakpoint: 767,
height: 400,
margin: { top: 80, right: 25, bottom: 50, left: 60 },
colors: ['#1a9850', '#66bd63', '#a6d96a', '#d9ef8b', '#fee08b', '#fdae61', '#f46d43', '#d73027'],
tooltipFormat: (d) => `${d.x}: ${d.y}
В очереди: ${d.value}`
};
// Объединяем настройки по умолчанию с пользовательскими
const cfg = { ...defaultConfig, ...config };
// Находим контейнер
const container = d3.select(`#${containerId}`);
container.html(''); // Очищаем контейнер
// Добавляем заголовок
container.append('div')
.attr('class', 'heatmap-title')
.style('font-size', '18px')
.style('font-weight', 'bold')
.style('margin-bottom', '10px')
.style('text-align', 'center')
.text(cfg.title);
// Определяем размеры в зависимости от ширины экрана
const containerWidth = container.node().getBoundingClientRect().width;
const isMobile = containerWidth <= cfg.mobileBreakpoint;
const width = containerWidth - 30; // Учитываем внутренние отступы контейнера
const cellWidth = Math.floor((width - cfg.margin.left - cfg.margin.right) / data.xLabels.length);
const cellHeight = Math.floor((cfg.height - cfg.margin.top - cfg.margin.bottom) / data.yLabels.length);
const height = cellHeight * data.yLabels.length + cfg.margin.top + cfg.margin.bottom;
// Создаем SVG элемент
const svg = container.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', `translate(${cfg.margin.left},${cfg.margin.top})`);
// Создаем шкалы
const x = d3.scaleBand()
.range([0, width - cfg.margin.left - cfg.margin.right])
.domain(data.xLabels)
.padding(0.05);
const y = d3.scaleBand()
.range([0, height - cfg.margin.top - cfg.margin.bottom])
.domain(data.yLabels)
.padding(0.05);
// Определяем цветовую шкалу
const minValue = d3.min(data.values, d => d.value);
const maxValue = d3.max(data.values, d => d.value);
const colorScale = d3.scaleQuantile()
.domain([minValue, maxValue])
.range(cfg.colors);
// Добавляем оси
svg.append('g')
.style('font-size', isMobile ? '10px' : '12px')
.call(d3.axisTop(x))
.selectAll('text')
.style('text-anchor', 'start')
.attr('dx', '.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-45)');
svg.append('g')
.style('font-size', isMobile ? '10px' : '12px')
.call(d3.axisLeft(y));
// Создаем уникальный идентификатор для тултипа
const tooltipId = `tooltip-${containerId}`;
// Создаем всплывающую подсказку, если её ещё нет
if (!tooltips[tooltipId]) {
tooltips[tooltipId] = d3.select('body').append('div')
.attr('class', 'tooltip')
.attr('id', tooltipId)
.style('max-height', '100px') // Ограничиваем высоту
.style('overflow-y', 'auto'); // Добавляем прокрутку при необходимости
}
const tooltip = tooltips[tooltipId];
// Добавляем ячейки
svg.selectAll()
.data(data.values)
.enter()
.append('rect')
.attr('x', d => x(d.x))
.attr('y', d => y(d.y))
.attr('width', x.bandwidth())
.attr('height', y.bandwidth())
.attr('class', 'cell')
.style('fill', d => colorScale(d.value))
.on('mouseover', function(event, d) {
tooltip
.style('display', 'block')
.html(cfg.tooltipFormat(d))
.style('left', (event.pageX + 10) + 'px')
.style('top', (event.pageY - 28) + 'px');
d3.select(this).style('stroke-width', '2px');
})
.on('mousemove', function(event, d) {
tooltip
.style('left', (event.pageX + 10) + 'px')
.style('top', (event.pageY - 28) + 'px');
})
.on('mouseout', function() {
tooltip
.style('display', 'none');
d3.select(this).style('stroke-width', '1px');
});
// Функция для обновления размеров при изменении размера окна
function updateDimensions() {
const newContainerWidth = container.node().getBoundingClientRect().width;
const newIsMobile = newContainerWidth <= cfg.mobileBreakpoint;
if (newIsMobile !== isMobile || Math.abs(newContainerWidth - containerWidth) > 50) {
// Если изменился тип устройства или значительно изменилась ширина, перерисовываем график
createHeatmap(containerId, data, config);
}
}
// Слушаем изменение размера окна
window.addEventListener('resize', updateDimensions);
// Возвращаем функцию для удаления обработчика
return function cleanup() {
window.removeEventListener('resize', updateDimensions);
};
}
// Данные для пункта пропуска "Брест"
const brestData = {
xLabels: ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье'],
yLabels: ['0:00', '2:00', '4:00', '6:00', '8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'],
values: [
// Понедельник
{x: 'Понедельник', y: '0:00', value: 475},
{x: 'Понедельник', y: '2:00', value: 461.25},
{x: 'Понедельник', y: '4:00', value: 415},
{x: 'Понедельник', y: '6:00', value: 391.25},
{x: 'Понедельник', y: '8:00', value: 351.25},
{x: 'Понедельник', y: '10:00', value: 332.5},
{x: 'Понедельник', y: '12:00', value: 348.75},
{x: 'Понедельник', y: '14:00', value: 367.5},
{x: 'Понедельник', y: '16:00', value: 391.25},
{x: 'Понедельник', y: '18:00', value: 415},
{x: 'Понедельник', y: '20:00', value: 427.5},
{x: 'Понедельник', y: '22:00', value: 425},
// Вторник
{x: 'Вторник', y: '0:00', value: 416.75},
{x: 'Вторник', y: '2:00', value: 398},
{x: 'Вторник', y: '4:00', value: 368.75},
{x: 'Вторник', y: '6:00', value: 341.25},
{x: 'Вторник', y: '8:00', value: 317.5},
{x: 'Вторник', y: '10:00', value: 297.5},
{x: 'Вторник', y: '12:00', value: 308.75},
{x: 'Вторник', y: '14:00', value: 300},
{x: 'Вторник', y: '16:00', value: 290},
{x: 'Вторник', y: '18:00', value: 308.75},
{x: 'Вторник', y: '20:00', value: 311.25},
{x: 'Вторник', y: '22:00', value: 347.5},
// Среда
{x: 'Среда', y: '0:00', value: 371.25},
{x: 'Среда', y: '2:00', value: 375.25},
{x: 'Среда', y: '4:00', value: 354.5},
{x: 'Среда', y: '6:00', value: 320},
{x: 'Среда', y: '8:00', value: 292.5},
{x: 'Среда', y: '10:00', value: 280},
{x: 'Среда', y: '12:00', value: 282.5},
{x: 'Среда', y: '14:00', value: 283.75},
{x: 'Среда', y: '16:00', value: 278.75},
{x: 'Среда', y: '18:00', value: 263.75},
{x: 'Среда', y: '20:00', value: 297.5},
{x: 'Среда', y: '22:00', value: 323.75},
// Четверг
{x: 'Четверг', y: '0:00', value: 360},
{x: 'Четверг', y: '2:00', value: 362.25},
{x: 'Четверг', y: '4:00', value: 344.75},
{x: 'Четверг', y: '6:00', value: 323.75},
{x: 'Четверг', y: '8:00', value: 303.75},
{x: 'Четверг', y: '10:00', value: 285},
{x: 'Четверг', y: '12:00', value: 273.75},
{x: 'Четверг', y: '14:00', value: 272.5},
{x: 'Четверг', y: '16:00', value: 277.5},
{x: 'Четверг', y: '18:00', value: 291.75},
{x: 'Четверг', y: '20:00', value: 298.75},
{x: 'Четверг', y: '22:00', value: 308.75},
// Пятница
{x: 'Пятница', y: '0:00', value: 327.5},
{x: 'Пятница', y: '2:00', value: 326.75},
{x: 'Пятница', y: '4:00', value: 310.25},
{x: 'Пятница', y: '6:00', value: 270},
{x: 'Пятница', y: '8:00', value: 240.25},
{x: 'Пятница', y: '10:00', value: 235},
{x: 'Пятница', y: '12:00', value: 226.25},
{x: 'Пятница', y: '14:00', value: 212.5},
{x: 'Пятница', y: '16:00', value: 208.75},
{x: 'Пятница', y: '18:00', value: 212.25},
{x: 'Пятница', y: '20:00', value: 207},
{x: 'Пятница', y: '22:00', value: 236.75},
// Суббота
{x: 'Суббота', y: '0:00', value: 243.5},
{x: 'Суббота', y: '2:00', value: 244.25},
{x: 'Суббота', y: '4:00', value: 233},
{x: 'Суббота', y: '6:00', value: 195.75},
{x: 'Суббота', y: '8:00', value: 181.25},
{x: 'Суббота', y: '10:00', value: 186.25},
{x: 'Суббота', y: '12:00', value: 196.25},
{x: 'Суббота', y: '14:00', value: 220},
{x: 'Суббота', y: '16:00', value: 242.5},
{x: 'Суббота', y: '18:00', value: 268.75},
{x: 'Суббота', y: '20:00', value: 285},
{x: 'Суббота', y: '22:00', value: 302.5},
// Воскресенье
{x: 'Воскресенье', y: '0:00', value: 312.5},
{x: 'Воскресенье', y: '2:00', value: 303},
{x: 'Воскресенье', y: '4:00', value: 293.5},
{x: 'Воскресенье', y: '6:00', value: 282.5},
{x: 'Воскресенье', y: '8:00', value: 286.25},
{x: 'Воскресенье', y: '10:00', value: 316.25},
{x: 'Воскресенье', y: '12:00', value: 375},
{x: 'Воскресенье', y: '14:00', value: 427.5},
{x: 'Воскресенье', y: '16:00', value: 498.75},
{x: 'Воскресенье', y: '18:00', value: 588.75},
{x: 'Воскресенье', y: '20:00', value: 608.75},
{x: 'Воскресенье', y: '22:00', value: 646.25}
]
};
// Создаем тепловую карту для пункта пропуска "Брест"
createHeatmap('heatmap-container-1', brestData, {
title: 'Тепловая карта очередей в пункте пропуска "Брест"',
tooltipFormat: (d) => `День: ${d.x}
Время: ${d.y}
В очереди: ${d.value}`,
colors: ['#1a9850', '#66bd63', '#a6d96a', '#d9ef8b', '#fee08b', '#fdae61', '#f46d43', '#d73027'],
height: 420,
margin: { top: 80, right: 25, bottom: 50, left: 60 }
});
// Функция для добавления новых данных и создания тепловой карты
function addCheckpointData(containerId, checkpointName, data) {
createHeatmap(containerId, data, {
title: `Тепловая карта очередей в пункте пропуска «${checkpointName}»`,
tooltipFormat: (d) => `День: ${d.x}
Время: ${d.y}
В очереди: ${d.value}`,
colors: ['#1a9850', '#66bd63', '#a6d96a', '#d9ef8b', '#fee08b', '#fdae61', '#f46d43', '#d73027'],
height: 420,
margin: { top: 80, right: 25, bottom: 50, left: 60 }
});
}
// Создаем глобальный объект для управления данными пунктов пропуска
window.checkpointManager = {
addCheckpointData: addCheckpointData
};
// Пример данных для других пунктов пропуска (будут заменены реальными данными)
const sampleData = {
xLabels: ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье'],
yLabels: ['0:00', '2:00', '4:00', '6:00', '8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'],
values: [
// Пример данных
{x: 'Понедельник', y: '0:00', value: 100},
{x: 'Понедельник', y: '2:00', value: 120}
// Здесь будут полные данные
]
};
// Данные для пункта пропуска "Брест" (автобусы)
const brestBusData = {
xLabels: ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье'],
yLabels: ['0:00', '2:00', '4:00', '6:00', '8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'],
values: [
// 0:00
{x: 'Понедельник', y: '0:00', value: 5},
{x: 'Вторник', y: '0:00', value: 0.25},
{x: 'Среда', y: '0:00', value: 1.25},
{x: 'Четверг', y: '0:00', value: 0},
{x: 'Пятница', y: '0:00', value: 2},
{x: 'Суббота', y: '0:00', value: 1.5},
{x: 'Воскресенье', y: '0:00', value: 9},
// 2:00
{x: 'Понедельник', y: '2:00', value: 4.5},
{x: 'Вторник', y: '2:00', value: 1.25},
{x: 'Среда', y: '2:00', value: 2.75},
{x: 'Четверг', y: '2:00', value: 1.25},
{x: 'Пятница', y: '2:00', value: 2},
{x: 'Суббота', y: '2:00', value: 6},
{x: 'Воскресенье', y: '2:00', value: 11.5},
// 4:00
{x: 'Понедельник', y: '4:00', value: 2},
{x: 'Вторник', y: '4:00', value: 0},
{x: 'Среда', y: '4:00', value: 0},
{x: 'Четверг', y: '4:00', value: 0},
{x: 'Пятница', y: '4:00', value: 1.5},
{x: 'Суббота', y: '4:00', value: 8.5},
{x: 'Воскресенье', y: '4:00', value: 13},
// 6:00
{x: 'Понедельник', y: '6:00', value: 5.5},
{x: 'Вторник', y: '6:00', value: 0},
{x: 'Среда', y: '6:00', value: 0},
{x: 'Четверг', y: '6:00', value: 0},
{x: 'Пятница', y: '6:00', value: 0.75},
{x: 'Суббота', y: '6:00', value: 2.25},
{x: 'Воскресенье', y: '6:00', value: 13.75},
// 8:00
{x: 'Понедельник', y: '8:00', value: 3.5},
{x: 'Вторник', y: '8:00', value: 0},
{x: 'Среда', y: '8:00', value: 0},
{x: 'Четверг', y: '8:00', value: 0},
{x: 'Пятница', y: '8:00', value: 2.5},
{x: 'Суббота', y: '8:00', value: 6.25},
{x: 'Воскресенье', y: '8:00', value: 10.5},
// 10:00
{x: 'Понедельник', y: '10:00', value: 3.5},
{x: 'Вторник', y: '10:00', value: 0.5},
{x: 'Среда', y: '10:00', value: 0},
{x: 'Четверг', y: '10:00', value: 0},
{x: 'Пятница', y: '10:00', value: 2.25},
{x: 'Суббота', y: '10:00', value: 5.5},
{x: 'Воскресенье', y: '10:00', value: 9.25},
// 12:00
{x: 'Понедельник', y: '12:00', value: 4.25},
{x: 'Вторник', y: '12:00', value: 2.25},
{x: 'Среда', y: '12:00', value: 0},
{x: 'Четверг', y: '12:00', value: 1.25},
{x: 'Пятница', y: '12:00', value: 4},
{x: 'Суббота', y: '12:00', value: 4.25},
{x: 'Воскресенье', y: '12:00', value: 8.5},
// 14:00
{x: 'Понедельник', y: '14:00', value: 4.5},
{x: 'Вторник', y: '14:00', value: 2.75},
{x: 'Среда', y: '14:00', value: 1.25},
{x: 'Четверг', y: '14:00', value: 5},
{x: 'Пятница', y: '14:00', value: 5.5},
{x: 'Суббота', y: '14:00', value: 5.75},
{x: 'Воскресенье', y: '14:00', value: 11.5},
// 16:00
{x: 'Понедельник', y: '16:00', value: 2.75},
{x: 'Вторник', y: '16:00', value: 1.5},
{x: 'Среда', y: '16:00', value: 3.25},
{x: 'Четверг', y: '16:00', value: 5.25},
{x: 'Пятница', y: '16:00', value: 3.75},
{x: 'Суббота', y: '16:00', value: 7.5},
{x: 'Воскресенье', y: '16:00', value: 9.75},
// 18:00
{x: 'Понедельник', y: '18:00', value: 2.5},
{x: 'Вторник', y: '18:00', value: 1},
{x: 'Среда', y: '18:00', value: 2},
{x: 'Четверг', y: '18:00', value: 3.75},
{x: 'Пятница', y: '18:00', value: 1.25},
{x: 'Суббота', y: '18:00', value: 8.5},
{x: 'Воскресенье', y: '18:00', value: 11.75},
// 20:00
{x: 'Понедельник', y: '20:00', value: 1},
{x: 'Вторник', y: '20:00', value: 1},
{x: 'Среда', y: '20:00', value: 0.5},
{x: 'Четверг', y: '20:00', value: 0},
{x: 'Пятница', y: '20:00', value: 0},
{x: 'Суббота', y: '20:00', value: 11},
{x: 'Воскресенье', y: '20:00', value: 12},
// 22:00
{x: 'Понедельник', y: '22:00', value: 0.25},
{x: 'Вторник', y: '22:00', value: 0},
{x: 'Среда', y: '22:00', value: 0},
{x: 'Четверг', y: '22:00', value: 0.25},
{x: 'Пятница', y: '22:00', value: 1},
{x: 'Суббота', y: '22:00', value: 9.75},
{x: 'Воскресенье', y: '22:00', value: 15.75}
]
};
// Создаем тепловую карту для пункта пропуска "Брест" (автобусы)
addCheckpointData('heatmap-container-2', 'Брест (автобусы)', brestBusData);
// Данные для пункта пропуска "Каменный Лог"
const kamLlogData = {
xLabels: ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье'],
yLabels: ['0:00', '2:00', '4:00', '6:00', '8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'],
values: [
// 0:00
{x: 'Понедельник', y: '0:00', value: 22.5},
{x: 'Вторник', y: '0:00', value: 7.5},
{x: 'Среда', y: '0:00', value: 0},
{x: 'Четверг', y: '0:00', value: 22.5},
{x: 'Пятница', y: '0:00', value: 25},
{x: 'Суббота', y: '0:00', value: 0},
{x: 'Воскресенье', y: '0:00', value: 2.5},
// 2:00
{x: 'Понедельник', y: '2:00', value: 12.5},
{x: 'Вторник', y: '2:00', value: 3.75},
{x: 'Среда', y: '2:00', value: 0},
{x: 'Четверг', y: '2:00', value: 20},
{x: 'Пятница', y: '2:00', value: 17.5},
{x: 'Суббота', y: '2:00', value: 0},
{x: 'Воскресенье', y: '2:00', value: 0},
// 4:00
{x: 'Понедельник', y: '4:00', value: 7.5},
{x: 'Вторник', y: '4:00', value: 0},
{x: 'Среда', y: '4:00', value: 0},
{x: 'Четверг', y: '4:00', value: 15},
{x: 'Пятница', y: '4:00', value: 10},
{x: 'Суббота', y: '4:00', value: 0},
{x: 'Воскресенье', y: '4:00', value: 0},
// 6:00
{x: 'Понедельник', y: '6:00', value: 7.5},
{x: 'Вторник', y: '6:00', value: 0},
{x: 'Среда', y: '6:00', value: 0},
{x: 'Четверг', y: '6:00', value: 12.5},
{x: 'Пятница', y: '6:00', value: 5},
{x: 'Суббота', y: '6:00', value: 0},
{x: 'Воскресенье', y: '6:00', value: 0},
// 8:00
{x: 'Понедельник', y: '8:00', value: 10},
{x: 'Вторник', y: '8:00', value: 0},
{x: 'Среда', y: '8:00', value: 0},
{x: 'Четверг', y: '8:00', value: 2.5},
{x: 'Пятница', y: '8:00', value: 7.5},
{x: 'Суббота', y: '8:00', value: 0},
{x: 'Воскресенье', y: '8:00', value: 0},
// 10:00
{x: 'Понедельник', y: '10:00', value: 5},
{x: 'Вторник', y: '10:00', value: 0},
{x: 'Среда', y: '10:00', value: 6.25},
{x: 'Четверг', y: '10:00', value: 7.5},
{x: 'Пятница', y: '10:00', value: 7.5},
{x: 'Суббота', y: '10:00', value: 0},
{x: 'Воскресенье', y: '10:00', value: 0},
// 12:00
{x: 'Понедельник', y: '12:00', value: 10},
{x: 'Вторник', y: '12:00', value: 7.5},
{x: 'Среда', y: '12:00', value: 20},
{x: 'Четверг', y: '12:00', value: 22.5},
{x: 'Пятница', y: '12:00', value: 7.5},
{x: 'Суббота', y: '12:00', value: 0},
{x: 'Воскресенье', y: '12:00', value: 12.5},
// 14:00
{x: 'Понедельник', y: '14:00', value: 15},
{x: 'Вторник', y: '14:00', value: 10},
{x: 'Среда', y: '14:00', value: 22.5},
{x: 'Четверг', y: '14:00', value: 40},
{x: 'Пятница', y: '14:00', value: 7.5},
{x: 'Суббота', y: '14:00', value: 0},
{x: 'Воскресенье', y: '14:00', value: 22.5},
// 16:00
{x: 'Понедельник', y: '16:00', value: 20},
{x: 'Вторник', y: '16:00', value: 15},
{x: 'Среда', y: '16:00', value: 20},
{x: 'Четверг', y: '16:00', value: 50},
{x: 'Пятница', y: '16:00', value: 5},
{x: 'Суббота', y: '16:00', value: 0},
{x: 'Воскресенье', y: '16:00', value: 32.5},
// 18:00
{x: 'Понедельник', y: '18:00', value: 17.5},
{x: 'Вторник', y: '18:00', value: 12.5},
{x: 'Среда', y: '18:00', value: 20},
{x: 'Четверг', y: '18:00', value: 45},
{x: 'Пятница', y: '18:00', value: 7.5},
{x: 'Суббота', y: '18:00', value: 3.75},
{x: 'Воскресенье', y: '18:00', value: 47.5},
// 20:00
{x: 'Понедельник', y: '20:00', value: 12.5},
{x: 'Вторник', y: '20:00', value: 7.5},
{x: 'Среда', y: '20:00', value: 17.5},
{x: 'Четверг', y: '20:00', value: 30},
{x: 'Пятница', y: '20:00', value: 5},
{x: 'Суббота', y: '20:00', value: 2.5},
{x: 'Воскресенье', y: '20:00', value: 50},
// 22:00
{x: 'Понедельник', y: '22:00', value: 10},
{x: 'Вторник', y: '22:00', value: 7.5},
{x: 'Среда', y: '22:00', value: 20},
{x: 'Четверг', y: '22:00', value: 27.5},
{x: 'Пятница', y: '22:00', value: 0},
{x: 'Суббота', y: '22:00', value: 2.5},
{x: 'Воскресенье', y: '22:00', value: 45}
]
};
// Создаем тепловую карту для пункта пропуска "Каменный Лог"
addCheckpointData('heatmap-container-3', 'Каменный Лог', kamLlogData);
// Данные для пункта пропуска "Бенякони"
const benyakoniData = {
xLabels: ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье'],
yLabels: ['0:00', '2:00', '4:00', '6:00', '8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'],
values: [
// 0:00
{x: 'Понедельник', y: '0:00', value: 7.5},
{x: 'Вторник', y: '0:00', value: 0},
{x: 'Среда', y: '0:00', value: 0},
{x: 'Четверг', y: '0:00', value: 0},
{x: 'Пятница', y: '0:00', value: 0},
{x: 'Суббота', y: '0:00', value: 0},
{x: 'Воскресенье', y: '0:00', value: 0},
// 2:00
{x: 'Понедельник', y: '2:00', value: 7.5},
{x: 'Вторник', y: '2:00', value: 0},
{x: 'Среда', y: '2:00', value: 0},
{x: 'Четверг', y: '2:00', value: 0},
{x: 'Пятница', y: '2:00', value: 0},
{x: 'Суббота', y: '2:00', value: 0},
{x: 'Воскресенье', y: '2:00', value: 0},
// 4:00
{x: 'Понедельник', y: '4:00', value: 7.5},
{x: 'Вторник', y: '4:00', value: 0},
{x: 'Среда', y: '4:00', value: 0},
{x: 'Четверг', y: '4:00', value: 0},
{x: 'Пятница', y: '4:00', value: 0},
{x: 'Суббота', y: '4:00', value: 0},
{x: 'Воскресенье', y: '4:00', value: 0},
// 6:00
{x: 'Понедельник', y: '6:00', value: 0},
{x: 'Вторник', y: '6:00', value: 0},
{x: 'Среда', y: '6:00', value: 0},
{x: 'Четверг', y: '6:00', value: 0},
{x: 'Пятница', y: '6:00', value: 0},
{x: 'Суббота', y: '6:00', value: 0},
{x: 'Воскресенье', y: '6:00', value: 0},
// 8:00
{x: 'Понедельник', y: '8:00', value: 0},
{x: 'Вторник', y: '8:00', value: 0},
{x: 'Среда', y: '8:00', value: 0},
{x: 'Четверг', y: '8:00', value: 0},
{x: 'Пятница', y: '8:00', value: 0},
{x: 'Суббота', y: '8:00', value: 0},
{x: 'Воскресенье', y: '8:00', value: 0},
// 10:00
{x: 'Понедельник', y: '10:00', value: 0},
{x: 'Вторник', y: '10:00', value: 0},
{x: 'Среда', y: '10:00', value: 0},
{x: 'Четверг', y: '10:00', value: 0},
{x: 'Пятница', y: '10:00', value: 0},
{x: 'Суббота', y: '10:00', value: 0},
{x: 'Воскресенье', y: '10:00', value: 0},
// 12:00
{x: 'Понедельник', y: '12:00', value: 0},
{x: 'Вторник', y: '12:00', value: 0},
{x: 'Среда', y: '12:00', value: 0},
{x: 'Четверг', y: '12:00', value: 2.5},
{x: 'Пятница', y: '12:00', value: 0},
{x: 'Суббота', y: '12:00', value: 0},
{x: 'Воскресенье', y: '12:00', value: 0},
// 14:00
{x: 'Понедельник', y: '14:00', value: 0},
{x: 'Вторник', y: '14:00', value: 0},
{x: 'Среда', y: '14:00', value: 0},
{x: 'Четверг', y: '14:00', value: 10},
{x: 'Пятница', y: '14:00', value: 0},
{x: 'Суббота', y: '14:00', value: 0},
{x: 'Воскресенье', y: '14:00', value: 3.75},
// 16:00
{x: 'Понедельник', y: '16:00', value: 5},
{x: 'Вторник', y: '16:00', value: 0},
{x: 'Среда', y: '16:00', value: 0},
{x: 'Четверг', y: '16:00', value: 15},
{x: 'Пятница', y: '16:00', value: 0},
{x: 'Суббота', y: '16:00', value: 0},
{x: 'Воскресенье', y: '16:00', value: 12.5},
// 18:00
{x: 'Понедельник', y: '18:00', value: 2.5},
{x: 'Вторник', y: '18:00', value: 0},
{x: 'Среда', y: '18:00', value: 0},
{x: 'Четверг', y: '18:00', value: 12.5},
{x: 'Пятница', y: '18:00', value: 0},
{x: 'Суббота', y: '18:00', value: 0},
{x: 'Воскресенье', y: '18:00', value: 22.5},
// 20:00
{x: 'Понедельник', y: '20:00', value: 2.5},
{x: 'Вторник', y: '20:00', value: 0},
{x: 'Среда', y: '20:00', value: 0},
{x: 'Четверг', y: '20:00', value: 10},
{x: 'Пятница', y: '20:00', value: 0},
{x: 'Суббота', y: '20:00', value: 0},
{x: 'Воскресенье', y: '20:00', value: 22.5},
// 22:00
{x: 'Понедельник', y: '22:00', value: 0},
{x: 'Вторник', y: '22:00', value: 6.25},
{x: 'Среда', y: '22:00', value: 0},
{x: 'Четверг', y: '22:00', value: 7.5},
{x: 'Пятница', y: '22:00', value: 0},
{x: 'Суббота', y: '22:00', value: 0},
{x: 'Воскресенье', y: '22:00', value: 25}
]
};
// Создаем тепловую карту для пункта пропуска "Бенякони"
addCheckpointData('heatmap-container-4', 'Бенякони', benyakoniData);
document.addEventListener('DOMContentLoaded', function() {
createLineChart('linechart-container', brestLineData, {
title: 'Динамика очередей в пункте пропуска «Брест» (февраль-март 2025 г.)',
lineColor: '#2a5a8a',
tooltipFormat: (d) => {
const dateFormat = new Intl.DateTimeFormat('ru', { day: '2-digit', month: '2-digit', year: 'numeric' });
const timeFormat = new Intl.DateTimeFormat('ru', { hour: '2-digit', minute: '2-digit' });
return `Дата: ${dateFormat.format(d.date)}
Время: ${timeFormat.format(d.date)}
В очереди: ${d.count} авто`;
}
});
createHeatmap('heatmap-container-1', brestData, {
title: 'Тепловая карта очередей в пункте пропуска «Брест»'
});
createHeatmap('heatmap-container-2', brestBusData, {
title: 'Тепловая карта очередей в пункте пропуска «Брест» (автобусы)'
});
createHeatmap('heatmap-container-3', kamLlogData, {
title: 'Тепловая карта очередей в пункте пропуска «Каменный Лог»'
});
createHeatmap('heatmap-container-4', benyakoniData, {
title: 'Тепловая карта очередей в пункте пропуска «Бенякони»'
});
});