Ruby On Rails Geographical Tools
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.