基于 Mapbox GL JS 实现中国省份点击抬升效果(附完整代码)

摘要:本文通过 Mapbox GL JS 结合阿里云 DataV 地理数据,实现中国地图的交互式省份抬升效果。点击省份时触发三维立体抬升,并伴随颜色变化,适用于大屏可视化等场景。

一、效果预览

  • 交互特性
    • 点击省份触发 100,000 单位高度抬升
    • 被点击省份变为红色高亮
    • 点击空白区域恢复所有省份状态
  • 技术栈
    • Mapbox GL JS(v1.13.2)
    • 阿里云 DataV 地理数据
    • HTML5/CSS3

二、数据来源说明

1. 地理数据获取

使用 阿里云 DataV 平台 的行政区划数据:

  • 访问 DataV 地理小工具
  • 按以下步骤获取数据:
    1. 选择 "中国" -> "省份"
    2. 逐个下载各省份 GeoJSON 数据
    3. 按 /data/xzq/省份名称.json 格式组织文件

2. 数据结构示例(北京市.json)

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "name": "北京市",
        "adcode": 110000
      },
      "geometry": {
        "type": "MultiPolygon",
        "coordinates": [
          // 坐标数据...
        ]
      }
    }
  ]
}

三、核心代码解析

1. 地图初始化

var map = new mapboxgl.Map({
  container: 'map',
  style: {
    "version": 8,
    "sources": {
      "raster-tiles": {
        "type": "raster",
        "tiles": [host + '/iserver/services/...'], // 地图瓦片服务
        "tileSize": 256
      }
    },
    "layers": [{/* 底图图层配置 */}]
  },
  center: [120.143, 30.236], // 杭州坐标
  zoom: 3,
  pitch: 30 // 3D 倾斜视角
});

2. 省份数据动态加载

const provinces = ['北京市', '天津市' /* ...其他省份 */];
const provinceLayers = {};

map.on('load', () => {
  provinces.map(province => {
    fetch(`/data/xzq/${province}.json`)
      .then(response => response.json())
      .then(data => {
        // 动态创建数据源与图层
        const sourceId = `${province}-source`;
        const layerId = `${province}-layer`;
        
        map.addSource(sourceId, { type: 'geojson', data });
        map.addLayer({
          id: layerId,
          type: 'fill-extrusion',
          source: sourceId,
          paint: {
            'fill-extrusion-color': '#305893',
            'fill-extrusion-height': 0,
            'fill-extrusion-opacity': 0.6
          }
        });

        provinceLayers[layerId] = { 
          sourceId, 
          isRaised: false 
        };
      })
  });
});

3. 点击交互逻辑

map.on('click', (e) => {
  const features = map.queryRenderedFeatures(e.point, {
    layers: Object.keys(provinceLayers)
  });

  if (features.length > 0) {
    const clickedLayerId = features[0].layer.id;
    Object.keys(provinceLayers).forEach(layerId => {
      if (layerId === clickedLayerId) {
        // 抬升当前点击省份
        map.setPaintProperty(layerId, 'fill-extrusion-height', 100000);
        map.setPaintProperty(layerId, 'fill-extrusion-color', '#FF0000');
      } else {
        // 重置其他省份
        map.setPaintProperty(layerId, 'fill-extrusion-height', 0);
        map.setPaintProperty(layerId, 'fill-extrusion-color', '#305893');
      }
    });
  }
});

四、完整实现代码

<!--********************************************************************
* Copyright© 2000 - 2024 SuperMap Software Co.Ltd. All rights reserved.
*********************************************************************-->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <title data-i18n="resources.title_tiledMapLayer"></title>
    <script type="text/javascript" src="../js/include-web.js"></script>
    <style>
      body {
        margin: 0;
        padding: 0;
      }

      #map {
        position: absolute;
        top: 0;
        bottom: 0;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script type="text/javascript" src="../../dist/mapboxgl/include-mapboxgl.js"></script>
    <script type="text/javascript">
      var host = window.isLocal ? window.server : 'https://iserver.supermap.io';
      var attribution =
        "<a href='https://www.mapbox.com/about/maps/' target='_blank'>© Mapbox </a>" +
        " with <span>© <a href='https://iclient.supermap.io' target='_blank'>SuperMap iClient</a> | </span>" +
        " Map Data <span>© <a href='http://support.supermap.com.cn/product/iServer.aspx' target='_blank'>SuperMap iServer</a></span> ";

       var map = new mapboxgl.Map({
           container: 'map', // container id
           style: {
               "version": 8,
               "sources": {
                   "raster-tiles": {
                       "attribution": attribution,
                       "type": "raster",
                       "tiles": [host + '/iserver/services/map-china400/rest/maps/ChinaDark/zxyTileImage.png?z={z}&x={x}&y={y}'],
                       "tileSize": 256
                   }
               },
               "layers": [{
                   "id": "simple-tiles",
                   "type": "raster",
                   "source": "raster-tiles",
                   "minzoom": 0,
                   "maxzoom": 22
               }]
           },
           center: [120.143, 30.236], // starting position
           zoom: 3, // starting zoom
           pitch: 30
       });
       map.addControl(new mapboxgl.NavigationControl(), 'top-left');
       // 省份数据
        const provinces = [
            '北京市', '天津市', '河北省', '山西省', '内蒙古自治区', '辽宁省', '吉林省', '黑龙江省', '上海市',
            '江苏省', '浙江省', '安徽省', '福建省', '江西省', '山东省', '河南省', '湖北省', '湖南省', '广东省', '广西壮族自治区',
            '海南省', '重庆市', '四川省', '贵州省', '云南省', '西藏自治区', '陕西省', '甘肃省', '青海省', '宁夏回族自治区',
            '新疆维吾尔自治区', '台湾省', '香港特别行政区', '澳门特别行政区'
        ];

        // 存储省份图层信息
        const provinceLayers = {};

        // 地图加载完成后初始化省份图层
        map.on('load', function () {
            // 加载每个省份的 GeoJSON 数据
            const provincePromises = provinces.map(province =>
                fetch(`/data/xzq/${province}.json`) // 替换为你的省份数据路径
                    .then(response => response.json())
                    .then(data => {
                        const sourceId = `${province}-source`;
                        const layerId = `${province}-layer`;

                        // 添加数据源
                        map.addSource(sourceId, {
                            type: 'geojson',
                            data: data
                        });

                        // 添加图层
                        map.addLayer({
                            id: layerId,
                            type: 'fill-extrusion',
                            source: sourceId,
                            paint: {
                                'fill-extrusion-color': '#305893', // 默认颜色
                                'fill-extrusion-height': 0, // 初始高度
                                'fill-extrusion-opacity': 0.6, // 透明度
                            }
                        });

                        // 存储图层信息
                        provinceLayers[layerId] = {
                            sourceId: sourceId,
                            isRaised: false // 是否抬升
                        };
                    })
                    .catch(error => console.error(`加载 ${province} 数据失败:`, error))
            );

            // 所有省份数据加载完成后绑定点击事件
            Promise.all(provincePromises)
                .then(() => {
                    console.log('所有省份数据加载完成');

                    // 点击省份时抬升效果
                    map.on('click', function (e) {
                        const features = map.queryRenderedFeatures(e.point, {
                            layers: Object.keys(provinceLayers)
                        });

                        if (features.length > 0) {
                            const clickedLayerId = features[0].layer.id;
                            Object.keys(provinceLayers).forEach(layerId => {
                                if (layerId === clickedLayerId) {
                                    const isRaised = provinceLayers[layerId].isRaised;
                                    if (!isRaised) {
                                        // 抬升并改变颜色
                                        map.setPaintProperty(layerId, 'fill-extrusion-height', 100000);
                                        map.setPaintProperty(layerId, 'fill-extrusion-color', '#FF0000');
                                        provinceLayers[layerId].isRaised = true;
                                    }
                                } else {
                                    // 恢复默认状态
                                    map.setPaintProperty(layerId, 'fill-extrusion-height', 0);
                                    map.setPaintProperty(layerId, 'fill-extrusion-color', '#305893');
                                    provinceLayers[layerId].isRaised = false;
                                }
                            });
                        } else {
                            // 点击空白区域时恢复所有省份
                            Object.keys(provinceLayers).forEach(layerId => {
                                map.setPaintProperty(layerId, 'fill-extrusion-height', 0);
                                map.setPaintProperty(layerId, 'fill-extrusion-color', '#305893');
                                provinceLayers[layerId].isRaised = false;
                            });
                        }
                    });
                })
                .catch(error => console.error('加载省份数据失败:', error));
        });
    </script>
  </body>
</html>

五、注意事项

数据准备

  • 确保阿里云 DataV 数据路径正确 (/data/xzq/)
  • 解决跨域问题(建议使用本地服务器)

性能优化

// 添加地形优化参数
map.addLayer({
  // ...
  paint: {
    'fill-extrusion-vertical-gradient': true // 启用垂直渐变
  }
});

扩展功能

  • 添加鼠标悬停效果
  • 结合弹出窗显示省份信息
  • 添加动画过渡效果

结语:本文完整实现了基于 Mapbox GL JS 的交互式三维地图效果,结合阿里云高质量地理数据,可快速应用于智慧城市、物流监控等可视化场景。建议开发者根据实际需求调整颜色、高度等参数实现定制化效果。

以下是一个使用MapboxGL添加一个marker并实现高亮闪烁效果完整代码示例: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>MapboxGL Marker Highlight Animation</title> <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" /> <script src="https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.js"></script> <link href="https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.css" rel="stylesheet" /> <style> body { margin: 0; padding: 0; } #map { position: absolute; top: 0; bottom: 0; width: 100%; } .highlight-animation { animation: highlight 1s infinite; } @keyframes highlight { 0% { opacity: 1; transform: scale(1); } 50% { opacity: 0.5; transform: scale(1.5); } 100% { opacity: 1; transform: scale(1); } } </style> </head> <body> <div id="map"></div> <script> mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN'; var lng = -122.4194; // 经度 var lat = 37.7749; // 纬度 var zoomLevel = 12; // 缩放级别 // 创建地图实例 var map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/streets-v11', center: [lng, lat], zoom: zoomLevel }); // 创建一个图层,并向地图添加一个marker var marker = new mapboxgl.Marker({ element: createMarkerElement() }) .setLngLat([lng, lat]) .addTo(map); // 为标记元素添加动画类 marker.getElement().classList.add('highlight-animation'); // 创建标记元素及样式 function createMarkerElement() { var el = document.createElement('div'); el.className = 'marker'; el.style.backgroundImage = 'url(https://example.com/your-marker-icon.png)'; // 替换为您自己的标记图标URL el.style.width = '40px'; el.style.height = '40px'; el.style.backgroundRepeat = 'no-repeat'; el.style.backgroundSize = 'cover'; return el; } </script> </body> </html> ``` 请确保将YOUR_MAPBOX_ACCESS_TOKEN替换为您自己的Mapbox访问令牌,并将`https://example.com/your-marker-icon.png`替换为您自己的标记图标的URL。 通过上述代码,您将能够在MapboxGL地图上添加一个marker,并为其添加高亮闪烁的动画效果。根据您的需求,您可以调整地图的样式、标记的位置和动画效果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃辣我第一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值