mapbox-google-maps-migration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Mapbox 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
    {lat, lng}
    , Mapbox uses
    [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使用
    {lat, lng}
    ,Mapbox使用
    [lng, lat]
  • 身份验证: Google在脚本标签中使用API密钥,Mapbox在代码中使用访问令牌
  • 样式设置: Google使用地图类型,Mapbox使用完整的样式URL

API Equivalents Reference

API对应关系参考

Map Methods

地图方法

Google MapsMapbox GL JSNotes
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()
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
Google MapsMapbox 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()
类似

Map Events

地图事件

Google MapsMapbox GL JSNotes
google.maps.event.addListener(map, 'click', fn)
map.on('click', fn)
Simpler syntax
event.latLng
event.lngLat
Event property name
'center_changed'
'move'
/
'moveend'
Different event names
'zoom_changed'
'zoom'
/
'zoomend'
Different event names
'bounds_changed'
'moveend'
No direct equivalent
'mousemove'
'mousemove'
Same
'mouseout'
'mouseleave'
Different name
Google MapsMapbox 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'
名称不同

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
    styles
    array (complex)
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:
  1. Create marker for each store
  2. Add click listeners to each marker
  3. Show info window on click
Mapbox approach:
  1. Add all stores as GeoJSON source
  2. Add symbol layer for markers
  3. Use layer click event for all markers
  4. More performant, cleaner code
Google Maps实现方式:
  1. 为每个门店创建标记
  2. 为每个标记添加点击监听器
  3. 点击时显示信息窗口
Mapbox实现方式:
  1. 将所有门店添加为GeoJSON数据源
  2. 添加符号图层作为标记
  3. 使用图层点击事件处理所有标记
  4. 性能更高,代码更简洁

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:
  1. Replace
    new google.maps.Map()
    with
    new mapboxgl.Map()
  2. Fix coordinate order (lat,lng → lng,lat)
  3. Update zoom/center
从基础地图初始化开始:
  1. new mapboxgl.Map()
    替换
    new google.maps.Map()
  2. 修正坐标顺序(lat,lng → lng,lat)
  3. 更新缩放/中心位置

Step 4: Convert Features One by One

步骤4:逐个转换功能

Prioritize by complexity:
  1. Easy: Map controls, basic markers
  2. Medium: Popups, polygons, lines
  3. Complex: Clustering, custom styling, data updates
按复杂度优先级处理:
  1. 简单: 地图控件、基础标记
  2. 中等: 弹出框、多边形、线条
  3. 复杂: 聚合、自定义样式、数据更新

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服务对比

ServiceGoogle MapsMapboxNotes
GeocodingGeocoding APIGeocoding APISimilar capabilities
Reverse GeocodingSimilar
DirectionsDirections APIDirections APIMapbox has traffic-aware routing
Distance MatrixDistance Matrix APIMatrix APISimilar
IsochronesMapbox exclusive
OptimizationMapbox exclusive (TSP)
Street ViewGoogle exclusive
Static MapsBoth supported
Satellite ImageryBoth supported
TilesetsLimitedFull APIMapbox more flexible
服务Google MapsMapbox说明
地理编码Geocoding APIGeocoding API功能类似
逆地理编码类似
路线规划Directions APIDirections APIMapbox支持基于交通状况的路线规划
距离矩阵Distance Matrix APIMatrix API类似
等时线Mapbox独有
路径优化Mapbox独有(旅行商问题)
街景Google独有
静态地图均支持
卫星影像均支持
瓦片集有限完整APIMapbox灵活性更高

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 PluginMapbox Alternative
MarkerClustererBuilt-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
mapbox-web-integration-patterns
skill for detailed framework guidance.
Google Maps:
javascript
import { GoogleMap } from 'vue3-google-map';
Mapbox:
javascript
import { MglMap } from 'vue-mapbox';
查看
mapbox-web-integration-patterns
指南获取详细的框架集成指导。

Testing 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顺序!