# Google maps composite queries

This is a working code snippet in JS, but could easily be adapted to PHP. It serves as a tutorial into something called waterfall processing, or composite queries. The idea is pretty simple:

• Google places limits you to 20 results per request, sorted by distance, around a postcode.
I want to find all the places within a certain bounding box that match a search term. For example, if I want to search for Starbucks near Washington D.C., not to find the nearest one, but to find where there isn’t one. Maybe I’d like to know this because I am a rival firm’s CEO. Maybe it’s just for the shits and giggles.

The Google Places API takes a search term, API key, starting location, and a bunch of other parameters. It returns an array of places in the object results.

The idea is simple:

1. Map the 20 results provided in a search query, keep the ones in our bounding box
2. When this is done, find the furthest away from our center and calculate the distance from the centre. As they are sorted by distance ascending, we can safely say that this is the maximum distance for the request, and draw a circle around it.
3. Pick three points separated by 260 degrees around the circle and search from those

The script is below - to see it live, go to http://blog.rescrape.it/index.php/2013/google-places-composite-queryes/. A small example of what it does:

```function(t) { var results = 0; var maxRadius = 0; var maxPoint = false; resultloop: for (var i = 0; i < t.results.length; i++) { var r = Math.sqrt(Math.pow(t.results[i].geometry.location.lat-t.x,2)+Math.pow(t.results[i].geometry.location.lng-t.y,2)); if (r > maxRadius) { maxRadius = r; maxPoint = i } if (t.results[i].geometry.location.lat < map.pos.ne.lat && t.results[i].geometry.location.lat > map.pos.sw.lat) { if (t.results[i].geometry.location.lng < map.pos.ne.lng && t.results[i].geometry.location.lng > map.pos.sw.lng) { for (var j = 0; j < map.markers.length; j++) { if (map.markers[j].id === t.results[i].id) { continue resultloop; } } } else { continue resultloop; } } else { continue resultloop; } var m = map._marker(t.results[i].geometry.location.lat, t.results[i].geometry.location.lng, t.results[i].name); m.id = t.results[i].id; results++; } var latToDistance = function(p1, p2) { var p1ll = new google.maps.LatLng(p1[0],p1[1]), p2ll = new google.maps.LatLng(p2[0],p2[1]); return google.maps.geometry.spherical.computeDistanceBetween(p1ll,p2ll); }; var mCircle = new google.maps.Circle({ center: new google.maps.LatLng(t.x,t.y), radius: latToDistance([t.x,t.y],[t.results[maxPoint].geometry.location.lat, t.results[maxPoint].geometry.location.lng]), fillColor: "#00FF00", fillOpacity: 0.2, strokeOpacity: 0 }); if (maxRadius > 0 && results > 0) { // Draw the circle mCircle.setMap(map.map); // Split request into three map._findNearby(inputSearchTerm.val(), t.x-maxRadius, t.y, cbF); map._findNearby(inputSearchTerm.val(), t.x+maxRadius*0.5, t.y+maxRadius * 0.86602540, cbF); map._findNearby(inputSearchTerm.val(), t.x+maxRadius*0.5, t.y-maxRadius * 0.86602540, cbF); } }; ```