Getting locations within radius using firestore in flutter - flutter

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.

Related

GeoFire query only returns last result

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);
}
}

Flutter & firebase get docs by geohash

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

Moving Car Animation Using Mapbox in flutter

I've implementing MabBox SDK with one of my flutter app. It has car live tracking screen, which will update car marker position on map based on location received. Here we would like to show car moving animation like this .
I've gone through the MabBox documents, Couldn't find any related document for our use cases. Then I've gone through google's flutter_animarker which doesn't support Mabbox. Anyone please help me on this.
I've finished car moving animation. Here is the final code.
import 'package:flutter/animation.dart';
import 'dart:math' as math;
LatLng? oldLatLng;
bool isCarAnimating = false;
onLocationChange(String latLng) async {
currentLatLng = LatLng(
double.parse(latLng.split(',')[0]), double.parse(latLng.split(',')[1]));
if (cabMarkerSymbol != null && !isCarAnimating)
_animateCabIcon(oldLatLng!, currentLatLng!);
}
_animateCabIcon(LatLng start, LatLng end) async {
try {
AnimationController controller =
AnimationController(vsync: this, duration: Duration(seconds: 3));
Animation animation;
var tween = Tween<double>(begin: 0, end: 1);
animation = tween.animate(controller);
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
oldLatLng = end;
isCarAnimating = false;
} else if (status == AnimationStatus.forward) {
isCarAnimating = true;
}
});
controller.forward();
var bearing = getBearing(start, end);
animation.addListener(() async {
var v = animation.value;
var lng = v * end.longitude + (1 - v) * start.longitude;
var lat = v * end.latitude + (1 - v) * start.latitude;
var latLng = LatLng(lat, lng);
var carSymbolOptions = SymbolOptions(
geometry: latLng,
iconRotate: bearing,
);
await _completer.future
.then((map) => map.updateSymbol(carMarkerSymbol!, carSymbolOptions));
});
} catch (e) {
print(e);
}
}
double getBearing(LatLng start, LatLng end) {
var lat1 = start.latitude * math.pi / 180;
var lng1 = start.longitude * math.pi / 180;
var lat2 = end.latitude * math.pi / 180;
var lng2 = end.longitude * math.pi / 180;
var dLon = (lng2 - lng1);
var y = math.sin(dLon) * math.cos(lat2);
var x = math.cos(lat1) * math.sin(lat2) -
math.sin(lat1) * math.cos(lat2) * math.cos(dLon);
var bearing = math.atan2(y, x);
bearing = (bearing * 180) / math.pi;
bearing = (bearing + 360) % 360;
return bearing;
}

Distance calculation Flutter background Location

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

snap markers to nearest polyline point Google Maps flutter

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.