GeoFlutterFire2는 지리적 위치 데이터를 사용하여 Firestore 문서를 저장하고 쿼리할 수 있도록 해주는 오픈소스 플러터 패키지입니다. Firebase Firestore에 지리공간 기능을 확장하여 근처의 관심사 지점을 찾는 등의 효율적인 위치 기반 쿼리를 가능하게 합니다. 크로스플랫폼 사용을 위해 설계되어 Android, iOS, 웹, macOS, Windows를 지원하며 실시간 위치 서비스가 필요한 앱에 적합합니다. 기존 Firebase 프로젝트와 원활하게 통합되며, 플러터 앱에서 지리정보 작업을 간편하게 만듭니다.
사용 사례
근처 사용자 또는 위치 찾기
실시간 위치 기반 알림
모바일 앱에서의 지오페싱
위치 인식 검색 기능
매핑 및 공간 데이터 시각화
주요 기능
Firestore에서 지리공간 쿼리
크로스 플랫폼 지원
Firebase와의 간편한 통합
거리 기반 필터링을 지원합니다
경량이고 성능이 뛰어남
적합한 대상
위치 기반 앱 개발
지도를 갖춘 Firebase 기반 앱
실시간 지오로케이션 서비스
공간 데이터를 갖춘 모바일 및 웹 애플리케이션
Flutter 및 Firestore를 사용하는 개발자
고려 사항
Firebase Firestore 설정이 필요합니다
Firestore의 지리공간 기능에 한정
대규모 데이터 세트에 대한 추가 처리가 필요할 수 있습니다
하위 Firebase SDK에 의존합니다
오프라인 전용 지리 위치에 적합하지 않습니다
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
GeoFlutterFire2 :earth_africa:
NB! GeoFlutterFire2 is the revisited & updated version of GeoFlutterFire
GeoFlutterFire is an open-source library that allows you to store and query a set of keys based on their geographic location. At its heart, GeoFlutterFire simply stores locations with string keys. Its main benefit, however, is the possibility of retrieving only those keys within a given geographic area - all in realtime.
GeoFlutterFire uses the Firebase Firestore Database for data storage, allowing query results to be updated in realtime as they change. GeoFlutterFire selectively loads only the data near certain locations, keeping your applications light and responsive, even with extremely large datasets.
GeoFlutterFire is designed as a lightweight add-on to cloud_firestore plugin. To keep things simple, GeoFlutterFire stores data in its own format within your Firestore database. This allows your existing data format and Security Rules to remain unchanged while still providing you with an easy solution for geo queries.
Calling geoFirePoint.data returns an object that contains a geohash string and a Firestore GeoPoint. It should look like this in your database. You can name the object whatever you want and even save multiple points on a single document.
Query Geo data
Warning
Querying locations creates 9 separated stream listeners on firestore. One for central geo hash and 8 surrounding it.
within function creates a convenient broadcast stream, if you need more than one subscriber for the locations. But it also causes
memory leak, because underlying stream listeners are not cancelled when you cancel StreamSubscription from within method.
It's much safer to use withinAsSingleStreamSubscription which you can cancel, and it will also cancel 9 underlying streams as well.
To query a collection of documents with 50kms from a point
// Create a geoFirePointGeoFirePoint center = geo.point(latitude: 12.960632, longitude: 77.641603);
// get the collection reference or queryvar 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);
The within function returns a Stream of the list of DocumentSnapshot data, plus some useful metadata like distance from the centerpoint.
You now have a realtime stream of data to visualize on a map.
:notebook: API
collection(collectionRef: CollectionReference)
Creates a GeoCollectionRef which can be used to make geo queries, alternatively can also be used to write data just like firestore's add / set functionality.
Example:
// Collection ref// var collectionReference = _firestore.collection('locations').where('city', isEqualTo: 'bangalore');var collectionReference = _firestore.collection('locations');
var geoRef = geo.collection(collectionRef: collectionReference);
Note: collectionReference can be of type CollectionReference or Query
Query the parent Firestore collection by geographic distance. It will return documents that exist within X kilometers of the center-point.
field supports nested objects in the firestore document.
Note: Use optional parameter strictMode = true to filter the documents strictly within the bound of given radius.
Example:
// 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);
Each documentSnapshot.data() also contains distance calculated on the query.
Returns:Stream<List<DocumentSnapshot>>
Write Data
Write data just like you would in Firestore
geoRef.add(data)
Or use one of the client's conveniece methods
geoRef.setDoc(String id, var data, {bool merge}) - Set a document in the collection with an ID.
geoRef.setPoint(String id, String field, double latitude, double longitude)- Add a geohash to an existing doc
Read Data
In addition to Geo-Queries, you can also read the collection like you would normally in Firestore, but as an Observable
geoRef.data()- Stream of documentSnapshot
geoRef.snapshot()- Stream of Firestore QuerySnapshot
point(latitude: double, longitude: double)
Returns a GeoFirePoint allowing you to create geohashes, format data, and calculate relative distance.
Example: var point = geo.point(38, -119)
Getters
point.hash Returns a geohash string at precision 9
point.geoPoint Returns a Firestore GeoPoint
point.data Returns data object suitable for saving to the Firestore database
Geo Calculations
point.distance(latitude, longitude) Haversine distance to a point
:zap: Tips
Scale to Massive Collections
It's possible to build Firestore collections with billions of documents. One of the main motivations of this project was to make geoqueries possible on a queried subset of data. You can pass a Query instead of a CollectionReference into the collection(), then all geoqueries will be scoped with the constraints of that query.
Note: This query requires a composite index, which you will be prompted to create with an error from Firestore on the first request.
Example:
var queryRef = _firestore.collection('locations').where('city', isEqualTo: 'bangalore');
var stream = geo
.collection(collectionRef: queryRef)
.within(center: center, radius: rad, field: 'position');
Usage of strictMode
It's advisable to use strictMode = false for smaller radius to make use of documents from neighbouring hashes as well.
As the radius increases to a large number, the neighbouring hash precisions fetch documents which would be considerably far from the radius bounds, hence its advisable to use strictMode = true for larger radius.
Note: filtering for strictMode happens on client side, hence filtering at larger radius is at the expense of making unnecessary document reads.
Make Dynamic Queries the RxDart Way
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);
Limitations
range queries on multiple fields is not suppoerted by cloud_firestore at the moment, since this library already uses range query on geohash field, you cannot perform range queries with GeoFireCollectionRef.
limit() and orderBy() are not supported at the moment. limit() could be used to limit docs inside each hash individually which would result in running limit on all 9 hashes inside the specified radius. orderBy() is first run on geohashes in the library, hence appending orderBy() with another feild wouldn't produce expected results. Alternatively documents can be sorted on client side.
Acknowledgements
NB! GeoFlutterFire2 is a revisited and updated version of GeoFlutterFire
The work originates from GeoFlutterFire by Darshan Narayanaswamy