Интерактивные веб-карты в Mapbox GL JS опираются на WebGL-рендеринг, где визуальная сцена формируется динамически: слои, стили, маркеры и подписи отрисовываются на canvas. Такая модель обеспечивает высокую производительность и гибкость, но одновременно создаёт фундаментальную проблему доступности: содержимое карты не представлено в виде семантического HTML и не интерпретируется напрямую вспомогательными технологиями.
Текстовая альтернатива карты — это не один элемент, а набор параллельных представлений геоданных, которые обеспечивают равнозначное понимание информации без визуального восприятия.
Ключевые цели текстовых альтернатив:
Mapbox GL JS не предоставляет полноценного DOM-представления карты, однако позволяет обогащать контейнер карты семантическими атрибутами.
Основной контейнер карты (map.getContainer()) и canvas
(map.getCanvas()) требуют явного описания роли:
map.getCanvas().setAttribute('role', 'img');
map.getCanvas().setAttribute('aria-label', 'Интерактивная карта с объектами инфраструктуры и дорогами');
Однако одиночный aria-label не решает задачу полноценно,
поскольку карта является динамической и многослойной. Поэтому
применяется стратегия дополнительного текстового слоя,
который обновляется синхронно с состоянием карты.
Любая карта в Mapbox GL JS имеет изменяемое состояние:
center)zoom)pitch)bearing)Эти параметры могут быть преобразованы в текстовое описание.
Пример структурированного представления состояния:
function describeMapState(map) {
const center = map.getCenter();
const zoom = map.getZoom();
return `
Координаты центра: ${center.lng.toFixed(4)}, ${center.lat.toFixed(4)}.
Масштаб: ${zoom.toFixed(2)}.
Поворот: ${map.getBearing()} градусов.
Наклон: ${map.getPitch()} градусов.
`;
}
Такой текст используется для обновления aria-live
региона.
Поскольку карта изменяется без перезагрузки страницы, требуется механизм уведомления о изменениях.
<div id="map-description" aria-live="polite"></div>
Обновление содержимого:
function updateDescription(map) {
const description = describeMapState(map);
document.getElementById('map-description').textContent = description;
}
События синхронизации:
moveendzoomendrotatepitchendmap.on('moveend', () => updateDescription(map));
map.on('zoomend', () => updateDescription(map));
map.on('rotate', () => updateDescription(map));
Такой подход превращает визуальные изменения в последовательный текстовый поток.
Географические объекты, отображаемые на карте, могут быть представлены в виде текстового списка. Это один из наиболее устойчивых способов обеспечения доступности.
Каждый объект слоя Symbol Layer или GeoJSON источника может быть преобразован в строку:
Пример преобразования:
function extractFeatures(map) {
const features = map.queryRenderedFeatures();
return features.map(f => {
const name = f.properties?.name || 'Без названия';
const type = f.layer?.type || 'объект';
const coords = f.geometry?.coordinates;
return `${name}, тип: ${type}, координаты: ${coords}`;
});
}
Результат может быть размещён в отдельном DOM-контейнере:
<ul id="map-feature-list"></ul>
В Mapbox GL JS стилизация слоёв определяет визуальное представление, но не их семантику. Для доступности требуется отдельное описание каждого слоя.
Примеры текстового описания слоёв:
function describeLayers(map) {
const layers = map.getStyle().layers;
return layers.map(layer => {
return `Слой: ${layer.id}, тип: ${layer.type}`;
});
}
При необходимости более детализированного описания учитываются фильтры и источники данных:
sourcesource-layerfilterМаркер в Mapbox GL JS часто представлен DOM-элементом или символом в
слое symbol. Для экранных читалок требуется явное текстовое
представление.
Структура альтернативы:
function describeMarker(feature) {
const name = feature.properties.name;
const category = feature.properties.category;
return `Объект ${name}, категория ${category}`;
}
Для групп объектов формируется список:
<div aria-label="Список объектов на карте">
<!-- текстовые элементы -->
</div>
Линейные геометрии (LineString) требуют последовательного описания узлов и логики движения.
Пример описания маршрута:
function describeRoute(feature) {
const coords = feature.geometry.coordinates;
const start = coords[0];
const end = coords[coords.length - 1];
return `
Маршрут начинается в ${start}.
Заканчивается в ${end}.
Количество сегментов: ${coords.length}.
`;
}
Такое представление заменяет визуальную линию последовательностью точек.
В средах, где WebGL недоступен, Mapbox GL JS может не отрисовать карту. В таких случаях применяется статическая или текстовая замена.
Стратегии fallback:
Пример переключения:
if (!mapboxgl.supported()) {
renderTextualMap();
}
Таблицы обеспечивают структурированное отображение информации:
| Объект | Тип | Широта | Долгота |
|---|---|---|---|
| A | POI | 51.5 | 0.12 |
Генерация:
function toTable(features) {
return features.map(f => ({
name: f.properties.name,
type: f.geometry.type,
lat: f.geometry.coordinates[1],
lng: f.geometry.coordinates[0]
}));
}
Popup в Mapbox GL JS содержит HTML-контент, но для доступности требуется параллельная текстовая репрезентация.
Пример:
function popupToText(properties) {
return `
Название: ${properties.name}
Описание: ${properties.description}
`;
}
Для синхронизации используется зеркальный DOM-контейнер:
<div id="popup-text-alternative"></div>
Альтернативой визуальному взаимодействию становится навигация через список состояний:
Пример структуры:
Регион: Карагандинская область
Объекты: 42
Активный слой: дороги
Выбранный объект: отсутствует
Mapbox GL JS отделяет геоданные от DOM, что создаёт архитектурную особенность: визуальная сцена не является источником семантики. Поэтому текстовые альтернативы строятся не на DOM-инспекции, а на исходных данных GeoJSON и состоянии карты.
Основные источники текстовой информации:
GeoJSON sourcemap.querySourceFeaturesmap.queryRenderedFeaturesmap.getCenter(), getZoom(),
getBearing()Критически важным аспектом является синхронизация:
Несоответствие между визуальной и текстовой моделью приводит к разрыву доступности, при котором вспомогательные технологии получают устаревшие данные.
Полноценная текстовая альтернатива может быть организована как многоуровневая модель:
Такое разделение позволяет формировать как краткое, так и детализированное представление без перегрузки информации.
В некоторых сценариях карта может быть полностью представлена как документ:
Такой формат заменяет интерактивность последовательной структурой, пригодной для чтения и анализа без визуального слоя.