Flutter地理位置查询插件geoquery_firestore的使用
Flutter地理位置查询插件geoquery_firestore的使用
引言
GeoQueryFirestore 是一个设计用于在 Firestore 中进行高效且高性能的地理空间查询的综合包。虽然现有的插件如 GeoFlutterFire 提供了基本的地理空间功能,但 GeoQueryFirestore 解决了它们的局限性,通过引入 orderby
、limit
和分页功能,这对于处理大量数据集尤为重要。
动机
GeoQueryFirestore 的主要动机是解决现有 Firestore 地理空间查询插件的不足。虽然这些插件提供了基本的功能,但缺乏一些关键特性:
- 排序:根据指定字段对结果进行排序的能力,对于有组织地检索数据至关重要。
- 限制:限制返回的结果数量,对于防止性能瓶颈和有效管理数据检索非常重要。
- 分页:按批次检索结果的能力,特别是在处理大数据集和分页用户界面时尤为重要。
这些限制阻碍了在 Firestore 中有效利用地理空间查询,特别是对于大规模应用。GeoQueryFirestore 旨在通过提供强大的地理空间查询解决方案来弥补这一差距。
系统概述
GeoQueryFirestore 通过智能选择适当的精度级别来运行,该级别基于提供的范围或地图边界。然后,它识别出指定区域内潜在的 GeoHashes,从而实现高效的过滤和基于坐标检索文档。
数据库结构
为了有效地使用 GeoQueryFirestore,Firestore 数据库应按以下方式构建:
- GeoHash 数组:每个 Firestore 文档必须包含一个字段,该字段保存表示文档位置的 GeoHashes 数组。字段名称可以根据开发人员的偏好进行自定义。
- GeoHash 精度:为获得最佳性能,确保 GeoHash 精度与查询需求相匹配。例如,对于大多数用例,建议使用 8 个字符的精度(例如
swg5r323
)。
示例用法
GeoQueryFirestore 提供了方便的方法来进行各种地理空间查询:
范围查询
参数:
center
:搜索区域的中心点(纬度和经度)。selectedRange
:预定义的地理空间范围(例如GeoQueryFirestoreRanges.km20
)。customRangeInMeters
(可选):如果selectedRange
是GeoQueryFirestoreRanges.custom
,则使用自定义范围(以米为单位)。startAfterDocument
(可选):用于分页的文档快照。enablePagination
(可选):是否启用分页以按批次检索结果。默认值为false
。limit
(可选):单个查询中要检索的最大文档数。默认值为 20。
final center = LatLng(37.7749, -122.4194); // 中心点
final range = GeoQueryFirestoreRanges.km20; // 搜索范围(20公里)
final query = GeoQueryFirestore(
query: FirebaseFirestore.instance.collection('restaurants'),
geohashFieldPath: 'location.geohashes',
);
final documents = await query.byRange(
center: center,
selectedRange: range,
);
自定义范围
final documents = await query.byRange(
center: center,
selectedRange: GeoQueryFirestoreRanges.custom,
customRangeInMeters: 10000
);
启用分页
final documents = await query.byRange(
center: center,
selectedRange: GeoQueryFirestoreRanges.custom,
customRangeInMeters: 10000,
enablePagination:true
);
手动处理分页
final documents = await query.byRange(
center: center,
selectedRange: GeoQueryFirestoreRanges.custom,
customRangeInMeters: 10000,
enablePagination:true,
startAfterDocument:theDocument
);
限制文档数量
final documents = await query.byRange(
center: center,
selectedRange: GeoQueryFirestoreRanges.custom,
customRangeInMeters: 10000,
enablePagination:true,
startAfterDocument:theDocument,
limit:10
);
按地图边界查询
参数:
bounds
:定义搜索区域的LatLngBounds
对象。strict
(可选):是否严格遵循边界过滤 GeoHashes。默认值为true
。enablePagination
(可选):是否启用分页以按批次检索结果。默认值为false
。limit
(可选):单个查询中要检索的最大文档数。默认值为 20。
final bounds = LatLngBounds(LatLng(37.7131, -122.4194), LatLng(37.8354, -122.3792)); // 搜索边界
final strict = true; // 严格的边界遵循
final query = GeoQueryFirestore(
query: FirebaseFirestore.instance.collection('restaurants'),
geohashFieldPath: 'location.geohashes',
);
final documents = await query.byMapBounds(
bounds: bounds,
strict: strict,
);
启用分页
final documents = await query.byMapBounds(
bounds: bounds,
strict: strict,
enablePagination:true
);
限制文档数量
final documents = await query.byMapBounds(
bounds: bounds,
strict: strict,
enablePagination:true,
limit: 10
);
警告
- 这是一个解决方案,但它并不能完全正确地工作。GeoHashes 将地球表面划分为矩形单元格,其边界可能不会完美地与实际搜索区域对齐,尤其是对于不规则形状或靠近 GeoHash 单元格边界的区域。这可能导致某些边缘情况,其中文档可能会落在预期搜索区域之外。
- 在第一次运行详细的查询时,你可能会收到索引错误。你可以根据错误消息中的链接创建索引。这样它将开始正常工作。
建议
- 了解更多关于 GeoHashes 可以查看 GeoHash 和 Wikipedia上的GeoHash。
示例代码
// 忽略文件:library_private_types_in_public_api, depend_on_referenced_packages
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:geoquery_firestore/geoquery_firestore.dart';
import 'package:geoquery_firestore/enums/enums.dart';
import 'package:latlong2/latlong.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'GeoQueryFirestore 示例(无地图)',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
[@override](/user/override)
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final LatLng _center = LatLng(37.7749, -122.4194); // 初始中心点
// 初始搜索半径(以米为单位)
List<String> _restaurants = [];
[@override](/user/override)
void initState() {
super.initState();
// 执行地理空间查询
_performQuery();
}
void _performQuery() {
// 创建 GeoQueryFirestore 实例
final query = GeoQueryFirestore(
query: FirebaseFirestore.instance.collection('restaurants'),
geohashFieldPath: 'location.geohashes',
);
// 在指定范围内执行地理空间查询
query
.byRange(_center, selectedRange: GeoQueryFirestoreRanges.km20)
.then((documents) {
// 提取餐厅名称
setState(() {
_restaurants =
documents.map((document) => document['name'] as String).toList();
});
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GeoQueryFirestore 示例(无地图)'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'在您所在位置附近找到的餐厅:',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 16),
if (_restaurants.isNotEmpty)
Column(
children:
_restaurants.map((restaurant) => Text(restaurant)).toList(),
),
if (_restaurants.isEmpty)
Text('此区域未找到餐厅。'),
SizedBox(height: 16),
ElevatedButton(
onPressed: _performQuery,
child: Text('刷新结果'),
),
],
),
),
);
}
}
更多关于Flutter地理位置查询插件geoquery_firestore的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter地理位置查询插件geoquery_firestore的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个使用 geoquery_firestore
插件在 Flutter 应用中查询地理位置的示例代码。这个插件允许你使用 Firestore 的 Geopoint 数据类型来执行地理查询。
首先,你需要确保在 pubspec.yaml
文件中添加了必要的依赖项:
dependencies:
flutter:
sdk: flutter
cloud_firestore: ^3.4.4 # 请检查最新版本
geoquery_firestore: ^0.3.0 # 请检查最新版本
geolocator: ^9.0.2 # 用于获取用户位置,请检查最新版本
然后,运行 flutter pub get
来获取这些依赖项。
初始化 Firestore 和 Geolocator
在你的主 Dart 文件中(通常是 main.dart
),你需要初始化 Firestore 和 Geolocator。
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:geoquery_firestore/geoquery_firestore.dart';
import 'package:geolocator/geolocator.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: GeoQueryScreen(),
);
}
}
class GeoQueryScreen extends StatefulWidget {
@override
_GeoQueryScreenState createState() => _GeoQueryScreenState();
}
class _GeoQueryScreenState extends State<GeoQueryScreen> {
late Firestore _firestore;
late CollectionReference<Map<String, dynamic>> _collectionRef;
late GeoFirestore? _geoFirestore;
late Position? _currentUserLocation;
@override
void initState() {
super.initState();
_firestore = Firestore.instance;
_collectionRef = _firestore.collection('locations');
_geoFirestore = GeoFirestore(_firestore, 'locations');
getCurrentLocation();
}
Future<void> getCurrentLocation() async {
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return Future.error('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
_currentUserLocation = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
if (_currentUserLocation != null) {
performGeoQuery(_currentUserLocation!.latitude, _currentUserLocation!.longitude);
}
}
Future<void> performGeoQuery(double latitude, double longitude) async {
GeoQuery query = _geoFirestore!.queryAtLocation(
new GeoPoint(latitude, longitude),
distance: 10.0, // 查询半径为10公里
);
query.getDocuments()
.then((snapshot) {
snapshot.documents.forEach((doc) {
print(doc.data());
});
})
.catchError((onError) {
print('Error: $onError');
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GeoQuery Firestore Example'),
),
body: Center(
child: Text('Getting your current location and performing geo query...'),
),
);
}
}
解释
-
依赖项:我们在
pubspec.yaml
中添加了cloud_firestore
、geoquery_firestore
和geolocator
依赖项。 -
初始化:在
GeoQueryScreen
的initState
方法中,我们初始化了 Firestore 和 GeoFirestore 实例,并请求当前用户的位置。 -
获取位置:
getCurrentLocation
方法使用 Geolocator 插件获取当前用户的位置,并在获取到位置后调用performGeoQuery
方法。 -
执行地理查询:
performGeoQuery
方法使用 GeoFirestore 插件执行地理查询,查询以当前用户位置为中心,半径为10公里的文档,并打印每个文档的数据。
这个示例展示了如何使用 geoquery_firestore
插件来执行地理查询。当然,你可以根据你的需求进一步扩展和修改这个示例。