My application collects geolocation point from the user every certain amount of time, I am trying to use these points in order to calculate the distance from the first point through all of the points.
please note that when the user moves in a straight line, the geolocation points do not form a straight line, because the points I collect have a margin of error due to inaccuracy, thus I can't use something like Haversine formula because it will give incorrect value (longer distance than real distance)
and I can't use Google Maps Distance API because it calculates the distance between 2 points only, and it will be so expensive to call it 200 times to calculate distance through all points.
and I want to calculate this value on the server-side because of some security rules I have. so using the google maps SDK in the front end to calculate it is not an option either.
Any idea ...
One option would be to simplify the line, then run the data through the Google Roads API (assuming the travel is on roads), then measure the length of the resulting line (following the roads).
for anyone facing the same problem,I have followed this link
https://developers-dot-devsite-v2-prod.appspot.com/maps/documentation/roads/snap
here is my code in PHP
// snap the collected points from user to the nearest road using google API
$fields = array(
'path' => '60.170880,24.942795|60.170879,24.942796|60.170877,24.942796|60.170902,24.942654',
'key' => '<YOUR_KEY_HERE>'
);
$url = "https://roads.googleapis.com/v1/snapToRoads?" . http_build_query($fields, '', '&');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
$response = json_decode($response);
$totalDistance = 0;
$previousPoint = null;
foreach ($response->snappedPoints as $pointOnRoad) {
if(!$previousPoint){
$previousPoint = $pointOnRoad;
continue;
}
$totalDistance += getDistance($pointOnRoad->location->latitude, $pointOnRoad->location->longitude,
$previousPoint->location->latitude, $previousPoint->location->longitude);
}
echo $totalDistance;
// calculate distance between 2 geo points
function getDistance($latitude1, $longitude1, $latitude2, $longitude2) {
$earth_radius = 6371;
$dLat = deg2rad($latitude2 - $latitude1);
$dLon = deg2rad($longitude2 - $longitude1);
$a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * sin($dLon/2) * sin($dLon/2);
$c = 2 * asin(sqrt($a));
$d = $earth_radius * $c;
return $d;
}
We are having similar kind of requirement. There are 2 paths and we need to make sure that each node in 1st path (except start and end) are at least 100 KM away from each other of 2nd path.
Can you please share code snippet or logic behind this.
Using Haversine formula in loop will impact performance. So, please suggest some better solution.
Related
I'm currently working on a campaign/advertisement script in Laravel 5.2. I'm having a table with ads, for example: Ad name, Location (lat/long), Radius (+10km).
Now I have a user location (lat/long). I want to see if he is in the radius of any ad and show the ad to him.
When I search on Google I only find solution to search ads based on lat/long + radius but I want to opposite. So see if a lat/long is in a radius of existing ads.
What is the best way to make this? And advice would be appreciated
This should work, all you would need to do is add this function to your User model.
public function ads()
{
return \App\Ad::
crossJoin('users', 'users.id', '=', \DB::raw($this->attributes['id']))
->where(\DB::raw('69 * DEGREES(ACOS(COS(RADIANS(users.Latitude)) * COS(RADIANS(ads.Latitude)) * COS(RADIANS(users.Longitude - ads.Longitude)) + SIN(RADIANS(users.Latitude)) * SIN(RADIANS(ads.Latitude))))'), '<=', 'ads.radius')
->get();
}
It's not a traditional Eloquent relationship so you won't be able to eager load it, although I'm sure that would be possible with more work.
In order to find a user's ads, you would simply do something like the following...
$ads = (new \App\User::find($user_id))->ads();
Note that this assumes both your users table and ads table contains a longitude and latitude column. It also assumes your ads table has a radius column. You might need to rename some columns but it should give you what you need.
Also this assumes your radius is in miles. If you use km, instead of 69, use 111.1111.
I have made some changes to the version of you user3158900.
Made a scope out of the function and the lat/long from the user is variable so I need them as a input in the query.
I now have this:
public function scopeAdsInLocation($query, $from_latitude, $from_longitude)
{
$query = CampaignModel::
where(\DB::raw('111.1111 * DEGREES(ACOS(COS(RADIANS(' . $from_latitude . ')) * COS(RADIANS(campaigns.loc_lat)) * COS(RADIANS(' . $from_longitude .' -
campaigns.loc_long)) + SIN(RADIANS(' . $from_latitude . ')) * SIN(RADIANS(campaigns.loc_lat))))'), '<=', 'campaigns.loc_radius')
->get();
return $query;
}
I call it like this:
$ads = CampaignModel::adsInLocation(51.191320, 5.987772);
var_dump($ads);
This code works, but only if I set the radius to a fixed value. So when I replace campaigns.loc_radius with '100' it works. But with the radius each campaign has it doesn't seem to do the job. Do you know maybe why? Or have a solution for this.
With the help of #Dr.Molle answer I learnt to do free hand drawing in Google maps. Now I'm trying to get the polygon drawn within a polygon something like in the below SS
I want to get the polygons marked in yellow and green within the black.
I'm not sure whether this is possible or not. Please shed some light on this issue.
Updates: on further research I learnt about a method called containsLocation(point, polygons) which is used to find whether the given lat/lng point is within the polygon or not.
But sadly there is no default method to check polygons within polygon provided by Google maps :(
You can check if a polygon is within another polygon by looping through each point of the inner polygon and testing if it is contained within the outer polygon using containsLocation().
var isPolygonInsidePolygon = function (innerPolygon, outerPolygon) {
var pointsInside = 0;
var pointsOutside = 0;
innerPolygon.getPath().getArray().map(function (x) {
(google.maps.geometry.poly.containsLocation(x, outerPolygon)) ? pointsInside++ : pointsOutside++;
});
return (pointsOutside > 0) ? false : true;
};
The JavaScript map() function may not work in older browsers, IE8 or lower.
This is a GIS question. Google Maps API isn't really a full-blown GIS. If you want an open-source solution, I suggest loading your yellow and green polygons into a PostGIS database. Then you can query the database.
As an example, you can encode the drawn polygon as a POLYGON object which has the format:
POLYGON((lon lat, lon lat, lon lat, lon lat, ... lon lat))
And then send that to a PHP file from javascript like (you'll wrap this in a $.get() command or similar and return json results:
getParcels.php?bounds=POLYGON((lon lat, lon lat, lon lat, lon lat, ... lon lat))
In the PHP file, query the PostGIS database and return the ids of the yellow and green polygons:
<?php
$pgcon = pg_connect ("dbname=gis user=gisuser connect_timeout=5") or die ( 'Can not connect to PG server' );
if (!$pgcon) {
echo "No connection to GIS database.\n";
}
$bounds = urldecode($_GET["bounds"];
$ewkt = 'SRID=4326;' . $bounds);
$json = ''; // this will contain your output
// Here I am returning the polygon geometry and the parcelID...
$query .= <<<EOD
SELECT
ST_AsGeoJSON(the_geom) as geom,
parid
FROM
parcels
WHERE
ST_Intersects(the_geom, ST_GeomFromEWKT( $1 ));
EOD;
$result = pg_query_params($pgcon, $query, array($ewkt));
if($result) {
$json = '{"type":"FeatureCollection", "features":[';
while($row = pg_fetch_assoc($result)) {
$json .= '{"geometry":' . $row['geom'] . ',';
$json .= '"type":"Feature","properties":{"parid":"' . $row['parid'] . '"}},';
}
$json = substr($json, 0,-1).']}';
}
echo $json;
?>
This will return the parcels that intersect your polygon using the ST_Intersects command in PostGIS.
An alternative implementation working from #chris-smith solution that might be faster, since it doesn't keep looping if it finds an outside point:
function isPolygonInsidePolygon( innerPolygon, outerPolygon ) {
var points = innerPolygon.getPath().getArray();
for( var i = 0; i < points.length; i++ ){
if( ! google.maps.geometry.poly.containsLocation( points[i], outerPolygon) ){
return false;
}
}
return true;
}
I'm kind of new to mongodb.
I want to use mongodb's geospatial feature to query locations in a boundary obtained from google maps. It's like withinBox(swLng, swLat, neLng, neLat). But it fails to give me correct results when the map zooms out, as neLng/neLat is less than swLng/swLat. It seems that this is calculated using x > swLng and x < neLng and y > swLat and y < neLat, not taking map projection into account.
So can I achieve the query by withinBox, or I have to adjust coordinates, or I should use near?
As I commented above, when northeast and southwest points cross the international date line, the query will fail as top right's y is smaller than bottom left's y.
If you are using mongodb with php and doctrine, you need make two queries and merge result on the client side like below:
if ($x1 > $x2) {
$qb->field('coordinates')->withinBox(-180, $y1, $x2, $y2);
$result1 = $qb->getQuery()->execute();
$qb->field('coordinates')->withinBox($x1, $y1, 180, $y2);
$result2 = $qb->getQuery()->execute();
$result = $result1->toArray() + $result2->toArray();
} else {
$qb->field('coordinates')->withinBox($x1, $y1, $x2, $y2);
$result = $qb->getQuery()->execute();
$result = $result->toArray();
}
I have a database of postcodes with Eastings/ Northings, is there a php script that can convert these values so I can use them on google maps?
Can I loop through the database and change each value?
Many thanks
Here is an API I wrote to do exactly this:
https://www.getthedata.com/bng2latlong
Syntax:
https://api.getthedata.com/bng2latlong/[easting]/[northing]
Example:
https://api.getthedata.com/bng2latlong/529090/179645
Some very basic PHP code might look like this:
$easting = 529090;
$northing = 179645;
$json = file_get_contents("https://api.getthedata.com/bng2latlong/$easting/$northing");
$arr = json_decode($json, true);
$latitude = $arr['latitude'];
$longitude = $arr['longitude'];
so for a project in school I am trying to simulate where students live in our town since official data is not available obviously due to privacy concerns. I started looking for a generator that works by zip code, radius or county but I haven't been able to find any (commercially or free) I would love it to be free, but I might be able to secure some funding for a license. If I find random generators, they cannot be limited to a ZIP code or city to produce real addresses randomly.
A good idea I found was here: https://stackoverflow.com/a/12289969/1778542
Based on that I would pick the city center's long\lat coordinates, find out the outskirts coordinates to create a plane, then randomly generate long\lat coordinates within the plane, feed them back in to have Google approximate the addresses for it. One concern that was raised (and I try to avoid) is that Google doesn't use verified addresses, rather approximations.
Does anyone have a hint where to find such a generator or a sleeker way to use GMaps?
Thanks a million!
GP
I use this code in one of my Laravel Seeder it gets a random street name in Romania provided that you give it the location area and Town,
It works by getting the latitude and longitude for that area and then randomly adds a radius of 2 Kilometers, after that it makes another request to google api, and from that it extracts a random street name.
I don't know if this will help you, adjusting this code can generate a real address provided that you give a first good location to look;
Here is the code:
protected function getRandomStreetNameFromCity($judet, $city){
$kmRange = 2;
$initalLocation = [];
$randomLocation= [];
$randomKmval = mt_rand(1, $kmRange) / mt_getrandmax();
// Poor Man Lat and Lng
//Latitude: 1 deg = 110.574 km
//Longitude: 1 deg = 111.320*cos(latitude) km
$guzzelCl = new Client();
$guzelReq = $guzzelCl->request('GET', 'http://maps.googleapis.com/maps/api/geocode/json?address=Romania,'.$judet.','.$city.'&sensor=false', [
'verify' => false,
]);
if($guzelReq->getStatusCode() == 200){
$arrJson = json_decode($guzelReq->getBody(), true);
while (count($arrJson['results']) <= 0){
$judet= $this->getNewJudet();
$city = $this->getNewOras();
$guzelReq = $guzzelCl->request('GET', 'http://maps.googleapis.com/maps/api/geocode/json?address=Romania,'.$judet.','.$city.'&sensor=false', [
'verify' => false,
]);
$arrJson = json_decode($guzelReq->getBody(), true);
}
$initalLocation = $arrJson['results'][0]['geometry']['location'];
}
$plusMinus = $this->generateRandomString(1);
$randomExp = [ 1 => $tempLat = eval("return (1 / (110.574 ".$plusMinus." ".$randomKmval." )+ ".$initalLocation['lat']." );"),
2 => eval('return ('.$initalLocation['lng'].' '.$plusMinus.' 1/111.320*cos($tempLat));'),
];
$guzelReq = $guzzelCl->request('GET', 'http://maps.googleapis.com/maps/api/geocode/json?latlng='.$randomExp[1].','.$randomExp[2], [
'verify' => false,
]);
return explode(',', json_decode($guzelReq->getBody(), true)['results'][0]['formatted_address'])[0];
}
protected function getNewJudet(){
//This is a administrative type of location named 'judet' Romania is divided in a number bellow 50 of this
return array_rand($this->judetOras, 1);
}
protected function getNewOras(){
//This is a Town String
return $this->judetOras[$iterateJud = array_rand($this->judetOras, 1)][array_rand($this->judetOras[$iterateJud], 1)];
}
protected function generateRandomString($length = 10) {
$characters = '-+';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}