Flutter地理位置编码与Firestore集成插件geohash_plus_firestore的使用
Flutter地理位置编码与Firestore集成插件geohash_plus_firestore的使用
geohash_plus_firestore
是一个用于在Firestore中进行地理查询的插件。它基于可定制的Geohash库 geohash_plus
进行地理编码。默认的编码是4位Base16编码,因此与传统的Geohash相比,在不同缩放级别下精度差异较小,搜索时损失较少。此外,该插件提供了两种查询方法,使得构建避免Firestore限制的查询变得更加容易。
geohash_plus
允许你自定义每个字符的位数和转换字母表。这意味着你可以根据需求选择Base16或其他编码方式,而无需更改转换算法,传统Geohash通常是Base32编码。
添加文档
首先,创建一个 GeohashStore
对象,并通过指定数据属性将其添加到Firestore中。
final store = GeohashStore(57.64911, 10.40744);
db.collection('user').add({
'location': store.data,
'name': 'test1',
'age': 30,
});
搜索数据
你可以通过指定区域坐标来搜索文档内的矩形范围。
final col = db.collection('user');
final bounds = LatLngBounds(
northEast: LatLng(57.650, 10.408),
southWest: LatLng(57.649, 10.407),
);
final result = await GeohashQuery.withinBounds(bounds,
query: col, field: 'location');
查询
query
可以传递为 CollectionReference
或 Query
。查询类型可以是数组或排序,分别使用 arrayContainsAny
和 orderBy
内部实现。根据使用的查询类型选择不同的类型。默认类型为数组,通信量较少。
final query = db.collection('user').where('age', isGreaterThan: 35);
final result = await GeohashQuery.withinBounds(bounds,
query: query, field: 'location', type: QueryType.array);
完整示例
以下是一个完整的示例,展示了如何在Flutter应用中使用 geohash_plus_firestore
插件进行地理编码和查询。
import 'dart:math';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fake_cloud_firestore/fake_cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart' hide LatLngBounds;
import 'package:latlong2/latlong.dart' as latlong2;
import 'package:geohash_plus_firestore/geohash_plus_firestore.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _controller = MapController();
final _fireStore = FakeFirebaseFirestore();
final _searched = <String>[];
final _circles = <String, CircleMarker>{};
final _polygons = <String, Polygon>{};
[@override](/user/override)
void initState() {
setup();
super.initState();
}
void setup() async {
final col = _fireStore.collection('users');
final user = await col.limit(1).get();
if (user.size == 0) {
Future.wait(
Iterable.generate(50).map((i) {
final store = GeohashStore(
51.4 + Random().nextDouble() / 5,
-0.3 + Random().nextDouble() / 2,
);
return col.add({
'location': store.data,
'name': 'user_$i',
});
}),
);
}
_controller.mapEventStream.listen((event) async {
if (event.zoom < 10) return;
if (event is MapEventMoveEnd) {
final ne = _controller.bounds?.northEast;
final sw = _controller.bounds?.southWest;
if (ne == null || sw == null) return;
final bounds = LatLngBounds(
northEast: LatLng(ne.latitude, ne.longitude),
southWest: LatLng(sw.latitude, sw.longitude),
);
final result = await GeohashQuery.withinBounds(bounds,
query: col, field: 'location', clip: false, exclude: _searched);
_searched.addAll(result.searched);
for (var doc in result.documents) {
final pos = doc.get('location.point') as GeoPoint;
setState(() {
_circles[doc.id] = CircleMarker(
point: latlong2.LatLng(pos.latitude, pos.longitude),
radius: 10,
color: Colors.red,
);
});
}
for (var geohash in result.searched) {
final bounds = GeoHash.decode(geohash, bits: 4).bounds;
setState(() {
_polygons[geohash] = Polygon(
points: [
latlong2.LatLng(bounds.northEast.latitude, bounds.northEast.longitude),
latlong2.LatLng(bounds.northWest.latitude, bounds.northWest.longitude),
latlong2.LatLng(bounds.southWest.latitude, bounds.southWest.longitude),
latlong2.LatLng(bounds.southEast.latitude, bounds.southEast.longitude),
],
borderColor: Colors.black45,
borderStrokeWidth: 3,
color: Colors.green.withOpacity(0.2),
isFilled: true,
);
});
}
}
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: FlutterMap(
mapController: _controller,
options: MapOptions(
center: latlong2.LatLng(51.509364, -0.128928),
zoom: 14,
),
nonRotatedChildren: [
AttributionWidget.defaultWidget(
source: 'OpenStreetMap contributors',
onSourceTapped: null,
),
],
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.example.app',
),
CircleLayer(
circles: _circles.values.toList(),
),
PolygonLayer(
polygons: _polygons.values.toList(),
),
],
),
),
);
}
}
更多关于Flutter地理位置编码与Firestore集成插件geohash_plus_firestore的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter地理位置编码与Firestore集成插件geohash_plus_firestore的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用geohash_plus_firestore
插件来实现地理位置编码并与Firestore集成的示例代码。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加geohash_plus_firestore
依赖:
dependencies:
flutter:
sdk: flutter
geohash_plus_firestore: ^最新版本号 # 请替换为实际的最新版本号
cloud_firestore: ^最新版本号 # 请替换为实际的最新版本号
然后运行flutter pub get
来安装依赖。
2. 初始化Firestore
在你的应用入口文件(通常是main.dart
)中初始化Firestore:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:geohash_plus_firestore/geohash_plus_firestore.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
3. 使用geohash_plus_firestore
插件
在你的页面或组件中,使用geohash_plus_firestore
插件来编码地理位置并存储到Firestore中。
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:geohash_plus_firestore/geohash_plus_firestore.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String? geohash;
double? latitude;
double? longitude;
final CollectionReference<Map<String, dynamic>> firestoreCollection =
FirebaseFirestore.instance.collection('locations');
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.');
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
setState(() {
latitude = position.latitude;
longitude = position.longitude;
geohash = GeoHash.encode(latitude!, longitude!);
});
// Save to Firestore
await firestoreCollection.add({
'latitude': latitude,
'longitude': longitude,
'geohash': geohash,
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GeoHash Plus Firestore Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Latitude: ${latitude ?? 'Loading...'}'),
Text('Longitude: ${longitude ?? 'Loading...'}'),
Text('GeoHash: ${geohash ?? 'Loading...'}'),
SizedBox(height: 20),
ElevatedButton(
onPressed: getCurrentLocation,
child: Text('Get Current Location'),
),
],
),
),
);
}
}
4. 权限配置
确保在AndroidManifest.xml
和Info.plist
中配置了必要的权限。
Android (AndroidManifest.xml
)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
iOS (Info.plist
)
<key>NSLocationAlwaysUsageDescription</key>
<string>Need location always permission</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Need location when in use permission</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Need location always and when in use permission</string>
5. 运行应用
现在你可以运行你的Flutter应用,它将请求位置权限,获取当前位置,生成GeoHash,并将其存储到Firestore中。
请确保你的Firebase项目已经配置好,并且你的应用有正确的Firebase凭证。