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

JavaScript 实战:用Haversine公式计算附近5公里内的商家(附完整代码)

JavaScript 实战用Haversine公式计算附近5公里内的商家附完整代码当你在开发一个本地生活服务应用时如何快速找到用户当前位置5公里范围内的商家这个问题看似简单但背后涉及到地理空间计算的精妙算法。今天我们就来深入探讨如何用JavaScript实现这一功能。1. 理解Haversine公式的核心原理Haversine公式是计算球面上两点之间距离的经典算法特别适用于地球表面的距离计算。与简单的平面距离计算不同它考虑了地球的曲率因此计算结果更加准确。公式的核心思想是将经纬度转换为弧度然后利用三角函数的性质计算两点之间的中心角最后乘以地球半径得到实际距离。具体来说a sin²(Δφ/2) cos φ1 ⋅ cos φ2 ⋅ sin²(Δλ/2) c 2 ⋅ atan2(√a, √(1−a)) d R ⋅ c其中φ1, φ2两点的纬度弧度Δφ纬度差弧度Δλ经度差弧度R地球半径平均值约为6371公里提示地球并非完美球体Haversine公式假设地球为完美球体实际应用中误差通常在0.5%以内对于大多数商业应用已经足够精确。2. JavaScript实现基础距离计算让我们先实现一个基础版的Haversine公式计算函数/** * 计算两个经纬度之间的球面距离单位米 * param {number} lat1 - 第一个点的纬度十进制度 * param {number} lon1 - 第一个点的经度十进制度 * param {number} lat2 - 第二个点的纬度十进制度 * param {number} lon2 - 第二个点的经度十进制度 * returns {number} 距离米 */ function getDistance(lat1, lon1, lat2, lon2) { const toRad angle angle * Math.PI / 180; const R 6371000; // 地球半径单位米 const φ1 toRad(lat1); const φ2 toRad(lat2); const Δφ toRad(lat2 - lat1); const Δλ toRad(lon2 - lon1); const a Math.sin(Δφ/2) * Math.sin(Δφ/2) Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ/2) * Math.sin(Δλ/2); const c 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); return R * c; }使用示例// 计算北京天安门和上海外滩的距离 const distance getDistance(39.9087, 116.3975, 31.2400, 121.4900); console.log(两地距离约为 ${(distance/1000).toFixed(2)} 公里); // 输出两地距离约为 1068.01 公里3. 优化性能批量计算附近商家在实际应用中我们通常需要从大量商家中筛选出附近5公里内的商家。直接逐个计算距离显然效率不高。我们可以采用以下优化策略3.1 预筛选优化在应用Haversine公式前先进行粗略筛选function findNearbyBusinesses(userLat, userLon, businesses, maxDistance 5000) { // 粗略筛选纬度1度约111公里经度1度在赤道约111公里 const latRange maxDistance / 111000; const lonRange maxDistance / (111000 * Math.cos(userLat * Math.PI / 180)); return businesses.filter(business { // 先进行粗略筛选 if (Math.abs(business.lat - userLat) latRange || Math.abs(business.lon - userLon) lonRange) { return false; } // 精确计算 const distance getDistance(userLat, userLon, business.lat, business.lon); return distance maxDistance; }); }3.2 使用空间索引对于更大规模的数据可以考虑使用空间索引技术索引类型适用场景JavaScript实现网格索引中小规模数据简单二维数组划分R树大规模数据使用rbush等库GeoHash分布式系统使用geohash-js// 使用rbush实现R树索引 import RBush from rbush; const tree new RBush(); businesses.forEach(business { tree.insert({ minX: business.lon, minY: business.lat, maxX: business.lon, maxY: business.lat, business: business }); }); // 查询附近商家 const nearby tree.search({ minX: userLon - 0.1, // 适当扩大范围 minY: userLat - 0.1, maxX: userLon 0.1, maxY: userLat 0.1 }).map(item item.business);4. 完整实战外卖配送场景实现让我们实现一个完整的外卖配送场景解决方案class NearbyService { constructor(businesses []) { this.businesses businesses; this.tree this._buildIndex(businesses); } _buildIndex(businesses) { const tree new RBush(); businesses.forEach(b { tree.insert({ minX: b.lon - 0.01, minY: b.lat - 0.01, maxX: b.lon 0.01, maxY: b.lat 0.01, business: b }); }); return tree; } getNearby(userLat, userLon, radius 5000) { // 1. 粗略筛选 const latRange radius / 111000; const lonRange radius / (111000 * Math.cos(userLat * Math.PI / 180)); const candidates this.tree.search({ minX: userLon - lonRange, minY: userLat - latRange, maxX: userLon lonRange, maxY: userLat latRange }).map(item item.business); // 2. 精确计算 return candidates.filter(business { const distance getDistance(userLat, userLon, business.lat, business.lon); business.distance distance; // 添加距离信息 return distance radius; }).sort((a, b) a.distance - b.distance); // 按距离排序 } } // 使用示例 const businesses [ {id: 1, name: 餐厅A, lat: 39.9087, lon: 116.3975}, {id: 2, name: 餐厅B, lat: 39.9187, lon: 116.4075}, // 更多商家数据... ]; const service new NearbyService(businesses); const nearby service.getNearby(39.9087, 116.3975, 5000); console.log(nearby);5. 高级优化与注意事项5.1 缓存计算结果对于静态商家数据可以预先计算并缓存距离const cache new Map(); function getCachedDistance(lat1, lon1, lat2, lon2) { const key ${lat1},${lon1},${lat2},${lon2}; if (!cache.has(key)) { cache.set(key, getDistance(lat1, lon1, lat2, lon2)); } return cache.get(key); }5.2 Web Worker并行计算对于大量计算可以使用Web Worker避免阻塞主线程// worker.js self.onmessage function(e) { const {userLat, userLon, businesses} e.data; const results businesses.map(b ({ ...b, distance: getDistance(userLat, userLon, b.lat, b.lon) })).filter(b b.distance 5000); self.postMessage(results); }; // 主线程 const worker new Worker(worker.js); worker.postMessage({userLat, userLon, businesses}); worker.onmessage e { console.log(附近商家:, e.data); };5.3 常见问题与解决方案精度问题Haversine公式的误差通常在0.5%以内对于更高精度需求可以考虑Vincenty公式性能瓶颈对于超过10万条数据建议使用专业的地理空间数据库如PostGIS移动端优化考虑使用原生模块或WebAssembly提升计算性能在实际项目中我们还需要考虑用户位置的实时更新、商家营业状态等因素这些都会影响最终的筛选结果。

相关文章:

JavaScript 实战:用Haversine公式计算附近5公里内的商家(附完整代码)

JavaScript 实战:用Haversine公式计算附近5公里内的商家(附完整代码) 当你在开发一个本地生活服务应用时,如何快速找到用户当前位置5公里范围内的商家?这个问题看似简单,但背后涉及到地理空间计算的精妙算法…...

零基础语音转换成文字教程包教包会避坑干货全攻略

作为靠内容吃饭的自媒体人,我曾经被语音转写坑到怀疑人生——1小时的博主访谈录音,用某主流工具转了40分钟,结果错别字连篇,“内容变现”写成“内容变线”,方言部分全是乱码,最后熬夜手动改到两点&#xff…...

Nunchaku-flux-1-dev在互联网产品设计中的应用:用户旅程图智能生成

Nunchaku-flux-1-dev在互联网产品设计中的应用:用户旅程图智能生成 1. 引言 互联网产品团队经常面临一个共同挑战:如何快速理解用户在各个环节的真实体验?传统方法依赖人工访谈、问卷调研和手动绘制用户旅程图,整个过程耗时耗力…...

毕设程序java基于Web的宠物医院管理系统 SpringBoot驱动的宠物诊疗服务平台设计与实现 智能化宠物医疗健康档案管理系统开发

毕设程序java基于Web的宠物医院管理系统c692jn28 (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。随着社会经济发展和生活水平提升,宠物已成为众多家庭的重要成员&…...

别再只用Flash了!STM32F103的BKP备份寄存器实战:存20字节数据、做RTC校准、还能当事件记录器

STM32F103的BKP备份寄存器:20字节数据存储的隐藏利器 在嵌入式系统开发中,数据存储一直是个让人头疼的问题。Flash擦写次数有限,EEPROM速度慢,而SRAM掉电就丢数据。但你可能忽略了STM32F103芯片中一个低调却强大的功能——BKP备份…...

差分信号走线长度匹配与偏斜控制—高频高速场景核心技巧

差分信号是高速电路、射频电路的主流信号形式,USB、HDMI、PCIe、LVDS、以太网等接口全靠差分传输实现高速低干扰传输,而差分对的长度匹配是决定差分性能的核心,对内偏斜超标会直接导致差分信号失衡、共模干扰剧增、眼图闭合。​Q1&#xff1a…...

高速PCB走线长度匹配核心概念与底层逻辑

​在低速PCB设计中,走线长短似乎无关紧要,只要连通就能正常工作,但进入MHz以上高速电路、高频射频电路后,走线长度直接决定信号质量、系统稳定性,长度不匹配引发的信号偏斜更是导致产品失效的常见元凶。Q1:…...

STM32H7的Cache到底怎么配?从MPU寄存器到实战避坑,一篇讲透

STM32H7的Cache到底怎么配?从MPU寄存器到实战避坑,一篇讲透 第一次在STM32H7上启用Cache时,我遇到了一个诡异的Bug:DMA传输的数据总是比预期慢半拍。调试三天后才发现,问题出在AXI SRAM区域的Cache策略配置错误——MPU…...

通义千问1.5-1.8B-Chat-GPTQ-Int4在网络安全领域的应用:模拟攻击与防御策略分析

通义千问1.5-1.8B-Chat-GPTQ-Int4在网络安全领域的应用:模拟攻击与防御策略分析 最近和几个做安全研究的朋友聊天,他们提到一个挺有意思的痛点:做攻防演练或者安全测试的时候,经常需要构思各种攻击场景、编写测试用例&#xff0c…...

Windows10下RTABMAP+T265三维建图避坑指南:从安装到标定的完整流程

Windows10下RTABMAPT265三维建图避坑指南:从安装到标定的完整流程 在三维视觉与机器人领域,实时建图与定位技术正成为研究热点。Intel RealSense T265作为一款专为SLAM优化的追踪相机,配合RTABMAP这一强大的实时外观基准建图工具&#xff0c…...

亚德诺半导体在泰国新落成的先进制造工厂正式启用 | 美通社头条

、美通社消息:全球领先的半导体公司Analog Devices,Inc. 宣布公司在泰国新落成的先进制造工厂已经正式启用。此举将进一步提升ADI的先进制造与测试能力,同时推动公司在亚太地区形成更具韧性和可持续性的半导体生产布局。此次扩建基于ADI的混合制造战略&a…...

Youtu-VL-4B-Instruct-GGUF系统集成:在.NET框架中调用多模态模型服务

Youtu-VL-4B-Instruct-GGUF系统集成:在.NET框架中调用多模态模型服务 最近在做一个智能内容审核的项目,需要让系统不仅能看懂文字,还得能理解图片里的信息。团队评估了一圈,发现腾讯开源的Youtu-VL-4B-Instruct这个多模态模型挺合…...

OpenClaw多模型切换:ollama-QwQ-32B与Qwen1.5双接口配置指南

OpenClaw多模型切换:ollama-QwQ-32B与Qwen1.5双接口配置指南 1. 为什么需要多模型切换 去年冬天,当我第一次尝试用OpenClaw自动生成技术文档时,发现了一个有趣的现象:同一个模型在处理创意性内容和结构化代码时的表现差异巨大。…...

基金公司集体布局播客,不好好干金融玩耳朵经济想干嘛?

这些年,伴随着金融市场的竞争日趋白热化,各家金融机构是八仙过海各显神通,通过各种手段来获客拉客,在这其中最近一个新趋势开始出现了,这就是各家基金公司开始全面布局播客,不好好干金融去玩耳朵经济&#…...

NetApp NVME SSD 盘的学习笔记

写在前面最近在研究NetApp的NVME扩展柜NS224,于是就有了对于NetApp NVME SSD盘的一些问题,看到市面上有各种NVME的盘,但是NetApp的ONTAP是如何支持的,花了些时间研究了下,下面就是这个学习和整理的笔记,有了…...

VSCode+GitHub新手必看:5分钟搞定代码上传(附.gitignore配置技巧)

VSCodeGitHub新手指南:零基础可视化代码托管全流程 第一次将代码推送到GitHub时,我盯着终端里红色的报错信息手足无措。作为过来人,我完全理解新手面对版本控制系统的迷茫——那些晦涩的命令行操作就像一堵高墙。但别担心,这篇指…...

Outlook客户端登录世纪互联邮箱常见错误代码CAAC03ED的排查与修复指南

1. 错误代码CAAC03ED现象解析 当你用Outlook客户端登录世纪互联版邮箱时,突然弹出一个红色警告框,显示"此用户名可能不正确",或者更直接地告诉你遇到了错误代码CAAC03ED。这种情况通常发生在输入完账号密码点击登录之后&#xff0c…...

Ollama部署granite-4.0-h-350m多场景案例:政务公文摘要、新闻稿生成、会议纪要整理

Ollama部署granite-4.0-h-350m多场景案例:政务公文摘要、新闻稿生成、会议纪要整理 1. 快速上手granite-4.0-h-350m模型 granite-4.0-h-350m是一个轻量级但功能强大的指令模型,专门为各种文本处理任务设计。这个模型最大的特点是体积小巧但能力全面&am…...

避坑指南:解决‘api-ms-win-crt-runtime.dll缺失‘报错时90%人会犯的3个错误

深度解析:Windows系统DLL缺失报错的高效排查与修复策略 当你在Windows系统上运行某个软件时,突然弹出一个令人困惑的错误提示:"api-ms-win-crt-runtime.dll缺失"或"无法定位程序输入点ucrtbase.terminate于动态链接库api-ms-w…...

SeqGPT-560M效果展示:新闻通稿中自动识别机构、事件、时间三元组

SeqGPT-560M效果展示:新闻通稿中自动识别机构、事件、时间三元组 1. 项目简介 SeqGPT-560M是一个专门为企业级信息抽取需求定制开发的智能系统。与常见的聊天对话模型不同,这个系统专注于一件事:从非结构化文本中精准提取关键信息。 系统基…...

手机远程操控Windows与Ubuntu:VNC连接实战指南

1. 为什么需要手机远程控制电脑? 想象一下这样的场景:你正在出差,突然需要查看家里电脑上的某个文件;或者你的Ubuntu服务器放在机房,但手边没有显示器;又或者你想躺在沙发上用手机控制书房里的Windows电脑播…...

STM32实战指南:从零构建智能窗户控制系统(硬件选型+软件逻辑+数据可视化)

1. 智能窗户系统设计思路 第一次接触智能家居项目时,我被各种传感器和执行器的组合搞得晕头转向。直到亲手完成这个智能窗户系统,才发现原来从零开始搭建一个完整的物联网设备并没有想象中那么难。这个系统最吸引人的地方在于,它完美展现了如…...

RAGFlow实战:解决DeepSeekR1模型配置中的102错误(Ollama端口避坑指南)

RAGFlow实战:解决DeepSeekR1模型配置中的102错误(Ollama端口避坑指南) 在AI模型部署的实践中,容器化技术已成为主流选择。但当RAGFlow与DeepSeekR1这类前沿模型相遇时,网络配置的细微差异往往会导致令人头疼的连接问题…...

CentOS7下Tailscale子网路由配置全攻略:从安装到内网穿透实战

CentOS7下Tailscale子网路由配置全攻略:从安装到内网穿透实战 在远程办公和分布式团队协作成为常态的今天,如何安全高效地实现多地内网互联成为许多中小企业和个人开发者的刚需。Tailscale作为基于WireGuard的现代组网工具,凭借其零配置、端到…...

6.1.1 数据库技术的发展(三阶段演进)

数据管理技术从早期到现代的三个核心发展阶段,清晰对比了各阶段的背景、特点与优缺点。一、基础概念先理清数据处理:对数据进行收集、存储、加工、传播的一系列活动。数据管理:对数据进行分类、组织、编码、存储、检索和维护的活动&#xff0…...

【ESP32】学习笔记04.工程架构、组件

学习笔记:ESP32 笔记内容:04.工程架构、组件 日期:2026年3月22日 参考视频:视频1 视频2,从25:00开始讲工程架构 📖 目录一、工程架构二、ESP-IDF 的核心组件分层(从底层到应用)三、一…...

Leather Dress Collection保姆级教程:LoRA权重滑动条精细调节实操演示

Leather Dress Collection保姆级教程:LoRA权重滑动条精细调节实操演示 1. 项目介绍 Leather Dress Collection 是一个基于Stable Diffusion 1.5的LoRA模型集合,专门用于生成各种皮革服装风格的图像。这个系列包含了12个不同风格的皮革服装模型&#xf…...

Local AI MusicGen入门必看:Text-to-Music一键部署实操手册

Local AI MusicGen入门必看:Text-to-Music一键部署实操手册 1. 快速了解:你的私人AI作曲家 Local AI MusicGen是一个基于Meta MusicGen-Small模型的本地音乐生成工具。它最大的特点就是简单易用——你不需要懂任何乐理知识,只需要输入一段文…...

3步快速配置Ryujinx:免费Switch模拟器终极使用指南

3步快速配置Ryujinx:免费Switch模拟器终极使用指南 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx Ryujinx是一款采用C#语言开发的免费开源Nintendo Switch模拟器&#xff…...

SOONet模型计算机组成原理教学应用:定位CPU工作原理讲解片段

SOONet模型在计算机组成原理教学中的应用:精准定位CPU工作原理讲解片段 1. 引言:当复杂课程遇上精准学习 你有没有过这样的经历?为了搞懂《计算机组成原理》里一个抽象的概念,比如“CPU流水线”或者“缓存一致性”,不…...