В интерактивных картах на базе Mapbox GL JS изображения используются на нескольких уровнях: как иконки маркеров, элементы символических слоёв, фоновые растровые тайлы и графика в пользовательских интерфейсах поверх карты. Любое изображение в таком контексте требует текстового описания, которое выполняет сразу несколько функций: обеспечивает доступность, улучшает поддержку поисковых систем, облегчает отладку и повышает семантическую ясность интерфейса.
Текстовое описание изображения не ограничивается HTML-атрибутом
alt. В Mapbox GL JS оно также проявляется в виде
идентификаторов иконок, метаданных слоёв, подписей в всплывающих окнах и
структурированных свойств GeoJSON.
При работе с изображениями, которые отображаются вне контекста canvas карты (например, в пользовательских компонентах интерфейса), применяется стандартный механизм HTML-доступности:
<img src="marker.png" alt="Ресторан на углу улицы с террасой">
В картах же основная визуализация происходит внутри WebGL, поэтому
прямой alt отсутствует. Вместо него используется концепция
семантических заменителей:
properties.title,
properties.description)Popup)text-field,
icon-text-fit)Пример использования описания через свойства данных:
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [69.5901, 42.3417]
},
properties: {
name: "Городской парк",
description: "Зелёная зона с пешеходными дорожками и фонтаном"
}
}
Эти поля затем используются для генерации описаний в интерфейсе, заменяя необходимость прямого alt-текста.
В Mapbox GL JS маркеры могут быть реализованы двумя способами:
new mapboxgl.Marker())symbol layer)DOM-маркеры позволяют использовать HTML-элементы, где описание изображения задаётся напрямую:
const el = document.createElement('div');
el.className = 'marker';
const img = document.createElement('img');
img.src = 'restaurant.png';
img.alt = 'Маркер ресторана';
el.appendChild(img);
new mapboxgl.Marker(el)
.setLngLat([69.5901, 42.3417])
.addTo(map);
В случае символических слоёв описание изображения переносится в метаданные и идентификаторы:
map.addLayer({
id: 'restaurants',
type: 'symbol',
source: 'places',
layout: {
'icon-image': 'restaurant-15',
'text-field': ['get', 'name'],
'text-offset': [0, 1.2]
}
});
Здесь визуальная иконка (icon-image) не имеет текстового
описания напрямую, но её семантика задаётся через:
icon-image и
spriteИконки в Mapbox GL JS обычно хранятся в sprite-атласе. Каждое изображение имеет идентификатор, который фактически выступает как его описание в системе.
map.loadImage('/icons/cafe.png', (error, image) => {
if (error) throw error;
map.addImage('cafe-icon', image);
map.addLayer({
id: 'cafes',
type: 'symbol',
source: 'cafes-data',
layout: {
'icon-image': 'cafe-icon'
}
});
});
В этом контексте cafe-icon выполняет роль семантического
описания изображения. Хотя это не текст для пользователя, оно важно
для:
При использовании sprite-атласа описания изображений становятся частью JSON-структуры:
{
"cafe": {
"x": 0,
"y": 0,
"width": 32,
"height": 32,
"pixelRatio": 2
}
}
Каждый ключ в sprite можно рассматривать как именованное описание визуального элемента.
addImageМетод addImage позволяет регистрировать изображения в
стиле карты. Здесь важную роль играет имя изображения, которое
фактически является его описанием в системе.
map.addImage('hospital-icon', image, {
sdf: true
});
Имя 'hospital-icon' выполняет несколько функций:
icon-imageПри большом количестве иконок становится критичным соблюдение единообразной системы именования:
poi-restaurantpoi-hoteltransport-bushealth-hospitalТакая структура заменяет необходимость текстовых описаний на уровне UI.
Popup-компоненты часто содержат изображения, где текстовое описание становится обязательным элементом разметки:
new mapboxgl.Popup()
.setLngLat([69.5901, 42.3417])
.setHTML(`
<div>
<img src="park.jpg" alt="Парк с фонтаном и аллеями">
<h3>Городской парк</h3>
<p>Зона отдыха в центре города</p>
</div>
`)
.addTo(map);
Здесь описание изображения влияет на:
В более сложных случаях описание формируется динамически:
function createPopup(feature) {
return new mapboxgl.Popup().setHTML(`
<img src="${feature.properties.image}"
alt="${feature.properties.description}">
<p>${feature.properties.name}</p>
`);
}
Текстовые описания изображений часто извлекаются из структуры данных GeoJSON. Это позволяет централизовать управление семантикой:
map.on('click', 'places', (e) => {
const feature = e.features[0];
new mapboxgl.Popup()
.setLngLat(feature.geometry.coordinates)
.setHTML(`
<img src="${feature.properties.image}"
alt="${feature.properties.alt}">
<strong>${feature.properties.name}</strong>
<p>${feature.properties.description}</p>
`)
.addTo(map);
});
При таком подходе изображение и его описание становятся частью единой модели данных, а не UI-логики.
В style specification Mapbox изображения часто не существуют как DOM-элементы. Их описание распределяется между несколькими уровнями:
icon-image)layer.id)text-field)filter)Пример семантически насыщенного слоя:
map.addLayer({
id: 'tourism-points',
type: 'symbol',
source: 'tourism',
layout: {
'icon-image': ['get', 'icon'],
'text-field': ['get', 'title'],
'text-size': 12
}
});
Здесь визуальная и текстовая части связаны через единый набор данных, что позволяет избегать дублирования описаний.
При работе с тысячами точек важно избегать избыточных или дублирующихся описаний изображений. Основные подходы:
text-fieldpropertiesПример шаблона:
'text-field': [
'format',
['get', 'name'], {},
'\n',
['get', 'category'], { 'font-scale': 0.8 }
]
Это позволяет формировать компактные, но информативные текстовые описания без перегрузки интерфейса.
Часто встречаются следующие проблемы:
alt у DOM-маркеровicon1,
img2)Особенно критичной является ситуация, когда изображение присутствует в интерфейсе, но не имеет связанного текстового описания ни на одном уровне представления.