当前位置: 首页 > news >正文

【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}from'@kit.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:number=map.calculateDistance(lan1,lan2)/1000;// toFixed(1) 保留一位小数,如 "0.8" "1.2"returndistance.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{constR=6371000;// 地球半径(米)constφ1=lat1*Math.PI/180;constφ2=lat2*Math.PI/180;constΔφ=(lat2-lat1)*Math.PI/180;constΔλ=(lon2-lon1)*Math.PI/180;consta=Math.sin(Δφ/2)*Math.sin(Δφ/2)+Math.cos(φ1)*Math.cos(φ2)*Math.sin(Δλ/2)*Math.sin(Δλ/2);constc=2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));returnR*c;// 距离(米)}

看起来复杂,但本质就是在球面上算两点之间的"大圆弧"长度。

用 MapKit 的calculateDistance省去了自己实现的麻烦,而且 MapKit 的实现经过优化,精度有保证。

3.2 返回值是"米"

map.calculateDistance返回单位是

letdistanceInMeters=map.calculateDistance(lan1,lan2);// 800(米)letdistanceInKm=distanceInMeters/1000;// 0.8(公里)

所以需要除以 1000 换算成公里。

四、toFixed(1) 精度处理

returndistance.toFixed(1);

toFixed(1)保留一位小数,结果是字符串

letd=0.83;d.toFixed(1);// "0.8"letd2=1.256;d2.toFixed(1);// "1.3"(四舍五入)letd3=10.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:boolean=false;

isCalculated是一个标志位,控制"是否显示距离"。为什么需要这个标志?

因为距离计算需要用户当前位置,而位置获取是异步的。在位置获取成功之前,currentLatitudecurrentLongitude都是 0,算出来的距离是无意义的(从 0,0 到加油站的距离)。

// init() 方法里openOrCloseMap(open?:boolean):void{this.isCalculated=true;// 只有打开地图(用户交互)后才显示距离// ...}

通过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:string=CalculateUtil.getDistance(31.93,118.86,31.94,118.87);// dist = "1.2"// 如果需要数字比较(比如排序)letdistNum:number=parseFloat(dist);

如果你需要对距离排序(由近到远),需要先用parseFloat()转回数字:

// 按距离排序letsorted=this.stationInfoList.sort((a,b)=>{letdistA=parseFloat(CalculateUtil.getDistance(lat,lon,a.latitude,a.longitude));letdistB=parseFloat(CalculateUtil.getDistance(lat,lon,b.latitude,b.longitude));returndistA-distB;});

总结

CalculateUtil.getDistance做了三件事:

  1. 构建 LatLng 对象:把经纬度数字打包成 MapKit 需要的格式
  2. 调用 calculateDistance:让 MapKit 计算球面距离(单位:米)
  3. 换算并格式化:除以 1000 换成公里,toFixed(1)保留一位小数

短短一个方法,封装了距离计算的所有细节,调用方只需要传入四个经纬度数字,得到一个格式化好的距离字符串,直接显示就行。

下一篇进入最重磅的部分——MapKit 地图组件,怎么把地图嵌入 HarmonyOS 应用里。

http://www.jsqmd.com/news/958201/

相关文章:

  • 为什么导航卫星都用L波段?从‘无线电窗口’到雷达命名的趣味GNSS信号频段冷知识
  • 如何快速掌握COMSOL自动化仿真:Python集成完整指南
  • 2026徐州家装消费调研深度报道:整装市场需求多元分化,本土标杆企业破解家装选购痛点 - 招财兔数字员工
  • 计算机视觉深度学习框架YOLOV8模型如何训练水下生物检测数据集 通过训练的权重推理识别检测海底生物检测数据集中的海参海胆扇贝海星鱼类潜水员海龟等
  • 从抽检到全检!光子精密闪测仪助力动力电池盖板高精度尺寸检测
  • 北京劳动纠纷律师费用多少钱 - mypinpai
  • DeepPCB数据集:3步构建高精度PCB缺陷检测AI系统
  • 实战演练:借助快马AI平台,用DevC++快速开发桌面时钟与倒计时器应用
  • AI视频与数字人工具
  • 终极JSON Viewer完全指南:27种主题定制你的Chrome浏览器JSON高亮体验
  • ArgoCD GitOps工作流实现:从应用部署到持续交付的完整实践
  • 2026 XCPC 杂题选解
  • 2026年新疆印刷包装工厂推荐榜:纸箱/礼盒/不干胶/画册与特种包装源头厂家实力解析 - 品牌企业推荐师(官方)
  • 智能编程伙伴:claude code在快马平台实现AI辅助代码审查与优化
  • 3分钟掌握VideoDownloadHelper:简单高效的网页视频下载插件终极指南 [特殊字符]
  • Sora 2雕塑动画化黄金参数表(含Subdivision Level/Frame Consistency Weight/Motion Prior Strength三维度最优阈值)
  • AI工具越强,人类越弱?斯坦福+MIT联合研究揭示创造力断层的3个隐藏拐点(仅开放72小时)
  • 一家教培机构在AI搜索里“消失“之后:GEO优化的技术复盘
  • 揭秘Java世界中内联函数机制解析之四
  • 2026年华中区域AI推广好用服务商排名 - mypinpai
  • 【LaTeX数学公式】
  • 北斗GNSS水库变形监测系统的应用与技术优势分析
  • AI礼品推荐准确率提升317%的关键:多模态意图识别+实时情感反馈闭环构建
  • DICOM文件里除了CT图像,还藏了哪些信息?一个Tag解读指南
  • 立足前沿趋势,读懂骁龙端侧AI进化逻辑
  • 如何利用单北斗GNSS系统实现大坝的变形监测?
  • AI表格工具
  • 2026苏州防水补漏哪家好?住建实地测评权威榜单TOP5|卫生间免砸砖/阳台屋顶/厨卫漏水维修(6月苏州专项调研) - 苏易修缮
  • Sora 2慢动作生成实测指南:5步完成4K/120fps超分辨率升格,附可复用Prompt模板
  • stm32中hal库配置st7789中的1.14ISP的tft屏幕