5. Brief history: early days
many 1000s BC: smoke signals
as early as 3200 BC: celestial navigation
from 1000 BC: homing pigeons
between 1100-1200 AD: the magnetic compass
6. Brief history: modern era
early 1900s: radio triangulation
1960s: satellite GPS
1990s: automotive GPS navigation
7. Brief history: web era
late 1990s: IP lookup
mid 2000s: WiFi, GSM relevation
2004: A-GPS on smartphones
8. Brief history: web era
late 1990s: IP lookup
mid 2000s: WiFi, GSM relevation
2004: A-GPS on smartphones
13. 1st step: display a map
We'll use Google Maps
# app/views/layouts/application.html.slim
= javascript_include_tag https://maps.googleapis.com/maps/api/js
// app/assets/javascripts/maps.js
$(document).ready(function() {
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(43.7710332, 11.2480006),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map($(’#map’)[0], mapOptions);
});
git: google maps
14. 2nd step: geolocate
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function(p) {
var latlng = new google.maps.LatLng(p.coords.latitude,
p.coords.longitude);
$(’span#position’).text(latlng);
if (myMarker) myMarker.setMap(null);
myMarker = new google.maps.Marker(
{position: latlng, map: map, title: This is me}
);
},
function(error) {
alert(error.message);
},
{maximumAge: 600000}
);
}
else { console.error(No HTML5? What are you using, IE6?); }
git: HTML5 geolocation
15. Geolocation API
supported by all current browers
location sources:
1. GPS
2. WiFi/Bluetooth MAC address
3. GSM/CDMA cell IDs
4. IP address
one-shot vs continuous
19. Geocode
New feature: set the position manually.
We need to geocode the address.
Several services:
Google: 2.500 requests/day [1]
Yahoo!: 50.000 requests/day [2]
Bing: 50.000 requests/day [3]
Nominatim: 1 request/second [4]
FreeGeoIP: 1000 requests/hour [5]
Geocoder.ca and Geocoder.us: ?
many others...
20. Gems for geocode
Geokit (deprecated, still lacks support for Rails 3) [7]
Graticule [6]
Geocoder [8]
21. Geocoder gem in action
# Gemfile
gem geocoder
# map_controller.rb
def geocode
position = Geocoder.coordinates(params[:query])
respond_to do |wants|
wants.json { render :json = position }
end
end
// applications.js
$(’[data-action=cheat]’).click(function() {
$.getJSON(’/geocode/’, {query: $(’#address’).val()}, map.cheat);
}); git: geocoding
22. Awesome! We have a user position.
Now to interact with other users we need:
store lat/long (and address, eventually) in a DB
query the DB for nearby points
23. 1st approach: D.I.Y. I
# migration
class CreateUsers ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.float :latitude
t.float :longitude
t.string :address
t.timestamps
end
end
end
24. 1st approach: D.I.Y. II
# app/controllers/users_controller.rb
class UsersController ApplicationController
respond_to :json
def index
@users = @user.nearbys(params[:radius])
respond_with @users
end
end
25. 1st approach: D.I.Y. III
# app/models/user.rb
class User ActiveRecord::Base
attr_accessible :address, :latitude, :longitude, :name
def nearbys(radius)
# ... oh s@#!t
end
end
26. Let’s do some math
Earth is a sphere: coordinates are in degree, distance is in
meters.
Solve the second (inverse) geodetic problem
Given two points, determine the azimuth and length of the
line (straight line, arc or geodesic) that connects them. [9]
spherical model: Haversine formula (approximation)
oblate spheroid model: Vincenty's formulae
27. Haversine formula
d = 2r arcsin h(φ2 − φ1 ) + cos(φ1 ) cos(φ2 ) h(ψ2 − ψ1 ) (1)
=2 r arcsin sin2
φ2 − φ1
2
+ cos(φ1 ) cos(φ2 ) sin2
ψ2 − ψ1
2
(2)
where:
h() is the haversine function: h(θ) = sin(θ/2) 2
= 1−cos(θ)
2
d distance
r radius of the sphere (6371.0 km)
φ1 , φ2 latitude of point 1 and latitude of point 2
ψ1 , ψ2 longitude of point 1 and longitude of point 2
28. Geocoder to the rescue!
# app/models/user.rb
geocoded_by :address
reverse_geocoded_by :latitude, :longitude
after_validation :reverse_geocode
# app/controllers/users_controller.rb
def index
@users = @user.nearbys(params[:radius])
respond_with @users
end
Geocoder implements the Haversine formula
directly in the SQL query.
git: Geocoder backend
29. Geocoder goodies
# Nearest point
user = User.near(Firenze, 50, :order = distance).first
# distance from arbitrary point to user
user.distance_from([40.714, 11.234])
# Find the bearing (direction) between places
bearing = user.bearing_to(Paris, France)
Geocoder::Calculations.compass_point(bearing)
= NW
# find the geographic center (aka center of gravity)
polygon = [user1, user2, [40.22,-73.99], user4]
Geocoder::Calculations.geographic_center(polygon)
= [35.14968, -90.048929]
31. 2nd approach: PostGIS
PostGIS [10] is an extention for PostgreSQL that adds
support for geographic objects.
Alternatives are:
MySQL: implements the datatype geometry
Sqlite: with SpatiaLite extension
32. PostGIS setup
# install on a ubuntu box
sudo apt-get install postgresql-9.1-postgis
# prepare the db
createdb template_postgis
createlang plpgsql template_postigs
psql -Upostgres -d template_postgis -f [...]/postgis.sql
psql -Upostgres -d template_postgis -f [...]/spatial_ref_sys.sql
psql -Upostgres -d template_postgis -f [...]/postgis_comments.sql
# inside postgres console
UPDATE pg_database SET datistemplate = TRUE
WHERE datname = ’template_postgis’;
33. PostGIS features
PostGIS follows the OpenGIS Simple Features
Specication for SQL [11]
data types for points, linestrings, polygons, multipoints,
multilinestrings, multipolygons and geometrycollections
functions for measurement like area, distance, length
and perimeter
spatial indexes for high speed spatial querying
34. PostGIS with Ruby
We'll use gem activerecord-postgis-adapter
to integrate postgis data types in ActiveRecord.
38. Thanks for watching!
Francesco Disperati | @nebirhos
http://nebirhos.com
Source code of the demo app available at
https://github.com/nebirhos/stalk-my-friends
Special thanks to Cantiere Creativo and Silvia Shell
39. Random stuff cited
Google Geocode API https://developers.google.com/maps/documentation/geocoding/
Yahoo! PlaceFinder http://developer.yahoo.com/geo/placefinder/guide/responses.html
Bing API http://msdn.microsoft.com/en-us/library/ff701715.aspx
Nominatim http://wiki.openstreetmap.org/wiki/Nominatim
FreeGeoIP http://github.com/fiorix/freegeoip/blob/master/README.rst
Graticule http://graticule.rubyforge.org/
Geokit http://geokit.rubyforge.org/
Geocoder http://www.rubygeocoder.com/
Geodesy on Wikipedia http://en.wikipedia.org/wiki/Geodesy
PostGIS http://postgis.refractions.net/
OpenGIS http://www.opengeospatial.org/standards/sfs
PostGIS ActiveRecord Adapter https://github.com/dazuma/activerecord-postgis-adapter