firebase search and create list - firebase

Kindly help me write this function
I have this list where each driver is listed with
1 current latitude longitude
2 onDuty or not
3 vehicle type
4 driver details
whenever user initiates search
function searches for
1 vehicel type user has chosen
2 AND if driver is on duty
3 AND if driver is within 10 km radius (using latitude and longitude)
and copy all eligible drivers to -> availableDriverList
distance calculating code
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2-lat1); // deg2rad below
var dLon = deg2rad(lon2-lon1);
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2)
;
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // Distance in km
return d;
}
function deg2rad(deg) {
return deg * (Math.PI/180)
}

Searching for a radius around a point requires that you perform a range comparison on the longitude and the latitude of each document. In a Firestore query you can only perform a range comparison on a single field, so with your current data structure you cannot perform the query you want.
There is a way to encode longitude and latitude into a single value that can be used for such comparisons, known as a Geohash. If you add a Geohash to each document, you can then perform the query. To learn how to do this, have a look at the Firestore documentation on performing Geoqueries.
I also recommend reading: Query for nearby locations. While for a different database, the limits and thus solution are the same. If you got the time, I also recommend watching this talk I gave a few years ago about how to add geoqueries to Firestore.

Related

How to do spatial polygon search with Lucene.net + Spatial4n?

I have a list of companies, each with a lat lng. In addition, each company has a radius within which they are prepared to work. What I need to do is be able to perform a search around a given location + radius and see if that radius overlaps with the working radius of any companies.
I'm using Lucene.net 2.9.4 (Umbraco) and am looking to use Spatial4n with it. I've currently got the Spatial4n 0.3 lib, and have compiled the lucene.net.contrib.spacial project against it (after having to mash together a bunch of code from the latest lucene repo ad some things seem to be missing).
So my question is, how would I a) go about indexing a company and their work radius? and b) search for companies who are prepared to work within a given search radius?
Can't answer a) but for b), get the distance between the 2 coordinates, and if it's less than the sum of both radii, then it's a match (i.e. overlap).
Get Distance:
public static double GetDistance(double latitude1, double longitude1, double latitude2, double longitude2, DistanceUnits distanceUnits = DistanceUnits.Miles)
{
try
{
// Earth radius in kilometers via NASA as of 2016 - https://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html
const double earthRadiusKilometers = 6378.137;
var latRadians = (latitude2 - latitude1).ToRadians();
var lonRadians = (longitude2 - longitude1).ToRadians();
var a = Math.Sin(latRadians / 2) *
Math.Sin(latRadians / 2) +
Math.Cos(latitude1.ToRadians()) *
Math.Cos(latitude2.ToRadians()) *
Math.Sin(lonRadians / 2) *
Math.Sin(lonRadians / 2);
var radianDistance = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a)));
switch (distanceUnits)
{
case DistanceUnits.Kilometers:
{
return radianDistance * earthRadiusKilometers;
}
case DistanceUnits.Miles:
{
return radianDistance * ConvertKilometersToMiles(earthRadiusKilometers);
}
}
return radianDistance;
}
catch
{
return double.MaxValue;
}
}
public static double ConvertKilometersToMiles(double kilometers)
{
return kilometers * 0.621371192;
}
public enum DistanceUnits
{
Miles,
Kilometers,
Radians
}

Meteor calculate distance between users. Server side vs Client Side

I have a situation where I need to calculate the distance between users. In this particular scenario I have:
Employer geolocation - One user one location.
Candidate geolocation - One location for each user but when the employer generates the list of candidates there are multiple candidates.
I'm currently successfully pushing geolocations down the wire and running a rudimentary distance formula on the client side to figure out the distance between the employer and each candidate on the fly and then showing/hiding the candidates, as per the request.
I've been told that I should be running the calculation on the server side and just pushing down a single number, ie. 10 representing 10km, for each candidate. Then running a filter on that number.
So far, I have only pushed collections and fields down the wire. Is it possible to run the formula below on the server side and just pass down one number and 'attach' it to a user?
The second question is, what would be best practice for Meteor?
I'm still learning to code, so apologies if this is a really obvious question.
Client Side
Path: List.js
specialisations = specialisations.filter(function(element){
let distance = Template.instance().distanceFromEmployerFilter.get();
let user = Meteor.users.findOne({_id: element.candidateUserId});
let candidateLat = user && user.profile && user.profile.address && user.profile.address.latitude;
let candidateLong = user && user.profile && user.profile.address && user.profile.address.longitude;
let company = CompanyDetails.findOne({employerUserId: Meteor.userId()});
let companyLat = company && company.latitude;
let companyLong = company && company.longitude;
var R = 6371; // Radius of the earth in km
var dLat = (companyLat-candidateLat) * (Math.PI/180);
var dLon = (companyLong-candidateLong) * (Math.PI/180);
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos((candidateLat) * (Math.PI/180)) * Math.cos((companyLat) * (Math.PI/180)) *
Math.sin(dLon/2) * Math.sin(dLon/2)
;
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var distanceInKM = R * c; // Distance in km
if (distanceInKM <= distance) {
return true;
} else {
return false;
}
});
I'd do the filtering on the fetching of the candidates to display. Either as you publish/subscribe on your template or as you fetch in your helper:
Meteor.users.find({
"profile.address" : {
$near: {
$geometry: {
type: "Point" ,
coordinates: [ <Employerlongitude> , <Employerlatitude> ]
},
$maxDistance: <distance in meters>,
$minDistance: <distance in meters>
}}}).fetch();
If address is a 2d index. Specify coordinates in this order: “longitude, latitude.”
from Mongodb docs :
$near Specifies a point for which a geospatial query returns the
documents from nearest to farthest. The $near operator can specify
either a GeoJSON point or legacy coordinate point.
$minDistance & $maxDistance are optional

Firebase query nearby object

In mysql, I was using haversine formula to query nearby object.
Using this formula
Formula
SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance
FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;
Which
3959: radius of earth in miles
37,-122 : given lat lng
25: within 25 miles
In Firebase,
Can I store the users lat lng like what I did in mysql?
Create a marker table. id, lat, lng columns and then use the formula to query
Updated
I should ask, what is the way to query nearby using this formula in firebase.
Short answer, not like you want it to.
Firebase essentially has two ways to query for data: by path and by
priority. This is more limited than SQL, and there's a very good
reason for that — our API is carefully designed to only allow
operations we can guarantee to be fast. Firebase is a real-time and
scalable backend, and we want to enable you to build great apps that
can serve millions of users without compromising on responsiveness.
See, what is firebase and deNormalizing data
Also, this SO question is similar.
Response to comment:
Firebase will not calculate the sin( radians(X) ) for you. That's a 'slow' operation. So, you would need to store that information into the data when you save it.
I'm not 100% certain, but you could store the markers and the also store the longitude/latitude in a separate parent.
Root
-> Markers
-> longitude (Use the value as priority) -> MarkerId
-> latitude (Use the value as priority) -> MarkerId
Then you should be able to use bounding to find the Max and Min longitude and latitude.
Use that to query the longitude and latitude paths by priority. If a MarkerId exists in both, you use it.
A quick bit of research found this article on Latitude Longitude Bounding Coordinates
Hey I just finished building a real time google map using firebase and GeoFire. GeoFire is really cool and easy to use. It allows you to query using lon lat and radius. It returns a key that you can use to query your firebase db. You set the key, while you create the geoFire object, to be whatever you want. It is usually a ref that you can use to get the object that is associated with that distance.
Here is a link to geoFire:
https://github.com/firebase/geofire-js
Here is an example use case:
You have a lon lat, that you got using navigator:
var lon = '123.1232';
var lat = '-123.756';
var user = {
name: 'test user',
longitude: lon,
latitude: lat
}
usersRef.push(user).then(function(response) {
var key = response.key;
var coords = [lon, lat];
geoFire.set(key, coords, function(success){
console.log('User and geofire object has been created');
});
})
Now you can query for the user using:
// Set your current lon lat and radius
var geoQuery = geoFire.query({
center: [latitude, longitude],
radius: radiusKm
});
geoQuery.on('key_entered', function(key, location, distance) {
// You can now get the user using the key
var user = firebaseRefUrl + '/' + key;
// Here you can create an array of users that you can bind to a scope in the controller
});
If you are using google maps. I reccomend you use angular-google-maps.
Its a really cool google maps directive that takes in an array of markers and circles. So when ever $scope.markers or $scope.circles change in the controller it will automatically be applied to the map without any messy code. They have very good documentation.
Here is a link:
http://angular-ui.github.io/angular-google-maps/

Database functions in Entity Framework could not return results

I have implemented the same function "distancebetween" as in Nerddinner. I created an airport repository and have these methods:
public IQueryable<AllAirports> ReturnAllAirportWithIn50milesOfAPoint(double lat, double lon)
{
var airports = from d in im.AllAirports
where DistanceBetween(lat, lon, (double)d.Lat, (double)d.Lon) < 1000.00
select d;
return airports;
}
[EdmFunction("AirTravelModel.Store", "DistanceBetween")]
public static double DistanceBetween(double lat1, double long1, double lat2, double long2)
{
throw new NotImplementedException("Only call through LINQ expression");
}
When I tested it, it shows:
base {"The specified method 'Double DistanceBetween(Double, Double, Double, Double)' on the type 'AirTravelMVC3.Models.Repository.AirportRepository' cannot be translated into a LINQ to Entities
store expression because no overload matches the passed arguments."} System.SystemException {System.NotSupportedException}
Do you have any ideas on why this happen? The only different between my work and nerddinner is that I used a POCO plugin in entity framework.
The SQL UDF is as follows, it works very well in the database:
CREATE FUNCTION [dbo].[DistanceBetween](#Lat1 as real,
#Long1 as real, #Lat2 as real, #Long2 as real)
RETURNS real
AS
BEGIN
DECLARE #dLat1InRad as float(53);
SET #dLat1InRad = #Lat1 * (PI()/180.0);
DECLARE #dLong1InRad as float(53);
SET #dLong1InRad = #Long1 * (PI()/180.0);
DECLARE #dLat2InRad as float(53);
SET #dLat2InRad = #Lat2 * (PI()/180.0);
DECLARE #dLong2InRad as float(53);
SET #dLong2InRad = #Long2 * (PI()/180.0);
DECLARE #dLongitude as float(53);
SET #dLongitude = #dLong2InRad - #dLong1InRad;
DECLARE #dLatitude as float(53);
SET #dLatitude = #dLat2InRad - #dLat1InRad;
/* Intermediate result a. */
DECLARE #a as float(53);
SET #a = SQUARE (SIN (#dLatitude / 2.0)) + COS (#dLat1InRad)
* COS (#dLat2InRad)
* SQUARE(SIN (#dLongitude / 2.0));
/* Intermediate result c (great circle distance in Radians). */
DECLARE #c as real;
SET #c = 2.0 * ATN2 (SQRT (#a), SQRT (1.0 - #a));
DECLARE #kEarthRadius as real;
/* SET kEarthRadius = 3956.0 miles */
SET #kEarthRadius = 6376.5; /* kms */
DECLARE #dDistance as real;
SET #dDistance = #kEarthRadius * #c;
return (#dDistance);
END
In order to do this using POCOs with the existing NerdDinner source I had to add this to the DinnerRepository class:
public IQueryable<Dinner> FindByLocation(float latitude, float longitude)
{
List<Dinner> resultList = new List<Dinner>();
var results = db.Database.SqlQuery<Dinner>("SELECT * FROM Dinners WHERE EventDate >= {0} AND dbo.DistanceBetween({1}, {2}, Latitude, Longitude) < 1000", DateTime.Now, latitude, longitude);
foreach (Dinner result in results)
{
resultList.Add(db.Dinners.Where(d => d.DinnerID == result.DinnerID).FirstOrDefault());
}
return resultList.AsQueryable<Dinner>();
}
Assuming DistanceBetween is implemented in c# The issue (as hinted at by #p.campbell) is that the query generator doesn't know how to calculate DistanceBetween
To get your code to work as is, you might need to do something like
public IQueryable<AllAirports> ReturnAllAirportWithIn50milesOfAPoint(double lat, double lon)
{
var airports = from d in im.AllAirports.ToList()
where DistanceBetween(lat, lon, (double)d.Lat, (double)d.Lon) < 1000.00
select d;
return airports;
}
the ToList() will force the AllAirports to evaluate to a list, then it can be evaluated in memory, using your c# function. Obviously this won't scale to a huge number of airports. If that was an issue, you might want to do a rough "box" query where you just do a cheap within-a-square expression to return a small number of airports, ToList that, and then call your distancebetween to refine the results.
I got this same exception after I made several changes to the DB schema and "updated the model from database".
After comparing my EDMX XML with the original from Nerd Dinner, I saw that mine had changed all of the types for the DistanceBetween function to "real", where Nerd Dinner had "float". Changing them back to float resolved the issue.

google maps api v3 - nearest streetview

When streetview is not available for a certain location, I would like to find the nearest possible location with streetview?
The only way I could think of is.
radius = 0;
noOfPoints = 3;
while(radius < 10 miles){
radius = radius + 0.2 miles
points = calculate 4 * noOfPoints at this radius
loop(points)
{
if(streetview visibile for point)
bingo, break;
}
break if bingo;
noOfPOints = noOfPoints+1;
}
But this is ridiculously expensive even if I want to find streetview within a 10-mile radius and I am counting on luck to find streetview at one of these points, i.e, I could just miss an actual point with streetview.
can somebody please direct me towards a better approach??
You can try to use the StreetViewService to help you find a nearest existing street view:
var astorPlace = new google.maps.LatLng(40.729884, -73.990988);
var webService = new google.maps.StreetViewService();
/**Check in a perimeter of 50 meters**/
var checkaround = 50;
/** checkNearestStreetView is a valid callback function **/
webService.getPanoramaByLocation(astorPlace,checkaround ,checkNearestStreetView);
function checkNearestStreetView(panoData){
if(panoData){
if(panoData.location){
if(panoData.location.latLng){
/**Well done you can use a nearest existing street view coordinates**/
}
}
}
/** Else do something... **/
}

Resources