【HarmonyOS实战】 距离计算:用MapKit一行代码算出两点距离
文章目录前言一、CalculateUtil 源码二、mapCommon.LatLng 坐标结构三、map.calculateDistance 的底层原理3.1 Haversine 公式了解即可3.2 返回值是米四、toFixed(1) 精度处理五、在 GasStationPage 中的使用5.1 isCalculated 是什么5.2 单位字符串获取六、距离显示效果七、注意事项7.1 坐标系的问题7.2 返回值是字符串总结前言打开附近加油站的列表每个加油站旁边都会显示0.8公里这样的距离信息。这个数字是怎么算出来的背后很简单拿到用户当前的经纬度和加油站的经纬度算一下球面两点距离再换算成公里。CalculateUtil.ets就是做这件事的只有短短一个方法但值得细讲。项目预览一、CalculateUtil 源码// entry/src/main/ets/utils/CalculateUtil.etsimport{map,mapCommon}fromkit.MapKit;exportclassCalculateUtil{// 计算两个经纬度坐标之间的距离公里publicstaticgetDistance(lat1:number,long1:number,lat2:number,long2:number):string{letlan1:mapCommon.LatLng{latitude:lat1,longitude:long1};letlan2:mapCommon.LatLng{latitude:lat2,longitude:long2};// map.calculateDistance 返回的是米除以 1000 换算成公里letdistance:numbermap.calculateDistance(lan1,lan2)/1000;// toFixed(1) 保留一位小数如 0.8 1.2returndistance.toFixed(1);}}就这么短但有几个知识点值得展开。二、mapCommon.LatLng 坐标结构letlan1:mapCommon.LatLng{latitude:lat1,// 纬度longitude:long1// 经度};mapCommon.LatLng是 MapKit 提供的坐标类型// MapKit 内部定义伪代码interfaceLatLng{latitude:number;// 纬度范围 [-90, 90]longitude:number;// 经度范围 [-180, 180]}这是 MapKit 的标准坐标格式传入 latitude纬度和 longitude经度构建出一个坐标点。三、map.calculateDistance 的底层原理map.calculateDistance(point1, point2)内部使用Haversine 公式计算两点在球面上的距离考虑了地球的球面曲率。3.1 Haversine 公式了解即可如果你不用 MapKit自己实现距离计算公式如下functioncalculateDistanceManually(lat1:number,lon1:number,lat2:number,lon2:number):number{constR6371000;// 地球半径米constφ1lat1*Math.PI/180;constφ2lat2*Math.PI/180;constΔφ(lat2-lat1)*Math.PI/180;constΔλ(lon2-lon1)*Math.PI/180;constaMath.sin(Δφ/2)*Math.sin(Δφ/2)Math.cos(φ1)*Math.cos(φ2)*Math.sin(Δλ/2)*Math.sin(Δλ/2);constc2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));returnR*c;// 距离米}看起来复杂但本质就是在球面上算两点之间的大圆弧长度。用 MapKit 的calculateDistance省去了自己实现的麻烦而且 MapKit 的实现经过优化精度有保证。3.2 返回值是米map.calculateDistance返回单位是米letdistanceInMetersmap.calculateDistance(lan1,lan2);// 800米letdistanceInKmdistanceInMeters/1000;// 0.8公里所以需要除以 1000 换算成公里。四、toFixed(1) 精度处理returndistance.toFixed(1);toFixed(1)保留一位小数结果是字符串letd0.83;d.toFixed(1);// 0.8letd21.256;d2.toFixed(1);// 1.3四舍五入letd310.0;d3.toFixed(1);// 10.0为什么返回string而不是number因为调用方需要把距离数字和单位字符串拼在一起显示// GasStationPage.etsText(${CalculateUtil.getDistance(this.currentLatitude,this.currentLongitude,gasStation.latitude,gasStation.longitude)}${单位字符串})直接返回字符串拼接更方便。五、在 GasStationPage 中的使用// GasStationPage.etsif(this.isCalculated){Text(${CalculateUtil.getDistance(this.currentLatitude,// 用户当前位置纬度this.currentLongitude,// 用户当前位置经度gasStation.latitude,// 加油站纬度gasStation.longitude// 加油站经度)}${this.getUIContext().getHostContext()?.resourceManager.getStringSync($r(app.string.calculate_text2).id)}).fontSize(Constants.FONT_SIZE_14).fontColor($r(app.color.gas_station_name_color));}5.1 isCalculated 是什么StateisCalculated:booleanfalse;isCalculated是一个标志位控制是否显示距离。为什么需要这个标志因为距离计算需要用户当前位置而位置获取是异步的。在位置获取成功之前currentLatitude和currentLongitude都是 0算出来的距离是无意义的从 0,0 到加油站的距离。// init() 方法里openOrCloseMap(open?:boolean):void{this.isCalculatedtrue;// 只有打开地图用户交互后才显示距离// ...}通过isCalculated控制确保距离只在有意义的时候显示。5.2 单位字符串获取this.getUIContext().getHostContext()?.resourceManager.getStringSync($r(app.string.calculate_text2).id)这一串是从资源管理器里获取字符串中文环境返回公里英文环境返回Km支持多语言。六、距离显示效果完整的距离文本0.8公里 中文环境 0.8Km 英文环境当用户点击某个列表项打开地图后isCalculated true距离数字就会出现在加油站名称旁边。七、注意事项7.1 坐标系的问题calculateDistance计算的是真实地球球面距离不受坐标系影响WGS84 还是 GCJ02。但是用户的当前位置通过geoLocationManager.getCurrentLocation()获取是WGS84 坐标而地图显示用的是GCJ02 坐标。对于距离计算来说WGS84 和 GCJ02 之间的偏差约 100-300 米在计算附近加油站距离时可以接受不需要转换。但如果你做的是精确路线规划则需要坐标转换。7.2 返回值是字符串// getDistance 返回 string不是 numberletdist:stringCalculateUtil.getDistance(31.93,118.86,31.94,118.87);// dist 1.2// 如果需要数字比较比如排序letdistNum:numberparseFloat(dist);如果你需要对距离排序由近到远需要先用parseFloat()转回数字// 按距离排序letsortedthis.stationInfoList.sort((a,b){letdistAparseFloat(CalculateUtil.getDistance(lat,lon,a.latitude,a.longitude));letdistBparseFloat(CalculateUtil.getDistance(lat,lon,b.latitude,b.longitude));returndistA-distB;});总结CalculateUtil.getDistance做了三件事构建 LatLng 对象把经纬度数字打包成 MapKit 需要的格式调用 calculateDistance让 MapKit 计算球面距离单位米换算并格式化除以 1000 换成公里toFixed(1)保留一位小数短短一个方法封装了距离计算的所有细节调用方只需要传入四个经纬度数字得到一个格式化好的距离字符串直接显示就行。下一篇进入最重磅的部分——MapKit 地图组件怎么把地图嵌入 HarmonyOS 应用里。