在前端项目中,我们经常需要根据用户数据中的地点信息,在地图上标出这些地点,并统计出现频次。例如,用户日志中提取出“城市: 北京”“城市: 上海”,然后将这些地点在地图上以圆点或热力图的形式展示。

本文介绍如何使用 高德地图 JavaScript API 实现这一功能,包括地点解析频次统计绘制地图,并详细说明了如何配置 API Key 的域名白名单,保证线上环境正常访问地图服务。


一功能概述

目标实现以下功能:

  1. 提取地点信息:从用户数据中提取所有地点名(如“北京”“上海”)。

  2. 统计出现次数:统计每个地点出现的次数。

  3. 地理编码:将地点名称转换为经纬度坐标。

  4. 地图绘制

    • 在地图上为每个地点绘制圆点。
    • 圆点大小与出现次数成比例。
    • 点击圆点显示地点名称与出现次数。
  5. 自动缩放地图视图,使所有点均可见。


二引入高德地图 API

1. 安全配置(安全 JS Code)

<script>
  window._AMapSecurityConfig = {
    securityJsCode: "YOUR_SECURITY_JS_CODE" // 替换为你的安全码
  }
</script>
  • 在加载高德地图脚本之前配置。
  • securityJsCode 是从高德开放平台申请的安全密钥,保护你的 API Key。

2. 加载高德地图脚本

<script src="https://webapi.amap.com/maps?v=2.0&key=YOUR_API_KEY&plugin=AMap.Geocoder"></script>
  • 替换 YOUR_API_KEY 为你申请的有效 Key。
  • 插件 AMap.Geocoder 用于地址转经纬度功能。

三初始化地图与 Geocoder

const map = new AMap.Map("map" {
  zoom: 4
  center: [104.1954 35.8617]  // 中国中心点坐标
})

const geocoder = new AMap.Geocoder({ city: '全国' })  // 支持全国地址解析

四地点统计与地图绘制逻辑

1. 假设已有地点数据

const placeNames = ["北京" "上海" "北京" "广州" "北京" "深圳" "广州"]

2. 统计出现次数

const counts = placeNames.reduce((acc name) => {
  acc[name] = (acc[name] || 0) + 1
  return acc
} {})

结果:

// counts = { 北京: 3 上海: 1 广州: 2 深圳: 1 }

3. 地址转换为经纬度(批量转换)

const uniqueNames = Object.keys(counts)

Promise.all(uniqueNames.map(name =>
  new Promise(resolve => {
    geocoder.getLocation(name (status result) => {
      if (status === 'complete' && result.geocodes.length) {
        const loc = result.geocodes[0].location
        resolve({ name lng: loc.lng lat: loc.lat count: counts[name] })
      } else {
        console.warn(`无法解析地址: ${name}`)
        resolve(null)
      }
    })
  })
)).then(locations => {
  const validLocations = locations.filter(l => l !== null)
  drawMarkers(validLocations)
})

4. 地图绘制圆点

function drawMarkers(locations) {
  map.clearMap()
  const bounds = new AMap.Bounds()

  locations.forEach(({ name lng lat count }) => {
    const radius = 10 + Math.sqrt(count) * 5  // 根据次数调整圆点大小

    const marker = new AMap.CircleMarker({
      center: [lng lat]
      radius
      fillColor: '#0078A8'
      fillOpacity: 0.5
      strokeColor: '#005577'
      strokeWeight: 1
      map: map
    })

    const infoWindow = new AMap.InfoWindow({
      content: `<strong>${name}</strong><br>出现次数: ${count}`
    })

    marker.on('click' () => {
      infoWindow.open(map [lng lat])
    })

    bounds.extend([lng lat])
  })

  if (locations.length > 0) {
    map.setBounds(bounds false [50 50 50 50])
  }
}

五完整示例代码(HTML + JS)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>多个地点统计并绘制地图</title>
  <style>
    #map { width: 100% height: 500px }
  </style>
  <script>
    window._AMapSecurityConfig = {
      securityJsCode: "YOUR_SECURITY_JS_CODE"
    }
  </script>
  <script src="https://webapi.amap.com/maps?v=2.0&key=YOUR_API_KEY&plugin=AMap.Geocoder"></script>
</head>
<body>
  <div id="map"></div>
  <script>
    const map = new AMap.Map("map" {
      zoom: 4
      center: [104.1954 35.8617]
    })

    const geocoder = new AMap.Geocoder({ city: '全国' })

    const placeNames = ["北京" "上海" "北京" "广州" "深圳" "广州"]

    const counts = placeNames.reduce((acc name) => {
      acc[name] = (acc[name] || 0) + 1
      return acc
    } {})

    const uniqueNames = Object.keys(counts)

    Promise.all(uniqueNames.map(name =>
      new Promise(resolve => {
        geocoder.getLocation(name (status result) => {
          if (status === 'complete' && result.geocodes.length) {
            const loc = result.geocodes[0].location
            resolve({ name lng: loc.lng lat: loc.lat count: counts[name] })
          } else {
            resolve(null)
          }
        })
      })
    )).then(locations => {
      const validLocations = locations.filter(l => l)
      drawMarkers(validLocations)
    })

    function drawMarkers(locations) {
      map.clearMap()
      const bounds = new AMap.Bounds()

      locations.forEach(({ name lng lat count }) => {
        const radius = 10 + Math.sqrt(count) * 5

        const marker = new AMap.CircleMarker({
          center: [lng lat]
          radius
          fillColor: '#0078A8'
          fillOpacity: 0.5
          strokeColor: '#005577'
          strokeWeight: 1
          map: map
        })

        const infoWindow = new AMap.InfoWindow({
          content: `<strong>${name}</strong><br>次数: ${count}`
        })

        marker.on('click' () => {
          infoWindow.open(map [lng lat])
        })

        bounds.extend([lng lat])
      })

      if (locations.length > 0) {
        map.setBounds(bounds false [50 50 50 50])
      }
    }
  </script>
</body>
</html>

六高德地图 API Key 域名白名单配置说明

1. 错误提示示例

如果你的网页域名未加入白名单,地图会加载失败,并报错:

<AMap JSAPI> KEY异常,错误信息:INVALID_USER_DOMAIN

2. 解决方案:配置域名白名单

  1. 访问 高德开放平台控制台
  2. 找到你申请的 Key
  3. 进入【应用管理】→【绑定域名】。
  4. 添加你网站的域名,格式示例:
  • 开发测试时:

    http://localhost/*
    http://127.0.0.1/*
    
  • 线上环境示例(假设部署在 Render):

    https://your-app.onrender.com/*
    
  1. 保存配置,等待几分钟生效。

3. 注意事项

  • 域名要包含协议(http 或 https)。
  • 支持通配符子域名,例如 *.onrender.com/*
  • 只允许已配置的域名使用你的 Key,提高安全性。

七总结

通过本文方法,结合高德地图 API 及域名白名单配置,开发者可以:

  • 实现多个地点的出现次数统计。
  • 将地点准确标注在地图上,使用圆点大小体现统计数据。
  • 实现交互式信息弹窗。
  • 保障线上环境稳定合法调用地图服务。

这对于用户行为分析销售网点可视化旅游足迹展示等场景非常实用。