GeoFlutterFire

GeoFlutterFire 是一个开源库,它允许您根据地理位置存储和查询一组键。GeoFlutterFire 的核心是将位置与字符串键一起存储。然而,它的主要优点是能够仅检索给定地理区域内的键——所有这些都实时进行。

GeoFlutterFire 使用 Firebase Firestore 数据库进行数据存储,允许查询结果随着其变化而实时更新。GeoFlutterFire 选择性地仅加载特定位置附近的数据,即使在处理海量数据集时,也能使您的应用程序保持轻便和响应迅速。

GeoFlutterFire 被设计为 cloud_firestore 插件的一个轻量级附加组件。为了保持简单,GeoFlutterFire 在您的 Firestore 数据库中以自己的格式存储数据。这使得您现有的数据格式和安全规则保持不变,同时仍然为您提供了一个简便的地理查询解决方案。

入门

您应该确保将 GeoFlutterFire 作为依赖项添加到您的 flutter 项目中。

dependencies:
  geoflutterfire: <latest-version>

如果您愿意,也可以直接引用 git 仓库

dependencies:
  geoflutterfire:
    git: git://github.com/DarshanGowda0/GeoFlutterFire.git

然后,您应该运行 flutter packages get 或在 IntelliJ 中更新您的包。

示例

example 文件夹中有一个详细的示例项目。可以看看它,或者继续阅读!

初始化

您需要一个已设置好 Firestore 的 Firebase 项目。

import 'package:geoflutterfire/geoflutterfire.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

// Init firestore and geoFlutterFire
final geo = Geoflutterfire();
final _firestore = FirebaseFirestore.instance;

写入地理数据

使用 GeoFirePoint 将地理数据添加到您的 firestore 文档中

GeoFirePoint myLocation = geo.point(latitude: 12.960632, longitude: 77.641603);

接下来,使用 Firestore 的 add 方法将 GeoFirePoint 添加到您的文档中

 _firestore
        .collection('locations')
        .add({'name': 'random name', 'position': myLocation.data});

调用 geoFirePoint.data 会返回一个包含 geohash 字符串 和一个 Firestore GeoPoint 的对象。它在您的数据库中应该看起来像这样。您可以随意命名该对象,甚至可以在单个文档中保存多个点。

查询地理数据

查询距离某个点 50 公里内的文档集合

// Create a geoFirePoint
GeoFirePoint center = geo.point(latitude: 12.960632, longitude: 77.641603);

// get the collection reference or query
var collectionReference = _firestore.collection('locations');

double radius = 50;
String field = 'position';

Stream<List<DocumentSnapshot>> stream = geo.collection(collectionRef: collectionReference)
                                        .within(center: center, radius: radius, field: field);

within 函数返回一个 DocumentSnapshot 数据列表的 Stream,以及一些有用的元数据,例如与中心点的距离。

stream.listen((List<DocumentSnapshot> documentList) {
        // doSomething()
      });

现在您有了一个实时数据流可以在地图上可视化。

:notebook: API

collection(collectionRef: CollectionReference)

创建一个 GeoCollectionRef,可用于进行地理查询,或者也可以像 firestore 的 add / set 功能一样用于写入数据。

示例

// Collection ref
// var collectionReference = _firestore.collection('locations').where('city', isEqualTo: 'bangalore');
var collectionReference = _firestore.collection('locations');
var geoRef = geo.collection(collectionRef: collectionReference);

注意:collectionReference 可以是 CollectionReference 或 Query 类型

执行地理查询

geoRef.within(center: GeoFirePoint, radius: double, field: String, {strictMode: bool})

按地理距离查询父 Firestore 集合。它将返回位于中心点 X 公里范围内的文档。
field 支持 Firestore 文档中的嵌套对象。

注意: 使用可选参数 strictMode = true 将文档严格限制在给定半径范围内。

示例

// For GeoFirePoint stored at the root of the firestore document
geoRef.within(center: centerGeoPoint, radius: 50, field: 'position', strictMode: true);

// For GeoFirePoint nested in other objects of the firestore document
geoRef.within(center: centerGeoPoint, radius: 50, field: 'address.location.position', strictMode: true);

每个 documentSnapshot.data() 还包含查询计算出的 distance

返回: Stream<List<DocumentSnapshot>>

写入数据

像在 Firestore 中一样写入数据

geoRef.add(data)

或者使用客户端的便捷方法之一

  • geoRef.setDoc(String id, var data, {bool merge}) - 使用 ID 设置集合中的文档。
  • geoRef.setPoint(String id, String field, double latitude, double longitude) - 向现有文档添加 geohash

读取数据

除了地理查询,您还可以像在 Firestore 中一样正常读取集合,但它是一个 Observable

  • geoRef.data() - documentSnapshot 的 Stream
  • geoRef.snapshot() - Firestore QuerySnapshot 的 Stream

point(latitude: double, longitude: double)

返回一个 GeoFirePoint,允许您创建 geohash、格式化数据和计算相对距离。

示例:var point = geo.point(38, -119)

获取器

  • point.hash 返回精度为 9 的 geohash 字符串
  • point.geoPoint 返回一个 Firestore GeoPoint
  • point.data 返回适合保存到 Firestore 数据库的数据对象

地理计算

  • point.distance(latitude, longitude) 到某点的 Haversine 距离

:zap: 提示

扩展到海量集合

可以构建包含数十亿文档的 Firestore 集合。此项目的主要动机之一是使地理查询成为可能,只需查询一部分数据即可。您可以将 Query 而不是 CollectionReference 传递给 collection(),然后所有地理查询都将通过该查询的约束进行范围限定。

注意:此查询需要复合索引,首次请求时 Firestore 会提示您创建该索引。

示例

var queryRef = _firestore.collection('locations').where('city', isEqualTo: 'bangalore');
var stream = geo
              .collection(collectionRef: queryRef)
              .within(center: center, radius: rad, field: 'position');

strictMode 的用法

对于较小的半径,建议使用 strictMode = false,以便也包含相邻哈希中的文档。

随着半径增大到较大的数值,相邻哈希的精度会获取到离半径边界相当远的文档,因此建议对较大的半径使用 strictMode = true

注意: strictMode 的过滤发生在客户端,因此在较大半径处进行过滤会以读取不必要的文档为代价。

以 RxDart 的方式进行动态查询

var radius = BehaviorSubject<double>.seeded(1.0);
var collectionReference = _firestore.collection('locations');

stream = radius.switchMap((rad) {
      return geo
          .collection(collectionRef: collectionReference)
          .within(center: center, radius: rad, field: 'position');
    });

// Now update your query
radius.add(25);

GitHub

https://github.com/DarshanGowda0/GeoFlutterFire