Миграция с Google Maps на Mapbox GL JS обычно связана с несколькими факторами:
Google Maps ориентирован на использование готовой картографической платформы с богатым набором сервисов. Mapbox GL JS предоставляет значительно больше возможностей по управлению визуализацией карты и процессом рендеринга.
Архитектура Google Maps строится вокруг объекта карты и набора оверлеев:
const map = new google.maps.Map(
document.getElementById('map'),
{
center: { lat: 55.751244, lng: 37.618423 },
zoom: 10
}
);
Основные сущности:
Каждый объект существует как отдельный экземпляр JavaScript-класса.
Mapbox GL JS использует концепцию:
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12',
center: [37.618423, 55.751244],
zoom: 10
});
Практически любой визуальный объект представляет собой комбинацию источника данных и слоя.
const map = new google.maps.Map(
document.getElementById('map'),
{
center: {
lat: 55.751244,
lng: 37.618423
},
zoom: 12
}
);
mapboxgl.accessToken = 'TOKEN';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v11',
center: [37.618423, 55.751244],
zoom: 12
});
Google Maps:
{
lat: 55.751244,
lng: 37.618423
}
Mapbox GL JS:
[37.618423, 55.751244]
Порядок координат отличается:
Google Maps: lat, lng
Mapbox GL JS: lng, lat
Это одна из наиболее частых причин ошибок после миграции.
google.maps.event.addListenerOnce(
map,
'idle',
() => {
console.log('Map ready');
}
);
map.on('load', () => {
console.log('Map ready');
});
Событие load является аналогом полной готовности карты к
работе.
const marker = new google.maps.Marker({
position: {
lat: 55.751244,
lng: 37.618423
},
map
});
new mapboxgl.Marker()
.setLngLat([37.618423, 55.751244])
.addTo(map);
const marker = new google.maps.Marker({
position: location,
map,
icon: '/images/pin.png'
});
const element = document.createElement('div');
element.className = 'custom-marker';
new mapboxgl.Marker(element)
.setLngLat([37.618423, 55.751244])
.addTo(map);
CSS:
.custom-marker {
width: 32px;
height: 32px;
background-image: url('/images/pin.png');
background-size: contain;
}
Mapbox значительно упрощает создание сложных HTML-маркеров.
const infoWindow =
new google.maps.InfoWindow({
content: '<h3>Москва</h3>'
});
marker.addListener('click', () => {
infoWindow.open(map, marker);
});
const popup = new mapboxgl.Popup()
.setHTML('<h3>Москва</h3>');
new mapboxgl.Marker()
.setLngLat([37.618423, 55.751244])
.setPopup(popup)
.addTo(map);
map.setCenter({
lat: 55.751244,
lng: 37.618423
});
map.setCenter([
37.618423,
55.751244
]);
map.setZoom(14);
map.setZoom(14);
Синтаксис практически идентичен.
map.panTo({
lat: 55.751244,
lng: 37.618423
});
map.flyTo({
center: [37.618423, 55.751244],
zoom: 14
});
Метод flyTo() обеспечивает плавную трехмерную анимацию
перемещения.
map.addListener('click', (event) => {
console.log(
event.latLng.lat(),
event.latLng.lng()
);
});
map.on('click', (event) => {
console.log(
event.lngLat.lat,
event.lngLat.lng
);
});
Структура объекта события различается, поэтому код обработки требует адаптации.
const line =
new google.maps.Polyline({
path: [
{ lat: 55.7, lng: 37.5 },
{ lat: 55.8, lng: 37.7 }
],
map
});
Сначала создается источник:
map.addSource('route', {
type: 'geojson',
data: {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [
[37.5, 55.7],
[37.7, 55.8]
]
}
}
});
Затем слой:
map.addLayer({
id: 'route-line',
type: 'line',
source: 'route',
paint: {
'line-color': '#ff0000',
'line-width': 4
}
});
new google.maps.Polygon({
paths: coordinates,
map
});
map.addSource('polygon', {
type: 'geojson',
data: polygonGeoJSON
});
map.addLayer({
id: 'polygon-fill',
type: 'fill',
source: 'polygon',
paint: {
'fill-color': '#0080ff',
'fill-opacity': 0.5
}
});
Google Maps часто использует собственные структуры данных:
[
{
lat: 55.7,
lng: 37.5
}
]
Основной формат данных:
{
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
37.618423,
55.751244
]
}
}
]
}
Во время миграции рекомендуется максимально перевести пространственные данные в формат GeoJSON.
Преимущества:
Обычно требуется дополнительная библиотека:
new MarkerClusterer({
map,
markers
});
Кластеризация встроена в движок.
map.addSource('points', {
type: 'geojson',
data: geojson,
cluster: true,
clusterRadius: 50
});
Создаются специальные слои кластеров и отдельных точек.
Такой подход лучше масштабируется при работе с десятками и сотнями тысяч объектов.
Настройка выполняется через массив описаний:
styles: [
{
featureType: 'road',
stylers: [
{
color: '#000000'
}
]
}
]
Используется полноценный стиль.
style:
'mapbox://styles/mapbox/dark-v11'
Либо собственный JSON-стиль:
style: customStyle
Стиль управляет:
Каждый маркер является отдельным DOM-объектом.
new google.maps.Marker(...)
При большом количестве объектов производительность начинает снижаться.
Рекомендуется использовать слой символов:
map.addLayer({
id: 'points',
type: 'circle',
source: 'points'
});
Все объекты рендерятся через WebGL.
Это позволяет отображать десятки тысяч элементов одновременно.
new google.maps.visualization.HeatmapLayer({
data: points
});
map.addLayer({
id: 'heatmap',
type: 'heatmap',
source: 'points'
});
Тепловые карты являются встроенным типом слоя.
navigator.geolocation.getCurrentPosition(
position => {
console.log(position.coords);
}
);
Используется тот же браузерный API либо готовый контрол:
map.addControl(
new mapboxgl.GeolocateControl({
trackUserLocation: true
})
);
Обычно требуется дополнительная реализация.
map.addControl(
new mapboxgl.FullscreenControl()
);
zoomControl: true
map.addControl(
new mapboxgl.NavigationControl()
);
Контрол включает:
Одно из важных преимуществ Mapbox GL JS — встроенная поддержка 3D-визуализации.
map.addLayer({
id: 'buildings',
source: 'composite',
'source-layer': 'building',
type: 'fill-extrusion',
paint: {
'fill-extrusion-height': [
'get',
'height'
]
}
});
В Google Maps подобная функциональность существенно ограничена и зависит от используемых сервисов.
Неверно:
[55.751244, 37.618423]
Верно:
[37.618423, 55.751244]
Неверно:
for (const point of points) {
new mapboxgl.Marker()
.setLngLat(point)
.addTo(map);
}
Правильнее использовать:
map.addSource(...)
map.addLayer(...)
Неверно:
map.addLayer(layer);
сразу после создания карты.
Верно:
map.on('load', () => {
map.addLayer(layer);
});
Распространенная ошибка — переносить архитектуру один-в-один:
Marker → Marker
Polyline → Polyline
Polygon → Polygon
Mapbox GL JS требует другого подхода:
Данные → Source → Layer → Style
Наиболее успешная миграция достигается не прямой заменой API-вызовов, а перестройкой приложения вокруг концепций GeoJSON, источников данных и слоев. Именно такая архитектура позволяет в полной мере использовать преимущества WebGL-рендеринга, масштабируемости и гибкой визуализации, лежащие в основе Mapbox GL JS.