mapbox-google-maps-migration by mapbox/mapbox-agent-skills
npx skills add https://github.com/mapbox/mapbox-agent-skills --skill mapbox-google-maps-migration从 Google Maps Platform 迁移到 Mapbox GL JS 的全面指南。提供 API 等效项、模式转换和成功迁移的策略。
.setMap(map) 添加到地图关键见解: Mapbox 将所有内容视为数据 + 样式,而非独立的对象。
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 37.7749, lng: -122.4194 },
zoom: 12,
mapTypeId: 'roadmap' // or 'satellite', 'hybrid', 'terrain'
});
mapboxgl.accessToken = 'YOUR_MAPBOX_TOKEN';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12', // or satellite-v9, outdoors-v12
center: [-122.4194, 37.7749], // [lng, lat] - 注意顺序!
zoom: 12
});
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
主要差异:
{lat, lng},Mapbox 使用 [lng, lat]| Google Maps | Mapbox GL JS | 备注 |
|---|---|---|
map.setCenter(latLng) | map.setCenter([lng, lat]) | 坐标顺序相反 |
map.getCenter() | map.getCenter() | 返回 LngLat 对象 |
map.setZoom(zoom) | map.setZoom(zoom) | 行为相同 |
map.getZoom() | map.getZoom() | 行为相同 |
map.panTo(latLng) | map.panTo([lng, lat]) | 带动画的平移 |
map.fitBounds(bounds) | map.fitBounds([[lng,lat],[lng,lat]]) | 边界格式不同 |
map.setMapTypeId(type) | map.setStyle(styleUrl) | 方法完全不同 |
map.getBounds() | map.getBounds() | 相似 |
| Google Maps | Mapbox GL JS | 备注 |
|---|---|---|
google.maps.event.addListener(map, 'click', fn) | map.on('click', fn) | 语法更简单 |
event.latLng | event.lngLat | 事件属性名称 |
'center_changed' | 'move' / 'moveend' | 事件名称不同 |
'zoom_changed' | 'zoom' / 'zoomend' | 事件名称不同 |
'bounds_changed' | 'moveend' | 无直接等效项 |
'mousemove' | 'mousemove' | 相同 |
'mouseout' | 'mouseleave' | 名称不同 |
Google Maps:
const marker = new google.maps.Marker({
position: { lat: 37.7749, lng: -122.4194 },
map: map,
title: 'San Francisco',
icon: 'custom-icon.png'
});
// 移除标记
marker.setMap(null);
Mapbox GL JS:
// 创建标记
const marker = new mapboxgl.Marker()
.setLngLat([-122.4194, 37.7749])
.setPopup(new mapboxgl.Popup().setText('San Francisco'))
.addTo(map);
// 移除标记
marker.remove();
Google Maps:
const markers = locations.map(
(loc) =>
new google.maps.Marker({
position: { lat: loc.lat, lng: loc.lng },
map: map
})
);
Mapbox GL JS(等效方法):
// 相同的面向对象方法
const markers = locations.map((loc) => new mapboxgl.Marker().setLngLat([loc.lng, loc.lat]).addTo(map));
Mapbox GL JS(数据驱动方法 - 推荐用于 100+ 个点):
// 作为 GeoJSON 源 + 图层添加(使用 WebGL,而非 DOM)
map.addSource('points', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: locations.map((loc) => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [loc.lng, loc.lat] },
properties: { name: loc.name }
}))
}
});
map.addLayer({
id: 'points-layer',
type: 'circle', // 或使用 'symbol' 表示图标
source: 'points',
paint: {
'circle-radius': 8,
'circle-color': '#ff0000'
}
});
性能优势: Google Maps 将所有标记渲染为 DOM 元素(即使使用数据层),当标记数量超过 500 个时会变得缓慢。Mapbox 的圆形和符号图层由 WebGL 渲染,对于大型数据集(1,000-10,000+ 个点)来说速度要快得多。在构建具有大量点的应用程序时,这是一个显著优势。
const infowindow = new google.maps.InfoWindow({
content: '<h3>Title</h3><p>Content</p>'
});
marker.addListener('click', () => {
infowindow.open(map, marker);
});
// 选项 1:附加到标记
const marker = new mapboxgl.Marker()
.setLngLat([-122.4194, 37.7749])
.setPopup(new mapboxgl.Popup().setHTML('<h3>Title</h3><p>Content</p>'))
.addTo(map);
// 选项 2:在图层点击时(用于数据驱动的标记)
map.on('click', 'points-layer', (e) => {
const coordinates = e.features[0].geometry.coordinates.slice();
const description = e.features[0].properties.description;
new mapboxgl.Popup().setLngLat(coordinates).setHTML(description).addTo(map);
});
const polygon = new google.maps.Polygon({
paths: [
{ lat: 37.7749, lng: -122.4194 },
{ lat: 37.7849, lng: -122.4094 },
{ lat: 37.7649, lng: -122.4094 }
],
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35,
map: map
});
map.addSource('polygon', {
type: 'geojson',
data: {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[-122.4194, 37.7749],
[-122.4094, 37.7849],
[-122.4094, 37.7649],
[-122.4194, 37.7749] // 闭合环
]
]
}
}
});
map.addLayer({
id: 'polygon-layer',
type: 'fill',
source: 'polygon',
paint: {
'fill-color': '#FF0000',
'fill-opacity': 0.35
}
});
// 添加轮廓
map.addLayer({
id: 'polygon-outline',
type: 'line',
source: 'polygon',
paint: {
'line-color': '#FF0000',
'line-width': 2,
'line-opacity': 0.8
}
});
const line = new google.maps.Polyline({
path: [
{ lat: 37.7749, lng: -122.4194 },
{ lat: 37.7849, lng: -122.4094 }
],
strokeColor: '#0000FF',
strokeWeight: 3,
map: map
});
map.addSource('route', {
type: 'geojson',
data: {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [
[-122.4194, 37.7749],
[-122.4094, 37.7849]
]
}
}
});
map.addLayer({
id: 'route-layer',
type: 'line',
source: 'route',
paint: {
'line-color': '#0000FF',
'line-width': 3
}
});
const marker = new google.maps.Marker({
position: { lat: 37.7749, lng: -122.4194 },
map: map,
icon: {
url: 'marker.png',
scaledSize: new google.maps.Size(32, 32)
}
});
选项 1:HTML 标记
const el = document.createElement('div');
el.className = 'marker';
el.style.backgroundImage = 'url(marker.png)';
el.style.width = '32px';
el.style.height = '32px';
new mapboxgl.Marker(el).setLngLat([-122.4194, 37.7749]).addTo(map);
选项 2:符号图层(性能更好)
// 加载图像
map.loadImage('marker.png', (error, image) => {
if (error) throw error;
map.addImage('custom-marker', image);
map.addLayer({
id: 'markers',
type: 'symbol',
source: 'points',
layout: {
'icon-image': 'custom-marker',
'icon-size': 1
}
});
});
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: '1600 Amphitheatre Parkway' }, (results, status) => {
if (status === 'OK') {
map.setCenter(results[0].geometry.location);
}
});
// 使用 Mapbox Geocoding API v6
fetch(
`https://api.mapbox.com/search/geocode/v6/forward?q=1600+Amphitheatre+Parkway&access_token=${mapboxgl.accessToken}`
)
.then((response) => response.json())
.then((data) => {
const [lng, lat] = data.features[0].geometry.coordinates;
map.setCenter([lng, lat]);
});
// 或使用 mapbox-gl-geocoder 插件
const geocoder = new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
mapboxgl: mapboxgl
});
map.addControl(geocoder);
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);
directionsService.route(
{
origin: 'San Francisco, CA',
destination: 'Los Angeles, CA',
travelMode: 'DRIVING'
},
(response, status) => {
if (status === 'OK') {
directionsRenderer.setDirections(response);
}
}
);
// 使用 Mapbox Directions API
const origin = [-122.4194, 37.7749];
const destination = [-118.2437, 34.0522];
fetch(
`https://api.mapbox.com/directions/v5/mapbox/driving/${origin.join(',')};${destination.join(',')}?geometries=geojson&access_token=${mapboxgl.accessToken}`
)
.then((response) => response.json())
.then((data) => {
const route = data.routes[0].geometry;
map.addSource('route', {
type: 'geojson',
data: {
type: 'Feature',
geometry: route
}
});
map.addLayer({
id: 'route',
type: 'line',
source: 'route',
paint: {
'line-color': '#3887be',
'line-width': 5
}
});
});
// 或使用 @mapbox/mapbox-gl-directions 插件
const directions = new MapboxDirections({
accessToken: mapboxgl.accessToken
});
map.addControl(directions, 'top-left');
// 控件是自动的,可以配置:
map.setOptions({
zoomControl: true,
mapTypeControl: true,
streetViewControl: false,
fullscreenControl: true
});
// 显式添加控件
map.addControl(new mapboxgl.NavigationControl()); // 缩放 + 旋转
map.addControl(new mapboxgl.FullscreenControl());
map.addControl(new mapboxgl.GeolocateControl());
map.addControl(new mapboxgl.ScaleControl());
// 定位控件
map.addControl(new mapboxgl.NavigationControl(), 'top-right');
// 需要 MarkerClusterer 库
import MarkerClusterer from '@googlemaps/markerclustererplus';
const markers = locations.map((loc) => new google.maps.Marker({ position: loc, map: map }));
new MarkerClusterer(map, markers, {
imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'
});
// 内置聚类支持
map.addSource('points', {
type: 'geojson',
data: geojsonData,
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
// 聚类圆圈
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'points',
filter: ['has', 'point_count'],
paint: {
'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 100, '#f1f075', 750, '#f28cb1'],
'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40]
}
});
// 聚类计数标签
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'points',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-size': 12
}
});
// 未聚类的点
map.addLayer({
id: 'unclustered-point',
type: 'circle',
source: 'points',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#11b4da',
'circle-radius': 8
}
});
关键优势: Mapbox 聚类是内置的且性能极高。
Google Maps:
styles 数组进行样式设置(复杂)Mapbox GL JS:
Google Maps:
const styledMapType = new google.maps.StyledMapType(
[
{ elementType: 'geometry', stylers: [{ color: '#242f3e' }] },
{ elementType: 'labels.text.stroke', stylers: [{ color: '#242f3e' }] }
// ... 更多规则
],
{ name: 'Dark' }
);
map.mapTypes.set('dark', styledMapType);
map.setMapTypeId('dark');
Mapbox GL JS:
// 使用预构建样式
map.setStyle('mapbox://styles/mapbox/dark-v11');
// 或在 Mapbox Studio 中创建自定义样式并引用它
map.setStyle('mapbox://styles/yourusername/your-style-id');
// 以编程方式修改经典样式
map.setPaintProperty('water', 'fill-color', '#242f3e');
// 更新标记位置
marker.setPosition({ lat: 37.7849, lng: -122.4094 });
// 更新多边形路径
polygon.setPath(newCoordinates);
// 更新源数据
map.getSource('points').setData(newGeojsonData);
// 或更新特定要素
const source = map.getSource('points');
const data = source._data;
data.features[0].geometry.coordinates = [-122.4094, 37.7849];
source.setData(data);
迁移提示: 如果您在使用 Google Maps 时遇到性能问题(许多标记),Mapbox 的性能可能会显著更好。
Google Maps 方法:
Mapbox 方法:
Google Maps:
Mapbox:
Google Maps:
const heatmap = new google.maps.visualization.HeatmapLayer({
data: points,
map: map
});
Mapbox:
map.addLayer({
id: 'heatmap',
type: 'heatmap',
source: 'points',
paint: {
'heatmap-intensity': 1,
'heatmap-radius': 50,
'heatmap-color': ['interpolate', ['linear'], ['heatmap-density'], 0, 'rgba(0,0,255,0)', 0.5, 'lime', 1, 'red']
}
});
识别您使用的所有 Google Maps 功能:
<!-- 替换 Google Maps 脚本 -->
<script src="https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.css" rel="stylesheet" />
从基本地图初始化开始:
new google.maps.Map() 替换为 new mapboxgl.Map()按复杂度确定优先级:
更改事件语法:
google.maps.event.addListener() → map.on()latLng → lngLat)利用 Mapbox 功能:
// Google Maps
{ lat: 37.7749, lng: -122.4194 }
// Mapbox(顺序相反!)
[-122.4194, 37.7749]
务必仔细检查坐标顺序!
// Google Maps
map.on('click', (e) => {
console.log(e.latLng.lat(), e.latLng.lng());
});
// Mapbox
map.on('click', (e) => {
console.log(e.lngLat.lat, e.lngLat.lng);
});
// Google Maps - 立即执行
const marker = new google.maps.Marker({ map: map });
// Mapbox - 等待加载
map.on('load', () => {
map.addSource(...);
map.addLayer(...);
});
// Google Maps
marker.setMap(null);
// Mapbox - 必须同时移除两者
map.removeLayer('layer-id');
map.removeSource('source-id');
| 服务 | Google Maps | Mapbox | 备注 |
|---|---|---|---|
| 地理编码 | Geocoding API | Geocoding API | 功能相似 |
| 反向地理编码 | ✅ | ✅ | 相似 |
| 路线规划 | Directions API | Directions API | Mapbox 具有交通感知路径规划 |
| 距离矩阵 | Distance Matrix API | Matrix API | 相似 |
| 等时线 | ❌ | ✅ | Mapbox 独有 |
| 优化 | ❌ | ✅ | Mapbox 独有(TSP) |
| 街景 | ✅ | ❌ | Google 独有 |
| 静态地图 | ✅ | ✅ | 两者都支持 |
| 卫星图像 | ✅ | ✅ | 两者都支持 |
| 瓦片集 | 有限 | 完整 API | Mapbox 更灵活 |
迁移提示: 根据您的使用场景了解定价模型的差异。
| Google Maps 插件 | Mapbox 替代方案 |
|---|---|
| MarkerClusterer | 内置聚类 |
| Drawing Manager | @mapbox/mapbox-gl-draw |
| Geocoder | @mapbox/mapbox-gl-geocoder |
| Directions | @mapbox/mapbox-gl-directions |
Google Maps:
import { GoogleMap, Marker } from '@react-google-maps/api';
Mapbox:
import Map, { Marker } from 'react-map-gl';
// 或
import { useMap } from '@mapbox/mapbox-gl-react';
Google Maps:
import { GoogleMap } from 'vue3-google-map';
Mapbox:
import { MglMap } from 'vue-mapbox';
有关详细的框架指导,请参阅 mapbox-web-integration-patterns 技能。
// 模拟 mapboxgl
jest.mock('mapbox-gl', () => ({
Map: jest.fn(() => ({
on: jest.fn(),
addSource: jest.fn(),
addLayer: jest.fn()
})),
Marker: jest.fn()
}));
考虑继续使用 Google Maps 的情况:
可与以下技能配合使用:
// GOOGLE MAPS
const map = new google.maps.Map(el, {
center: { lat: 37.7749, lng: -122.4194 },
zoom: 12
});
const marker = new google.maps.Marker({
position: { lat: 37.7749, lng: -122.4194 },
map: map
});
google.maps.event.addListener(map, 'click', (e) => {
console.log(e.latLng.lat(), e.latLng.lng());
});
// MAPBOX GL JS
mapboxgl.accessToken = 'YOUR_TOKEN';
const map = new mapboxgl.Map({
container: el,
center: [-122.4194, 37.7749], // 顺序相反!
zoom: 12,
style: 'mapbox://styles/mapbox/streets-v12'
});
const marker = new mapboxgl.Marker()
.setLngLat([-122.4194, 37.7749]) // 顺序相反!
.addTo(map);
map.on('click', (e) => {
console.log(e.lngLat.lat, e.lngLat.lng);
});
记住: Mapbox 中的坐标顺序是 lng, lat!
每周安装次数
188
代码仓库
GitHub 星标数
35
首次出现
2026年2月3日
安全审计
安装于
gemini-cli171
opencode169
codex167
github-copilot165
kimi-cli158
amp157
Comprehensive guidance for migrating from Google Maps Platform to Mapbox GL JS. Provides API equivalents, pattern translations, and strategies for successful migration.
.setMap(map)Key Insight: Mapbox treats everything as data + styling, not individual objects.
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 37.7749, lng: -122.4194 },
zoom: 12,
mapTypeId: 'roadmap' // or 'satellite', 'hybrid', 'terrain'
});
mapboxgl.accessToken = 'YOUR_MAPBOX_TOKEN';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12', // or satellite-v9, outdoors-v12
center: [-122.4194, 37.7749], // [lng, lat] - note the order!
zoom: 12
});
Key Differences:
{lat, lng}, Mapbox uses [lng, lat]| Google Maps | Mapbox GL JS | Notes |
|---|---|---|
map.setCenter(latLng) | map.setCenter([lng, lat]) | Coordinate order reversed |
map.getCenter() | map.getCenter() | Returns LngLat object |
map.setZoom(zoom) | map.setZoom(zoom) | Same behavior |
map.getZoom() |
| Google Maps | Mapbox GL JS | Notes |
|---|---|---|
google.maps.event.addListener(map, 'click', fn) | map.on('click', fn) | Simpler syntax |
event.latLng | event.lngLat | Event property name |
'center_changed' | 'move' / 'moveend' |
Google Maps:
const marker = new google.maps.Marker({
position: { lat: 37.7749, lng: -122.4194 },
map: map,
title: 'San Francisco',
icon: 'custom-icon.png'
});
// Remove marker
marker.setMap(null);
Mapbox GL JS:
// Create marker
const marker = new mapboxgl.Marker()
.setLngLat([-122.4194, 37.7749])
.setPopup(new mapboxgl.Popup().setText('San Francisco'))
.addTo(map);
// Remove marker
marker.remove();
Google Maps:
const markers = locations.map(
(loc) =>
new google.maps.Marker({
position: { lat: loc.lat, lng: loc.lng },
map: map
})
);
Mapbox GL JS (Equivalent Approach):
// Same object-oriented approach
const markers = locations.map((loc) => new mapboxgl.Marker().setLngLat([loc.lng, loc.lat]).addTo(map));
Mapbox GL JS (Data-Driven Approach - Recommended for 100+ points):
// Add as GeoJSON source + layer (uses WebGL, not DOM)
map.addSource('points', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: locations.map((loc) => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [loc.lng, loc.lat] },
properties: { name: loc.name }
}))
}
});
map.addLayer({
id: 'points-layer',
type: 'circle', // or 'symbol' for icons
source: 'points',
paint: {
'circle-radius': 8,
'circle-color': '#ff0000'
}
});
Performance Advantage: Google Maps renders all markers as DOM elements (even when using the Data Layer), which becomes slow with 500+ markers. Mapbox's circle and symbol layers are rendered by WebGL, making them much faster for large datasets (1,000-10,000+ points). This is a significant advantage when building applications with many points.
const infowindow = new google.maps.InfoWindow({
content: '<h3>Title</h3><p>Content</p>'
});
marker.addListener('click', () => {
infowindow.open(map, marker);
});
// Option 1: Attach to marker
const marker = new mapboxgl.Marker()
.setLngLat([-122.4194, 37.7749])
.setPopup(new mapboxgl.Popup().setHTML('<h3>Title</h3><p>Content</p>'))
.addTo(map);
// Option 2: On layer click (for data-driven markers)
map.on('click', 'points-layer', (e) => {
const coordinates = e.features[0].geometry.coordinates.slice();
const description = e.features[0].properties.description;
new mapboxgl.Popup().setLngLat(coordinates).setHTML(description).addTo(map);
});
const polygon = new google.maps.Polygon({
paths: [
{ lat: 37.7749, lng: -122.4194 },
{ lat: 37.7849, lng: -122.4094 },
{ lat: 37.7649, lng: -122.4094 }
],
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35,
map: map
});
map.addSource('polygon', {
type: 'geojson',
data: {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[-122.4194, 37.7749],
[-122.4094, 37.7849],
[-122.4094, 37.7649],
[-122.4194, 37.7749] // Close the ring
]
]
}
}
});
map.addLayer({
id: 'polygon-layer',
type: 'fill',
source: 'polygon',
paint: {
'fill-color': '#FF0000',
'fill-opacity': 0.35
}
});
// Add outline
map.addLayer({
id: 'polygon-outline',
type: 'line',
source: 'polygon',
paint: {
'line-color': '#FF0000',
'line-width': 2,
'line-opacity': 0.8
}
});
const line = new google.maps.Polyline({
path: [
{ lat: 37.7749, lng: -122.4194 },
{ lat: 37.7849, lng: -122.4094 }
],
strokeColor: '#0000FF',
strokeWeight: 3,
map: map
});
map.addSource('route', {
type: 'geojson',
data: {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [
[-122.4194, 37.7749],
[-122.4094, 37.7849]
]
}
}
});
map.addLayer({
id: 'route-layer',
type: 'line',
source: 'route',
paint: {
'line-color': '#0000FF',
'line-width': 3
}
});
const marker = new google.maps.Marker({
position: { lat: 37.7749, lng: -122.4194 },
map: map,
icon: {
url: 'marker.png',
scaledSize: new google.maps.Size(32, 32)
}
});
Option 1: HTML Marker
const el = document.createElement('div');
el.className = 'marker';
el.style.backgroundImage = 'url(marker.png)';
el.style.width = '32px';
el.style.height = '32px';
new mapboxgl.Marker(el).setLngLat([-122.4194, 37.7749]).addTo(map);
Option 2: Symbol Layer (Better Performance)
// Load image
map.loadImage('marker.png', (error, image) => {
if (error) throw error;
map.addImage('custom-marker', image);
map.addLayer({
id: 'markers',
type: 'symbol',
source: 'points',
layout: {
'icon-image': 'custom-marker',
'icon-size': 1
}
});
});
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: '1600 Amphitheatre Parkway' }, (results, status) => {
if (status === 'OK') {
map.setCenter(results[0].geometry.location);
}
});
// Use Mapbox Geocoding API v6
fetch(
`https://api.mapbox.com/search/geocode/v6/forward?q=1600+Amphitheatre+Parkway&access_token=${mapboxgl.accessToken}`
)
.then((response) => response.json())
.then((data) => {
const [lng, lat] = data.features[0].geometry.coordinates;
map.setCenter([lng, lat]);
});
// Or use mapbox-gl-geocoder plugin
const geocoder = new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
mapboxgl: mapboxgl
});
map.addControl(geocoder);
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);
directionsService.route(
{
origin: 'San Francisco, CA',
destination: 'Los Angeles, CA',
travelMode: 'DRIVING'
},
(response, status) => {
if (status === 'OK') {
directionsRenderer.setDirections(response);
}
}
);
// Use Mapbox Directions API
const origin = [-122.4194, 37.7749];
const destination = [-118.2437, 34.0522];
fetch(
`https://api.mapbox.com/directions/v5/mapbox/driving/${origin.join(',')};${destination.join(',')}?geometries=geojson&access_token=${mapboxgl.accessToken}`
)
.then((response) => response.json())
.then((data) => {
const route = data.routes[0].geometry;
map.addSource('route', {
type: 'geojson',
data: {
type: 'Feature',
geometry: route
}
});
map.addLayer({
id: 'route',
type: 'line',
source: 'route',
paint: {
'line-color': '#3887be',
'line-width': 5
}
});
});
// Or use @mapbox/mapbox-gl-directions plugin
const directions = new MapboxDirections({
accessToken: mapboxgl.accessToken
});
map.addControl(directions, 'top-left');
// Controls are automatic, can configure:
map.setOptions({
zoomControl: true,
mapTypeControl: true,
streetViewControl: false,
fullscreenControl: true
});
// Add controls explicitly
map.addControl(new mapboxgl.NavigationControl()); // Zoom + rotation
map.addControl(new mapboxgl.FullscreenControl());
map.addControl(new mapboxgl.GeolocateControl());
map.addControl(new mapboxgl.ScaleControl());
// Position controls
map.addControl(new mapboxgl.NavigationControl(), 'top-right');
// Requires MarkerClusterer library
import MarkerClusterer from '@googlemaps/markerclustererplus';
const markers = locations.map((loc) => new google.maps.Marker({ position: loc, map: map }));
new MarkerClusterer(map, markers, {
imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'
});
// Built-in clustering support
map.addSource('points', {
type: 'geojson',
data: geojsonData,
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
// Cluster circles
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'points',
filter: ['has', 'point_count'],
paint: {
'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 100, '#f1f075', 750, '#f28cb1'],
'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40]
}
});
// Cluster count labels
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'points',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-size': 12
}
});
// Unclustered points
map.addLayer({
id: 'unclustered-point',
type: 'circle',
source: 'points',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#11b4da',
'circle-radius': 8
}
});
Key Advantage: Mapbox clustering is built-in and highly performant.
Google Maps:
styles array (complex)Mapbox GL JS:
Google Maps:
const styledMapType = new google.maps.StyledMapType(
[
{ elementType: 'geometry', stylers: [{ color: '#242f3e' }] },
{ elementType: 'labels.text.stroke', stylers: [{ color: '#242f3e' }] }
// ... many more rules
],
{ name: 'Dark' }
);
map.mapTypes.set('dark', styledMapType);
map.setMapTypeId('dark');
Mapbox GL JS:
// Use pre-built style
map.setStyle('mapbox://styles/mapbox/dark-v11');
// Or create custom style in Mapbox Studio and reference it
map.setStyle('mapbox://styles/yourusername/your-style-id');
// Modify classic styles programmatically
map.setPaintProperty('water', 'fill-color', '#242f3e');
// Update marker position
marker.setPosition({ lat: 37.7849, lng: -122.4094 });
// Update polygon path
polygon.setPath(newCoordinates);
// Update source data
map.getSource('points').setData(newGeojsonData);
// Or update specific features
const source = map.getSource('points');
const data = source._data;
data.features[0].geometry.coordinates = [-122.4094, 37.7849];
source.setData(data);
Migration Tip: If you have performance issues with Google Maps (many markers), Mapbox will likely perform significantly better.
Google Maps approach:
Mapbox approach:
Google Maps:
Mapbox:
Google Maps:
const heatmap = new google.maps.visualization.HeatmapLayer({
data: points,
map: map
});
Mapbox:
map.addLayer({
id: 'heatmap',
type: 'heatmap',
source: 'points',
paint: {
'heatmap-intensity': 1,
'heatmap-radius': 50,
'heatmap-color': ['interpolate', ['linear'], ['heatmap-density'], 0, 'rgba(0,0,255,0)', 0.5, 'lime', 1, 'red']
}
});
Identify all Google Maps features you use:
<!-- Replace Google Maps script -->
<script src="https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.css" rel="stylesheet" />
Start with basic map initialization:
new google.maps.Map() with new mapboxgl.Map()Prioritize by complexity:
Change event syntax:
google.maps.event.addListener() → map.on()latLng → lngLat)Take advantage of Mapbox features:
// Google Maps
{ lat: 37.7749, lng: -122.4194 }
// Mapbox (REVERSED!)
[-122.4194, 37.7749]
Always double-check coordinate order!
// Google Maps
map.on('click', (e) => {
console.log(e.latLng.lat(), e.latLng.lng());
});
// Mapbox
map.on('click', (e) => {
console.log(e.lngLat.lat, e.lngLat.lng);
});
// Google Maps - immediate
const marker = new google.maps.Marker({ map: map });
// Mapbox - wait for load
map.on('load', () => {
map.addSource(...);
map.addLayer(...);
});
// Google Maps
marker.setMap(null);
// Mapbox - must remove both
map.removeLayer('layer-id');
map.removeSource('source-id');
| Service | Google Maps | Mapbox | Notes |
|---|---|---|---|
| Geocoding | Geocoding API | Geocoding API | Similar capabilities |
| Reverse Geocoding | ✅ | ✅ | Similar |
| Directions | Directions API | Directions API | Mapbox has traffic-aware routing |
| Distance Matrix | Distance Matrix API | Matrix API | Similar |
| Isochrones | ❌ | ✅ | Mapbox exclusive |
| Optimization | ❌ | ✅ | Mapbox exclusive (TSP) |
Migration Tip: Understand how pricing models differ for your use case.
| Google Maps Plugin | Mapbox Alternative |
|---|---|
| MarkerClusterer | Built-in clustering |
| Drawing Manager | @mapbox/mapbox-gl-draw |
| Geocoder | @mapbox/mapbox-gl-geocoder |
| Directions | @mapbox/mapbox-gl-directions |
Google Maps:
import { GoogleMap, Marker } from '@react-google-maps/api';
Mapbox:
import Map, { Marker } from 'react-map-gl';
// or
import { useMap } from '@mapbox/mapbox-gl-react';
Google Maps:
import { GoogleMap } from 'vue3-google-map';
Mapbox:
import { MglMap } from 'vue-mapbox';
See mapbox-web-integration-patterns skill for detailed framework guidance.
// Mock mapboxgl
jest.mock('mapbox-gl', () => ({
Map: jest.fn(() => ({
on: jest.fn(),
addSource: jest.fn(),
addLayer: jest.fn()
})),
Marker: jest.fn()
}));
Consider staying with Google Maps if:
Works with:
// GOOGLE MAPS
const map = new google.maps.Map(el, {
center: { lat: 37.7749, lng: -122.4194 },
zoom: 12
});
const marker = new google.maps.Marker({
position: { lat: 37.7749, lng: -122.4194 },
map: map
});
google.maps.event.addListener(map, 'click', (e) => {
console.log(e.latLng.lat(), e.latLng.lng());
});
// MAPBOX GL JS
mapboxgl.accessToken = 'YOUR_TOKEN';
const map = new mapboxgl.Map({
container: el,
center: [-122.4194, 37.7749], // REVERSED!
zoom: 12,
style: 'mapbox://styles/mapbox/streets-v12'
});
const marker = new mapboxgl.Marker()
.setLngLat([-122.4194, 37.7749]) // REVERSED!
.addTo(map);
map.on('click', (e) => {
console.log(e.lngLat.lat, e.lngLat.lng);
});
Remember: lng, lat order in Mapbox!
Weekly Installs
188
Repository
GitHub Stars
35
First Seen
Feb 3, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli171
opencode169
codex167
github-copilot165
kimi-cli158
amp157
Genkit JS 开发指南:AI 应用构建、错误排查与最佳实践
7,700 周安装
map.getZoom() |
| Same behavior |
map.panTo(latLng) | map.panTo([lng, lat]) | Animated pan |
map.fitBounds(bounds) | map.fitBounds([[lng,lat],[lng,lat]]) | Different bound format |
map.setMapTypeId(type) | map.setStyle(styleUrl) | Completely different approach |
map.getBounds() | map.getBounds() | Similar |
| Different event names |
'zoom_changed' | 'zoom' / 'zoomend' | Different event names |
'bounds_changed' | 'moveend' | No direct equivalent |
'mousemove' | 'mousemove' | Same |
'mouseout' | 'mouseleave' | Different name |
| Street View | ✅ | ❌ | Google exclusive |
| Static Maps | ✅ | ✅ | Both supported |
| Satellite Imagery | ✅ | ✅ | Both supported |
| Tilesets | Limited | Full API | Mapbox more flexible |