跳转到内容

天气获取

刀刀

12/31/2024

0 字

0 分钟

首页中实现了获取用户的天气和地理坐标功能,其原理是在用户进入网页时获取用户当前地理坐标的经纬度和城市编码,然后调用高德地图的 API 获取天气数据。

思考

首先先考虑实现的思路,而不是急于敲代码。

基本经纬度信息

如何获取基本经纬度信息呢?通过搜索,发现 Javascript 有一个内置的方法 Geolocation.getCurrentPosition() ,可以用来获取设备当前位置。他接收三个参数:

  • success :获取成功的回调函数,可以拿到当前坐标 position 参数,参数信息如下:

    数据类型描述
    coordsobject地理数据对象
    timestampstring时间戳

    其中,coords 对象的属性字段如下:

    描述
    latitude十进制数的纬度
    longitude十进制数的经度
    accuracy位置精度
    altitude海拔,海平面以上以米计
    altitudeAccuracy位置的海拔精度
    heading方向,从正北开始以度计
    speed速度,以米/每秒计
  • error :获取失败的回调函数,可以拿到 positionError 失败编码参数,编码参数示意如下:

    描述
    1地理位置信息的获取失败,因为该页面没有获取地理位置信息的权限。
    2地理位置获取失败,因为至少有一个内部位置源返回一个内部错误。
    3获取地理位置超时,通过定义PositionOptions.timeout 来设置获取地理位置的超时时长。
  • options :可选项对象,参数对象相关配置如下:

    描述
    enableHighAccuracytrue/false,它将告诉浏览器是否启用高精度设备,所谓的高精度设备包含但不局限于前面所提到的 GPS 和 WIFI,值为 true 的时候,浏览器会尝试启用这些设备,默认指为 true,在这种情况下,浏览器会尽可能地进行更为精确的查询。
    maximumAge单位毫秒,告诉设备缓存时间,主要用于设备的省电或者节省带宽方面。
    timeout单位毫秒,超时事件,获取位置信息时超出设定的这个时长,将会触发错误,捕获错误的函数将被调用,并且错误码指向TIMEOUT。

地理数据

不过该方法获取的经纬度并不太精确,为了确保精确度,这个模块额外引入了百度地图的方法,登录百度地图的个人控制台,复制相应的 script 资源引入代码。

html
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=自己的ak密钥"></script>

把获取到的经纬度作为参数传递给百度地图的 Point 方法,获取更精确的经纬度坐标。

然后把精确的经纬度坐标作为参数,调用 Geocoder().getLocation() 方法,获取城市地理数据。该方法接收两个参数:

  • 参数一:百度地图的 Point 方法返回的经纬度数据

  • 参数二:回调函数,接收一个参数对象,包含用户当前所在的省市区、经纬度等数据。

    点击查看详细参数
    js
    {
        "point": {
            "lng": 113.05787699999755,
            "lat": 23.038139009451392,
            "Xe": "inner"
        },
        "address": "广东省佛山市禅城区工业路20号",
        "addressComponents": {
            "streetNumber": "20号",
            "street": "工业路",
            "district": "禅城区",
            "city": "佛山市",
            "province": "广东省",
            "town": ""
        },
        "surroundingPois": [
            {
                "title": "东成立亿产业园",
                "uid": "6f4b4341d1c69898841541e0",
                "point": {
                    "lng": 113.05814433170181,
                    "lat": 23.039142113778208,
                    "Xe": "inner"
                },
                "city": "佛山市",
                "ij": "公司企业",
                "type": 0,
                "address": "佛山市禅城区张槎一路大富工业路20号",
                "postcode": null,
                "phoneNumber": null,
                "tags": [
                    "公司企业"
                ]
            },
            {
                "title": "彩业纺织",
                "uid": "08cb2818e53f7b631676430c",
                "point": {
                    "lng": 113.05756043312051,
                    "lat": 23.03737116624639,
                    "Xe": "inner"
                },
                "city": "佛山市",
                "ij": "",
                "type": 0,
                "address": "广东省佛山市禅城区工业路16-8号",
                "postcode": null,
                "phoneNumber": null
            }
        ],
        "business": "张槎",
        "content": {
            "address": "广东省佛山市禅城区工业路20号",
            "address_detail": {
                "adcode": 440604,
                "city": "佛山市",
                "city_code": 138,
                "country": "中国",
                "country_code": 0,
                "direction": "附近",
                "distance": "37",
                "district": "禅城区",
                "province": "广东省",
                "street": "工业路",
                "street_number": "20号",
                "town": "",
                "town_code": ""
            },
            "edz": {
                "name": ""
            },
            "business": "张槎",
            "poi_desc": "东成立亿产业园内",
            "poi_region": [
                {
                    "direction_desc": "内",
                    "name": "东成立亿产业园",
                    "tag": "公司企业",
                    "uid": "6f4b4341d1c69898841541e0",
                    "distance": "0"
                }
            ],
            "point": {
                "x": "12585682.240447",
                "y": "2619759.351684"
            },
            "surround_poi": [
                {
                    "addr": "佛山市禅城区张槎一路大富工业路20号",
                    "cp": "",
                    "direction": "内",
                    "distance": "0",
                    "name": "东成立亿产业园",
                    "poiType": "公司企业",
                    "point": {
                        "x": "12585712.000000",
                        "y": "2619880.000000"
                    },
                    "tag": "公司企业",
                    "tel": "",
                    "uid": "6f4b4341d1c69898841541e0",
                    "zip": ""
                },
                {
                    "addr": "广东省佛山市禅城区工业路16-8号",
                    "cp": " ",
                    "direction": "北",
                    "distance": "98",
                    "name": "彩业纺织",
                    "poiType": "",
                    "point": {
                        "x": "12585647.000000",
                        "y": "2619667.000000"
                    },
                    "tag": "",
                    "tel": "",
                    "uid": "08cb2818e53f7b631676430c",
                    "zip": ""
                }
            ]
        }
    }

天气

后续接入高德地图的相关 API,获取天气信息。具体方法这里不做过多详细的描述,可自行查看官方文档说明。

  • 添加应用

    我的应用 中点击右上角添加新的应用

  • 添加 key

    我的应用 中点击添加 key 按钮,选中 Web 服务 ,输入名称,白名单选填,完成后点击提交

    完成后

  • 添加数字签名

    为了防止有人爬了接口数据使用天气接口服务配额(之前配额被刷了两个星期,当时每天配额都直接被刷完了),点击刚添加好的 key 右侧的 “设置” 按钮,往下滑,开启数字签名,保存后数字签名后续有用

  • 调用接口

    接下来就可以调接口了,天气接口地址如下:

    js
    https://restapi.amap.com/v3/weather/weatherInfo?city=城市编码&extensions=all&key=自己的key&sig=hash值

    后面几个参数一一解释:

    1. city :高德地图对应的城市编码,对应的静态文件可前往 Github 仓库 copy —— 城市编码 。通过前面获取到的城市名称遍历获取对应的编码

    2. extensions :是否要展示4天的天气数据,不传则只展示当天,为 true 则展示4天

    3. key :对应的 key ,具体位置参考上方「添加 key 」步骤的截图马赛克部分

    4. sig :md5加密。根据官方文档的加密要求,把接口所有参数根据字母表 A-Z 的顺序,设置 hash

      js
      let hash = md5(`city=${code}&extensions=all&key=c687eb90870c9b75cf7c54d1124e2023d4af823828bdc195310c1e700a262ce6`)

现在就能根据接口的调用获取天气数据,做自己想要的操作即可。

注意事项

md5使用

Javascript 中,没有内置的 md5 加密算法的方法,如果直接使用会报错。

想要使用 md5 算法,需要先从 CDN 引入相关资源。

html
<script src="https://cdn.bootcss.com/blueimp-md5/2.12.0/js/md5.min.js"></script>

加密

在执行最后一步,调接口 hash 加密时,需要注意加密的字段和调用接口时传参的字段要按照字母表 A-Z 的顺序设置,否则接口报错,提示无权限。

代码

点击查看完整代码
js
const addComp = ref({});
const userPoint = ref({});
// 创建一个函数,主要功能是在调用html5的geolocation()前,先判断当前浏览器是否支持html5,(PC绝大部分浏览器不支持或者拒绝html5定位)
function getLocation() {
  let options = {
    enableHighAccuracy: true,
    maximumAge: 1000,
  };
  if (navigator.geolocation) {
    addComp.value = "正在定位...";
    // 调用html5的geolocation()方法
    // 第一个参数是定位成功后的回调函数,第二个参数是定位失败后的回调函数,第三个参数是定位的一些配置参数
    // 注意:第三个参数里有一个enableHighAccuracy属性,这个属性是用来开启高精度定位的,默认是false,开启后,会消耗更多的电量和流量
    navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
  } else {
    // 否则浏览器不支持geolocation
    addComp.value = "浏览器不支持定位";
    handleWeather();
  }
}

// 成功时的回调函数,获取定位成功返回的经纬度数据,结合百度那边提供的接口进行具体位置的转换
function onSuccess(position) {
  // 经度
  userPoint.value.longitude = position.coords.longitude;
  // 纬度
  userPoint.value.latitude = position.coords.latitude;
  // 根据经纬度获取地理位置,不太准确,获取城市区域还是可以的
  let point = new BMap.Point(
    userPoint.value.longitude,
    userPoint.value.latitude
  );
  let gc = new BMap.Geocoder();
  gc.getLocation(point, function (rs) {
    addComp.value = rs.addressComponents;
    let cityList = adcodeList.find((item) => {
      return addComp.value.province.includes(item.provice);
    });
    if (!cityList) return;
    let city = cityList.city.find((item) => {
      return addComp.value.city.includes(item.name);
    });
    handleWeather(city.adcode);
  });
}

// 失败时的回调函数,错误提示信息
function onError(error) {
  handleWeather();
  switch (error.code) {
    case 1:
      addComp.value = "位置服务被拒绝!";
      break;
    case 2:
      addComp.value = "暂时获取不到位置信息!";
      break;
    case 3:
      addComp.value = "获取信息超时!";
      break;
    case 4:
      addComp.value = "未知错误!";
      break;
  }
}

// 获取天气数据
const handleWeather = (code = "440100") => {
  let hash = md5(`city=${code}&extensions=all&key=c687eb90870c9b75cf7c54d1124e2023d4af823828bdc195310c1e700a262ce6`)
  axios
    .get(
      `https://restapi.amap.com/v3/weather/weatherInfo?city=${code}&extensions=all&key=c687eb90870c9b75cf7c54d1124e2023&sig=${hash}`
    )
    .then((res) => {
      // ...
    });
};