mapbox-google-maps-migration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMapbox Google Maps Migration Skill
Mapbox Google Maps 迁移指南
Comprehensive guidance for migrating from Google Maps Platform to Mapbox GL JS. Provides API equivalents, pattern translations, and strategies for successful migration.
面向从Google Maps Platform迁移到Mapbox GL JS的全面指导,提供API对应关系、模式转换以及成功迁移的策略。
Core Philosophy Differences
核心理念差异
Google Maps: Imperative & Object-Oriented
Google Maps:命令式与面向对象
- Create objects (Marker, Polygon, etc.)
- Add to map with
.setMap(map) - Update properties with setters
- Heavy reliance on object instances
- 创建对象(Marker、Polygon等)
- 通过添加到地图
.setMap(map) - 使用setter更新属性
- 严重依赖对象实例
Mapbox GL JS: Declarative & Data-Driven
Mapbox GL JS:声明式与数据驱动
- Add data sources
- Define layers (visual representation)
- Style with JSON
- Update data, not object properties
Key Insight: Mapbox treats everything as data + styling, not individual objects.
- 添加数据源
- 定义图层(视觉表现)
- 使用JSON设置样式
- 更新数据而非对象属性
核心要点: Mapbox将所有内容视为数据+样式,而非独立对象。
Map Initialization
地图初始化
Google Maps
Google Maps
javascript
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 37.7749, lng: -122.4194 },
zoom: 12,
mapTypeId: 'roadmap' // or 'satellite', 'hybrid', 'terrain'
});javascript
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 37.7749, lng: -122.4194 },
zoom: 12,
mapTypeId: 'roadmap' // or 'satellite', 'hybrid', 'terrain'
});Mapbox GL JS
Mapbox GL JS
javascript
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:
- Coordinate order: Google uses , Mapbox uses
{lat, lng}[lng, lat] - Authentication: Google uses API key in script tag, Mapbox uses access token in code
- Styling: Google uses map types, Mapbox uses full style URLs
javascript
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
});核心差异:
- 坐标顺序: Google使用,Mapbox使用
{lat, lng}[lng, lat] - 身份验证: Google在脚本标签中使用API密钥,Mapbox在代码中使用访问令牌
- 样式设置: Google使用地图类型,Mapbox使用完整的样式URL
API Equivalents Reference
API对应关系参考
Map Methods
地图方法
| Google Maps | Mapbox GL JS | Notes |
|---|---|---|
| | Coordinate order reversed |
| | Returns LngLat object |
| | Same behavior |
| | Same behavior |
| | Animated pan |
| | Different bound format |
| | Completely different approach |
| | Similar |
| Google Maps | Mapbox GL JS | 说明 |
|---|---|---|
| | 坐标顺序相反 |
| | 返回LngLat对象 |
| | 行为一致 |
| | 行为一致 |
| | 动画平移 |
| | 边界格式不同 |
| | 实现方式完全不同 |
| | 类似 |
Map Events
地图事件
| Google Maps | Mapbox GL JS | Notes |
|---|---|---|
| | Simpler syntax |
| | Event property name |
| | Different event names |
| | Different event names |
| | No direct equivalent |
| | Same |
| | Different name |
| Google Maps | Mapbox GL JS | 说明 |
|---|---|---|
| | 语法更简洁 |
| | 事件属性名称不同 |
| | 事件名称不同 |
| | 事件名称不同 |
| | 无直接对应事件 |
| | 一致 |
| | 名称不同 |
Markers and Points
标记与点
Simple Marker
基础标记
Google Maps:
javascript
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:
javascript
// 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:
javascript
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:
javascript
// 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();Multiple Markers
多个标记
Google Maps:
javascript
const markers = locations.map(loc =>
new google.maps.Marker({
position: { lat: loc.lat, lng: loc.lng },
map: map
})
);Mapbox GL JS (Equivalent Approach):
javascript
// 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):
javascript
// 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.
Google Maps:
javascript
const markers = locations.map(loc =>
new google.maps.Marker({
position: { lat: loc.lat, lng: loc.lng },
map: map
})
);Mapbox GL JS(对应实现方式):
javascript
// Same object-oriented approach
const markers = locations.map(loc =>
new mapboxgl.Marker()
.setLngLat([loc.lng, loc.lat])
.addTo(map)
);Mapbox GL JS(数据驱动方式 - 推荐用于100+个点):
javascript
// 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'
}
});性能优势: Google Maps将所有标记渲染为DOM元素(即使使用数据层),当标记数量超过500个时会变慢。Mapbox的圆形和符号图层通过WebGL渲染,在处理大型数据集(1000-10000+个点)时速度更快。这对于构建包含大量点的应用来说是一个显著优势。
Info Windows / Popups
信息窗口 / 弹出框
Google Maps
Google Maps
javascript
const infowindow = new google.maps.InfoWindow({
content: '<h3>Title</h3><p>Content</p>'
});
marker.addListener('click', () => {
infowindow.open(map, marker);
});javascript
const infowindow = new google.maps.InfoWindow({
content: '<h3>Title</h3><p>Content</p>'
});
marker.addListener('click', () => {
infowindow.open(map, marker);
});Mapbox GL JS
Mapbox GL JS
javascript
// 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);
});javascript
// 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);
});Polygons and Shapes
多边形与形状
Google Maps
Google Maps
javascript
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
});javascript
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
});Mapbox GL JS
Mapbox GL JS
javascript
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
}
});javascript
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
}
});Polylines / Lines
折线 / 线条
Google Maps
Google Maps
javascript
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
});javascript
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
});Mapbox GL JS
Mapbox GL JS
javascript
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
}
});javascript
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
}
});Custom Icons and Symbols
自定义图标与符号
Google Maps
Google Maps
javascript
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)
}
});javascript
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)
}
});Mapbox GL JS
Mapbox GL JS
Option 1: HTML Marker
javascript
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)
javascript
// 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
}
});
});选项1:HTML标记
javascript
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:符号图层(性能更优)
javascript
// 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
}
});
});Geocoding
地理编码
Google Maps
Google Maps
javascript
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: '1600 Amphitheatre Parkway' }, (results, status) => {
if (status === 'OK') {
map.setCenter(results[0].geometry.location);
}
});javascript
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: '1600 Amphitheatre Parkway' }, (results, status) => {
if (status === 'OK') {
map.setCenter(results[0].geometry.location);
}
});Mapbox GL JS
Mapbox GL JS
javascript
// 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);javascript
// 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);Directions / Routing
路线规划 / 导航
Google Maps
Google Maps
javascript
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);
}
});javascript
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 GL JS
Mapbox GL JS
javascript
// 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');javascript
// 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
控件
Google Maps
Google Maps
javascript
// Controls are automatic, can configure:
map.setOptions({
zoomControl: true,
mapTypeControl: true,
streetViewControl: false,
fullscreenControl: true
});javascript
// Controls are automatic, can configure:
map.setOptions({
zoomControl: true,
mapTypeControl: true,
streetViewControl: false,
fullscreenControl: true
});Mapbox GL JS
Mapbox GL JS
javascript
// 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');javascript
// 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');Clustering
聚合
Google Maps
Google Maps
javascript
// 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'
});javascript
// 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'
});Mapbox GL JS
Mapbox GL JS
javascript
// 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.
javascript
// 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
}
});核心优势: Mapbox聚合功能内置且性能极高。
Styling and Appearance
样式与外观
Map Types vs. Styles
地图类型 vs 样式
Google Maps:
- Limited map types: roadmap, satellite, hybrid, terrain
- Styling via array (complex)
styles
Mapbox GL JS:
- Full control over every visual element
- Pre-built styles: standard, standard-satellite, streets, outdoors, light, dark
- Custom styles via Mapbox Studio for unique branding and design
- Dynamic styling based on data properties
- For classic styles (pre Mapbox Standard) you can modify style programmatically by using the setPaintProperty()
Google Maps:
- 有限的地图类型:roadmap、satellite、hybrid、terrain
- 通过数组进行样式设置(复杂)
styles
Mapbox GL JS:
- 完全控制每个视觉元素
- 预构建样式:standard、standard-satellite、streets、outdoors、light、dark
- 通过Mapbox Studio创建自定义样式以实现独特品牌和设计
- 基于数据属性的动态样式
- 对于经典样式(Mapbox Standard之前的版本),可以通过setPaintProperty()以编程方式修改样式
Custom Styling Example
自定义样式示例
Google Maps:
javascript
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:
javascript
// 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');Google Maps:
javascript
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:
javascript
// 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');Data Updates
数据更新
Google Maps
Google Maps
javascript
// Update marker position
marker.setPosition({ lat: 37.7849, lng: -122.4094 });
// Update polygon path
polygon.setPath(newCoordinates);javascript
// Update marker position
marker.setPosition({ lat: 37.7849, lng: -122.4094 });
// Update polygon path
polygon.setPath(newCoordinates);Mapbox GL JS
Mapbox GL JS
javascript
// 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);javascript
// 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);Performance Considerations
性能考量
Google Maps
Google Maps
- Individual objects for each feature
- Can be slow with 1000+ markers
- Requires MarkerClusterer for performance
- 每个要素对应独立对象
- 当标记数量超过1000个时可能变慢
- 需要MarkerClusterer来提升性能
Mapbox GL JS
Mapbox GL JS
- Data-driven rendering
- WebGL-based (hardware accelerated)
- Handles 10,000+ points smoothly
- Built-in clustering
Migration Tip: If you have performance issues with Google Maps (many markers), Mapbox will likely perform significantly better.
- 数据驱动渲染
- 基于WebGL(硬件加速)
- 流畅处理10000+个点
- 内置聚合功能
迁移提示: 如果你的Google Maps存在性能问题(大量标记),Mapbox的性能可能会显著提升。
Common Migration Patterns
常见迁移模式
Pattern 1: Store Locator
模式1:门店定位器
Google Maps approach:
- Create marker for each store
- Add click listeners to each marker
- Show info window on click
Mapbox approach:
- Add all stores as GeoJSON source
- Add symbol layer for markers
- Use layer click event for all markers
- More performant, cleaner code
Google Maps实现方式:
- 为每个门店创建标记
- 为每个标记添加点击监听器
- 点击时显示信息窗口
Mapbox实现方式:
- 将所有门店添加为GeoJSON数据源
- 添加符号图层作为标记
- 使用图层点击事件处理所有标记
- 性能更高,代码更简洁
Pattern 2: Drawing Tools
模式2:绘图工具
Google Maps:
- Use Drawing Manager library
- Creates overlay objects
Mapbox:
- Use Mapbox Draw plugin
- More powerful, customizable
- Better for complex editing
Google Maps:
- 使用Drawing Manager库
- 创建覆盖对象
Mapbox:
- 使用Mapbox Draw插件
- 功能更强大,可定制性更高
- 更适合复杂编辑
Pattern 3: Heatmaps
模式3:热力图
Google Maps:
javascript
const heatmap = new google.maps.visualization.HeatmapLayer({
data: points,
map: map
});Mapbox:
javascript
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:
javascript
const heatmap = new google.maps.visualization.HeatmapLayer({
data: points,
map: map
});Mapbox:
javascript
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'
]
}
});Migration Strategy
迁移策略
Step 1: Audit Current Implementation
步骤1:审核当前实现
Identify all Google Maps features you use:
- Basic map with markers
- Info windows/popups
- Polygons/polylines
- Geocoding
- Directions
- Clustering
- Custom styling
- Drawing tools
- Street View (no Mapbox equivalent)
- Other advanced features
识别你使用的所有Google Maps功能:
- 带标记的基础地图
- 信息窗口/弹出框
- 多边形/折线
- 地理编码
- 路线规划
- 聚合
- 自定义样式
- 绘图工具
- 街景(Mapbox无对应功能)
- 其他高级功能
Step 2: Set Up Mapbox
步骤2:搭建Mapbox环境
html
<!-- 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">html
<!-- 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">Step 3: Convert Core Map
步骤3:转换核心地图
Start with basic map initialization:
- Replace with
new google.maps.Map()new mapboxgl.Map() - Fix coordinate order (lat,lng → lng,lat)
- Update zoom/center
从基础地图初始化开始:
- 用替换
new mapboxgl.Map()new google.maps.Map() - 修正坐标顺序(lat,lng → lng,lat)
- 更新缩放/中心位置
Step 4: Convert Features One by One
步骤4:逐个转换功能
Prioritize by complexity:
- Easy: Map controls, basic markers
- Medium: Popups, polygons, lines
- Complex: Clustering, custom styling, data updates
按复杂度优先级处理:
- 简单: 地图控件、基础标记
- 中等: 弹出框、多边形、线条
- 复杂: 聚合、自定义样式、数据更新
Step 5: Update Event Handlers
步骤5:更新事件处理器
Change event syntax:
- →
google.maps.event.addListener()map.on() - Update event property names (→
latLng)lngLat
修改事件语法:
- →
google.maps.event.addListener()map.on() - 更新事件属性名称(→
latLng)lngLat
Step 6: Optimize for Mapbox
步骤6:针对Mapbox优化
Take advantage of Mapbox features:
- Convert multiple markers to data-driven layers
- Use clustering (built-in)
- Leverage vector tiles for custom styling
- Use expressions for dynamic styling
利用Mapbox的特性:
- 将多个标记转换为数据驱动图层
- 使用内置的聚合功能
- 利用矢量瓦片进行自定义样式
- 使用表达式实现动态样式
Step 7: Test Thoroughly
步骤7:全面测试
- Cross-browser testing
- Mobile responsiveness
- Performance with real data volumes
- Touch/gesture interactions
- 跨浏览器测试
- 移动端响应式测试
- 真实数据量下的性能测试
- 触摸/手势交互测试
Gotchas and Common Issues
常见陷阱与问题
❌ Coordinate Order
❌ 坐标顺序
javascript
// Google Maps
{ lat: 37.7749, lng: -122.4194 }
// Mapbox (REVERSED!)
[-122.4194, 37.7749]Always double-check coordinate order!
javascript
// Google Maps
{ lat: 37.7749, lng: -122.4194 }
// Mapbox (反转!)
[-122.4194, 37.7749]务必仔细检查坐标顺序!
❌ Event Properties
❌ 事件属性
javascript
// 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);
});javascript
// 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);
});❌ Timing Issues
❌ 时序问题
javascript
// Google Maps - immediate
const marker = new google.maps.Marker({ map: map });
// Mapbox - wait for load
map.on('load', () => {
map.addSource(...);
map.addLayer(...);
});javascript
// Google Maps - 立即执行
const marker = new google.maps.Marker({ map: map });
// Mapbox - 等待加载完成
map.on('load', () => {
map.addSource(...);
map.addLayer(...);
});❌ Removing Features
❌ 删除要素
javascript
// Google Maps
marker.setMap(null);
// Mapbox - must remove both
map.removeLayer('layer-id');
map.removeSource('source-id');javascript
// Google Maps
marker.setMap(null);
// Mapbox - 必须同时移除两者
map.removeLayer('layer-id');
map.removeSource('source-id');API Services Comparison
API服务对比
| 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) |
| Street View | ✅ | ❌ | Google exclusive |
| Static Maps | ✅ | ✅ | Both supported |
| Satellite Imagery | ✅ | ✅ | Both supported |
| Tilesets | Limited | Full API | Mapbox more flexible |
| 服务 | Google Maps | Mapbox | 说明 |
|---|---|---|---|
| 地理编码 | Geocoding API | Geocoding API | 功能类似 |
| 逆地理编码 | ✅ | ✅ | 类似 |
| 路线规划 | Directions API | Directions API | Mapbox支持基于交通状况的路线规划 |
| 距离矩阵 | Distance Matrix API | Matrix API | 类似 |
| 等时线 | ❌ | ✅ | Mapbox独有 |
| 路径优化 | ❌ | ✅ | Mapbox独有(旅行商问题) |
| 街景 | ✅ | ❌ | Google独有 |
| 静态地图 | ✅ | ✅ | 均支持 |
| 卫星影像 | ✅ | ✅ | 均支持 |
| 瓦片集 | 有限 | 完整API | Mapbox灵活性更高 |
Pricing Differences
定价差异
Google Maps Platform
Google Maps Platform
- Charges per API call
- Free tier: $200/month credit
- Different rates for different APIs
- Can get expensive with high traffic
- 按API调用次数收费
- 免费额度:每月200美元信用额
- 不同API费率不同
- 高流量情况下成本可能很高
Mapbox
Mapbox
- Charges per map load
- Free tier: 50,000 map loads/month
- Unlimited API requests per map session
- More predictable costs
Migration Tip: Understand how pricing models differ for your use case.
- 按地图加载次数收费
- 免费额度:每月50000次地图加载
- 每个地图会话内API请求无限制
- 成本更可预测
迁移提示: 针对你的使用场景,了解定价模型的差异。
Plugins and Extensions
插件与扩展
Google Maps Plugins → Mapbox Alternatives
Google Maps插件 → Mapbox替代方案
| 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 |
| - | @mapbox/mapbox-gl-traffic |
| - | @mapbox/mapbox-gl-compare |
| Google Maps插件 | Mapbox替代方案 |
|---|---|
| MarkerClusterer | 内置聚合功能 |
| Drawing Manager | @mapbox/mapbox-gl-draw |
| Geocoder | @mapbox/mapbox-gl-geocoder |
| Directions | @mapbox/mapbox-gl-directions |
| - | @mapbox/mapbox-gl-traffic |
| - | @mapbox/mapbox-gl-compare |
Framework Integration
框架集成
React
React
Google Maps:
javascript
import { GoogleMap, Marker } from '@react-google-maps/api';Mapbox:
javascript
import Map, { Marker } from 'react-map-gl';
// or
import { useMap } from '@mapbox/mapbox-gl-react';Google Maps:
javascript
import { GoogleMap, Marker } from '@react-google-maps/api';Mapbox:
javascript
import Map, { Marker } from 'react-map-gl';
// or
import { useMap } from '@mapbox/mapbox-gl-react';Vue
Vue
Google Maps:
javascript
import { GoogleMap } from 'vue3-google-map';Mapbox:
javascript
import { MglMap } from 'vue-mapbox';See skill for detailed framework guidance.
mapbox-web-integration-patternsGoogle Maps:
javascript
import { GoogleMap } from 'vue3-google-map';Mapbox:
javascript
import { MglMap } from 'vue-mapbox';查看指南获取详细的框架集成指导。
mapbox-web-integration-patternsTesting Strategy
测试策略
Unit Tests
单元测试
javascript
// Mock mapboxgl
jest.mock('mapbox-gl', () => ({
Map: jest.fn(() => ({
on: jest.fn(),
addSource: jest.fn(),
addLayer: jest.fn()
})),
Marker: jest.fn()
}));javascript
// Mock mapboxgl
jest.mock('mapbox-gl', () => ({
Map: jest.fn(() => ({
on: jest.fn(),
addSource: jest.fn(),
addLayer: jest.fn()
})),
Marker: jest.fn()
}));Integration Tests
集成测试
- Test map initialization
- Test data loading and updates
- Test user interactions (click, pan, zoom)
- Test API integrations (geocoding, directions)
- 测试地图初始化
- 测试数据加载与更新
- 测试用户交互(点击、平移、缩放)
- 测试API集成(地理编码、路线规划)
Visual Regression Tests
视觉回归测试
- Compare before/after screenshots
- Ensure visual parity with Google Maps version
- 对比迁移前后的截图
- 确保与Google Maps版本的视觉一致性
Checklist: Migration Complete
迁移完成检查清单
- Map initializes correctly
- All markers/features display
- Click/hover interactions work
- Popups/info windows display
- Geocoding integrated
- Directions/routing working
- Custom styling applied
- Controls positioned correctly
- Mobile/touch gestures work
- Performance is acceptable
- Cross-browser tested
- API keys secured
- Error handling in place
- Analytics/monitoring updated
- Documentation updated
- Team trained on Mapbox
- 地图初始化正常
- 所有标记/要素正常显示
- 点击/悬停交互正常
- 弹出框/信息窗口正常显示
- 地理编码集成完成
- 路线规划功能正常
- 自定义样式已应用
- 控件位置正确
- 移动端/触摸手势正常
- 性能符合预期
- 跨浏览器测试完成
- API密钥已安全配置
- 错误处理已实现
- 分析/监控已更新
- 文档已更新
- 团队已接受Mapbox培训
When NOT to Migrate
不建议迁移的场景
Consider staying with Google Maps if:
- Street View is critical - Mapbox doesn't have equivalent
- Tight Google Workspace integration - Places API deeply integrated
- Already heavily optimized - Migration cost > benefits
- Team expertise - Retraining costs too high
- Short-term project - Not worth migration effort
如果存在以下情况,考虑继续使用Google Maps:
- 街景是核心需求 - Mapbox无对应功能
- 深度集成Google Workspace - Places API深度集成
- 已高度优化 - 迁移成本大于收益
- 团队技术栈匹配 - 重新培训成本过高
- 短期项目 - 迁移投入不值得
Additional Resources
额外资源
Integration with Other Skills
与其他指南的集成
Works with:
- mapbox-web-integration-patterns: Framework-specific migration guidance
- mapbox-web-performance-patterns: Optimize after migration
- mapbox-token-security: Secure your Mapbox tokens properly
- mapbox-geospatial-operations: Use Mapbox's geospatial tools effectively
- mapbox-search-patterns: Migrate geocoding/search functionality
搭配使用:
- mapbox-web-integration-patterns:框架特定的迁移指导
- mapbox-web-performance-patterns:迁移后的优化指南
- mapbox-token-security:正确安全地配置Mapbox令牌
- mapbox-geospatial-operations:有效使用Mapbox的地理空间工具
- mapbox-search-patterns:迁移地理编码/搜索功能
Quick Reference: Side-by-Side Comparison
快速参考:对比示例
javascript
// 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!
javascript
// 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顺序!