Ruby On Rails Geographical Tools

  Ruby   Rails   GIS  
 Aug 2019

There is nothing quite like a geographicly aware web application. The data, the maps. It’s all very nice. With Ruby, some gems and Rails, it is all very easy too.

Distance Queries

The starting point for a geo-aware web app is adding location data to your records and querying based on that data. gem ‘geokit-rails’ get’s that working for you.

The goal of this plugin is to provide the common functionality for location-oriented applications (geocoding, location lookup, distance calculation) in an easy-to-use package.

Add it to your models like this:

class Location < ActiveRecord::Base
  acts_as_mappable :default_units => :miles,
                   :default_formula => :sphere,
                   :distance_field_name => :distance,
                   :lat_column_name => :lat,
                   :lng_column_name => :lng
end

And now you can search your model like this:

Location.within(5, :origin => @somewhere)

There are various methods to search in binding boxes, by distance, within a range.

You should store your lat and lng as decimals. GeoKitRails has these definitions in the specs, which are probably overkill, but safe:

t.column :lat,         :decimal, :precision => 15, :scale => 10
t.column :lng,         :decimal, :precision => 15, :scale => 10

Geolocation

The magical ability to turn an address into coordinates. Geokit is required by Geokit-rails and provides geolocation capabilities. Install it and add api keys for your favourite service and use it to fetch Latitude and Longitude for your locations.

loc=Geocoder.geocode('100 Spear St, San Francisco, CA')
if loc.success
  puts loc.lat
  puts loc.lng
  puts loc.full_address
end

UK Easting & Northings, Grid Reference and Latitude & Longitude Conversions

For UK apps, your users will often use Eastings & Northings, ie Grid Refs.

To deal with this, store Latitude and Longitude for locations in the models, and convert to Grid Reference when required.

Note that you need to store latitude and longitude to 6 decimal places to have a precision of approximately 10cm. There isn’t much point in more than that, your device won’t be accurate enough. You probably don’t want less than say 5 as 4 dp is 11m. Which is pretty vague.

In general, you want to be using latitude & longitude on a wgs84 projection. That’s the international projection that things like google maps use. There are conversion tools to switch to osgb36 coordinates, but I haven’t had a need to use them yet.

Latitude and Longitude to Eastings and Northings.

Ruby has a simple gem for this, osgb_convert.

WGS84 lat/lons -> OSGB36 lat/lons-> OS Eastings & Northings

lat = 50.7797907593207
lon = -3.07382296309886
height = 0

wgs84_point = OsgbConvert::WGS84.new(lat, lon, height)
=> #<OsgbConvert::WGS84:0x00007ffda7fbe7d8 @lat=50.7797907593207, @long=-3.07382296309886, @height=0>

> osUKgridPoint = OsgbConvert::OSGrid.from_wgs84(wgs84_point)
=> #<OsgbConvert::OSGrid:0x00007ffda45a8968 @easting=324389.1756907749, @northing=98352.22235149551>

irb(main):009:0> osUKgridPoint.easting
=> 324389.1756907749

> osUKgridPoint.northing
=> 98352.22235149551

Eastings and Northings to Latitude and Longitude

If you want to convert, back the other way, Grid Ref Eastings and Northings to latitude & longitude use OsgbConvert’s wgs84 method.

easting = 324389.1756907749
northing = 98352.22235149551
osUKgridPoint = OsgbConvert::OSGrid.new(easting, northing)
osUKgridPoint.wgs84

=> #<OsgbConvert::WGS84:0x00007ffda362e708 @lat=50.77979073832952, @long=-3.073822994860748, @height=48.90441460069269>

Eastings Northings to Map Reference

You can get a grid ref from OsgbConvert like this:

osUKgridPoint.grid_ref(12)
=> "SY243891983522"

Note with this gem, if you subsequently want a map reference to a different accuracy, recreate the object otherwise you will get the cached value.

osUKgridPoint.grid_ref(8)
=> "SY24389198352

Map Reference to Latitude & Longitude.

Whilst there is a method for this in osgb_convert, it gives an error. I’ve been using osgb gem to do this conversion.

> "SY2438998352".to_latlng  
=> #<Osgb::Point:0x00007ffda499d168 @lat=50.77979569285011, @lng=-3.0738256002965434, @datum=:wgs84, @precision=6>

Alternative gems

Breasal

If You prefer, there is an alternative Gem that does the same thing, Breasal. Currently breasal does not have Lat/Lng to Grid, but it does have TM75 specific calculations if you are in Ireland and need that.

en = Breasal::EastingNorthing.new(easting: 412617, northing: 308885, type: :ie)
en.to_wgs84 # => {:latitude=>52.67752501534847, :longitude=>-1.8148108086293673}

OsMapRef

For map references from eastings and northings, and eastings and northings from map references, you can use OsMapRef from DEFRA.

> location = OsMapRef::Location.for "#{easting}, #{northing}"

=> #<OsMapRef::Location:0x00007fa0a5efa4a0 @easting="400096", @northing="233507">

> location.map_reference

=> "SY 24389 98352"

location = OsMapRef::Location.for 'SY 24389 98352'

location.easting
=> "324389"
location.northing
=> "098352"

Wrap Up

With these tools you can find lat and long from eastings and northings, map references or postal addresses. You can query records by distances from a point. You can output locations back into map references. Yay for maps. Yay for ruby.

We of course want to show all this on a nice map, Ill cover that in the next post.


Web Development

Got a web project you need doing well? We can help with that!

Find out more