My questions is related on what is the best way to handle data related gps points in a database (in my case nosql DB MongoDB) in order to returns only closest points.
I have a collections of users in my database.
Now I need to create a new "table" which associate users with gps points (an user can have more points). For example:
User,lat,long
ALFA,40,50
ALFA,30,50
BETA,42,33
...
The server should makes available a function that, given a position in input, returns a list of users which are associated to points near the input.
For example:
function nearestUsers(lat,lon){
var mindif = 10000;
var closest;
users = getAllRecordsFromDataBase(); //query for MongoDB that returnst all records of the new table
for ( i = 0 ; i < users.length; i++){
if(this.distance(lat,lon,users[i].lat,users[i].lon)>mindif) delete users[i];
}
return users;
}
The distance function is the following:
function distance(lat1, lon1, lat2, lon2) {
lat1 = Deg2Rad(lat1);
lat2 = Deg2Rad(lat2);
lon1 = Deg2Rad(lon1);
lon2 = Deg2Rad(lon2);
var R = 6371;
var x = (lon2 - lon1) * Math.cos((lat1 + lat2) / 2);
var y = (lat2 - lat1);
var d = Math.sqrt(x * x + y * y) * R;
return d;
}
I'm afraid that, for big amount of data, this approch will result slow. Which is the best way to make more scalable the algorithm? Any suggestions?
Considering that this funcionality is inside my server in Node.js using a MongoDB, can I implement this function directly via query or using some special structure in my database?
You can use mongodb geospatial indexes and queries. Just store you points as GeoJSON points and perform queries using GeoJSON polylines as bbox-es and $geoWithin.
Related
I am getting a Firestore collection as QuerySnapShot.
List userDocs = snapShot.data!.docs;
userDocs has a GeoPoint value and I am calculating the distance from the current user location to every userDocs GeoPoint item.
I am able to calculate the distance using a function, but I need to add this calculated distance as part of userDocs, in a way that I can sort the userDocs items from closest to farthest.
Here you have my attempt:
List userDocs = snapShot.data!.docs;
String distKm ="";
//iterate para calcular la distancia al spot
int i = 0;
userDocs.forEach((e) {
print("loop ${i}");
print(e['spot_id']);
//calculamos distancia
GeoPoint geoPoint = e['location'];
var latPost = geoPoint.latitude;
var lonPost = geoPoint.longitude;
var distancia = calculateDistance(
widget.latitud, widget.longitud, latPost, lonPost);
print("distancia ${distancia}");
distKm = distancia.toStringAsFixed(2);
print("distkm ${distKm}");
//update item in userDocs
i++;
});
Is there any way to add a new item to userDocs or update an existing (key,value) to be able to sort userDocs for nearest to farthest item?
EDIT
Function to calculate distances:
double calculateDistance(lat1, lon1, lat2, lon2) {
var p = 0.017453292519943295;
var c = cos;
var a = 0.5 -
c((lat2 - lat1) * p) / 2 +
c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p)) / 2;
return 12742 * asin(sqrt(a));
}
Firestore doesn't have a built-in operator for distance based filtering, and it can only order/filter on values that are stored in the documents that it returns.
If you know the lat/lon pairs for both locations when you write the document, you can indeed also add a field with the distance you calculate based on that, and then use that field in queries.
If either of the locations is not known at write time, that is not an option. Your only option at that point would be to use geohash (or similar) based querying, as explained in the Firebase documentation on geoqueries.
I am using postgres and postgis.
I have Posts which have a geometry, with an attribute visible_within_m which is how many meters from that point the Post should be shown in results.
I can find Posts within some random radius of some random points by doing ST_DWithin(geometry, ST_SetSRID(ST_Point(a, b), 4326), 10000)
However, I want to know how many Posts are visible with a radius of some random point.
How can I look up how many Posts are visible within a radius of some arbitrary point?
Is there a better way to do this?
You can calculate the distance between each point and the center of your circle. If the distance is grater than the radius then it is outside otherwise it's inside.
const EARTH_RADIUS = 6371000;
const toRad = function(num){return num*Math.PI/180};
var calculateDistance =
function(lat1, lon1, lat2, lon2){
var dLat = toRad(lat2 - lat1);
var dLon = toRad(lon2 - lon1);
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
var distance = EARTH_RADIUS * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return distance;
}
Instead of using a constant value for the distance, use the value stored in visible_within_m
SELECT * FROM mytable
WHERE ST_DWithin(geometry, ST_SetSRID(ST_Point(a, b), 4326), visible_within_m);
On a side note, st_dwithin with geometries uses the distance unit of the projection, so for 4326 it is a (meaningless) distance in degrees, not in meters.
I am using PostgreSQL to store the location of a user send to the server by my android app. I needed to find the total distance travelled by the user for a particular time duration.
The user location is stored in the following table :
CREATE TABLE userlocation
(
latitude character varying,
longitude character varying,
geopoint point,
userid integer,
locationtime timestamp
)
I retrieved the records and calculated the distance in java using the following haversine distance method :
public double getdistance(final double lat1, final double lon1, final double lat2, final double lon2, final char unit) {
final double theta = lon1 - lon2;
double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2))
+ Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2))
* Math.cos(deg2rad(theta));
dist = Math.acos(dist);
dist = rad2deg(dist);
dist = dist * 60 * 1.1515;
if (unit == 'K') {
dist = dist * 1.609344;
} else if (unit == 'N') {
dist = dist * 0.8684;
}
if (Double.isNaN(dist)) {
dist = 0.0;
}
return (dist);
}
However this calculation is time consuming especially while calculating the distance for multiple days as there are a lot of records. I decided to try doing the distance calculation at the database level to reduce the calculation time. I found the the following query which allows me to calculate the distance to a certain point :
SELECT latitude, longitude, geopoint <-> '19.23,72.89' AS distance FROM userlocation ORDER BY distance;
I tried to create a query that would either return the total distance traveled or atleast calculate the distance between two consecutive rows and store it in another column so that I calculate the sum in Java instead of the distance calculations.
I have tried searching for a solution but I have been unable to find one yet. Most of the questions on SO deal with distance calculation between two points.
I do not have PostGIS at the moment. Would it be possible to calculate distance in PostgreSQL or should I just continue with my current approach? In that case is there an alternative for reducing the distance calculation time.
I had the same problem last month.
I added the module Earthdistance to PostgreSQL. This plugin add functions to compute the great circle distances between two points.
Installation is simple:
CREATE EXTENSION "cube";
CREATE EXTENSION "earthdistance";
Hope that helps
I am using Node.js and MongoDB.
Let say I have predefined few cities (e.g. Seattle, Miami, New York) with Lat & Lon. and there is a user click to my website and I know his IP address, and find out the lat & lon. Then I want to know which city that I've defined is the closest to the user.
I know I can do it using Mongo's geospatial feature. but it would be quite 'expensive' to use DB to calculate that for every web request.
Is there a Node.js NPM package that can do the geo feature as I described above?
How many "pre-defined" cities are you working with? If it's a small number, you could probably just store the list in memory and do a linear scan.
Also, you should probably just give the mongo geospatial query a try to get an idea of exactly how expensive it is, before assuming that it's unreasonable - if you index the city locations and the, it will be pretty fast..
If you are dealing with a lot of points, still don't want to rely on mongo geo-indexing, and need something really specialized, maybe an R-Tree would be worth experimenting with. Here's an r-tree implementation for javascript. https://github.com/imbcmdth/RTree
If you already have the user's location and the location of each city, it should be quite fast to compute the distance to the nearest. Check out this site: http://www.movable-type.co.uk/scripts/latlong.html
I have used the first algorithm a few times.
function getDistance(lat1,lat2,lon1,lon2){
var R = 6371; // km
var c = Math.PI / 180;
var dLat = (lat2-lat1) * c;
var dLon = (lon2-lon1) * c;
var lat1 = lat1 * c;
var lat2 = lat2 * c;
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d;
}
var closest, dist = Number.MAX_VALUE;
for(var i = 0, l=cities.length;i<l;++i){
if(getDistance(cities[i].longitude, cities[i].latitude, user.longitude, user.latitude) < max){
closest = cities[i];
}
}
alert(closest.name + ' is the winner :)');
You might want to add some exception handling here :)
Is it possible to get places by latitude/longitude and radius using the Graph API (or by address/zip)? I don't see it anywhere in the documentation
The following format for the search URL will return a list of places near a location:
https://graph.facebook.com/search?type=place¢er=<lat>,<lon>&distance=1000&access_token=<token>
I'm not sure on the units of the "distance" parameter.
I don't know much about the facebook API, but if you can pull the lat/long of two places, you can calculate their distance quite easily. Here it is in JavaScript, but it's portable to about any language:
var R = 6371; // km .. use 3963 for miles
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // km
Hopefully this helps you with your endeavor.