Flutter & firebase get docs by geohash - flutter

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

Related

Getting locations within radius using firestore in 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.

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

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.

Google maps v3 geocoder: callback for latitude and longitude

I can create a Google map that centers on Niwot, CO using geocode. If I click the "Encode" button from the form (which I got from Google's website), it puts a marker on the map in the correct location.
My question is: How do I retrieve the position coordinates (latitude and longitude) using the javascript callback function? I need to put the lat and long into my database.
Also, right now I'm using this for just one place for troubleshooting, but I'll then need it for numerous addresses.
Code is below.
var geocoder;
var map;
function initialize() {
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(40.171776, -105.116737);
var myOptions = {
zoom: 10,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
}
function codeAddress() {
var address = 'Niwot, CO';//this will eventually be an array
geocoder.geocode( { 'address': address}, function(results) {
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
//code below added based on comment 1; how do I display that lat and lng? I'll need to do this for troubleshooting.
var latlng = results[0].geometry.location;
var lat = latlng.lat();
var lng = latlng.lng();
});
}
The location returned is an instance of the LatLng class. You can call lat() to get the latitude and lng() to get the longitude. e.g.
var latlng = results[0].geometry.location;
var lat = latlng.lat();
var lng = latlng.lng();
Here are the relevant docs:
http://code.google.com/apis/maps/documentation/javascript/reference.html#LatLng
You can do that inside your geocode call
geocoder.geocode( { 'address': 'CA, US'}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
location = results[0].geometry.location;
alert(location)
}
});
This will show the latitude and longitude of the address.
(lat,lng)