1. 背景介绍 Google Map 的 API 在国内不可用, 社区里面有一些通过反向代理的方式使用 Google Map API 的方式也不稳定, 经常遇到的问题就是ECONNREFUSED
. 为了提供稳定的服务本文试图调研 Bing Map API 来替换 Google Map API 的可行性, 结合业务背景, 主要调研替换两个接口:
海拔数据 : Elevation API, 可返回地球上某个位置的海拔数据,或沿路径的抽样海拔数据
时区 : Time Zone API 服务可接受纬度/经度坐标的 HTTP 请求以及所需的日期和时间。它返回该位置的时区数据,包括世界协调时间 (UTC) 的偏移量和夏令时
2. Bing Map 开发者 与其他服务一样, 使用 Bing Map 需要注册开发者账号并创建一个 Key, 使用 API 时需要提供 Key. 具体获取 Key 的步骤如下:
在开发者中心 注册账号
在My Account
中选择My keys
: 创建账号
完整的帮助文档请查看:Getting a Bing Maps Key
3. 使用和对比 3.1 代码 使用 nodejs 的 axios 客户端发送请求, 详细代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 import axios from "axios" ;const BingMapsKey = "使用个人开发者中心生成的Key" ;const getTimezone = async (point: string , datetime_utc: string ) => { const url = `https://dev.virtualearth.net/REST/v1/TimeZone/${point} ?datetime=${datetime_utc} &key=${BingMapsKey} ` ; const res = await axios.get (url, { headers : { "Content-Type" : "application/json" , }, }); const { resourceSets, statusCode, errorDetails } = res.data ; if (errorDetails) { console .error ("%j" , errorDetails); } if ( statusCode === 200 && resourceSets[0 ]?.resources [0 ]?.timeZone ?.utcOffset ) { console .log (statusCode); console .log ("%j" , resourceSets); const utcOffsets = resourceSets[0 ].resources [0 ].timeZone .utcOffset .split (":" ); const hours = +utcOffsets[0 ] > 0 ? +utcOffsets[0 ] + utcOffsets[1 ] / 60 : +utcOffsets[0 ] + (-1 * utcOffsets[1 ]) / 60 ; console .log (hours); } else { console .warn ("%j" , resourceSets); } }; const getElevation = async (point: string ) => { const url = `http://dev.virtualearth.net/REST/v1/Elevation/List?points=${point} &key=${BingMapsKey} ` ; const res = await axios.get (url, { headers : { "Content-Type" : "application/json" , }, }); const { resourceSets, statusCode, errorDetails } = res.data ; if (errorDetails) { console .error ("%j" , errorDetails); } if (statusCode === 200 && resourceSets[0 ]?.resources [0 ]?.elevations ?.length ) { console .log (statusCode); console .log ("%j" , resourceSets); console .log (resourceSets[0 ].resources [0 ].elevations [0 ]); } else { console .warn ("%j" , resourceSets); } }; (async () => { await getTimezone ( "39.6034810,-119.6822510" , new Date (1331766000000 ).toUTCString () ); await getElevation ("39.7391536,-104.9847034" ); })() .then (() => { console .log ("all done" ); }) .catch ((err ) => { console .log (err.response .data ); });
3.2 测试说明
海拔数据 Google Map API 取其返回值 中的elevation
字段, 而 Bing Map API 取elevations
数组中的值, 单位都是米
时区数据 Google Map API 取其返回值 中的rawOffset
字端,单位是秒, 而 Bing Map API 取utcOffset
, 是(+/-)hh:mm
格式, 上面的代码中将其换算成小时进行比较
代码中使用 Google Map API 中文档中的例子中的数据请求 Bing Map API 并将结果和 Google Map 文档中的案例值进行对比
3.3 测试结论
数据有效性上如果 Google Map 的结果四舍五入和 Bing Map 结果相差不大, 可以完全替换
Bing Map API 国内使用更友好, 推荐在国内使用 Bing Map API 替换 Google Map API
3.4 压缩数据 在使用海拔接口时如果点太多可以考虑压缩, 详细查看Point Compression Algorithm, 压缩算法代码(Typescript)如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 const encodePoints = (points: number [][] ) => { let latitude = 0 ; let longitude = 0 ; const result : string [] = []; for (const point in points) { const newLatitude = Math .round (points[point][0 ] * 100000 ); const newLongitude = Math .round (points[point][1 ] * 100000 ); let dy = newLatitude - latitude; let dx = newLongitude - longitude; latitude = newLatitude; longitude = newLongitude; dy = (dy << 1 ) ^ (dy >> 31 ); dx = (dx << 1 ) ^ (dx >> 31 ); let index = ((dy + dx) * (dy + dx + 1 )) / 2 + dy; while (index > 0 ) { let rem = index & 31 ; index = (index - rem) / 32 ; if (index > 0 ) rem += 32 ; result.push ( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-" [rem] ); } } return result.join ("" ); };
许可介绍
开发者许可证: 每年少于 125000 个计费请求事务, 免费
企业许可证: 超过则需要请求报价单, 使用企业许可证
Bing Map API 请求事务 : 在 REST Services 的统计表中, 除了以下几个之外, 其余的都是计费事务
RESTImagery-BasicMetadata
Route-IsochroneAsyncCallback
Routes-OptimizeItineraryAsyncCallbac
Route-SnapToRoadAsyncCallback
Route-TruckAsyncCallback
GeospatialEndpoint
所以本文使用的时区(RESTTimezone)和海拔(RESTElevations)都是计费事务