T U T O R I A L



                                                    Adv. Rails
                                        ...
01
Using Geocoders
Geocoding
 What is it?
Geocoding
            What is it?

• “the heart of most mapping
  applications”
              Google Maps Apps. with Rails...
Geocoding
            What is it?

• “the heart of most mapping
  applications”Google Maps Apps. with Rails and Ajax by A....
Geocoding
  Example
Geocoding
            Example

• “1800 Old Bayshore Highway,
  Burlingame, CA, USA” > lat =
  37.368613, lng =-121.906458
Geocoding
             Example

• “1800 Old Bayshore Highway,
  Burlingame, CA, USA” > lat =
  37.368613, lng =-121.906458...
Geocoding
Complications
Geocoding
         Complications

• Geographic information system has
  to be updated regularly
Geocoding
         Complications

• Geographic information system has
  to be updated regularly
• Distinguishing between a...
Geocoding
  Services
Geocoding
            Services


• Google Maps
Geocoding
            Services


• Google Maps
• Yahoo! Maps
Geocoding
            Services


• Google Maps
• Yahoo! Maps
• Geocoder.us & Geocoder.ca
Intro to Yahoo! Maps with
             AJAX


$ rails yahoo


$ cd yahoo


$ ruby script/generate controller intro
app/views/intro/map.rhtml
<html>
     <head>
         <script type="text/javascript"
     src="http://api.maps.yahoo.com/a...
app/views/intro/map.rhtml



    <body onload="load()">
          <div id="map" style="width: 500px; height: 500px"></div>...
Geocoding
Using REST web services
Geocoding
  Using REST web services

• Format a REST query
Geocoding
  Using REST web services

• Format a REST query
• Use OpenURI to send the query
Geocoding
  Using REST web services

• Format a REST query
• Use OpenURI to send the query
• Parse the XML response to ext...
Google Maps’ REST Geocoder



http://maps.google.com/maps/geo?q=Torstra%C3%9Fe
+104,+Berlin,
+Germany&output=xml&key=ABQIA...
XML response
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.0">
  <Response>

    <name>...
Rails plugins
An initial comparison
Rails plugins
    An initial comparison

• GeoKit
Rails plugins
    An initial comparison

• GeoKit
• Graticule & acts_as_geocodable
Rails plugins
    An initial comparison

• GeoKit
• Graticule & acts_as_geocodable
• ActsAsLocatable
Rails plugins
    An initial comparison

• GeoKit
• Graticule & acts_as_geocodable
• ActsAsLocatable
• YM4R
GeoKit
Lab Exercise
GeoKit
           Lab Exercise


• Install GeoKit
GeoKit
           Lab Exercise


• Install GeoKit
• Get API Keys
GeoKit
           Lab Exercise


• Install GeoKit
• Get API Keys
• Try Geocoding (in Rails console)
GeoKit install


$ rails geokit


$ cd geokit


$ ruby script/plugin install
svn://rubyforge.org/var/svn/geokit/trunk
API keys in config/
                 envirnoment.rb

# This is your Google Maps geocoder key.
# See http://www.google.com/a...
Geocoding from multiple
          providers




GeoKit::Geocoders::provider_order = [:google, :yahoo]
Test it out
$ ruby script/console
Loading development environment.

>> include GeoKit::Geocoders
=> Object

>> home = Mult...
In memory calculations
>> office = MultiGeocoder.geocode("Lepsiusstrasse 70, Steglitz, Berlin,
Germany")
=> #<GeoKit::GeoL...
Advance Geocoding
geocoding with ActiveRecord
Advance Geocoding
geocoding with ActiveRecord


• setup database tables
Advance Geocoding
geocoding with ActiveRecord


• setup database tables
• auto-geocode your model
Auto geocoding a model’s
         address on create
class CreateLocations < ActiveRecord::Migration
  def self.up
      cr...
app/models/location.rb



class Location < ActiveRecord::Base
  acts_as_mappable :auto_geocode => true
end
Testing this out
>> Location.find :all
=> []

>> Location.create(:address => "Torstrasse 104, Berlin, Germany")
=> #<Locat...
Sort by distance
>> locs = Location.find :all

=> [
	   #<Location:0x3375d90
	   	   @attributes={"lng"=>#<BigDecimal:3375...
sort_by_distance_from
>>   locs.sort_by_distance_from(home)

=> [
	   #<Location:0x3375d90 @distance=0.0,
	   	   @attribu...
AR drops the distance column

>> locs = Location.find :all, :origin=>home, :within=>5, :order=>'distance'

=> [
	 #<Locati...
end of geocoding exercise
        (break?)
More GeoKit features
More GeoKit features


• Bounding queries
More GeoKit features


• Bounding queries
• Eager loading
More GeoKit features


• Bounding queries
• Eager loading
• IP geocoding
GeoKit::Bounds



>> bounds = GeoKit::Bounds.new( sq_sw_point,
	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 sq_ne_point )

>> locs = Lo...
Eager Loading



>> locs   =   Location.find :all,
	 	 	 	   	   	 	 	 	 	 :origin => home,
	 	 	 	   	   	 	 	 	 	 :inclu...
IP address geocoding



>> location = GeoKit::Geocoders::IpGeocoder.geocode('85.178.26.159')

=> #<GeoKit::GeoLoc:0x3756fe...
Cached IP location in
          app/controllers/
           application.rb
class ApplicationController < ActionController:...
Graticule
and its Rails plugin
Graticule
      and its Rails plugin


• Automatically geocodes your models
  when they are saved
Graticule
      and its Rails plugin


• Automatically geocodes your models
  when they are saved
• Comes as a gem plus a ...
Inital config



$ ruby script/generate geocodable_migration
add_geocodable_tables


$ rake db:migrate
Multi geocoder & key in
     config/envirnoment.rb
Geocode.geocoder = Graticule.service(:multi).new (

  Graticule.service(...
Model with required
             attributes
class CreateLocations < ActiveRecord::Migration
  def self.up
    create_table...
app/model/location.rb



class Location < ActiveRecord::Base
  acts_as_geocodable
end
Graticule in action
$ ruby script/console
Loading development environment.

>> Location.find :all
=> []

>> conf = Locatio...
distance_to
>> prevConf = Location.create :street => "777 NE Martin Luther King, Jr.
Blvd.", :locality => "Portland", :reg...
Search by location

>> Location.find(:all, :within => 50, :origin =>
"Torstrasse 104, Berlin, Germany")


=> [#<Location:0...
IP geocoding


def index
  @nearest = Location.find(	:nearest,
	 	 	 	 	 	 	 	 	 	 	      	
                           :or...
02
Location data in
 ActiveRecord
(PostGIS/PostgreSQL)
Geospatial Domain
 What’s so special about
         spatial?
Geospatial Domain
   What’s so special about
           spatial?



• Point, Line, Polygon, Multi*
OGC simple features
Vector data
http://www.ngdc.noaa.gov/mgg/shorelines
Vector data
http://www.ngdc.noaa.gov/mgg/shorelines
Geospatial Domain
   What’s so special about
           spatial?


• Point, Line, Polygon, Multi*
Geospatial Domain
   What’s so special about
           spatial?


• Point, Line, Polygon, Multi*
• Raster Data
Raster data
http://edc.usgs.gov/products/elevation/gtopo30/gtopo30.html
Raster data
http://edc.usgs.gov/products/elevation/gtopo30/gtopo30.html
Geospatial Domain
   What’s so special about
           spatial?
• Point, Line, Polygon, Multi*
• Raster Data
Geospatial Domain
   What’s so special about
           spatial?
• Point, Line, Polygon, Multi*
• Raster Data
• Spatial Re...
Geospatial Domain
   What’s so special about
           spatial?
• Point, Line, Polygon, Multi*
• Raster Data
• Spatial Re...
Geospatial Domain
   What’s so special about
           spatial?
• Point, Line, Polygon, Multi*
• Raster Data
• Spatial Re...
Free desktop GIS (uDig)
     http://udig.refractions.net
Free desktop GIS (uDig)
     http://udig.refractions.net
Free desktop GIS (uDig)
     http://udig.refractions.net
OpenJump




 http://openjump.org
GeoRails Stack
   Overview
GeoRails Stack
    Overview




  PostGIS Database
GeoRails Stack
    Overview




     GeoRuby

  PostGIS Database
GeoRails Stack
    Overview


   Spatial Adapter

      GeoRuby

  PostGIS Database
GeoRails Stack
    Overview

       YM4R

   Spatial Adapter

      GeoRuby

  PostGIS Database
Prerequisites
Installing PostGIS
Prerequisites
       Installing PostGIS

• Install PostgreSQL without PostGIS
  extension
Prerequisites
        Installing PostGIS

• Install PostgreSQL without PostGIS
  extension
• Install PostGIS separately
Prerequisites
        Installing PostGIS

• Install PostgreSQL without PostGIS
  extension
• Install PostGIS separately
• ...
Prerequisites
template_postgis
Prerequisites
       template_postgis
• create a clone of template1
Prerequisites
       template_postgis
• create a clone of template1
• add pl/pgsql language
Prerequisites
       template_postgis
• create a clone of template1
• add pl/pgsql language
• install the postgis function...
Prerequisites
       template_postgis
• create a clone of template1
• add pl/pgsql language
• install the postgis function...
Prerequisites
       template_postgis
• create a clone of template1
• add pl/pgsql language
• install the postgis function...
template_postgis code
c template1
CREATE DATABASE template_postgis with template = template1;

-- set the 'datistemplate' ...
SRID explained
$ psql -d template_postgis
template_postgis=# x -- to turn on expanded display
template_postgis=# SELECT * ...
using template_postgis




createdb -T template_postgis my_database
Prequisites
  GeoRuby
Prequisites
             GeoRuby
• Data structs to hold Spatial Data from
  PostGIS/MySQL-Spatial
Prequisites
             GeoRuby
• Data structs to hold Spatial Data from
  PostGIS/MySQL-Spatial
• Based on OGC Simple Fe...
Prequisites
             GeoRuby
• Data structs to hold Spatial Data from
  PostGIS/MySQL-Spatial
• Based on OGC Simple Fe...
Prequisites
             GeoRuby
• Data structs to hold Spatial Data from
  PostGIS/MySQL-Spatial
• Based on OGC Simple Fe...
GeoRuby




$ sudo gem install georuby
GeoStack
Running on Edge Rails
GeoStack
    Running on Edge Rails


• Installing Edge Rails from Git
GeoStack
    Running on Edge Rails


• Installing Edge Rails from Git
• Add Geo-plugins as Git Submodules
GeoStack
    Running on Edge Rails


• Installing Edge Rails from Git
• Add Geo-plugins as Git Submodules
• Git it up and ...
Livin’ on the Edge
# new rails project
$ rails geostack -d postgresql
$ cd geostack/
$ cat ~/Templates/dot-gitignore > .gi...
Add Useful Plugins
# spatail_adapter2, geo_scaffold
$ git submodule add http://github.com/sabman/spatial_adapter2.git 
   ...
Complete Mapping Solution
     using Ruby on Rails


1. Web Mapping Application using Google Maps


2. Using cURL API & Ac...
Complete Mapping Solutons
     using Rails (1)




Web Mapping Application using Google Maps
Magic of GeoScaffold


$ ruby script/generate geo_scaffold 
	 	 	 	 Location 
	 	 	 	 name:string 
	 	 	 	 description:tex...
running geo_scaffold
  mime_types Mime::Type.register "application/vnd.google-earth.kml+xml", :kml
  mime_types Mime::Type...
$ rake db:migrate
$ mongrel_rails start
voilà!
WTF!




okay okay! lets dig in and see what just
               happened
Housekeeping was done

       Migrations


     Spatial Indices


     Test Fixtures


       Mime-Types
Small Gotcha


in current edge-rails if a generator creates two
  migrations are they get the same timestamp



create   d...
Migrations with spatial data

def self.up
  create_table :locations do |t|
      t.point :geom, :null => false,
          ...
Spatial indices
$ ruby script/generate migration
add_index_to_locations


def self.up
  add_index :locations, :geom, :spat...
Spatial data migrations
              (points)
$ ruby script/generate migration add_locations_data

def self.up
  Location...
http://localhost:3000/locations/1
http://localhost:3000/locations/1
Spatial data migrations
            (polygon)
loc = Hotspot.new( :name => "Jinnah's Tomb",
      :geom => Polygon.from_coo...
Tests (fixtures)
one:
 id: 1
 geom: <%= Point.from_x_y_z(67.0665783392958,
	 	 	 	 	 	 	 	 	 	 	 	 	   24.9357149717549,
	 ...
Tests (unit/functional)
def test_should_create_location
  old_count = Location.count
  post :create, :location => {
      ...
Tests (unit/functional)




$ rake test:units
$ rake test:functionals


                          http://www.flickr.com/pho...
Tests (JavaScript)


$ ruby script/plugin install
http://dev.rubyonrails.org/svn/rails/plugins/
javascript_test


$ ruby s...
Tests (JavaScript)
new Test.Unit.Runner({

     setup: function() {
         $('sandbox').innerHTML =
	   	 	 	 "<div id='...
Tests (JavaScript)




$ rake test:javascripts
GeoRSS & KML Mime-Types
                  config/initializers.rb




Mime::Type.register
	 "application/vnd.google-earth.k...
Views & Controllers were
       generated



        all that
          CRUD
Read




lets take it one step at a time
CRUD location: show
             (helper)
 def show_map rec
   @map = GMap.new("map#{rec.id}_div")
   @map.control_init(	 ...
CRUD location:
           show (view template)
<%= GMap.header %>
<%= show_map @hotspot %>


...


      <b>Geom:</b>
    ...
CRUD location:
                  show (kml template)
xml.kml("xmlns" => KML_NS) do
    xml.tag! "Document" do
        xml....
http://localhost:3000/hotspots/1
CRUD location: index (helper)
    def show_maps recs
      @map = GMap.new("map_div", "map")
      @map.control_init(:smal...
CRUD location: index (helper)

        Lets looks at the code in bite size chunks



 def show_maps recs
   @map = GMap.ne...
CRUD location: index (helper)

    def show_maps recs
	    ...
     coords = (recs.length == 0) ? [[-45,-45],[45,45]] : []...
CRUD location: index (helper)


    def show_maps recs
	    ...
     bnd = MultiPoint.from_coordinates(coords).envelope
  ...
CRUD location:
            index (view template)
<%= GMap.header %>
<%= show_maps @hotspots %>
<% content_for :header do %...
http://localhost:3000/hotspots
Create




lets take it one step at a time
CRUD location:
new (JavaScript application.js)
function create_draggable_editable_marker()
{
      // intialize the values...
CRUD location:
new (JavaScript application.js)
      // if the user clicks somewhere other than existing marker
      else...
CRUD location:
     (layouts/hotspots.rb)



<%= stylesheet_link_tag 'geo_scaffold' %>
<%= javascript_include_tag :default...
CRUD location: new (helper)

 def new_map
   @map = GMap.new("map_div")
   @map.control_init(:small_map => true,
	 	 	 :hi...
CRUD location:
        new (view template)

<%= GMap.header %>
<% new_map %>
<%= @map.to_html %>
  ...
<%= @map.div :width...
CRUD location: new
           (controller)

def create
  @location = Location.new(params[:location])
  geom = Point.from_x...
http://localhost:3000/hotspots/new
http://localhost:3000/locations/new
http://localhost:3000/locations/new
http://localhost:3000/locations/new
Update




lets take it one step at a time
CRUD location: edit
                     (JavaScript)
function create_draggable_marker_for_edit(lng, lat) {
     // inital...
CRUD location: edit (helper)

def edit_map rec
  @map = GMap.new( "map_div" )
  @map.control_init(:large_map => true,:map_...
CRUD location:
        edit (view template)

<%= GMap.header %>
<% edit_map @location %>
<%= @map.to_html %>
  ...
<%= @ma...
CRUD location: edit
               (controller)
def update
  @location = Location.find( params[:id] )
 geom = Point.from_x...
http://localhost:3000/hotspots/2/edit
http://localhost:3000/hotspots/2/edit
http://localhost:3000/hotspots/2/edit
Complete Mapping Solutons
     using Rails (2)




instant RESTful mapping API with GeoScaffold
GET
$ curl http://localhost:3000/hotspots/2.kml


<kml xmlns="http://earth.google.com/kml/2.2">
  <Document>
     <Style i...
Authentication

class HotspotsController < ApplicationController
  before_filter :authenticate


  ...


  protected
  def...
GET (Basic HTTP Auth)
$ curl -u shoaib:secret http://localhost:3000/hotspots/2.kml


<kml xmlns="http://earth.google.com/k...
WRITE


curl -u shoaib:secret 
	 -X POST 
	 -d "hotspot[name]=My+Current+Location&
   hotspot[description]=At+Where20+conf...
WRITE (Response)


<?xml version="1.0" encoding="UTF-8"?>
<hotspot>
  <created-at type="timestamp">Mon May 12 01:18:54 -07...
Text
UPDATE


curl -u shoaib:secret 
	 -X PUT 
	 -d "hotspot[name]=My+Current+Location&
   hotspot[description]=Back+downunder+...
UPDATE (Response)
$ curl -u shoaib:secret -X PUT -d "hotspot[name]=My+Current
+Location&hotspot[description]=Back+downunde...
Text
DELETE



curl -u shoaib:secret -X DELETE 
	   http://localhost:3000/hotspots/2.xml
Holy Cow! that was hardcore!




Lets use ActiveResource instead of low level cURL
to do something Cool!




I want to geocode the location of my Twitter Friends
TwitterUser Model as
            ActiveResource



$ ruby script/generate model TwitterUser 
	   	   --skip-migration
TwitterUser Model

class TwitterUser < ActiveResource::Base
  self.site = "http://sabman:secret@twitter.com"


  def self....
Need GeoKit to Geocode



$ ruby script/plugin install 
	   git://github.com/ptb/geokit.git


edit config/environment.rb w...
Try it Out!
$ ruby script/console


>> friends = TwitterUser.my_friends
=> [
       #<TwitterUser:0x23ccdd0
@attributes={"...
Exercise




as an exercise you can add markers to the map for
         your twitter friends location
Complete Mapping Solutons
     using Rails (3)




    Google Earth & Rails GeoStack
Getting Google Earth & Rails
            Talking

Create a new Rails app:
$ rails ge_app


Add mime-type:
Mime::Type.regis...
network_link.kml.builder
server = url_for :controller => 'lesson_one', :only_path => false
xml.instruct!
xml.kml(:xmlns =>...
index.kml.builder
text =    "<font><em>
Centre Lng: #{@centre[0]}
Centre Lat: #{@centre[1]}
X Min: #{@bbox[0]}
Y Min: #{@b...
index.kml.builder
	   	       ...
              xml.LabelStyle {
                 xml.scale(0)
               }
          ...
tragically simple example of
           geoprocessing
ruby script/generate controller Task2


app/controller/task2_control...
tragic geoprocessing ...
...
  def circle
      lng = params[:lng]
      lat = params[:lat]
      radius = params[:radius]...
app/controller/application_helper.rb



# Methods added to this helper will be available to all templates
in the applicati...
app/controller/application_helper.rb ...
    def circle_builder(lngcent, latcent, radius_m)
        circle_coords = Array....
app/view/task2/index.kml.builder

xml.instruct!(:xml)
xml.kml(:xmlns=>"http://earth.google.com/kml/2.2") do
  xml.Document...
app/view/task2/index.kml.builder
	   	       ....
         xml.Style {
                xml.LabelStyle{ xml.scale(0) }
    ...
app/view/task2/circle.kml.builder
app/view/task2/circle.kml.builder


xml.instruct! :xml
xml.kml(:xmlns=>"http://earth.goo...
app/view/task2/circle.kml.builder

 xml.visibility(0)
  xml.open(0)
  xml.name("10km Circle")
  xml.visibility(1)
  xml.Pl...
Question?
Where20 2008 Ruby Tutorial
Where20 2008 Ruby Tutorial
Where20 2008 Ruby Tutorial
Where20 2008 Ruby Tutorial
Where20 2008 Ruby Tutorial
Upcoming SlideShare
Loading in...5
×

Where20 2008 Ruby Tutorial

2,635

Published on

Slide 60 onwards is my favorite material

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,635
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
128
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Where20 2008 Ruby Tutorial

  1. 1. T U T O R I A L Adv. Rails GIS Hacks San Francisco | USA Shoaib Burq | Kashif Rasul | Brian Hamlin Monday, May 12, 2008 8:30 – 1:00 http://www.flickr.com/photos/thomashawk/221827536/
  2. 2. 01 Using Geocoders
  3. 3. Geocoding What is it?
  4. 4. Geocoding What is it? • “the heart of most mapping applications” Google Maps Apps. with Rails and Ajax by A. Lewis, et al.
  5. 5. Geocoding What is it? • “the heart of most mapping applications”Google Maps Apps. with Rails and Ajax by A. Lewis, et al. • Convert any information to geographic identifiers for plotting on your map
  6. 6. Geocoding Example
  7. 7. Geocoding Example • “1800 Old Bayshore Highway, Burlingame, CA, USA” > lat = 37.368613, lng =-121.906458
  8. 8. Geocoding Example • “1800 Old Bayshore Highway, Burlingame, CA, USA” > lat = 37.368613, lng =-121.906458 • “72.14.207.99” > Mountain View, CA, US
  9. 9. Geocoding Complications
  10. 10. Geocoding Complications • Geographic information system has to be updated regularly
  11. 11. Geocoding Complications • Geographic information system has to be updated regularly • Distinguishing between ambiguous addresses needs increasing information
  12. 12. Geocoding Services
  13. 13. Geocoding Services • Google Maps
  14. 14. Geocoding Services • Google Maps • Yahoo! Maps
  15. 15. Geocoding Services • Google Maps • Yahoo! Maps • Geocoder.us & Geocoder.ca
  16. 16. Intro to Yahoo! Maps with AJAX $ rails yahoo $ cd yahoo $ ruby script/generate controller intro
  17. 17. app/views/intro/map.rhtml <html> <head> <script type="text/javascript" src="http://api.maps.yahoo.com/ajaxymap? v=3.7&appid=pPDCgjnV34FJ3TxysU9K.FpFYQ3A_QYJ4VrAJQuyFcFv91Hf0r3PU5tr3SYBhMvOoM__"> </script> <script type="text/javascript"> function load () { // Create a map object var map = new YMap(document.getElementById('map')); // Add map type control map.addTypeControl(); // Set map type to either of: YAHOO_MAP_SAT, // YAHOO_MAP_HYB, YAHOO_MAP_REG map.setMapType(YAHOO_MAP_HYB); // Display the map centered on a geocoded location map.drawZoomAndCenter("Berlin", 3); } </script> </head>
  18. 18. app/views/intro/map.rhtml <body onload="load()"> <div id="map" style="width: 500px; height: 500px"></div> </body> </html>
  19. 19. Geocoding Using REST web services
  20. 20. Geocoding Using REST web services • Format a REST query
  21. 21. Geocoding Using REST web services • Format a REST query • Use OpenURI to send the query
  22. 22. Geocoding Using REST web services • Format a REST query • Use OpenURI to send the query • Parse the XML response to extract geo information using REXML
  23. 23. Google Maps’ REST Geocoder http://maps.google.com/maps/geo?q=Torstra%C3%9Fe +104,+Berlin, +Germany&output=xml&key=ABQIAAAAwWqh7sPpuhNCdGZ0pieS hBTJQa0g3IQ9GZqIMmInSLzwtGDKaBQOJH6Tw4jIlz7bMDU6qtLF _9TSHQ
  24. 24. XML response <?xml version="1.0" encoding="UTF-8"?> <kml xmlns="http://earth.google.com/kml/2.0"> <Response> <name>Torstraße 104, Berlin, Germany</name> <Status> <code>200</code> <request>geocode</request> </Status> <Placemark id="p1"> <address>Torstraße 104, 10119 Mitte, Berlin, Germany</address> <AddressDetails xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" Accuracy="8"> <Country> <CountryNameCode>DE</CountryNameCode> <AdministrativeArea> <AdministrativeAreaName>Berlin</AdministrativeAreaName> <SubAdministrativeArea> <SubAdministrativeAreaName>Berlin</SubAdministrativeAreaName> <Locality> <LocalityName>Berlin</LocalityName> <DependentLocality> <DependentLocalityName>Mitte</DependentLocalityName> <Thoroughfare> <ThoroughfareName>Torstraße 104</ThoroughfareName> </Thoroughfare> <PostalCode> <PostalCodeNumber>10119</PostalCodeNumber> </PostalCode> </DependentLocality> </Locality> </SubAdministrativeArea> </AdministrativeArea> </Country> </AddressDetails> <Point> <coordinates>13.403424,52.529717,0</coordinates> </Point> </Placemark> </Response> </kml>
  25. 25. Rails plugins An initial comparison
  26. 26. Rails plugins An initial comparison • GeoKit
  27. 27. Rails plugins An initial comparison • GeoKit • Graticule & acts_as_geocodable
  28. 28. Rails plugins An initial comparison • GeoKit • Graticule & acts_as_geocodable • ActsAsLocatable
  29. 29. Rails plugins An initial comparison • GeoKit • Graticule & acts_as_geocodable • ActsAsLocatable • YM4R
  30. 30. GeoKit Lab Exercise
  31. 31. GeoKit Lab Exercise • Install GeoKit
  32. 32. GeoKit Lab Exercise • Install GeoKit • Get API Keys
  33. 33. GeoKit Lab Exercise • Install GeoKit • Get API Keys • Try Geocoding (in Rails console)
  34. 34. GeoKit install $ rails geokit $ cd geokit $ ruby script/plugin install svn://rubyforge.org/var/svn/geokit/trunk
  35. 35. API keys in config/ envirnoment.rb # This is your Google Maps geocoder key. # See http://www.google.com/apis/maps/signup.html # and http://www.google.com/apis/maps/documentation/#Geocoding_Examples GeoKit::Geocoders::google = 'ABQIAAAAwWqh7sPpuhNCdGZ0pieShBTJQa0g3IQ9GZqIMmInSLzwtGDKaBQOJH6Tw4jIlz 7bMDU6qtLF_9TSHQ' # This is your yahoo application key for the Yahoo Geocoder. # See http://developer.yahoo.com/faq/index.html#appid # and http://developer.yahoo.com/maps/rest/V1/geocode.html GeoKit::Geocoders::yahoo = 'pPDCgjnV34FJ3TxysU9K.FpFYQ3A_QYJ4VrAJQuyFcFv91Hf0r3PU5tr3SYBhMvOoM__'
  36. 36. Geocoding from multiple providers GeoKit::Geocoders::provider_order = [:google, :yahoo]
  37. 37. Test it out $ ruby script/console Loading development environment. >> include GeoKit::Geocoders => Object >> home = MultiGeocoder.geocode("Torstrasse 104, 10119, Berlin, Germany") => #<GeoKit::GeoLoc:0x35de820 @lat=52.530051, @state="Germany", @street_address="Torstrasse 104", @country_code="DE", @provider="yahoo", @precision="address", @zip=nil, @lng=13.403495, @city="10119 Mitte", @success=true> >> home.lat => 52.530051 >> home.lng => 13.403495
  38. 38. In memory calculations >> office = MultiGeocoder.geocode("Lepsiusstrasse 70, Steglitz, Berlin, Germany") => #<GeoKit::GeoLoc:0x341e5f8 @lat=52.460126, @state="Germany", @street_address="Lepsiusstrasse 70", @country_code="DE", @provider="yahoo", @precision="address", @zip=nil, @lng=13.316571, @city="12163 Steglitz", @success=true> >> office.distance_to(home, :units => :kms) => 9.75995820357575 >> heading = home.heading_to(office) # result is in degrees, 0 is north => 217.15430202928 >> endpoint = home.endpoint(90, 2) # given a heading (east) and distance => #<GeoKit::LatLng:0x33f6878 @lat=52.5300414818178, @lng=13.4510238774836> >> midpoint = home.midpoint_to(office) => #<GeoKit::LatLng:0x33f08b0 @lat=52.4950964615994, @lng=13.3599984433113>
  39. 39. Advance Geocoding geocoding with ActiveRecord
  40. 40. Advance Geocoding geocoding with ActiveRecord • setup database tables
  41. 41. Advance Geocoding geocoding with ActiveRecord • setup database tables • auto-geocode your model
  42. 42. Auto geocoding a model’s address on create class CreateLocations < ActiveRecord::Migration def self.up create_table :locations do |t| t.string :address, :limit => 100 t.decimal :lat, :precision => 15, :scale => 10 t.decimal :lng, :precision => 15, :scale => 10 end end def self.down drop_table :locations end end
  43. 43. app/models/location.rb class Location < ActiveRecord::Base acts_as_mappable :auto_geocode => true end
  44. 44. Testing this out >> Location.find :all => [] >> Location.create(:address => "Torstrasse 104, Berlin, Germany") => #<Location:0x344d074 @errors=#<ActiveRecord::Errors:0x341c99c @errors={}, @base=#<Location:0x344d074 ...>>, @attributes={"id"=>4, "lng"=>13.403495, "lat"=>52.530051, "address"=>"Torstrasse 104, Berlin, Germany"}, @new_record=false> >> home = Location.find :first => #<Location:0x3416e34 @attributes={"lng"=>#<BigDecimal: 3416e5c,'0.13403495E2',12(16)>, "id"=>"4", "lat"=>#<BigDecimal: 3416e84,'0.52530051E2',12(16)>, "address"=>"Torstrasse 104, Berlin, Germany"}> >> Location.create(:address => "Lepsiusstrasse 70, Berlin, Germany") => #<Location:0x3413608 @errors=#<ActiveRecord::Errors:0x33e52f8 @errors={}, @base=#<Location:0x3413608 ...>>, @attributes={"id"=>5, "lng"=>13.316571, "lat"=>52.460126, "address"=>"Lepsiusstrasse 70, Berlin, Germany"}, @new_record=false>
  45. 45. Sort by distance >> locs = Location.find :all => [ #<Location:0x3375d90 @attributes={"lng"=>#<BigDecimal:3375f20,'0.13403495E2',12(16)>, "id"=>"4", "lat"=>#<BigDecimal:3375f48,'0.52530051E2',12(16)>, "address"=>"Torstrasse 104, Berlin, Germany"}>, #<Location:0x3375d68 @attributes={"lng"=>#<BigDecimal:3375e94,'0.13316571E2',12(16)>, "id"=>"5", "lat"=>#<BigDecimal:3375ea8,'0.52460126E2',12(16)>, "address"=>"Lepsiusstrasse 70, Berlin, Germany"}>, #<Location:0x3375d40 @attributes={"lng"=>#<BigDecimal:3375e1c,'0.13365749E2',12(16)>, "id"=>"6", "lat"=>#<BigDecimal:3375e30,'0.5249112E2',12(16)>, "address"=>"Crellestrasse 23, Berlin, Germany"}>, #<Location:0x3375d18 @attributes={"lng"=>#<BigDecimal:3375da4,'0.13386817E2',12(16)>, "id"=>"7", "lat"=>#<BigDecimal:3375db8,'0.52510553E2',12(16)>, "address"=>"Mauerstrasse 65, Berlin, Germany"}> ]
  46. 46. sort_by_distance_from >> locs.sort_by_distance_from(home) => [ #<Location:0x3375d90 @distance=0.0, @attributes={"lng"=>#<BigDecimal:3375f20,'0.13403495E2',12(16)>, "id"=>"4", "lat"=>#<BigDecimal:3375f48,'0.52530051E2',12(16)>, "address"=>"Torstrasse 104, Berlin, Germany"}>, #<Location:0x3375d18 @distance=1.52043248966975, @attributes={"lng"=>#<BigDecimal:3375da4,'0.13386817E2',12(16)>, "id"=>"7", "lat"=>#<BigDecimal:3375db8,'0.52510553E2',12(16)>, "address"=>"Mauerstrasse 65, Berlin, Germany"}>, #<Location:0x3375d40 @distance=3.12676959370349, @attributes={"lng"=>#<BigDecimal:3375e1c,'0.13365749E2',12(16)>, "id"=>"6", "lat"=>#<BigDecimal:3375e30,'0.5249112E2',12(16)>, "address"=>"Crellestrasse 23, Berlin, Germany"}>, #<Location:0x3375d68 @distance=6.06585345156976, @attributes={"lng"=>#<BigDecimal:3375e94,'0.13316571E2',12(16)>, "id"=>"5", "lat"=>#<BigDecimal:3375ea8,'0.52460126E2',12(16)>, "address"=>"Lepsiusstrasse 70, Berlin, Germany"}> ]
  47. 47. AR drops the distance column >> locs = Location.find :all, :origin=>home, :within=>5, :order=>'distance' => [ #<Location:0x3362268 @attributes={"lng"=>#<BigDecimal:3362394,'0.13403495E2', 12(16)>, "id"=>"4", "lat"=>#<BigDecimal:33623bc,'0.52530051E2',12(16)>, "address"=>"Torstrasse 104, Berlin, Germany", "distance"=>"0"}>, #<Location:0x3362240 @attributes={"lng"=>#<BigDecimal:33622f4,'0.13386817E2', 12(16)>, "id"=>"7", "lat"=>#<BigDecimal:3362308,'0.52510553E2',12(16)>, "address"=>"Mauerstrasse 65, Berlin, Germany", "distance"=>"1.52043248966975"}>, #<Location:0x3362218 @attributes={"lng"=>#<BigDecimal:336227c,'0.13365749E2', 12(16)>, "id"=>"6", "lat"=>#<BigDecimal:3362290,'0.5249112E2',12(16)>, "address"=>"Crellestrasse 23, Berlin, Germany", "distance"=>"3.1267695948189"}> ]
  48. 48. end of geocoding exercise (break?)
  49. 49. More GeoKit features
  50. 50. More GeoKit features • Bounding queries
  51. 51. More GeoKit features • Bounding queries • Eager loading
  52. 52. More GeoKit features • Bounding queries • Eager loading • IP geocoding
  53. 53. GeoKit::Bounds >> bounds = GeoKit::Bounds.new( sq_sw_point, sq_ne_point ) >> locs = Location.find :all, :bounds => bounds
  54. 54. Eager Loading >> locs = Location.find :all, :origin => home, :include => [:reviews], :within => 5, :order => 'distance'
  55. 55. IP address geocoding >> location = GeoKit::Geocoders::IpGeocoder.geocode('85.178.26.159') => #<GeoKit::GeoLoc:0x3756fe0 @lat=52.5, @state=nil, @street_address=nil, @country_code="DE", @provider="hostip", @precision="unknown", @zip=nil, @lng=13.4167, @city="Berlin", @success=true>
  56. 56. Cached IP location in app/controllers/ application.rb class ApplicationController < ActionController::Base # Pick a unique cookie name to distinguish our session # data from others' session :session_key => '_geokit_session_id' # Auto-geocode the user's ip address and store # it in the session. geocode_ip_address def loc # @location is a GeoLoc instance. @location = session[:geo_location] end end
  57. 57. Graticule and its Rails plugin
  58. 58. Graticule and its Rails plugin • Automatically geocodes your models when they are saved
  59. 59. Graticule and its Rails plugin • Automatically geocodes your models when they are saved • Comes as a gem plus a plugin for your Rails app
  60. 60. Inital config $ ruby script/generate geocodable_migration add_geocodable_tables $ rake db:migrate
  61. 61. Multi geocoder & key in config/envirnoment.rb Geocode.geocoder = Graticule.service(:multi).new ( Graticule.service(:yahoo).new ( 'pPDCgjnV34FJ3TxysU9K.FpFYQ3A_QYJ4VrAJQuyFcFv91Hf0r3 PU5tr3SYBhMvOoM__' ), Graticule.service(:google).new ( 'ABQIAAAAwWqh7sPpuhNCdGZ0pieShBTJQa0g3IQ9GZqIMmInSLz wtGDKaBQOJH6Tw4jIlz7bMDU6qtLF_9TSHQ' ) )
  62. 62. Model with required attributes class CreateLocations < ActiveRecord::Migration def self.up create_table :locations do |t| t.string "street" t.string "locality" t.string "region" t.string "postal_code" t.string "country" end end def self.down drop_table :locations end end
  63. 63. app/model/location.rb class Location < ActiveRecord::Base acts_as_geocodable end
  64. 64. Graticule in action $ ruby script/console Loading development environment. >> Location.find :all => [] >> conf = Location.create :street => "Friedrichstrasse 151", :locality => "Berlin" => #<Location:0x357ec40 @geocoding=#<Geocoding:0x356e9a8 @errors=#<ActiveRecord::Errors:0x356dd78 @errors={}, @base=#<Geocoding: 0x356e9a8 ...>>, @geocode=#<Geocode:0x357490c @attributes={"postal_code"=>nil, "latitude"=>#<BigDecimal:35749d4,'0.5251818E2',12(20)>, "region"=>"Germany", "country"=>"DE", "id"=>"2", "locality"=>"10117 Mitte", "street"=>"Friedrichstrasse 151", "query"=>"Friedrichstrasse 151nBerlin, ", "longitude"=>#<BigDecimal: 35749ac,'0.13388423E2',12(20)>}>, @attributes={"geocodable_type"=>"Location", "id"=>4, "geocodable_id"=>4, "geocode_id"=>2}, @new_record=false>, @errors=#<ActiveRecord::Errors:0x357ccd8 @errors={}, @base=#<Location:0x357ec40 ...>>, @attributes={"postal_code"=>nil, "region"=>"Germany", "country"=>"DE", "id"=>4, "locality"=>"Berlin", "street"=>"Friedrichstrasse 151"}, @new_record=false> >> conf.geocode.latitude => #<BigDecimal:35749d4,'0.5251818E2',12(20)> >> conf.geocode.longitude => #<BigDecimal:35749ac,'0.13388423E2',12(20)>
  65. 65. distance_to >> prevConf = Location.create :street => "777 NE Martin Luther King, Jr. Blvd.", :locality => "Portland", :region => "Oregon", :postal_code => 97232 => #<Location:0x355c924 @geocoding=#<Geocoding:0x3555e6c @errors=#<ActiveRecord::Errors:0x355578c @errors={}, @base=#<Geocoding: 0x3555e6c ...>>, @geocode=#<Geocode:0x3557cd0 @attributes={"postal_code"=>"97232-2742", "latitude"=>#<BigDecimal: 3557d98,'0.45528468E2',12(20)>, "region"=>"OR", "country"=>"US", "id"=>"1", "locality"=>"Portland", "street"=>"777 Ne M L King Blvd", "query"=>"777 NE Martin Luther King, Jr. Blvd.nPortland, Oregon 97232", "longitude"=>#<BigDecimal:3557d70,'-0.122661895E3',12(20)>}>, @attributes={"geocodable_type"=>"Location", "id"=>5, "geocodable_id"=>5, "geocode_id"=>1}, @new_record=false>, @errors=#<ActiveRecord::Errors: 0x355b894 @errors={}, @base=#<Location:0x355c924 ...>>, @attributes={"postal_code"=>97232, "region"=>"Oregon", "country"=>"US", "id"=>5, "locality"=>"Portland", "street"=>"777 NE Martin Luther King, Jr. Blvd."}, @new_record=false> >> conf.distance_to prevConf => 5185.541406646
  66. 66. Search by location >> Location.find(:all, :within => 50, :origin => "Torstrasse 104, Berlin, Germany") => [#<Location:0x35239f8 @readonly=true, @attributes={"postal_code"=>nil, "region"=>"Germany", "country"=>"DE", "id"=>"4", "locality"=>"Berlin", "street"=>"Friedrichstrasse 151", "distance"=>"1.03758608910963"}>]
  67. 67. IP geocoding def index @nearest = Location.find( :nearest, :origin => remote_location) if remote_location @locations = Location.find(:all) end
  68. 68. 02 Location data in ActiveRecord (PostGIS/PostgreSQL)
  69. 69. Geospatial Domain What’s so special about spatial?
  70. 70. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi*
  71. 71. OGC simple features
  72. 72. Vector data http://www.ngdc.noaa.gov/mgg/shorelines
  73. 73. Vector data http://www.ngdc.noaa.gov/mgg/shorelines
  74. 74. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi*
  75. 75. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi* • Raster Data
  76. 76. Raster data http://edc.usgs.gov/products/elevation/gtopo30/gtopo30.html
  77. 77. Raster data http://edc.usgs.gov/products/elevation/gtopo30/gtopo30.html
  78. 78. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi* • Raster Data
  79. 79. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi* • Raster Data • Spatial Reference System (SRS), SRIDs
  80. 80. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi* • Raster Data • Spatial Reference System (SRS), SRIDs • Spatial Indices
  81. 81. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi* • Raster Data • Spatial Reference System (SRS), SRIDs • Spatial Indices • Map Projections
  82. 82. Free desktop GIS (uDig) http://udig.refractions.net
  83. 83. Free desktop GIS (uDig) http://udig.refractions.net
  84. 84. Free desktop GIS (uDig) http://udig.refractions.net
  85. 85. OpenJump http://openjump.org
  86. 86. GeoRails Stack Overview
  87. 87. GeoRails Stack Overview PostGIS Database
  88. 88. GeoRails Stack Overview GeoRuby PostGIS Database
  89. 89. GeoRails Stack Overview Spatial Adapter GeoRuby PostGIS Database
  90. 90. GeoRails Stack Overview YM4R Spatial Adapter GeoRuby PostGIS Database
  91. 91. Prerequisites Installing PostGIS
  92. 92. Prerequisites Installing PostGIS • Install PostgreSQL without PostGIS extension
  93. 93. Prerequisites Installing PostGIS • Install PostgreSQL without PostGIS extension • Install PostGIS separately
  94. 94. Prerequisites Installing PostGIS • Install PostgreSQL without PostGIS extension • Install PostGIS separately • Ensures you get the latest PostGIS release
  95. 95. Prerequisites template_postgis
  96. 96. Prerequisites template_postgis • create a clone of template1
  97. 97. Prerequisites template_postgis • create a clone of template1 • add pl/pgsql language
  98. 98. Prerequisites template_postgis • create a clone of template1 • add pl/pgsql language • install the postgis functions/libraries
  99. 99. Prerequisites template_postgis • create a clone of template1 • add pl/pgsql language • install the postgis functions/libraries • grant role-based permissions
  100. 100. Prerequisites template_postgis • create a clone of template1 • add pl/pgsql language • install the postgis functions/libraries • grant role-based permissions • vacuum freeze
  101. 101. template_postgis code c template1 CREATE DATABASE template_postgis with template = template1; -- set the 'datistemplate' record in the 'pg_database' table for -- 'template_postgis' to TRUE indicating its a template UPDATE pg_database SET datistemplate = TRUE where datname = 'template_postgis'; c template_postgis CREATE LANGUAGE plpgsql; i /usr/share/postgresql/contrib/lwpostgis.sql i /usr/share/postgresql/contrib/spatial_ref_sys.sql -- set role based permissions in production env. GRANT ALL ON geometry_columns TO PUBLIC; GRANT ALL ON spatial_ref_sys TO PUBLIC; -- vacuum freeze: it will guarantee that all rows in the database are -- "frozen" and will not be subject to transaction ID wraparound -- problems. VACUUM FREEZE;
  102. 102. SRID explained $ psql -d template_postgis template_postgis=# x -- to turn on expanded display template_postgis=# SELECT * from spatial_ref_sys where srid = 4326; -[ RECORD 1 ]---------------------------------------------------------- srid | 4326 auth_name | EPSG auth_srid | 4326 srtext | GEOGCS["WGS 84",DATUM["WGS_1984", SPHEROID["WGS 84",6378137,298.25722 3563, AUTHORITY["EPSG","7030"]], TOWGS84[0,0,0,0,0,0,0], AUTHORITY["EPSG","6326"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.01745329251994328, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4326"]] proj4text | +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
  103. 103. using template_postgis createdb -T template_postgis my_database
  104. 104. Prequisites GeoRuby
  105. 105. Prequisites GeoRuby • Data structs to hold Spatial Data from PostGIS/MySQL-Spatial
  106. 106. Prequisites GeoRuby • Data structs to hold Spatial Data from PostGIS/MySQL-Spatial • Based on OGC Simple Features Spec
  107. 107. Prequisites GeoRuby • Data structs to hold Spatial Data from PostGIS/MySQL-Spatial • Based on OGC Simple Features Spec • No geometry operations or reprojections
  108. 108. Prequisites GeoRuby • Data structs to hold Spatial Data from PostGIS/MySQL-Spatial • Based on OGC Simple Features Spec • No geometry operations or reprojections • Allows geo-data I/O into a number of useful formats
  109. 109. GeoRuby $ sudo gem install georuby
  110. 110. GeoStack Running on Edge Rails
  111. 111. GeoStack Running on Edge Rails • Installing Edge Rails from Git
  112. 112. GeoStack Running on Edge Rails • Installing Edge Rails from Git • Add Geo-plugins as Git Submodules
  113. 113. GeoStack Running on Edge Rails • Installing Edge Rails from Git • Add Geo-plugins as Git Submodules • Git it up and running
  114. 114. Livin’ on the Edge # new rails project $ rails geostack -d postgresql $ cd geostack/ $ cat ~/Templates/dot-gitignore > .gitignore $ touch log/.gitignore $ git init $ git add . $ git commit -m "Initial commit" # edge rails $ git submodule add git://github.com/rails/rails.git vendor/ rails $ git commit -m "Use git submodules to track HEAD of edgerails repository." $ git submodule init $ git submodule update
  115. 115. Add Useful Plugins # spatail_adapter2, geo_scaffold $ git submodule add http://github.com/sabman/spatial_adapter2.git vendor/plugins/spatial_adapter2 # ym4r_gm, $ ruby script/plugin install git://github.com/bitbckt/ym4r-gm.git # geokit $ ruby script/plugin install git://github.com/ptb/geokit.git # rspec $ git submodule add git://github.com/dchelimsky/rspec.git vendor/plugins/rspec $ git submodule add git://github.com/dchelimsky/rspec-rails.git vendor/plugins/rspec_on_rails
  116. 116. Complete Mapping Solution using Ruby on Rails 1. Web Mapping Application using Google Maps 2. Using cURL API & ActiveResource 3. Using Google Earth as the client
  117. 117. Complete Mapping Solutons using Rails (1) Web Mapping Application using Google Maps
  118. 118. Magic of GeoScaffold $ ruby script/generate geo_scaffold Location name:string description:text geom:point
  119. 119. running geo_scaffold mime_types Mime::Type.register "application/vnd.google-earth.kml+xml", :kml mime_types Mime::Type.register "application/rss+xml", :georss create app/views/hotspots/index.html.erb create app/views/hotspots/show.html.erb create app/views/hotspots/new.html.erb create app/views/hotspots/edit.html.erb create app/views/hotspots/index.kml.builder create app/views/hotspots/index.georss.builder create app/views/hotspots/show.kml.builder create app/views/hotspots/show.georss.builder create app/views/layouts/hotspots.html.erb create public/stylesheets/geo_scaffold.css ... create public/javascripts/geo_scaffold.js create app/controllers/hotspots_controller.rb create app/helpers/hotspots_helper.rb route map.resources :hotspots create public/javascripts/geo_scaffold.js create app/controllers/hotspots_controller.rb create app/helpers/hotspots_helper.rb route map.resources :hotspots create db/migrate/20080511045307_create_hotspots.rb create db/migrate/20080511045307_add_index_to_hotspots.rb
  120. 120. $ rake db:migrate $ mongrel_rails start
  121. 121. voilà!
  122. 122. WTF! okay okay! lets dig in and see what just happened
  123. 123. Housekeeping was done Migrations Spatial Indices Test Fixtures Mime-Types
  124. 124. Small Gotcha in current edge-rails if a generator creates two migrations are they get the same timestamp create db/migrate/20080511045307_create_hotspots.rb create db/migrate/20080511045307_add_index_to_hotspots.rb
  125. 125. Migrations with spatial data def self.up create_table :locations do |t| t.point :geom, :null => false, :srid => 4326, :with_z => true t.string :name, :null => false t.text :description t.datetime end end
  126. 126. Spatial indices $ ruby script/generate migration add_index_to_locations def self.up add_index :locations, :geom, :spatial => true end def self.down remove_index :locations, :geom end
  127. 127. Spatial data migrations (points) $ ruby script/generate migration add_locations_data def self.up Location.create :geom => Point.from_x_y_z( 67.1069627266882, 24.9153581895111, 3, 4326 ), :name => "ALLADIN WATER PARK", :category => "AMUSEMENT PARK", :description => "A new amusement park built on the main Rashid Minhas Road is the latest attraction of Karachi..." end
  128. 128. http://localhost:3000/locations/1
  129. 129. http://localhost:3000/locations/1
  130. 130. Spatial data migrations (polygon) loc = Hotspot.new( :name => "Jinnah's Tomb", :geom => Polygon.from_coordinates( [[ [67.04331636428833, 24.87680084533657], [67.03837037086487, 24.876956578914644], [67.03658938407898, 24.87328705430993], [67.04217910766602, 24.87241102436732], [67.04331636428833, 24.87680084533657] ]], 3, 4326) )
  131. 131. Tests (fixtures) one: id: 1 geom: <%= Point.from_x_y_z(67.0665783392958, 24.9357149717549, 3, 4326 ).to_fixture_format %> name: "GULBERG POLICE STATION" category: "POLICE" description: "Yet another well regarded police station in Karach"
  132. 132. Tests (unit/functional) def test_should_create_location old_count = Location.count post :create, :location => { :geom => Point.from_x_y_z(67.0665783392958, 24.9357149717549, 3, 4326), :name => "GULBERG POLICE STATION", :category => "POLICE", :description => "Yet another well regarded police station in Karachi" } assert_equal old_count+1, Location.count assert_redirected_to location_path( assigns(:location) ) end
  133. 133. Tests (unit/functional) $ rake test:units $ rake test:functionals http://www.flickr.com/photo_zoom.gne? id=317182464&size=l
  134. 134. Tests (JavaScript) $ ruby script/plugin install http://dev.rubyonrails.org/svn/rails/plugins/ javascript_test $ ruby script/generate javascript_test application
  135. 135. Tests (JavaScript) new Test.Unit.Runner({ setup: function() { $('sandbox').innerHTML = "<div id='123_a' style='display:none;'></div>"; }, teardown: function() { }, testTruth: function() { with(this) { assert(true); }} }, "testlog");
  136. 136. Tests (JavaScript) $ rake test:javascripts
  137. 137. GeoRSS & KML Mime-Types config/initializers.rb Mime::Type.register "application/vnd.google-earth.kml+xml", :kml Mime::Type.register "application/rss+xml", :georss
  138. 138. Views & Controllers were generated all that CRUD
  139. 139. Read lets take it one step at a time
  140. 140. CRUD location: show (helper) def show_map rec @map = GMap.new("map#{rec.id}_div") @map.control_init( :small_map => true, :hierarchical_map_type => true) @map.record_init @map.add_map_type(GMapType::G_PHYSICAL_MAP) @map.center_zoom_init([rec.geom.y,rec.geom.x],16) @map.set_map_type_init(GMapType::G_PHYSICAL_MAP) @map.overlay_init( GMarker.new([rec.geom.y,rec.geom.x], :title => "#{rec.name}", :info_window => "#{rec.description}" )) @map.to_html end
  141. 141. CRUD location: show (view template) <%= GMap.header %> <%= show_map @hotspot %> ... <b>Geom:</b> <%= @map.div :width => 600, :height => 400 %><br/> ... <%= link_to image_tag("/images/kml_icon16x16.png"), formatted_hotspot_path(@hotspot, :kml) %> <%= link_to image_tag("/images/feed-icon16x16.gif"), formatted_hotspot_path(@hotspot, :georss) %> ...
  142. 142. CRUD location: show (kml template) xml.kml("xmlns" => KML_NS) do xml.tag! "Document" do xml.tag! "Style", :id => "myStyle" do xml.tag! "PointStyle" do xml.color "ffff0000" #format is aabbggrr xml.outline 0 end end xml.tag! "Placemark" do xml.description( (@hotspot.description + "n #{hotspot_url(@hotspot)}") xml.name @hotspot.name xml.styleUrl "#myStyle" xml << @hotspot.geom.as_kml end end end
  143. 143. http://localhost:3000/hotspots/1
  144. 144. CRUD location: index (helper) def show_maps recs @map = GMap.new("map_div", "map") @map.control_init(:small_map => true, :hierarchical_map_type => true) @map.record_init @map.add_map_type(GMapType::G_PHYSICAL_MAP) @map.set_map_type_init(GMapType::G_PHYSICAL_MAP) coords = (recs.length == 0) ? [[-45,-45],[45,45]] : [] recs.each do |rec| coords << [rec.geom.x.to_f, rec.geom.y.to_f] @map.record_init @map.add_overlay( GMarker.new([rec.geom.y.to_f, rec.geom.x.to_f], :title => "#{rec.name}" , :info_window => "#{rec.description}" )) end bnd = MultiPoint.from_coordinates(coords).envelope @map.center_zoom_on_bounds_init( [[bnd.lower_corner.y,bnd.lower_corner.x], [bnd.upper_corner.y,bnd.upper_corner.x]] ) @map.to_html end
  145. 145. CRUD location: index (helper) Lets looks at the code in bite size chunks def show_maps recs @map = GMap.new("map_div", "map") @map.control_init(:small_map => true, :hierarchical_map_type => true) @map.record_init @map.add_map_type(GMapType::G_PHYSICAL_MAP) @map.set_map_type_init(GMapType::G_PHYSICAL_MAP) coords = (recs.length == 0) ? [[-45,-45],[45,45]] : [] ... end
  146. 146. CRUD location: index (helper) def show_maps recs ... coords = (recs.length == 0) ? [[-45,-45],[45,45]] : [] recs.each do |rec| coords << [rec.geom.x.to_f, rec.geom.y.to_f] @map.record_init @map.add_overlay( GMarker.new([rec.geom.y.to_f, rec.geom.x.to_f], :title => "#{rec.name}" , :info_window => "#{rec.description}" )) end ... end
  147. 147. CRUD location: index (helper) def show_maps recs ... bnd = MultiPoint.from_coordinates(coords).envelope @map.center_zoom_on_bounds_init( [[bnd.lower_corner.y,bnd.lower_corner.x], [bnd.upper_corner.y,bnd.upper_corner.x]] ) @map.to_html end
  148. 148. CRUD location: index (view template) <%= GMap.header %> <%= show_maps @hotspots %> <% content_for :header do %> <h1> Listing hotspots <%= link_to image_tag("/images/kml_icon16x16.png"), formatted_hotspots_path(:kml) %> <%= link_to image_tag("/images/feed-icon16x16.gif"), formatted_hotspots_path(:georss) %> </h1> <% end %> <% content_for :sidebar do %> <%= link_to 'New hotspot', new_hotspot_path %> <% end %> <%= @map.div :width => 600, :height => 400 -%>
  149. 149. http://localhost:3000/hotspots
  150. 150. Create lets take it one step at a time
  151. 151. CRUD location: new (JavaScript application.js) function create_draggable_editable_marker() { // intialize the values in form fields to 0 document.getElementById("lng").value = 0; document.getElementById("lat").value = 0; var currMarker; // if the user click on an existing marker remove it GEvent.addListener(map, "click", function(marker, point) { if (marker) { if (confirm("Do you want to delete marker?")) { map.removeOverlay(marker); } }
  152. 152. CRUD location: new (JavaScript application.js) // if the user clicks somewhere other than existing marker else { // remove the previous marker if it exists if (currMarker) { map.removeOverlay(currMarker); } currMarker = new GMarker(point, {draggable: true}); map.addOverlay(currMarker); // update the form fields document.getElementById("lng").value = point.x; document.getElementById("lat").value = point.y; } // Similarly drag event is used to update the form fields GEvent.addListener(currMarker, "drag", function() { document.getElementById("lng").value = currMarker.getPoint().lng(); document.getElementById("lat").value = currMarker.getPoint().lat(); }); }); }
  153. 153. CRUD location: (layouts/hotspots.rb) <%= stylesheet_link_tag 'geo_scaffold' %> <%= javascript_include_tag :defaults %> <%= javascript_include_tag 'geo_scaffold' %>
  154. 154. CRUD location: new (helper) def new_map @map = GMap.new("map_div") @map.control_init(:small_map => true, :hierarchical_map_type => true) @map.record_init( @map.add_map_type(GMapType::G_PHYSICAL_MAP)) @map.center_zoom_init([0,0],2) @map.record_init('create_draggable_editable_marker();') @map.to_html end
  155. 155. CRUD location: new (view template) <%= GMap.header %> <% new_map %> <%= @map.to_html %> ... <%= @map.div :width => 600, :height => 400 %> Lat: <%= text_field_tag :lat -%> Lng: <%= text_field_tag :lng -%>
  156. 156. CRUD location: new (controller) def create @location = Location.new(params[:location]) geom = Point.from_x_y_z(params[:lng], params[:lat], 3, 4326) @location.geom = geom ... end
  157. 157. http://localhost:3000/hotspots/new
  158. 158. http://localhost:3000/locations/new
  159. 159. http://localhost:3000/locations/new
  160. 160. http://localhost:3000/locations/new
  161. 161. Update lets take it one step at a time
  162. 162. CRUD location: edit (JavaScript) function create_draggable_marker_for_edit(lng, lat) { // initalize form fields document.getElementById('lng').value = lng; document.getElementById('lat').value = lat; // initalize marker var currMarker = new GMarker( new GLatLng(lat, lng), {draggable: true} ); map.addOverlay(currMarker); // Handle drag events to update the form text fields GEvent.addListener(currMarker, 'drag', function() { document.getElementById('lng').value = currMarker.getPoint().lng(); document.getElementById('lat').value = currMarker.getPoint().lat(); }); }
  163. 163. CRUD location: edit (helper) def edit_map rec @map = GMap.new( "map_div" ) @map.control_init(:large_map => true,:map_type => true) @map.set_map_type_init( GMapType::G_SATELLITE_MAP ) @map.center_zoom_init( [rec.geom.y, rec.geom.x], 12 ) @map.record_init( "create_draggable_marker_for_edit( #{rec.geom.x}, #{rec.geom.y} );" ) end
  164. 164. CRUD location: edit (view template) <%= GMap.header %> <% edit_map @location %> <%= @map.to_html %> ... <%= @map.div :width => 600, :height => 400 %> Lat: <%= text_field_tag :lat -%> Lng: <%= text_field_tag :lng -%>
  165. 165. CRUD location: edit (controller) def update @location = Location.find( params[:id] ) geom = Point.from_x_y_z( params[:lng], params[:lat], 3, 4326 ) @location.geom = geom ... end
  166. 166. http://localhost:3000/hotspots/2/edit
  167. 167. http://localhost:3000/hotspots/2/edit
  168. 168. http://localhost:3000/hotspots/2/edit
  169. 169. Complete Mapping Solutons using Rails (2) instant RESTful mapping API with GeoScaffold
  170. 170. GET $ curl http://localhost:3000/hotspots/2.kml <kml xmlns="http://earth.google.com/kml/2.2"> <Document> <Style id="myStyle"> <PointStyle> <color>ffff0000</color> <outline>0</outline> </PointStyle> </Style> <Placemark> <description>Where 2.0 conference not 2b missed!!</description> <name>Merriot San Francisco</name> <styleUrl>#myStyle</styleUrl> <Point> <coordinates>-122.37096,37.602561</coordinates> </Point> </Placemark> </Document> </kml>
  171. 171. Authentication class HotspotsController < ApplicationController before_filter :authenticate ... protected def authenticate authenticate_or_request_with_http_basic do |username, password| username == "shoaib" && password == "secret" end end end
  172. 172. GET (Basic HTTP Auth) $ curl -u shoaib:secret http://localhost:3000/hotspots/2.kml <kml xmlns="http://earth.google.com/kml/2.2"> <Document> <Style id="myStyle"> <PointStyle> <color>ffff0000</color> <outline>0</outline> </PointStyle> </Style> <Placemark> <description>Where 2.0 conference not 2b missed!!</description> <name>Merriot San Francisco</name> <styleUrl>#myStyle</styleUrl> <Point> <coordinates>-122.37096,37.602561</coordinates> </Point> </Placemark> </Document> </kml>
  173. 173. WRITE curl -u shoaib:secret -X POST -d "hotspot[name]=My+Current+Location& hotspot[description]=At+Where20+conference+Dont+ caught+outside& lat=37.602561&lng=-122.37096" http://localhost:3000/hotspots.xml
  174. 174. WRITE (Response) <?xml version="1.0" encoding="UTF-8"?> <hotspot> <created-at type="timestamp">Mon May 12 01:18:54 -0700 2008</created-at> <description>At Where20 conference Dont caught outside</description> <geom type="geometry">#&lt;GeoRuby::SimpleFeatures::Point:0x236e0a0&gt;</geom> <id type="integer">5</id> <name>My Current Location</name> <updated-at type="timestamp">Mon May 12 01:18:54 -0700 2008</updated-at> </hotspot>
  175. 175. Text
  176. 176. UPDATE curl -u shoaib:secret -X PUT -d "hotspot[name]=My+Current+Location& hotspot[description]=Back+downunder+@FOSS4G2009& lat=-33.868135032968624&lng=151.20208740234375" http://localhost:3000/hotspots/5.xml
  177. 177. UPDATE (Response) $ curl -u shoaib:secret -X PUT -d "hotspot[name]=My+Current +Location&hotspot[description]=Back+downunder +@FOSS4G2009&lat=-33.868135032968624&lng=151.20208740234375" http://localhost:3000/hotspots/5.xml $ curl -u shoaib:secret http://localhost:3000/hotspots/5.kml <kml xmlns="http://earth.google.com/kml/2.2"> <Document> <Style id="myStyle"> <PointStyle> <color>ffff0000</color> <outline>0</outline> </PointStyle> </Style> <Placemark> <description>Back downunder @FOSS4G2009</description> <name>My Current Location</name> <styleUrl>#myStyle</styleUrl> <Point> <coordinates>151.202087402344,-33.8681350329686</coordinates> </Point> </Placemark> </Document> </kml>
  178. 178. Text
  179. 179. DELETE curl -u shoaib:secret -X DELETE http://localhost:3000/hotspots/2.xml
  180. 180. Holy Cow! that was hardcore! Lets use ActiveResource instead of low level cURL
  181. 181. to do something Cool! I want to geocode the location of my Twitter Friends
  182. 182. TwitterUser Model as ActiveResource $ ruby script/generate model TwitterUser --skip-migration
  183. 183. TwitterUser Model class TwitterUser < ActiveResource::Base self.site = "http://sabman:secret@twitter.com" def self.my_friends find(:all, :from => "/statuses/friends.xml") end end
  184. 184. Need GeoKit to Geocode $ ruby script/plugin install git://github.com/ptb/geokit.git edit config/environment.rb with you API keys
  185. 185. Try it Out! $ ruby script/console >> friends = TwitterUser.my_friends => [ #<TwitterUser:0x23ccdd0 @attributes={"status"=>#<TwitterUser::Status:0x23b9258 ... @prefix_options={}> ] >> GeoKit::Geocoders::MultiGeocoder.geocode(friends[0].location) => #<GeoKit::GeoLoc:0x237a030 @lat=-37.814251, @street_address=nil, @full_address="Melbourne, VIC, Australia", @zip=nil, @precision="city", @state="VIC", @success=true, @provider="google", @lng=144.963169, @city="Melbourne", @country_code="AU">
  186. 186. Exercise as an exercise you can add markers to the map for your twitter friends location
  187. 187. Complete Mapping Solutons using Rails (3) Google Earth & Rails GeoStack
  188. 188. Getting Google Earth & Rails Talking Create a new Rails app: $ rails ge_app Add mime-type: Mime::Type.register "application/vnd.google-earth.kml +xml", :kml Create new controller with actions: network_link, index: $ ruby script/generate lesson_one controller network_link index
  189. 189. network_link.kml.builder server = url_for :controller => 'lesson_one', :only_path => false xml.instruct! xml.kml(:xmlns => "http://earth.google.com/kml/2.2") { xml.Document { xml.name("Pass parameters to my Rails app") xml.open(1) xml.visible(1) xml.NetworkLink { xml.name("My rails app being passed parameters") xml.open(1) xml.visibility(0) xml.Link { xml.href("#{server}") xml.viewRefreshMode("onStop") xml.viewRefreshTime(0.5) xml.viewFormat("BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth] &amp;CENTRE=[lookatLon],[lookatLat]") } } } }
  190. 190. index.kml.builder text = "<font><em> Centre Lng: #{@centre[0]} Centre Lat: #{@centre[1]} X Min: #{@bbox[0]} Y Min: #{@bbox[1]} X Max: #{@bbox[2]} Y Max: #{@bbox[3]} </em></font>" xml.instruct! :xml xml.kml(:xmlns => "http://earth.google.com/kml/2.2") do xml.Document { xml.Placemark { xml.Snippet(:maxLines => "9") { xml.cdata!(text) } xml.name("cross-hair") xml.Style { ...
  191. 191. index.kml.builder ... xml.LabelStyle { xml.scale(0) } xml.IconStyle { xml.color("ffefebde") xml.Icon { xml.href("root://icons/palette-3.png") xml.x(128) xml.y(32) xml.w(32) xml.h(32) } } } xml.Point { xml.coordinates("#{@centre[0]}, #{@centre[1]}"); } } } end
  192. 192. tragically simple example of geoprocessing ruby script/generate controller Task2 app/controller/task2_controller.rb class Task2Controller < ApplicationController include ApplicationHelper def index @center = Point.from_coordinates params[:CENT].split(","), :srid => 4326 respond_to do |wants| wants.kml { render :layout => false } end end ...
  193. 193. tragic geoprocessing ... ... def circle lng = params[:lng] lat = params[:lat] radius = params[:radius] || 1000 @circle = Polygon.from_coordinates[ circle_builder(lng, lat, radius)], :srid => 4326 respond_to do |wants| wants.kml {render :layout => false} end end end
  194. 194. app/controller/application_helper.rb # Methods added to this helper will be available to all templates in the application. module ApplicationHelper include Math def array2coords(arr) coords = []; i=0 while(i < arr.length-1) ; coords << arr[i..i+1]; i=i+2 ;end coords end
  195. 195. app/controller/application_helper.rb ... def circle_builder(lngcent, latcent, radius_m) circle_coords = Array.new lng1 = (lngcent.to_f)*PI/180 lat1 = (latcent.to_f)*PI/180 d_rad = (radius_m.to_f)/6378137.0 range = (0..36 ).to_a.map{|i| i*10} for i in range do radial = i * PI/180 lat_rad = asin(sin(lat1)*cos(d_rad) + cos(lat1)*sin(d_rad)*cos(radial)) dlon_rad = atan2(sin(radial)*sin(d_rad)*cos(lat1), cos(d_rad)-sin(lat1)*sin(lat_rad)) lon_rad = ((lng1 + dlon_rad + PI) % (2*PI)) - PI circle_coords << [(lon_rad*180/PI) , (lat_rad*180/PI) , 0] end return circle_coords end
  196. 196. app/view/task2/index.kml.builder xml.instruct!(:xml) xml.kml(:xmlns=>"http://earth.google.com/kml/2.2") do xml.Document { xml.Placemark { xml.Snippet(:maxlines => "3") { xml.cdata!( link_to("Make 10 km Circle", :action => "circle", :only_path => false, "lng" => @center.x, "lat" => @center.y )) } xml.name("cross-hair") ...
  197. 197. app/view/task2/index.kml.builder .... xml.Style { xml.LabelStyle{ xml.scale(0) } xml.IconStyle{ xml.color("ffefebde") xml.Icon{ xml.href("root://icons.pallette-3.png") xml.x(128) xml.y(32) xml.w(32) xml.h(32) }} } xml << @center.as_kml } } end
  198. 198. app/view/task2/circle.kml.builder app/view/task2/circle.kml.builder xml.instruct! :xml xml.kml(:xmlns=>"http://earth.google.com/kml/2.2") do xml.Document { xml.name("My 10km Circle") xml.Style(:id => "ReturnPoly"){ xml.LineStyle{ xml.width(4) } xml.PolyStyle{ xml.color("9466FFFF") } } ...
  199. 199. app/view/task2/circle.kml.builder xml.visibility(0) xml.open(0) xml.name("10km Circle") xml.visibility(1) xml.Placemark{ xml.name("10km Radius") xml.styleUrl("#ReturnPoly") xml << @circle.as_kml } } end
  200. 200. Question?
  1. Gostou de algum slide específico?

    Recortar slides é uma maneira fácil de colecionar informações para acessar mais tarde.

×