基于 mapbox 开发大屏地图应用中,有时候需要展示一些三维图层立体效果。Mapbox 本身可以支持三维开发,详细介绍请参考文章:用 Mapbox 做 3D 地图,这篇文章快说透了 (技术&案例大盘点)。但要实现一些复杂的三维功能开发,有一定的门槛,包括但不限于掌握以下技术:

  • 具备一定的计算机图形学基础,掌握三维计算相关数学知识;
  • 深度掌握 Mapbox 相关应用场景及功能使用方式;
  • 熟悉WebGL/OpenGL技术;
  • 熟悉 Cesium、Three.js、Babylon.js 等三维引擎的使用。 :::warning 在这里,我们介绍使用 mapbox 的 line-translate 绘图属性实现简单的图层拔高立体效果。 :::

一、最终效果对比

图层来源说明:基于杭州市行政边界及下属区县行政边界进行开发。

  • 二维平面呈现效果

基于 line-translate 模拟图层立体效果 - 图1

  • 三维立体呈现效果

基于 line-translate 模拟图层立体效果 - 图2

二、实现步骤

2.1、Mapbox 地图初始化

代码略,详见:Mapbox 使用入门

2.2、添加全国行政区划图层数据源

  1. // map 为 mapbox 地图实例(下同)
  2. map.addSource('region_info', {
  3. type: 'vector',
  4. scheme: 'tms',
  5. tiles: ['https://gis.certifarm.cn/geoserver/gwc/service/tms/1.0.0/wisdomOrchard:region_info@EPSG:900913@pbf/{z}/{x}/{y}.pbf'],
  6. })

2.3、添加杭州市行政边界线图层

  • 展示二维平面边界线
  1. // 添加杭州市边界线图层
  2. let lineOption = {
  3. id: `city-line-layer`,
  4. source: `region_info`,
  5. 'source-layer': `region_info`,
  6. type: 'line',
  7. layout: {
  8. 'line-cap': 'round',
  9. 'line-join': 'round',
  10. },
  11. filter: ['match', ['get', 'code'], '330100', true, false],
  12. paint: {
  13. 'line-color': '#6AF9F8',
  14. 'line-width': 4,
  15. },
  16. }
  17. map.addLayer(lineOption)

效果如下:
基于 line-translate 模拟图层立体效果 - 图3

  • 添加一系列边界线图层,通过 line-translate 进行边界线偏移处理,模拟三维立体效果
  1. // 采用边界偏移的方式,添加杭州区域下沉模糊效果(多个图层,每个图层偏移量递增)
  2. const blurValueList = [0.6, 0.5, 0.5, 0.4, 0.4, 0.3, 0.3, 0.2, 0.2, 0.1, 0.1, 0.1, 0.3, 0.3]
  3. blurValueList.forEach(async (item, index) => {
  4. let tempOption = {
  5. id: `city-line-blur-layer${index}`,
  6. source: `region_info`,
  7. 'source-layer': `region_info`,
  8. type: 'line',
  9. layout: {
  10. 'line-cap': 'round',
  11. 'line-join': 'round',
  12. },
  13. filter: ['match', ['get', 'code'], '330100', true, false],
  14. paint: {
  15. 'line-color': `rgba(106, 249, 248, ${item})`,
  16. 'line-width': 2,
  17. // 横向:左右无偏移,纵向:每一个图层向下偏移+1px
  18. 'line-translate': [0, (index + 1) * 1],
  19. 'line-translate-anchor': 'map',
  20. },
  21. }
  22. map.addLayer(tempOption)
  23. })

效果如下:
基于 line-translate 模拟图层立体效果 - 图4
但在地图放大过程中,出现了奇怪的干扰线(见下图),地图缩小则没有这个问题。目前还没找到引起这个问题的具体原因,大家有空可以一起来调试研究下。
基于 line-translate 模拟图层立体效果 - 图5

2.4、添加杭州市下属区县行政边界线图层、杭州市区域面图层

:::warning 说明一下,可以将杭州市区域面图层放到边界线偏移图层之上,则可以掩盖掉干扰线。 ::: 最终完整图层处理代码如下:

  1. // 添加杭州市及下属区县行政区划图层
  2. export default function addHZBoundaryLayer(map) {
  3. return new Promise(async (resolve, reject) => {
  4. try {
  5. // 添加杭州市边界线图层
  6. let lineOption = {
  7. id: `city-line-layer`,
  8. source: `region_info`,
  9. 'source-layer': `region_info`,
  10. type: 'line',
  11. layout: {
  12. 'line-cap': 'round',
  13. 'line-join': 'round',
  14. },
  15. filter: ['match', ['get', 'code'], '330100', true, false],
  16. paint: {
  17. 'line-color': '#6AF9F8',
  18. 'line-width': 4,
  19. },
  20. }
  21. await map.addLayer(lineOption)
  22. // 采用边界偏移的方式,添加杭州区域下沉模糊效果(多个图层,每个图层偏移量递增)
  23. const blurValueList = [0.6, 0.5, 0.5, 0.4, 0.4, 0.3, 0.3, 0.2, 0.2, 0.1, 0.1, 0.1, 0.3, 0.3]
  24. blurValueList.forEach(async (item, index) => {
  25. let tempOption = {
  26. id: `city-line-blur-layer${index}`,
  27. source: `region_info`,
  28. 'source-layer': `region_info`,
  29. type: 'line',
  30. layout: {
  31. 'line-cap': 'round',
  32. 'line-join': 'round',
  33. },
  34. filter: ['match', ['get', 'code'], '330100', true, false],
  35. paint: {
  36. 'line-color': `rgba(106, 249, 248, ${item})`,
  37. 'line-width': 2,
  38. // 横向:左右无偏移,纵向:每一个图层向下偏移+1px
  39. 'line-translate': [0, (index + 1) * 1],
  40. 'line-translate-anchor': 'map',
  41. },
  42. }
  43. await map.addLayer(tempOption)
  44. })
  45. // 杭州市范围填充面图层
  46. let options = {
  47. id: `city-area-layer`,
  48. source: `region_info`,
  49. 'source-layer': `region_info`,
  50. type: 'fill',
  51. filter: ['match', ['get', 'code'], '330100', true, false],
  52. paint: {
  53. 'fill-color': '#1C4B5D',
  54. },
  55. }
  56. await map.addLayer(options)
  57. // 杭州下属各区县边界线图层
  58. let districtLineOption = {
  59. id: `district-line-layer`,
  60. source: `region_info`,
  61. 'source-layer': `region_info`,
  62. type: 'line',
  63. layout: {
  64. 'line-cap': 'round',
  65. 'line-join': 'round',
  66. },
  67. filter: ['match', ['get', 'parent_code'], '330100', true, false],
  68. paint: {
  69. 'line-color': '#245D6D',
  70. 'line-width': 1,
  71. },
  72. }
  73. await map.addLayer(districtLineOption)
  74. resolve()
  75. } catch (err) {
  76. reject(err)
  77. }
  78. })
  79. }

最终展示效果如下:
基于 line-translate 模拟图层立体效果 - 图6