Picky: Geosearch 1

ruby / picky

This is a post in the Picky series on its workings.

Let me show you how to do a simple and fun geo search in Picky.

But first, lean back.

Enjoy the show

The index contains around 21’000 Swiss places, taken from Wikipedia.

First, I click a little around – Picky gives me places around the clicked location.

After that I show what happens if I just give Picky a latitude or a longitude. Then, combined with the place text, finally, just with the place text.

You’ll understand when you see it :)

It’s best to switch to full-screen:

The blob in the middle is Switzerland, by the way ;)

How do we do it?

The server code

The server … you probably could have done sleeping if you’ve been reading this blog dilligently ;)

The data comes from the CSV file data/swiss_places.csv

places = Index::Memory.new :geo do
  source         Sources::CSV.new(:location, :north, :east, file: 'data/swiss_places.csv')
  category       :location, partial: Partial::Substring.new(from: 1)
  geo_categories :north, :east, 1, precision: 3
end

What’s interesting here is the geo_categories method. It takes two categories, north, and east, which are both in the lat/lng format, e.g. 47.2, 8.3. (It also takes options lat_from, and lng_from if the categories don’t have the same names as in the data source)

Also, the 1 parameter in geo_categories denotes that we search 1 km around the clicked location.

This is actually the simple part. It does no exact calculation, but an approximate one that’s most correct in temperate zones. But as you see in the video, it works well. Especially in a “what’s around me” type search.

Still in the server config app/application.rb:

route %r{\A/places\Z} => Search.new(places)

Self-explanatory, eh? As regexp, you could also use %r{^/places$}.

That’s it for the server. Nothing special so far.

rake index; rake start and off we go.

The client code

In this part we’re going to install the map.

So we’re using the generated code, but add a little more information to the returned json hash.

We not only need the list results, but also the coordinates themselves. So we’re going to add them to the results separately.

We (ab)use populate_with, the method that makes models out of the returned ids and yields them to the block to be rendered.

We then use the models to add geo coordinates to the result hash that is sent to the client.

results = Geo.search params[:query], :ids => params[:ids], :offset => params[:offset]
results.extend Picky::Convenience
results[:geo] ||= [] # <= We initialize an array of coordinates in the results hash.
results.populate_with Location do |location|
  results[:geo] << [location.north, location.east] # <- and we populate it with the coordinates.
  location.to_s
end

So essentially, our geo data piggybacks to the Javascript client. JS, here we come!

The javascript client code

The javascript client requires a bit more work. Well, the map does.

We insert this after the PickyClient code. The first 6 lines are noise and map preparation.

// The map
//
$(document).ready(function() {
  if (GBrowserIsCompatible()) {
    // Map setup.
    //
    map = new GMap2(document.getElementById('map_div'));
    map.addControl(new GSmallMapControl());
    map.setCenter(new GLatLng(46.85, 8.05), 13);
    map.setZoom(7);

    // Click listener.
    //
    GEvent.addListener(map, "click", function(overlay, latlng) {
      if (latlng) {
        pickyClient.insert(Math.round(latlng.lat()*1000)/1000 + ' ' + Math.round(latlng.lng()*1000)/1000);
      }
    });
  }
});

Then, we add the most important part: A click listener that inserts the coordinates (rounded to 3 digits) in the search field, as you have seen in the video.

Now, searches are already sent off to Picky and come back. Whoosh!

What do we need to do now? Yes, draw some markers in the map. The PickyClient offers a callback that is called after Picky has updated the results (there are also before and success):

after: function(data, query) {
  map.clearOverlays();

  var geo = data.original_hash.geo;
  if (geo) {
    for (var i = 0; i < geo.length; i++) {
      map.addOverlay(new GMarker(new GLatLng(geo[i][0], geo[i][1])));
    };
  }
},

First we clear the overlays for the new results.

Then, we get the piggybacking geo data using the data object’s original_hash function, finally iterating over all coordinates and adding overlays as we go.

By default, the client only gets 20 results at a time. We set it to 100 using the fullResults option.

fullResults: 100

That’s it. It’s fast and quite easy to set up.

Sidenote

Since for Swiss data it is clear which is the longitude and which is the latitude (no data intersection), we can just enter e.g. 47.2 8.3, but if your data area isn’t exclusive, e.g. 33.1 33.2, meaning that latitude values can also be longitude values, just add north:33.1 east:33.2, to denote what is what if north, east are the names of your categories.

Conclusion

So we’ve seen

  1. that a geo search in Picky is quite snappy.
  2. that you can search for latitude and location name only, for example.
  3. how you can configure the server.
  4. how you can configure the client and the web frontend.

Hope you learnt something new!

Next Picky: Geosearch 2

Share


Previous

Comments?