I want to search records in Sphinx Search, which are within "X" miles from a "Polyline", records can be on either side of Polyline. I have set of geocode which forms a route.
$polylineLatLon = array("48.1390965,11.580255","48.2617271,11.6472244","48.3885849,11.5972817","48.5191226,11.5829265","48.6465704,11.516","48.773632,11.4611113","48.9037728,11.4703166","49.0096128,11.3544774","49.1298187,11.272155","49.2569017,11.2146807","49.3872571,11.2021279","49.5046949,11.2919927","49.6166182,11.3960946","49.7263312,11.5053892","49.8565686,11.51806","49.9767852,11.606766","50.0952959,11.6940558","50.2140534,11.7813241","50.3446984,11.7877614","50.4762554,11.8022454","50.6064391,11.791023","50.7318807,11.8482506","50.8638132,11.8523812","50.9891474,11.9091797","51.1166811,11.9561183","51.2372088,12.0594156","51.3430381,12.1846855","51.4732111,12.2154129","51.6025257,12.183516","51.7333531,12.2172689","51.8452442,12.3273146","51.9514704,12.4555349","52.03493,12.6191926","52.1380019,12.7586353","52.227931,12.913034","52.3074102,13.0885363","52.4209106,13.1959963","52.5143051,13.3479595","52.5234738,13.4115041");
I tried 2 approach and failed.
1st Approach:
$cl = new SphinxClient;
$cl->SetLimits(0, 20);
$cl->SetMatchMode(SPH_MATCH_EXTENDED);
foreach($polylineLatLon as $singPoly){
$singPolyExplode = explode(",",$singPoly);
$latRad = Deg2Rad($singPolyExplode[0]);
$LonRad = Deg2Rad($singPolyExplode[1]);
$cl->SetGeoAnchor('latitude', 'longitude', $latRad,$LonRad);
$cl->AddQuery("", $indexerName);
}
$result =$cl->runQueries();
The problem with this approach is it will fetch records nearby each Geocode. Not the complete route path. Also, calculating distance for each records with polyline Geocode will take a lot of time. Thus its not an optimized approach.
2nd Approach:
$cl = new SphinxClient;
$cl->SetLimits(0, 2000);
$cl->SetMatchMode(SPH_MATCH_EXTENDED);
$cl->SetSelect("*, CONTAINS(GEOPOLY2D(48.1390965,11.580255,48.2617271,11.6472244,48.3885849,11.5972817,48.5191226,11.5829265,48.6465704,11.516,48.773632,11.4611113,48.9037728,11.4703166,49.0096128,11.3544774,49.1298187,11.272155,49.2569017,11.2146807,49.3872571,11.2021279,49.5046949,11.2919927,49.6166182,11.3960946,49.7263312,11.5053892,49.8565686,11.51806,49.9767852,11.606766,50.0952959,11.6940558,50.2140534,11.7813241,50.3446984,11.7877614,50.4762554,11.8022454,50.6064391,11.791023,50.7318807,11.8482506,50.8638132,11.8523812,50.9891474,11.9091797,51.1166811,11.9561183,51.2372088,12.0594156,51.3430381,12.1846855,51.4732111,12.2154129,51.6025257,12.183516,51.7333531,12.2172689,51.8452442,12.3273146,51.9514704,12.4555349,52.03493,12.6191926,52.1380019,12.7586353,52.227931,12.913034,52.3074102,13.0885363,52.4209106,13.1959963,52.5143051,13.3479595,52.5234738,13.4115041), latitude_deg,longitude_deg) as inside");
$cl->setFilter("inside",array(1));
$result = $cl->Query("", $indexer);
The problem with this approach is that the records a fetched only within the polygon. However, I want records on both side of polyline within few miles away from route.
In the most general sense, need to 'buffer' the polyline, to turn it into a polygon :) Ie create a polygon that encompasses the area within X distance of the polyline.
Buffer is a well known GIS operation. ... I dont actully know of a any simple PHP libraries that will do it (Sphinx doesnt have one!)
Google also created a 'Route Boxer' algorithm, which is taking a polyline and turning into a series of non-overlapping rectangles. Sort of a simplified (and more approximate!) system. Looking up results for a series of boxes is simpler than full polygone queries, even though sphinx can do them.
There is PHP library: https://github.com/bazo/route-boxer
I have a form in my PHP application which searches for locations in europe: Germany, France, Poland, Czech Republic, Spain, Austria, Romania, Italy.
This is my base query:
$address = "https://maps.googleapis.com/maps/api/geocode/json?address={$input}";
If the users tries to find "mannheim" for example, it should return Mannheim, Germany.
Instead it returns a point in Pennsylvania, US. I tried adding this address component restriction: &components=administrative_area:EU, but this is not reliable because instead of finding Mannheim Germany, it points to a place 300 km away:
https://www.google.ro/maps/dir/50.5320001,6.6364339/Mannheim/#50.0388399,8.4546225,7.25z/data=!4m8!4m7!1m0!1m5!1m1!1s0x4797cc24518e3f45:0xb1e4fe7aa406e687!2m2!1d8.4660395!2d49.4874592?hl=en
If I append , Germany:
$address = "https://maps.googleapis.com/maps/api/geocode/json?address={$input}, Germany";
then the response is correct.
Is there a way to specify some countries for which the search should happen?
A last resort is to make a separate search for each country, but this would be really slow given the ~10-12 countries I search in.
restrict google search result via country restriction like this below:
var options = {
componentRestrictions: {country: 'DE'}
};
and place options parameter in Autocomplete or other function you are using.
var autocomplete = new google.maps.places.Autocomplete(input,options);
Looks like it's possible now: https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete-multiple-countries#maps_places_autocomplete_multiple_countries-javascript
// Sets a listener on a given radio button. The radio buttons specify
// the countries used to restrict the autocomplete search.
function setupClickListener(id, countries) {
const radioButton = document.getElementById(id);
radioButton.addEventListener("click", () => {
autocomplete.setComponentRestrictions({ country: countries });
});
}
setupClickListener("changecountry-usa", "us");
setupClickListener("changecountry-usa-and-uot", [
"us",
"pr",
"vi",
"gu",
"mp",
]);
According to google docs, we can add up to 5 countries in component filter
https://developers.google.com/maps/documentation/places/web-service/autocomplete
A grouping of places to which you would like to restrict your results. Currently, you can use components to filter by up to 5 countries. Countries must be passed as a two character, ISO 3166-1 Alpha-2 compatible country code. For example: components=country:fr would restrict your results to places within France. Multiple countries must be passed as multiple country:XX filters, with the pipe character | as a separator. For example: components=country:us|country:pr|country:vi|country:gu|country:mp would restrict your results to places within the United States and its unincorporated organized territories.
I am building a database of local spots, each of these local spots is stored in my mysql database with a latitude and longitude coordinate set.
I want to build a search where a user enters his location into a form, my form will determine the users latitude and longitude via the google maps geolocation API successfully.
My problem/question is this. How can I determine which sets of latitude and longitude of the items in my mysql database would be within a predined radius (say 25miles).
Is there a way to calculate the outerbounds of latitude and longitude frm the 25 mile radius and then check for items that match that # with a basic mysql select? If so what would the converion between miles and lat/long degrees? If not, is there any other way to pull this off anyone could think of?
Thank you!!
Almost the same as what #Skidrow wrote before but in php. Php could be better if you want to check the result you already loaded else you have to iterate all the post in mysql. If that case the mysql version would be better
function calculateDistance($targetLat, $targetLng, $againstLat, $againstLng, $units = 'miles')
{
$result = 3958.75 * acos(
sin($targetLat / 57.2958) * sin($againstLat / 57.2958) +
cos($targetLat / 57.2958) * cos($againstLat / 57.2958) *
cos($againstLng / 57.2958 - $targetLng / 57.2958)
);
switch ($units)
{
default:
case "":
case "miles":
$result *= 1;
break;
}
return $result;
}
Test using this function
$target* = the spot in your database
$against* = user location
There are many ways. You can use a spatial index or the harvesine formula. You can also create a proximity search. Read here: https://developers.google.com/maps/articles/geospatial. The harvesine formula is the great circle distance between two points on a sphere.
I am using Redis with php and its library phpredis.
I have the following structure:
city:1 {
lat->14.02,
lon->10.00,
hash->sp2f1h60w5j
}
city:2 {
lat->14.03,
lon->10.1,
hash->sp2f1h60w5m
}
But I haven't found a way to search by hash. I would like to search for example the exact same hash or approximately the same hash.
Should I change my structure?
Thanks.
You could follow Josiah Carlson's advice from this thread, and convert the hash into a number, and use that as a score in a sorted set. Something like this:
city:1 {
lat->14.02,
lon->10.00,
hash->sp2f1h60w5j
}
city:2 {
lat->14.03,
lon->10.1,
hash->sp2f1h60w5m
}
To use the geohashes as numbers, you need to decode them from base 32 using a particular character map -- see Geohash on Wikipedia. I'll use example numbers below.
cities { (1, 4711), (2, 4712) }
Now you can use zrangebyscore to find cities in an area:
zrangebyscore cities 4000 5000
Why you don't use the key in redis to get the elements.
like:
city-sp2f1h60w5j = 1
city-sp2f1h60w5m = 2
then if you wish the closets:
get by "key":
city-sp2f1h60w*
if will return "array" of keys... or city id's. Can't remember the exact return data.
cheers
Do you want to find all items near a specific point? Since you're looking for exact or near match, I assume that's the case. Redis just released brand new Geo functionality yesterday (http://redis.io/commands#geo).
GeoRadius and GeoRadiusByMember are the ones you're looking for.
I just want to know how can I add the data type here? Google Charts requires data type when I use this code:
Im getting an error from google charts that it datatype needs to be defined...please help. Thanks!
while($r = mysql_fetch_assoc($query)) {
$google_JSON = "{cols: [";
$column = array_keys($r);
foreach($column as $key=>$value){
$google_JSON_cols[]="{id: '".$key."', label: '".$value."'}";
}
$google_JSON .= implode(",",$google_JSON_cols)."],rows: [";
$google_JSON_rows[] = "{c:[{v: '".$r['id']."'}, {v: ".$r['count']."}]}";
}
// you may need to change the above into a function that loops through rows, with $r['id'] etc, referring to the fields you want to inject..
//pass it into google charts data
echo $google_JSON.implode(",",$google_JSON_rows)."]}";
I haven't done any PHP in ages so I'm not going to offer to provide any patches to your code but the data type goes in the array elements of the cols hash reference i.e., where you create {id:"key", label: "value"} you need to add 'type: "number"' or 'type: "string"' etc. How you determine whether it is a number of a string by referring back to your query is not going to be that simple. You could look at the column type via whatever metadata the mysql module provides for the column but most charts require number types for the series and string/number types for the x-axis values. You could always set the first column to string or number and the other columns to number but it would depend on the sort of chart you are creating.
BTW, I'm surprised you cannot create the structure natively in PHP and then convert to JSON rather than building the JSON string up like this - you can in Perl quite easily.