You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:基于Haversine/Great Circle Distance实现本地数据库地点高效筛选

嘿,这个问题我刚好有实践经验,咱们可以用haversine或者great_circle_distance结合本地数据库的边界框预筛选来高效实现,完全不用依赖Google Maps API,也不用遍历全量数据。下面分步骤给你讲清楚:

第一步:安装依赖

先在你的pubspec.yaml里添加需要的插件(选一个就行,功能差异不大):

dependencies:
  flutter:
    sdk: flutter
  haversine: ^1.0.2 # 或者用 great_circle_distance: ^1.0.1
  sqflite: ^2.3.0 # 假设你用sqflite做本地数据库,可根据实际情况调整

然后运行flutter pub get安装依赖。

需求1:生成当前位置周边的范围数值(边界框)

为了避免全量计算所有地点的距离,我们先计算一个经纬度边界框——也就是当前位置周围半径范围内的最大/最小纬度、经度值。这个边界框就是你需要的“周边范围数值”,同时能帮我们快速从数据库过滤出候选地点,大大减少后续计算量。

用基础地理公式就能计算(不用插件也能搞定):

import 'dart:math';

/// 根据当前经纬度和半径(公里)计算边界框
Map<String, double> calculateBoundingBox(double currentLat, double currentLng, double radiusKm) {
  const earthAvgRadius = 6371.0; // 地球平均半径(公里)
  // 计算纬度偏移量:每公里对应的纬度度数
  final latOffset = (radiusKm / earthAvgRadius) * (180 / pi);
  // 计算经度偏移量:受纬度影响,需要乘以cos(纬度)
  final lngOffset = (radiusKm / (earthAvgRadius * cos(currentLat * pi / 180))) * (180 / pi);

  return {
    'minLat': currentLat - latOffset,
    'maxLat': currentLat + latOffset,
    'minLng': currentLng - lngOffset,
    'maxLng': currentLng + lngOffset,
  };
}

比如你在圣保罗(-23.5505, -46.6333)、要筛选500公里半径的地点,调用这个函数就能得到对应的经纬度边界范围。

需求2:本地数据库筛选附近地点(高效版)

核心思路是两步筛选:先通过数据库的WHERE条件过滤边界框内的候选地点,再用距离插件计算精确距离,最终筛选出符合半径要求的地点。

步骤1:数据库预筛选(关键优化)

先给数据库表的latitude和longitude字段添加索引(这步很重要,能让边界框查询速度提升数倍)。然后用SQL语句快速过滤候选地点:

import 'package:sqflite/sqflite.dart';
import 'package:haversine/haversine.dart';

// 假设你的数据库表叫`places`,包含id、name、latitude、longitude字段
Future<List<Map<String, dynamic>>> fetchNearbyPlaces(double currentLat, double currentLng, double radiusKm) async {
  // 1. 获取数据库实例(替换成你的数据库初始化逻辑)
  final Database db = await openDatabase(
    'your_database.db',
    version: 1,
  );

  // 2. 计算边界框
  final boundingBox = calculateBoundingBox(currentLat, currentLng, radiusKm);

  // 3. 查询边界框内的候选地点(利用索引快速过滤)
  final candidatePlaces = await db.query(
    'places',
    where: 'latitude BETWEEN ? AND ? AND longitude BETWEEN ? AND ?',
    whereArgs: [
      boundingBox['minLat'],
      boundingBox['maxLat'],
      boundingBox['minLng'],
      boundingBox['maxLng'],
    ],
  );

  // 4. 用haversine计算精确距离,筛选最终符合条件的地点
  final List<Map<String, dynamic>> nearbyPlaces = [];
  final haversine = Haversine();

  for (final place in candidatePlaces) {
    final placeLat = place['latitude'] as double;
    final placeLng = place['longitude'] as double;

    // 计算当前位置到该地点的距离(公里)
    final distance = haversine.distance(
      LatLng(currentLat, currentLng),
      LatLng(placeLat, placeLng),
      Unit.Kilometer,
    );

    // 如果距离在半径范围内,加入结果列表(可顺便带上距离字段)
    if (distance <= radiusKm) {
      nearbyPlaces.add({...place, 'distance_km': distance.toStringAsFixed(2)});
    }
  }

  return nearbyPlaces;
}

如果用great_circle_distance,替换距离计算部分即可:

import 'package:great_circle_distance/great_circle_distance.dart';

// 替换上面的距离计算代码
final distance = GreatCircleDistance(
  latitude1: currentLat,
  longitude1: currentLng,
  latitude2: placeLat,
  longitude2: placeLng,
).distance(Unit.Kilometers);
关键注意事项
  • 一定要给经纬度字段加索引:没有索引的话,数据库会全表扫描,效率大打折扣。
  • 边界框是“近似范围”:因为地球是球体,边界框筛选出的候选地点可能有少量超出半径,但后续的精确距离计算会把它们排除,这个误差完全可以接受。
  • 性能优势:如果数据库有上万条数据,这种两步筛选的方式比全量计算距离快10倍以上,非常高效。

内容的提问来源于stack exchange,提问作者Colque Paxi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:24:00