What is Spatial Searching?

Geospatial technology or geomatics engineering is the discipline of gathering, storing, processing, and delivering geographic information, or spatially referenced information.

For the purposes of this tutorial were going to use Spatial search to find the locations which are near the place that we specify.

Configure schema.xml File

First you need to configure the schema.xml to include a special field type called location. This field type is specifically used for geospatial searching. Depending on the Solr version that you’re running on your machine it may or may not already be included on the default schema.xml file. But in case its not already included, here’s what you have to add:

After that you can now declare fields which will use the dynamic field:

1

<fieldname="locm_places">12.3456,-987.65</field>

As you can see from the example above, you can use dynamic fields by using the its name as the prefix for any field. In this case the prefix that we used is locm_. On the dynamic field declaration earlier we used the star * to tell to Solr that any field which uses the strings before the star will be using the attributes for this dynamic field.

Geocoding

We need actual coordinates as our data-source for the project that were going to build later on so were going to use the Google Geocoding API to convert places into coordinates. I’m just going to use some places in my town as an example:

If you want you can also use places in your town. Just make sure you check it with the Google Geocoding API first so that you know that it actually returns something. You can just paste the following URL in your browser’s address bar and replace SOME_PLACE_THAT_YOU_KNOW with an actual place that you know, well-known and highly accessible places in your town is a great choice since there will be a greater chance that its already been geocoded.

It’s important that you set the sensor to false since were not actually using a device with a location sensor (GPS locator) in this application.

Next we use the Google Geocoding API to convert the places that we specified earlier into a coordinate (latitude and longitude). Here were simply extracting the data returned from the Google Geocoding API for each of the places then storing it in a variable called $data. Then we convert it back to a JSON string using the json_encode() method.

Id is a required attribute for documents indexed in Solr so we have to generate a unique ID for each of the places that were going to add. Also remember to use dynamic fields that are already available from the schema.xml file in Solr. In this case were using the dynamic field ss_* which has a data type of string to store the name of the place, and the dynamic field locm_* to store the coordinates of the place.

123456789101112131415161718192021

<?php$url='http://maps.googleapis.com/maps/api/geocode/json?address=place&sensor=false';$data=array();$id_prefix='SUPER';//it's a good idea to prefix your Solr ID's just to make sure it won't have the same id as something that already existsforeach($placesas$i=>$place){$request_url=str_replace('place',urlencode($place),$url);//replace the place string in the url with the urlencoded address$response=file_get_contents($request_url);//make the actual request to the google geocoding api$results=json_decode($response);//google geocoding api returns a JSON string so we have to use json_decode() to convert it to an array$data[]=array('id'=>$id_prefix.$i,'ss_place'=>$results->results[0]->address_components[0]->short_name,'locm_lat'=>$results->results[0]->geometry->location->lat.','.$results->results[0]->geometry->location->lng);}$doc=json_encode($data);//convert the data to a json string?>

Next make the request to the Solr server to update the index using curl:

12345678910111213141516171819202122232425262728

<?php$solr_update_url='http://localhost:8080/solr/update/json';//the url for updating the solr index using a json document$solr_commit_url='http://localhost:8080/solr/update?commit=true';//the url for commiting the updates$curl=curl_init();curl_setopt_array($curl,array(CURLOPT_RETURNTRANSFER=>1,//specify that we want to return the response so we can store it in a variableCURLOPT_HTTPHEADER=>array("Content-type:application/json"),//header typeCURLOPT_URL=>$solr_update_url,CURLOPT_POST=>1,//specify that we want to post a dataCURLOPT_POSTFIELDS=>$doc,//the data that we want to post));$update_response=curl_exec($curl);curl_close($curl);//commit the changes to SOLR$curl=curl_init();curl_setopt_array($curl,array(CURLOPT_URL=>$solr_commit_url));$commit_response=curl_exec($curl);curl_close($curl);?>

Building the Application

Now for the fun part, let’s start building the application. You’re going to need to download and link up the following resources if you want to follow along:

jQuery

jQuery UI (Use the custom builder to only include the dependencies of jQuery UI slider and the Slider itself)

The application that were going to build is simple, were just going to need a text field in which the user will input the base location, then a slider to adjust the distance. The markers that points out the nearby places which matches the current distance will automatically be updated once the user moves the slider.

varloc;//this will store information about the current base location selected by the uservarmarkers_array=[];//this will store an array of the markers that were created//set the map optionsvarmap_options={center:newgoogle.maps.LatLng(16.61096000671,120.31346130371),zoom:17,//set zoom level to 17 mapTypeId:google.maps.MapTypeId.ROADMAP//set map type to road map};//layout the map in the pagevarmap=newgoogle.maps.Map(document.getElementById("map-canvas"),map_options);varhomeLatlng=newgoogle.maps.LatLng(18.35827827454,121.63744354248);//set the base coordinate//set the marker for the base coordinatevarhomeMarker=newgoogle.maps.Marker({position:homeLatlng,map:map,draggable:false});//get the text field which the user will use to search for a base locationvarinput=document.getElementById('location');varautocomplete=newgoogle.maps.places.Autocomplete(input);//google maps autocompleteautocomplete.bindTo('bounds',map);//bind the selected place in the autocomplete text field to the map/* executed when a place is selected from the search bar this will automatically adjust the map settings to display the place that was entered in the text field*/google.maps.event.addListener(autocomplete,'place_changed',function(){varplace=autocomplete.getPlace();//if the selected location has a geometry then show it on the mapif(place.geometry.viewport){map.fitBounds(place.geometry.viewport);//automatically adjust the display on the viewport}else{map.setCenter(place.geometry.location);//if the location doesn't have a geometry, use the locationmap.setZoom(17);//set the zoom level to 17}homeMarker.setMap(map);//place the marker in the maphomeMarker.setPosition(place.geometry.location);//set the position of the marker based on the selected location//update the global variable which stores the current locationloc=place.geometry.location;});

Add a method to clear the markers, this will be used to clear the markers every time the distance is updated:

$("#slider").slider({slide:function(event,ui){map.clear_markers();//clear all the markers that are currently in the mapvardistance=ui.value;//the selected distance//get the latitude and longitude from the location that we updated earliervarcoordinate=loc.lat()+","+loc.lng();//create the object which will store the data that were going to pass through solrvardata={'coordinate':coordinate,'distance':distance};$("#distance").val(distance);//update the text field which shows the current distance//make the request$.post('http://my.dev/tester/geocoding/get_nearby_places.php',data,function(response){varresults=JSON.parse(response);//convert the JSON string to a JavaScript objectif(results.response){varplaces=results.response.docs;//the actual places that were returned by solrvarplace_count=places.length;varmarkers=[];//loop through all the places that were returnedfor(varx=0;x<place_count;x++){varcoordinate=(places[x]['locm_lat'][0]).split(',');//convert the string to an arrayvarlat=coordinate[0];varlng=coordinate[1];//create the marker which points out the nearby placesmarker=newgoogle.maps.Marker({position:newgoogle.maps.LatLng(lat,lng),map:map});//push the marker to the array which stores the markers so we can clear them later//when user moves the slider againmarkers_array.push(marker);}}});}});

Finally, add the code which will make the query to the Solr server to get the places which are near to the selected location. What were doing here is constructing the URL needed to make the request to the solr server, then getting the actual response that was returned and then outputting it.

1234567891011121314151617181920212223

<?php//the url for querying solr, 8080 is the port where solr is running$url='http://localhost:8080/solr/select?q=*:*&fq=';//the string that we need to replace in the query$search=array('BASE_COORDINATE','DISTANCE');//the string that were going to use as a query$replace=array($_POST['coordinate'],//the latitude and longitude pair (eg. 123.111,456.233)$_POST['distance']//the selected distance);//replace the placeholder text with the input supplied by the user then encode the part of the url which has the query$url.=urlencode(str_replace($search,$replace,'{!geofilt pt=BASE_COORDINATE sfield=locm_lat d=DISTANCE}'));$url.='&wt=json';//set the response type to json$response=file_get_contents($url);//get the json string returned from the queryecho$response;//output the json string?>

The output will look like this:

Conclusion

That’s it for this tutorial, you’ve learned how to use Solr’s spatial search with Google Maps.
You can further improve the application that we just built. For example, you can make a distinction to the marker for the selected location by giving it a different color or icon since the marker for that is currently the same with the markers for locations which are near the selected location. You can also add filters, for example only show nearby restaurants to the location that you selected.