Sorry this page looks weird. It was automatically migrated from my old blog, which had a different layout and different CSS.

GPS-style Latitudes and Longitudes on Rails Forms

The Problem

Globe with great circles

Quite a few plugins exist to help you geocode post codes or zipcodes, and calculate the distance between two points. However few, if any, help you with latitudes and longitudes on forms, with input elements that match your GPS, and intelligent validation.

This is now solved.

The Situation

I’m building a web application that holds cruising information for offshore sailors. Much of the information is located, in the sense that it applies to a specific point on the Earth’s surface. A report on an anchorage, for example, might say something like, “Beware the submerged rock to starboard at the entrance; strong katabatic winds in spring; herds of wildebeest ashore.” So we need the sailors to supply latitudes and longitudes on most of the forms they fill in.

Offshore sailing

The consensus among existing geo plugins is to treat latitudes and longitudes as floats, storing them in the database as decimals. However that’s not how people handle latitudes and longitudes. For excellent historical reasons, we think in degrees, minutes and seconds. Or, if you use a GPS — and all sailors do — degrees, minutes and decimal minutes (milli-minutes).

If you want people to enter the latitude and longitude of an anchorage on your site, and to enter them correctly, you need to let them just read it off their GPS. And if they enter an invalid value, you need to flag whether it’s the degrees, the minutes, or the milli-minutes — not simply say the whole lot “is invalid”.

The Solution

Buy the Rails 2 Plugin Patterns Peepcode PDF. Using the PDF I was able to write the plugin right, first-time. No trial and error: straight to the solution. The time it saved was more than worth $9. And the author’s a great guy ;-)

So, install my GeoTools plugin:

$ script/plugin install git://github.com/airblade/geo_tools.git

(I’ll move all my plugins to Git soon, but they’ll remain available in Subversion for the forseeable future.)

Run a database migration to add latitudes and longitudes to the relevant tables. Let’s say we are talking about buried treasure:

  def self.up
    create_table :treasures do |t|
      t.string :name
      t.boolean :display_on_map, :default => true
      t.with_options :precision => 15, :scale => 10 do |c|
        c.decimal :latitude
        c.decimal :longitude
      end
      t.timestamps
    end
  end

Looking for treasure (credit: Pirates of the Caribbean)

Note we store latitudes and longitudes in the conventional way, so you can use other geo plugins too.

Tell your models what to do:

  class Treasure < ActiveRecord::Base
    acts_as_location
  end

Add latitude and longitude fields to your forms:

<% form_for @treasure do |f| %>
  <p>
    <label for='treasure_name'>Name</label>
    <%= f.text_field :name %>
  </p>
  <p>
    <label for='treasure_display_on_map'>Display on map?</label>
    <%= f.check_box :display_on_map %>
  </p>
  <p>
    <label for='treasure_latitude'>Latitude</label>
    <%= f.latitude_field :latitude %>
  </p>
  <p>
    <label for='treasure_longitude'>Longitude</label>
    <%= f.longitude_field :longitude %>
  </p>
<% end %>

Done.

You can also easily display a latitude and longitude:

<p>Name: <%=h @treasure.name %></p>
<p>Location: <%= @treasure.location %></p>

And that will produce:

<p>Name: King Solomon's Mines</p>
<p>Location: 12°34.567′S, 98°76.543′W</p>

Bonus

Happily for you, my AirBudd accessible form builder plugin is fully compatible with GeoTools. So instead of the form shown above, you can write this:

<% airbudd_form_for @treasure do |f| %>
  <%= f.text_field      :name %>
  <%= f.check_box       :display_on_map %>
  <%= f.latitude_field  :latitude %>
  <%= f.longitude_field :longitude %>
<% end %>

And that’s compatible with HAML too.

So now we have meaningful, GPS-style latitudes and longitudes with minimal effort. And treasure.

Comments

imo this is quiet unnecessary, no user can enter geo-cordinates(at least not 98%…), so they have to choose them from a map (enter: google maps api), and no user can interpret geolocation information(98%…) so i need a map output again. Which leaves me with a plain latitude and longitude field and NO need for all this minutes second etc calculation/validation.

What i would have packed into this plugin: – what is the distance between a and b, – which locations are in 5km distance to this location

ym2c

grosser • 3 June 2008

I disagree with the above commenter.

I think this plugin is great. Indeed, I have been on the lookout for something just like this.

I am writing an application for maritime users. These users do want to supply latitude/longitude coordinates in DMS.

Thanks Andy!

Xin

Xin • 3 June 2008

Grosser, different audiences have different needs. This plugin is aimed at people who do understand (and want) exact latitudes and longitudes — such as Xin’s (and my) maritime users.

It sounds like you need the GeoKit plugin, which will calculate distances between a and b, and locations within 5km of somewhere. And since GeoKit exists, there was no reason for me to duplicate it.

Finally, you don’t have to choose between this plugin or GeoKit. You can use both, because the database column types are the same.

Andy Stewart • 3 June 2008

Xin, that’s great to hear. Thanks!

Andy Stewart • 3 June 2008

Andrew Stewart • 22 May 2008 • RailsClient Work
You can reach me by email or on Twitter.