We are trying to loop through a set of documents in Firestore that are geo based, though the query only appears to be returning the last result and we cannot work out why
Future fetchTasksNearBy(double lat, double lng) async {
debugPrint('getting tasks in location');
GeoFirePoint center = geo.point(latitude: lat, longitude: lng);
double radius = 70; //in km
String field = 'location';
// Reading nearby tasks based on lat, lng parameters
try {
// Collection ref
var collectionReference = tasksRef.where('status', isEqualTo: 'Posted');
var geoRef = geo.collection(collectionRef: collectionReference);
Stream<List<DocumentSnapshot>> placesStream = geoRef.within(
center: center,
radius: radius,
field: field,
strictMode: true,
); // = false includes borderline places
await for (var doclist in placesStream) {
for (DocumentSnapshot ds in doclist) {
GeoPoint point = ds['location']['geopoint'];
_tasks.add(Task.fromDoc(ds));
return _tasks;
}
}
} catch (e) {
throw Exception(e);
}
}
Related
Future<List<Places>> nearYou(GeoPoint point) async {
final CollectionReference places = firestore.collection('places');
// 10km radius around point (in lat and long)
double radius = 0.01;
final query = places
.where('coordinates',
isGreaterThanOrEqualTo: GeoPoint(
point.latitude - radius,
point.longitude - radius,
))
.where('coordinates',
isLessThanOrEqualTo: GeoPoint(
point.latitude + radius,
point.longitude + radius,
))
.limit(5);
var snapshot = await query.get();
...
I have tried this but it doesn't seem to work. I don't even get the place nearest to my location.
I have an application where i need to get user real time location and push it into database using geoflutterfire , the code is working but i do not understand why it keeps generating multiple documents , while on the other side using the same plugin , we should use only collection reference , can any help me with this , Thank you
Picture showing pushing data into firestore using geoflutterfire plugin ( but why it keeps
generating new documents , i just want to solely update the same document )
Push data into firebase firestore
void updateDriverLocation(){
Fluttertoast.showToast(msg: "Called");
geoFlutterFire = Geoflutterfire();
if(latitude != 0.0 && longitude != 0.0){
GeoFirePoint geoFirePoint = geoFlutterFire.point(latitude: latitude, longitude: longitude);
var map = HashMap<String,Object>();
map['name'] = _firebaseAuth.currentUser!.uid;
map['position'] = geoFirePoint.data;
_firebaseFirestore.collection("Drivers").add(map);
}
}
get data from firebase firebase
void getDrivers(){
geoFlutterFire = Geoflutterfire();
GeoFirePoint geoFirePoint = GeoFirePoint(latitude, longitude);
var ref = _firebaseFirestore.collection('Drivers');
var locations = geoFlutterFire.collection(collectionRef: ref).within(center: geoFirePoint, radius: maxRadius, field: 'position');
locations.listen((List<DocumentSnapshot> documentList) {
for (var documentSnapshot in documentList) {
Map<String,Object> map = documentSnapshot.get('geopoint');
double lat = map['latitude'] as double;
double lon = map['longitude'] as double;
//_list.add(LatLng(lat, lon));
markers.add(Marker(
markerId: MarkerId(DateTime.now().millisecond.toString()),
position: LatLng(lat,lon),
icon: BitmapDescriptor.defaultMarker
));
}
});
}
This is how i'm trying to listen to the data returned from firestore
_firebaseFirestore.collection("Drivers").add(map);
The .add() method creates a new document with a random ID every time. If you want to update a specific document then you must use update() on a DocumentReference. Try the following:
_firebaseFirestore.collection("Drivers").doc(_firebaseAuth.currentUser!.uid).update(map);
This will update current drivers document only.
GeoFlutterFire has the following to specify DocID:
geoRef.setDoc(String id, var data, {bool merge})
I'm trying to get a documents based on a geohash upper and lower bounds. 'Location A' is the location of a user, and we're trying to find other locations that are within the bounds of 'Location A' in firebase. I was following this tutorial
https://levelup.gitconnected.com/nearby-location-queries-with-cloud-firestore-e7f4a2f18f9d
and checking with this website
https://www.movable-type.co.uk/scripts/geohash.html
The coordinates of 'location A' are 52.462570419161594, -2.0120335164758725 and the coordinates of 'Location B' are 52.46648448588268, -1.9841125656313279. These are very close to eachother so, with the code below i'd assume that with the firebase query it'd return 'Location B' when querying from 'Location A's geohash but this isn't happening and it's because the getGeohashRange function is returning the wrong value but i'm not sure what i'm doing wrong
Location A geohash = 'gcqd6'
Location B geohash = 'gcqd6'
The upper & lower that the getGeohashRange is returning =
lower: "mpt5mf52n18z"
upper: "mptmvc2wncyc"
This is the code & firebase query
// Get the geohash upper & lower bounds
GeoHashRange getGeohashRange({
required double latitude,
required double longitude,
double distance = 12, // miles
}) {
double lat = 0.0144927536231884; // degrees latitude per mile
double lon = 0.0181818181818182; // degrees longitude per mile
double lowerLat = latitude - lat * distance;
double lowerLon = longitude - lon * distance;
double upperLat = latitude + lat * distance;
double upperLon = longitude + lon * distance;
GeoHasher geo = GeoHasher();
var lower = geo.encode(lowerLat, lowerLon);
var upper = geo.encode(upperLat, upperLon);
return GeoHashRange(lower: lower, upper: upper);
}
class GeoHashRange {
late String upper;
late String lower;
GeoHashRange({
required this.upper,
required this.lower,
})
}
// Query firebase for locations
Stream<List<Garage>> getLocalGarages(Position position) {
GeoHashRange range = getGeohashRange(
latitude: position.latitude,
longitude: position.longitude,
);
var ref = _db
.collection('garages')
.where("geohash", isGreaterThan: range.upper)
.where("geohash", isLessThan: range.lower);
return ref.snapshots().map(
(list) =>
list.docs.map((doc) => Garage.fromJson(doc.data())).toList(),
);
}
Turns out theres a plugin that can actually help with this. This doc has some info https://firebase.google.com/docs/firestore/solutions/geoqueries#java and this is the plugin https://pub.dev/packages/geoflutterfire
This is my new code
Stream<List<Garage>> localGarages(Position position) {
var ref = _db.collection('garages');
GeoFirePoint center =
geo.point(latitude: position.latitude, longitude: position.longitude);
double radiusInKM = 20;
return geo
.collection(collectionRef: ref)
.within(center: center, radius: radiusInKM, field: 'position')
.map((x) => x.map((e) => Garage.fromJson(e.data()!)).toList());
}
There is an example of the 'position' field in the doc above
I am trying to develop a Location service-based Distance calculation app. the problem is I need this to be run when the app is in the background as well. so I used the following plugin to do so.
https://pub.dev/packages/background_location
when this runs on AVD nothing happened but worked fine. but when run on some devices I got the wrong calculations. I have noticed that this happens when the app is in the background this is. following is a partial recreation of mine. I just want to know if my code get any error before posting this as an issue
Future<void> startLocationUpdate() async {
var permission = await Permission.locationAlways.isGranted;
if (!permission) {
var t = await Permission.locationAlways.request();
}
oldPosition2 = Location(
longitude: currentPosition.longitude, latitude: currentPosition.latitude);
isWaited = false;
waitButtonText = "WAIT";
BackgroundLocation.stopLocationService();
BackgroundLocation.setAndroidConfiguration(2000);
await BackgroundLocation.setAndroidNotification(
title: "XXXXXXXXXXXXXXXXX",
message: "XXXXXXXXXXXXXXXXXX",
icon: "XXXXXXXXXXXXXXXXXX",
);
//print('Inside startLocationUpdate');
await BackgroundLocation.startLocationService(distanceFilter: 20);
print('Inside startLocationUpdate 1');
BackgroundLocation.getLocationUpdates((location) {
try {
currentPosition = location;
LatLng pos = LatLng(location.latitude, location.longitude);
var rotation = MapKitHelper.getMarkerRotation(oldPosition2.latitude,
oldPosition2.longitude, pos.latitude, pos.longitude);
Marker movingMaker = Marker(
markerId: MarkerId('moving'),
position: pos,
icon: movingMarkerIcon,
rotation: rotation,
infoWindow: InfoWindow(title: 'Current Location'),
);
setState(() {
CameraPosition cp = new CameraPosition(target: pos, zoom: 17);
rideMapController.animateCamera(CameraUpdate.newCameraPosition(cp));
_markers.removeWhere((marker) => marker.markerId.value == 'moving');
_markers.add(movingMaker);
this.accuracy = location.accuracy.toStringAsFixed(0);
totalSpeed = location.speed;
totalDistance = totalDistance +
CalDistance(oldPosition2 != null ? oldPosition2 : location,
location, DistanceType.Kilometers);
var totalDistanceAdj = totalDistance != 0 ? totalDistance : 1;
//totalFare = 50 + (kmPrice * totalDistanceAdj);
print("distance $totalDistance");
});
oldPosition2 = location;
updateTripDetails(location);
Map locationMap = {
'latitude': location.latitude.toString(),
'longitude': location.longitude.toString(),
};
rideRef = FirebaseDatabase.instance
.reference()
.child("rideRequest/${widget.tripDetails.rideID}");
rideRef.child('driver_location').set(locationMap);
} catch (e) {
FirebaseService.logtoGPSData('Error in updates ${e}');
}
});
}
and the CalDistance method as follows
double CalDistance(Location pos1, Location pos2, DistanceType type) {
print("pos1 : ${pos1.latitude} pos2: ${pos2.latitude}");
double R = (type == DistanceType.Miles) ? 3960 : 6371;
double dLat = this.toRadian(pos2.latitude - pos1.latitude);
double dLon = this.toRadian(pos2.longitude - pos1.longitude);
double a = sin(dLat / 2) * sin(dLat / 2) +
cos(this.toRadian(pos1.latitude)) *
cos(this.toRadian(pos2.latitude)) *
sin(dLon / 2) *
sin(dLon / 2);
double c = 2 * asin(min(1, sqrt(a)));
double d = R * c;
//d = (d*80)/100;
return d;
}
double toRadian(double val) {
return (pi / 180) * val;
}
Any help or hint would be much appreciated
I am writing a simple bus tracking app for my university shuttles. I have plotted out the bus GPS as navigation arrows along with the route on the map. Now as the actual GPS coordinate is a little of from the real location of bus than the plotted route, it is slightly off the road. Is there any way or method that I can call to snap these markers to the polyline nearest point in google maps flutter plugin? I just wanted to know if there is anything already in place that I am missing. I would be willing to write the custom nearest neighbor logic if necessary. Thanks!
I have done using locationIndexOnPath, computeDistanceBetween, computeHeading.
Step 1. add this function to get nearest polyline segment index based on you current location. pass your polyline coordinates list and current location. below function return index of given polyline coordinates list.
int getEdgeIndex(List<mt.LatLng> _coordinates, mt.LatLng currentLocation) {
final int edgeIndex1 = mt.PolygonUtil.locationIndexOnPath(
currentLocation, _coordinates, true,
tolerance: 1);
final int edgeIndex2 = mt.PolygonUtil.locationIndexOnPath(
currentLocation, _coordinates, true,
tolerance: 2);
final int edgeIndex6 = mt.PolygonUtil.locationIndexOnPath(
currentLocation, _coordinates, true,
tolerance: 6);
final int edgeIndex10 = mt.PolygonUtil.locationIndexOnPath(
currentLocation, _coordinates, true,
tolerance: 10);
final int edgeIndex15 = mt.PolygonUtil.locationIndexOnPath(
currentLocation, _coordinates, true,
tolerance: 15);
int finalIndex = -1;
if (edgeIndex1 >= 0) {
finalIndex = edgeIndex1;
} else if (edgeIndex2 >= 0) {
finalIndex = edgeIndex2;
} else if (edgeIndex6 >= 0) {
finalIndex = edgeIndex6;
} else if (edgeIndex10 >= 0) {
finalIndex = edgeIndex10;
} else if (edgeIndex15 >= 0) {
finalIndex = edgeIndex15;
}
print(
"getEdgeIndex: index : $edgeIndex1, $edgeIndex2, $edgeIndex6, $edgeIndex10, $edgeIndex15, $finalIndex");
return finalIndex;
}
Step 2. Now add this function to get snap LatLag to snap you current marker in centre of polyline route.
Map<String, dynamic> getSnapLatLng(LatLng currentLocation) {
final currentLocationMT =
mt.LatLng(currentLocation.latitude, currentLocation.longitude);
if (coordinatesMT.isEmpty) {
for (LatLng latLng in coordinates) {
coordinatesMT.add(mt.LatLng(latLng.latitude, latLng.longitude));
}
}
final int finalIndex = getEdgeIndex(coordinatesMT, currentLocationMT);
if (finalIndex >= 0) {
final snappedLatLng2 = (finalIndex < coordinatesMT.length - 1)
? coordinatesMT[finalIndex + 1]
: currentLocationMT;
final snappedLatLng = coordinatesMT[finalIndex];
final distanceM2 = mt.SphericalUtil.computeDistanceBetween(
snappedLatLng, currentLocationMT);
double heading = mt.SphericalUtil.computeHeading(
snappedLatLng2,
snappedLatLng,
);
final extrapolated =
mt.SphericalUtil.computeOffset(snappedLatLng, -distanceM2, heading);
print(
"snapToPolyline:distanceM $distanceM2, $heading, $finalIndex, ${coordinatesMT.length}");
return {
"index": finalIndex,
"latLng": LatLng(extrapolated.latitude, extrapolated.longitude)
};
}
return {"index": finalIndex, "latLng": currentLocation};
}
NOTE: I have used https://pub.dev/packages/maps_toolkit this package for locationIndexOnPath, computeDistanceBetween, computeHeading.