Flutter地理位置查询插件geoquery_firestore的使用

Flutter地理位置查询插件geoquery_firestore的使用

引言

336932728-dba4ac67-d3c1-43d1-9389-2756b52427e4

GeoQueryFirestore 是一个设计用于在 Firestore 中进行高效且高性能的地理空间查询的综合包。虽然现有的插件如 GeoFlutterFire 提供了基本的地理空间功能,但 GeoQueryFirestore 解决了它们的局限性,通过引入 orderbylimit 和分页功能,这对于处理大量数据集尤为重要。

动机

GeoQueryFirestore 的主要动机是解决现有 Firestore 地理空间查询插件的不足。虽然这些插件提供了基本的功能,但缺乏一些关键特性:

  • 排序:根据指定字段对结果进行排序的能力,对于有组织地检索数据至关重要。
  • 限制:限制返回的结果数量,对于防止性能瓶颈和有效管理数据检索非常重要。
  • 分页:按批次检索结果的能力,特别是在处理大数据集和分页用户界面时尤为重要。

这些限制阻碍了在 Firestore 中有效利用地理空间查询,特别是对于大规模应用。GeoQueryFirestore 旨在通过提供强大的地理空间查询解决方案来弥补这一差距。

系统概述

GeoQueryFirestore 通过智能选择适当的精度级别来运行,该级别基于提供的范围或地图边界。然后,它识别出指定区域内潜在的 GeoHashes,从而实现高效的过滤和基于坐标检索文档。

数据库结构

Adsız tasarım (1)

为了有效地使用 GeoQueryFirestore,Firestore 数据库应按以下方式构建:

  • GeoHash 数组:每个 Firestore 文档必须包含一个字段,该字段保存表示文档位置的 GeoHashes 数组。字段名称可以根据开发人员的偏好进行自定义。
  • GeoHash 精度:为获得最佳性能,确保 GeoHash 精度与查询需求相匹配。例如,对于大多数用例,建议使用 8 个字符的精度(例如 swg5r323)。

示例用法

GeoQueryFirestore 提供了方便的方法来进行各种地理空间查询:

范围查询

参数:

  • center:搜索区域的中心点(纬度和经度)。
  • selectedRange:预定义的地理空间范围(例如 GeoQueryFirestoreRanges.km20)。
  • customRangeInMeters(可选):如果 selectedRangeGeoQueryFirestoreRanges.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 单元格边界的区域。这可能导致某些边缘情况,其中文档可能会落在预期搜索区域之外。
  • 在第一次运行详细的查询时,你可能会收到索引错误。你可以根据错误消息中的链接创建索引。这样它将开始正常工作。

建议

示例代码

// 忽略文件: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

1 回复

更多关于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...'),
      ),
    );
  }
}

解释

  1. 依赖项:我们在 pubspec.yaml 中添加了 cloud_firestoregeoquery_firestoregeolocator 依赖项。

  2. 初始化:在 GeoQueryScreeninitState 方法中,我们初始化了 Firestore 和 GeoFirestore 实例,并请求当前用户的位置。

  3. 获取位置getCurrentLocation 方法使用 Geolocator 插件获取当前用户的位置,并在获取到位置后调用 performGeoQuery 方法。

  4. 执行地理查询performGeoQuery 方法使用 GeoFirestore 插件执行地理查询,查询以当前用户位置为中心,半径为10公里的文档,并打印每个文档的数据。

这个示例展示了如何使用 geoquery_firestore 插件来执行地理查询。当然,你可以根据你的需求进一步扩展和修改这个示例。

回到顶部