In this article we’ll describe how to perform a map radius search. Although we’re basing this on a WordPress project with Google Maps and property listings, the same approach can be used in any framework, any map library and certainly with different objects in place of real estate properties.
The general concept of a map radius search is that we have a center point on the map from which we want to include objects within a radius distance. This means for example if the user chooses 5KM as their radius, any object returned should be 5,000m or less distance when comparing the object coordinates to the center point coordinates.
We will presume that whatever the data is that your searching, it has latitude and longitude data stored (coordinates). These do generally need to be stored separately if you are to use an initial range search to limit results. We’ll discuss this more later, just understand for now that if you have a dataset with coordinates stored such as (46.4322, 6.214455) this will likely not work for searching, because what we need is to have the latitude and longitude stored separately so we can search based on a range.
In this tutorial the approach we’re demonstrating is a 2-stage search. Stage 1 is designed to limit the results to a rectangular area bounded by the min/max for latitude and longitude which we calculate simply by adding and substracting from our center point. In stage 2 we refine the initial list to a circular radius by eliminating objects that are within the bounding range we used to search the data in stage 1, but are on the edges outside of the radius. We do this using a spherical geometry library provided by Google Maps. If you’re using another map library check if it provides a way of calculating the exact distance (direct distance) between 2 coordinate points.
If having to do 2 different steps to finalize your search results sounds like it’s not performant enough, or sounds like a pain to program… consider that the alternative is likely a fairly complex mathematical formula. Obviously you can’t run a search for every point around a circular radius which is constructed from thousands of coordinates on the map. Perhaps a subquery would enable you to check each property during a database query? Or you could try to combine the latitude and longitude values together and create a bounding range in this manner… but would it be accurate? If you think you know of a way to do a radius search in 1 single step, let us know in the comments below.
The upside of the 2-stage method is the both steps are actually simple enough to setup. In step 1 we’ll be using a post meta query which is specific to WordPress, but if you’re working in a different platform or even working with WP data that isn’t stored in post meta, just think of this as a wrapper around a MySQL between query because that’s more or less what we’re creating.
In our example project our starting point was a zipcode in Switzerland, such as 5286. Yes the swiss zipcodes are just 4 numbers long. Using this, we can get the coordinates for the central point of the zipcode with the Google Maps Geocoding API. We will send the request as approximately “nation = CH” and “postal_code = 5286”. We’re going to skip the specifics here because you might already have a central point, or might be using a different dataset as you’re starting point. Whatever your central point data is, it’s going to be a set of coordinates, a single latitude and longitude point. Our next step is to create a rectangular bounding box around the center point which is defined as a minimum lat, maximum lat, minumum lng, maximum lng. So we’ll take a single coordinate, and we’ll get back 4 points that can also be thought of the top/right/bottom/left representing a box around the initial center point.
The calculation for latitude uses the number 111,000 which is the number of meters that represents 1 degree of latitude. Whatever our radius distance is going to be, we will divide that radius (in meters) by 111,000 to get the degrees of latitude that need to be added and subtracted to find the latMin and latMax.
$centerPointLat = 46.9283; $oneDegreeLatitude = 111000; // In meters. You could also use KM's here, or convert to miles. $radius = 5000; // Radius in meters, we could use KM's here or miles but it must be the same unit of measurement as what we used for one degree latitude. $latRadiusDegrees = $radius / $oneDegreeLatitude;
At this point we have the variable $latRadiusDegrees which again is the number of degrees we need to add to and remove from the center point radius in order to create a searchable range. Unless your radius is over 111 km’s, this is usually going to be a number less than 1. For instance a 5km radius is going to be 0.045045045 degrees of latitude, that’s based on 5,000 meters of radius divided by 111,000 which is one degree of latitude in meters.
$latMin = $centerPointLat - $latRadiusDegrees; $latMax = $centerPointLat + $latRadiusDegrees;
As you can see above it’s a simple addition and subtraction to get our bounding range for the latitude. We now want to follow this same process for longitude, but there is one extra surprise speedbump in the road. Unlike latitude, the longitude varies at different points on the earth. Fortunately you can get this number by search for a utility that provides it. The variation is actually based on latitudes, because as you go further north or south the longitude distance per degree varies. If you’re building something that works in many different regions (where it varies by latitude) then you may have to get a data set that covers a wide range of variable longitude numbers. For now we’re going to presume you’re developing for 1 region, and will therefore have a single number representing distance (in meters) for one degree of longitude. For our example project, because it’s Switzerland only, we have the number 76,000 meters as the distance for 1 degree of longitude.
After you’ve got all 4 boundaries calculated you can imagine being able to draw a rectangular area around your center point, the size of which is the exact distance of the radius from each boundary point to the center point. This is not a radius at this point, but it is a bounding area that will constrain our search enough to make it workable and later we will reduce this to objects that match after checking the distance from the center point to the object in a loop.
Now we need to create the query, in pseudo code this query is going to be “give me records where the latitude is BETWEEN the latMin and latMax AND where the longitude is BETWEEN the lngMin and lngMax”. If you’re querying a MySQL DB table directly, you will likely use the “BETWEEN” comparison, or you can use comparison operators such as >=, <= and do 4 comparisons, for each latMin, latMax, lngMin, lngMax.
As our project that inspired this guide was in WordPress and our properties were stored as posts with post meta, we were focused on building a meta query. We won’t go indepth here about how meta queries work, but the code belong will illustrate the concept. We have an array forming the meta_query, and inside this we have multiple arrays. The overall property search has other criteria aside from this radius search, such as number of bedrooms, price range etc. This is therefore an excerpt from the full meta query.
$meta_query = array( array( 'key' => 'lat', 'value' => array( $latMin, $latMax ), 'type' => 'DECIMAL( 5, 2 )', 'comparison' => 'BETWEEN' ) );