SlideShare a Scribd company logo
1 of 205
Download to read offline
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/
01
Using Geocoders
Geocoding
 What is it?
Geocoding
            What is it?

• “the heart of most mapping
  applications”
              Google Maps Apps. with Rails and Ajax by A. Lewis, et al.
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
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
• “72.14.207.99” > Mountain View, CA,
  US
Geocoding
Complications
Geocoding
         Complications

• Geographic information system has
  to be updated regularly
Geocoding
         Complications

• Geographic information system has
  to be updated regularly
• Distinguishing between ambiguous
  addresses needs increasing
  information
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/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>
app/views/intro/map.rhtml



    <body onload="load()">
          <div id="map" style="width: 500px; height: 500px"></div>
    </body>
</html>
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 extract
  geo information using REXML
Google Maps’ REST Geocoder



http://maps.google.com/maps/geo?q=Torstra%C3%9Fe
+104,+Berlin,
+Germany&output=xml&key=ABQIAAAAwWqh7sPpuhNCdGZ0pieS
hBTJQa0g3IQ9GZqIMmInSLzwtGDKaBQOJH6Tw4jIlz7bMDU6qtLF
_9TSHQ
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>
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/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__'
Geocoding from multiple
          providers




GeoKit::Geocoders::provider_order = [:google, :yahoo]
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
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>
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
      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
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")
=> #<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>
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"}>
]
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"}>
]
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"}>
]
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 = Location.find :all, :bounds => bounds
Eager Loading



>> locs   =   Location.find :all,
	 	 	 	   	   	 	 	 	 	 :origin => home,
	 	 	 	   	   	 	 	 	 	 :include => [:reviews],
	 	 	 	   	   	 	 	 	 	 :within => 5,
	 	 	 	   	   	 	 	 	 	 :order   => 'distance'
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>
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
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 plugin for
  your Rails app
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(:yahoo).new (
	 'pPDCgjnV34FJ3TxysU9K.FpFYQ3A_QYJ4VrAJQuyFcFv91Hf0r3
PU5tr3SYBhMvOoM__'
  ),

  Graticule.service(:google).new (
	 'ABQIAAAAwWqh7sPpuhNCdGZ0pieShBTJQa0g3IQ9GZqIMmInSLz
wtGDKaBQOJH6Tw4jIlz7bMDU6qtLF_9TSHQ'
  )
)
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
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 = 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)>
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
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"}>]
IP geocoding


def index
  @nearest = Location.find(	:nearest,
	 	 	 	 	 	 	 	 	 	 	      	
                           :origin => remote_location)
	 	 	 	 	 if remote_location
  @locations = Location.find(:all)
end
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 Reference System (SRS), SRIDs
Geospatial Domain
   What’s so special about
           spatial?
• Point, Line, Polygon, Multi*
• Raster Data
• Spatial Reference System (SRS), SRIDs
• Spatial Indices
Geospatial Domain
   What’s so special about
           spatial?
• Point, Line, Polygon, Multi*
• Raster Data
• Spatial Reference System (SRS), SRIDs
• Spatial Indices
• Map Projections
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
• Ensures you get the latest PostGIS
  release
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 functions/libraries
Prerequisites
       template_postgis
• create a clone of template1
• add pl/pgsql language
• install the postgis functions/libraries
• grant role-based permissions
Prerequisites
       template_postgis
• create a clone of template1
• add pl/pgsql language
• install the postgis functions/libraries
• grant role-based permissions
• vacuum freeze
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;
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
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 Features Spec
Prequisites
             GeoRuby
• Data structs to hold Spatial Data from
  PostGIS/MySQL-Spatial
• Based on OGC Simple Features Spec
• No geometry operations or
  reprojections
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
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 running
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
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
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
Complete Mapping Solutons
     using Rails (1)




Web Mapping Application using Google Maps
Magic of GeoScaffold


$ ruby script/generate geo_scaffold 
	 	 	 	 Location 
	 	 	 	 name:string 
	 	 	 	 description:text 
	 	 	 	 geom:point
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
$ 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   db/migrate/20080511045307_create_hotspots.rb
create   db/migrate/20080511045307_add_index_to_hotspots.rb
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
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
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
http://localhost:3000/locations/1
http://localhost:3000/locations/1
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)
     )
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"
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
Tests (unit/functional)




$ rake test:units
$ rake test:functionals


                          http://www.flickr.com/photo_zoom.gne?
                          id=317182464&size=l
Tests (JavaScript)


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


$ ruby script/generate javascript_test application
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");
Tests (JavaScript)




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




Mime::Type.register
	 "application/vnd.google-earth.kml+xml", :kml


Mime::Type.register
	 "application/rss+xml", :georss
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(	 :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
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) %>
...
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
http://localhost:3000/hotspots/1
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
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
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
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
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 -%>
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 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);
       }
     }
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();
      });
    });
}
CRUD location:
     (layouts/hotspots.rb)



<%= stylesheet_link_tag 'geo_scaffold' %>
<%= javascript_include_tag :defaults %>
<%= javascript_include_tag 'geo_scaffold' %>
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
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 -%>
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
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) {
     // 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();
    });
 }
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
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 -%>
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
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 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>
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
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>
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
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>
Text
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
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>
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.my_friends
      find(:all, :from => "/statuses/friends.xml")
  end
end
Need GeoKit to Geocode



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


edit config/environment.rb with you API keys
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">
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.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
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]")
      }
    }
  }
}
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 {
	 	 ...
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
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
    ...
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
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
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
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")
	 	 ...
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
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")
      }
  }
 ...
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
Question?
Where20 2008 Ruby Tutorial

More Related Content

Viewers also liked

サイバー攻撃の脅威が増大する中、 我が国の情報セキュリティ政策の現状
サイバー攻撃の脅威が増大する中、 我が国の情報セキュリティ政策の現状サイバー攻撃の脅威が増大する中、 我が国の情報セキュリティ政策の現状
サイバー攻撃の脅威が増大する中、 我が国の情報セキュリティ政策の現状itforum-roundtable
 
政府機関における情報セキュリティ対策について
政府機関における情報セキュリティ対策について政府機関における情報セキュリティ対策について
政府機関における情報セキュリティ対策についてitforum-roundtable
 
被災者の行政手続きにおける地図情報の活用とAPIの利用
被災者の行政手続きにおける地図情報の活用とAPIの利用被災者の行政手続きにおける地図情報の活用とAPIの利用
被災者の行政手続きにおける地図情報の活用とAPIの利用Yoichi Kayama
 
Is Your Phone Turning You into an Adrenaline Junkie?
Is Your Phone Turning You into an Adrenaline Junkie?Is Your Phone Turning You into an Adrenaline Junkie?
Is Your Phone Turning You into an Adrenaline Junkie?Mark Ostach
 
Forjando Compromiso Cie Automotive Galfor
Forjando Compromiso Cie Automotive GalforForjando Compromiso Cie Automotive Galfor
Forjando Compromiso Cie Automotive Galforiurdiales
 
Como progresar en las redes sociales
Como progresar en las redes socialesComo progresar en las redes sociales
Como progresar en las redes socialesAdalid inmark
 
Presentazione telesurvey italia
Presentazione telesurvey italiaPresentazione telesurvey italia
Presentazione telesurvey italiaTiziana Urru
 
BALPARMAK CORPORATE PRESENTATION
BALPARMAK CORPORATE PRESENTATIONBALPARMAK CORPORATE PRESENTATION
BALPARMAK CORPORATE PRESENTATIONDorukan Tarman
 
Training Games - und schon macht es mehr Spass!
Training Games - und schon macht es mehr Spass!Training Games - und schon macht es mehr Spass!
Training Games - und schon macht es mehr Spass!Michael Wyrsch
 
Informe políticas fiscales
Informe políticas fiscalesInforme políticas fiscales
Informe políticas fiscalesAuditoriaVLC
 
Beiersdorf interim report_jan-sept_2010
Beiersdorf interim report_jan-sept_2010Beiersdorf interim report_jan-sept_2010
Beiersdorf interim report_jan-sept_2010farouk123
 
Revista de Hosteleria - 4º trimestre 2012
Revista de Hosteleria - 4º trimestre 2012Revista de Hosteleria - 4º trimestre 2012
Revista de Hosteleria - 4º trimestre 2012Hosteleria de Asturias
 
Gp0012 sampledescription
Gp0012 sampledescriptionGp0012 sampledescription
Gp0012 sampledescriptionIvan Leyzan
 
deSymfony 2013: Anotaciones, no es parte de mi aplicación, pero es mi aplicación
deSymfony 2013: Anotaciones, no es parte de mi aplicación, pero es mi aplicacióndeSymfony 2013: Anotaciones, no es parte de mi aplicación, pero es mi aplicación
deSymfony 2013: Anotaciones, no es parte de mi aplicación, pero es mi aplicaciónaferrandini
 
Bachelor Project Degree-Processing and mechanical analysis of electro-spun an...
Bachelor Project Degree-Processing and mechanical analysis of electro-spun an...Bachelor Project Degree-Processing and mechanical analysis of electro-spun an...
Bachelor Project Degree-Processing and mechanical analysis of electro-spun an...Alvaro Diaz Mendoza
 

Viewers also liked (20)

サイバー攻撃の脅威が増大する中、 我が国の情報セキュリティ政策の現状
サイバー攻撃の脅威が増大する中、 我が国の情報セキュリティ政策の現状サイバー攻撃の脅威が増大する中、 我が国の情報セキュリティ政策の現状
サイバー攻撃の脅威が増大する中、 我が国の情報セキュリティ政策の現状
 
政府機関における情報セキュリティ対策について
政府機関における情報セキュリティ対策について政府機関における情報セキュリティ対策について
政府機関における情報セキュリティ対策について
 
被災者の行政手続きにおける地図情報の活用とAPIの利用
被災者の行政手続きにおける地図情報の活用とAPIの利用被災者の行政手続きにおける地図情報の活用とAPIの利用
被災者の行政手続きにおける地図情報の活用とAPIの利用
 
Is Your Phone Turning You into an Adrenaline Junkie?
Is Your Phone Turning You into an Adrenaline Junkie?Is Your Phone Turning You into an Adrenaline Junkie?
Is Your Phone Turning You into an Adrenaline Junkie?
 
Forjando Compromiso Cie Automotive Galfor
Forjando Compromiso Cie Automotive GalforForjando Compromiso Cie Automotive Galfor
Forjando Compromiso Cie Automotive Galfor
 
Como progresar en las redes sociales
Como progresar en las redes socialesComo progresar en las redes sociales
Como progresar en las redes sociales
 
Presentazione telesurvey italia
Presentazione telesurvey italiaPresentazione telesurvey italia
Presentazione telesurvey italia
 
Santa Bertilla
Santa BertillaSanta Bertilla
Santa Bertilla
 
BALPARMAK CORPORATE PRESENTATION
BALPARMAK CORPORATE PRESENTATIONBALPARMAK CORPORATE PRESENTATION
BALPARMAK CORPORATE PRESENTATION
 
Training Games - und schon macht es mehr Spass!
Training Games - und schon macht es mehr Spass!Training Games - und schon macht es mehr Spass!
Training Games - und schon macht es mehr Spass!
 
Oracion nochebuena
Oracion nochebuenaOracion nochebuena
Oracion nochebuena
 
Informe políticas fiscales
Informe políticas fiscalesInforme políticas fiscales
Informe políticas fiscales
 
Beiersdorf interim report_jan-sept_2010
Beiersdorf interim report_jan-sept_2010Beiersdorf interim report_jan-sept_2010
Beiersdorf interim report_jan-sept_2010
 
Carta Bodega El Capricho 2016
Carta Bodega El Capricho 2016Carta Bodega El Capricho 2016
Carta Bodega El Capricho 2016
 
Revista de Hosteleria - 4º trimestre 2012
Revista de Hosteleria - 4º trimestre 2012Revista de Hosteleria - 4º trimestre 2012
Revista de Hosteleria - 4º trimestre 2012
 
Gp0012 sampledescription
Gp0012 sampledescriptionGp0012 sampledescription
Gp0012 sampledescription
 
Netschools (2)
Netschools (2)Netschools (2)
Netschools (2)
 
deSymfony 2013: Anotaciones, no es parte de mi aplicación, pero es mi aplicación
deSymfony 2013: Anotaciones, no es parte de mi aplicación, pero es mi aplicacióndeSymfony 2013: Anotaciones, no es parte de mi aplicación, pero es mi aplicación
deSymfony 2013: Anotaciones, no es parte de mi aplicación, pero es mi aplicación
 
Clase 2 Diseño de Hormigón Armado -
Clase 2 Diseño de Hormigón Armado -Clase 2 Diseño de Hormigón Armado -
Clase 2 Diseño de Hormigón Armado -
 
Bachelor Project Degree-Processing and mechanical analysis of electro-spun an...
Bachelor Project Degree-Processing and mechanical analysis of electro-spun an...Bachelor Project Degree-Processing and mechanical analysis of electro-spun an...
Bachelor Project Degree-Processing and mechanical analysis of electro-spun an...
 

Similar to Where20 2008 Ruby Tutorial

Ioannis Doxaras on GIS and Gmaps at 1st GTUG meetup Greece
Ioannis Doxaras on GIS and Gmaps at 1st GTUG meetup Greece Ioannis Doxaras on GIS and Gmaps at 1st GTUG meetup Greece
Ioannis Doxaras on GIS and Gmaps at 1st GTUG meetup Greece CoLab Athens
 
Drupal and the GeoSpatial Web
Drupal and the GeoSpatial WebDrupal and the GeoSpatial Web
Drupal and the GeoSpatial WebAndrew Turner
 
Gmaps Railscamp2008
Gmaps Railscamp2008Gmaps Railscamp2008
Gmaps Railscamp2008xilinus
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineAndy McKay
 
What are customers building with new Bing Maps capabilities
What are customers building with new Bing Maps capabilitiesWhat are customers building with new Bing Maps capabilities
What are customers building with new Bing Maps capabilitiesMicrosoft Tech Community
 
Handling Real-time Geostreams
Handling Real-time GeostreamsHandling Real-time Geostreams
Handling Real-time Geostreamsguest35660bc
 
Handling Real-time Geostreams
Handling Real-time GeostreamsHandling Real-time Geostreams
Handling Real-time GeostreamsRaffi Krikorian
 
GeoTechTalk InkSatogaeri Project
GeoTechTalk InkSatogaeri ProjectGeoTechTalk InkSatogaeri Project
GeoTechTalk InkSatogaeri ProjectKentaro Ishimaru
 
ESRI Dev Meetup: Building Distributed JavaScript Map Widgets
ESRI Dev Meetup: Building Distributed JavaScript Map WidgetsESRI Dev Meetup: Building Distributed JavaScript Map Widgets
ESRI Dev Meetup: Building Distributed JavaScript Map WidgetsAllan Glen
 
CTOs Perspective on Adding Geospatial and Location-based Information
CTOs Perspective on Adding Geospatial and Location-based InformationCTOs Perspective on Adding Geospatial and Location-based Information
CTOs Perspective on Adding Geospatial and Location-based InformationBradley Brown
 
CHUG_presentation_Hope
CHUG_presentation_HopeCHUG_presentation_Hope
CHUG_presentation_HopeLara Juliusson
 
Building Location-Aware Apps using Open Source (AnDevCon SF 2014)
Building Location-Aware Apps using Open Source (AnDevCon SF 2014)Building Location-Aware Apps using Open Source (AnDevCon SF 2014)
Building Location-Aware Apps using Open Source (AnDevCon SF 2014)Chuck Greb
 
Five Pound App talk: hereit.is, Web app architecture, REST, CSS3
Five Pound App talk: hereit.is, Web app architecture, REST, CSS3Five Pound App talk: hereit.is, Web app architecture, REST, CSS3
Five Pound App talk: hereit.is, Web app architecture, REST, CSS3Jamie Matthews
 

Similar to Where20 2008 Ruby Tutorial (20)

Rails Gis Hacks
Rails Gis HacksRails Gis Hacks
Rails Gis Hacks
 
Ioannis Doxaras on GIS and Gmaps at 1st GTUG meetup Greece
Ioannis Doxaras on GIS and Gmaps at 1st GTUG meetup Greece Ioannis Doxaras on GIS and Gmaps at 1st GTUG meetup Greece
Ioannis Doxaras on GIS and Gmaps at 1st GTUG meetup Greece
 
Rails GIS Hacks
Rails GIS HacksRails GIS Hacks
Rails GIS Hacks
 
Drupal and the GeoSpatial Web
Drupal and the GeoSpatial WebDrupal and the GeoSpatial Web
Drupal and the GeoSpatial Web
 
Gmaps Railscamp2008
Gmaps Railscamp2008Gmaps Railscamp2008
Gmaps Railscamp2008
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App Engine
 
Geolocation and Mapping
Geolocation and MappingGeolocation and Mapping
Geolocation and Mapping
 
Google maps
Google mapsGoogle maps
Google maps
 
Google maps
Google mapsGoogle maps
Google maps
 
What are customers building with new Bing Maps capabilities
What are customers building with new Bing Maps capabilitiesWhat are customers building with new Bing Maps capabilities
What are customers building with new Bing Maps capabilities
 
Handling Real-time Geostreams
Handling Real-time GeostreamsHandling Real-time Geostreams
Handling Real-time Geostreams
 
Handling Real-time Geostreams
Handling Real-time GeostreamsHandling Real-time Geostreams
Handling Real-time Geostreams
 
GeoTechTalk InkSatogaeri Project
GeoTechTalk InkSatogaeri ProjectGeoTechTalk InkSatogaeri Project
GeoTechTalk InkSatogaeri Project
 
ESRI Dev Meetup: Building Distributed JavaScript Map Widgets
ESRI Dev Meetup: Building Distributed JavaScript Map WidgetsESRI Dev Meetup: Building Distributed JavaScript Map Widgets
ESRI Dev Meetup: Building Distributed JavaScript Map Widgets
 
CTOs Perspective on Adding Geospatial and Location-based Information
CTOs Perspective on Adding Geospatial and Location-based InformationCTOs Perspective on Adding Geospatial and Location-based Information
CTOs Perspective on Adding Geospatial and Location-based Information
 
huhu
huhuhuhu
huhu
 
CHUG_presentation_Hope
CHUG_presentation_HopeCHUG_presentation_Hope
CHUG_presentation_Hope
 
Building Location-Aware Apps using Open Source (AnDevCon SF 2014)
Building Location-Aware Apps using Open Source (AnDevCon SF 2014)Building Location-Aware Apps using Open Source (AnDevCon SF 2014)
Building Location-Aware Apps using Open Source (AnDevCon SF 2014)
 
CARTO ENGINE
CARTO ENGINECARTO ENGINE
CARTO ENGINE
 
Five Pound App talk: hereit.is, Web app architecture, REST, CSS3
Five Pound App talk: hereit.is, Web app architecture, REST, CSS3Five Pound App talk: hereit.is, Web app architecture, REST, CSS3
Five Pound App talk: hereit.is, Web app architecture, REST, CSS3
 

More from Shoaib Burq

Async. and Realtime Geo Applications with Node.js
Async. and Realtime Geo Applications with Node.jsAsync. and Realtime Geo Applications with Node.js
Async. and Realtime Geo Applications with Node.jsShoaib Burq
 
Global Random Hacks of Kindness Berlin
Global Random Hacks of Kindness BerlinGlobal Random Hacks of Kindness Berlin
Global Random Hacks of Kindness BerlinShoaib Burq
 
OpenStreetMap & Walking-Papers Workflow
OpenStreetMap & Walking-Papers WorkflowOpenStreetMap & Walking-Papers Workflow
OpenStreetMap & Walking-Papers WorkflowShoaib Burq
 
Ian Batley's MAPS (Spatial@Gov 2009)
Ian Batley's MAPS (Spatial@Gov 2009)Ian Batley's MAPS (Spatial@Gov 2009)
Ian Batley's MAPS (Spatial@Gov 2009)Shoaib Burq
 
OpenStreetMap Response to Haiti earthquake
OpenStreetMap Response to Haiti earthquake OpenStreetMap Response to Haiti earthquake
OpenStreetMap Response to Haiti earthquake Shoaib Burq
 
Haiti Qake2010 Bar Camp Canberra2010
Haiti Qake2010 Bar Camp Canberra2010Haiti Qake2010 Bar Camp Canberra2010
Haiti Qake2010 Bar Camp Canberra2010Shoaib Burq
 
Using Cuda Within Mathematica
Using Cuda Within MathematicaUsing Cuda Within Mathematica
Using Cuda Within MathematicaShoaib Burq
 
Opening of Geographic Data
Opening of Geographic DataOpening of Geographic Data
Opening of Geographic DataShoaib Burq
 
Mapping Multan and beyond with OSM
Mapping Multan and beyond with OSMMapping Multan and beyond with OSM
Mapping Multan and beyond with OSMShoaib Burq
 
learning interoperability from web2.0
learning interoperability from web2.0learning interoperability from web2.0
learning interoperability from web2.0Shoaib Burq
 

More from Shoaib Burq (10)

Async. and Realtime Geo Applications with Node.js
Async. and Realtime Geo Applications with Node.jsAsync. and Realtime Geo Applications with Node.js
Async. and Realtime Geo Applications with Node.js
 
Global Random Hacks of Kindness Berlin
Global Random Hacks of Kindness BerlinGlobal Random Hacks of Kindness Berlin
Global Random Hacks of Kindness Berlin
 
OpenStreetMap & Walking-Papers Workflow
OpenStreetMap & Walking-Papers WorkflowOpenStreetMap & Walking-Papers Workflow
OpenStreetMap & Walking-Papers Workflow
 
Ian Batley's MAPS (Spatial@Gov 2009)
Ian Batley's MAPS (Spatial@Gov 2009)Ian Batley's MAPS (Spatial@Gov 2009)
Ian Batley's MAPS (Spatial@Gov 2009)
 
OpenStreetMap Response to Haiti earthquake
OpenStreetMap Response to Haiti earthquake OpenStreetMap Response to Haiti earthquake
OpenStreetMap Response to Haiti earthquake
 
Haiti Qake2010 Bar Camp Canberra2010
Haiti Qake2010 Bar Camp Canberra2010Haiti Qake2010 Bar Camp Canberra2010
Haiti Qake2010 Bar Camp Canberra2010
 
Using Cuda Within Mathematica
Using Cuda Within MathematicaUsing Cuda Within Mathematica
Using Cuda Within Mathematica
 
Opening of Geographic Data
Opening of Geographic DataOpening of Geographic Data
Opening of Geographic Data
 
Mapping Multan and beyond with OSM
Mapping Multan and beyond with OSMMapping Multan and beyond with OSM
Mapping Multan and beyond with OSM
 
learning interoperability from web2.0
learning interoperability from web2.0learning interoperability from web2.0
learning interoperability from web2.0
 

Recently uploaded

Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 

Recently uploaded (20)

Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 

Where20 2008 Ruby Tutorial

  • 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/
  • 4. Geocoding What is it? • “the heart of most mapping applications” Google Maps Apps. with Rails and Ajax by A. Lewis, et al.
  • 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
  • 7. Geocoding Example • “1800 Old Bayshore Highway, Burlingame, CA, USA” > lat = 37.368613, lng =-121.906458
  • 8. Geocoding Example • “1800 Old Bayshore Highway, Burlingame, CA, USA” > lat = 37.368613, lng =-121.906458 • “72.14.207.99” > Mountain View, CA, US
  • 10. Geocoding Complications • Geographic information system has to be updated regularly
  • 11. Geocoding Complications • Geographic information system has to be updated regularly • Distinguishing between ambiguous addresses needs increasing information
  • 13. Geocoding Services • Google Maps
  • 14. Geocoding Services • Google Maps • Yahoo! Maps
  • 15. Geocoding Services • Google Maps • Yahoo! Maps • Geocoder.us & Geocoder.ca
  • 16. Intro to Yahoo! Maps with AJAX $ rails yahoo $ cd yahoo $ ruby script/generate controller intro
  • 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. app/views/intro/map.rhtml <body onload="load()"> <div id="map" style="width: 500px; height: 500px"></div> </body> </html>
  • 19.
  • 21. Geocoding Using REST web services • Format a REST query
  • 22. Geocoding Using REST web services • Format a REST query • Use OpenURI to send the query
  • 23. 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
  • 24. Google Maps’ REST Geocoder http://maps.google.com/maps/geo?q=Torstra%C3%9Fe +104,+Berlin, +Germany&output=xml&key=ABQIAAAAwWqh7sPpuhNCdGZ0pieS hBTJQa0g3IQ9GZqIMmInSLzwtGDKaBQOJH6Tw4jIlz7bMDU6qtLF _9TSHQ
  • 25. 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>
  • 27. Rails plugins An initial comparison • GeoKit
  • 28. Rails plugins An initial comparison • GeoKit • Graticule & acts_as_geocodable
  • 29. Rails plugins An initial comparison • GeoKit • Graticule & acts_as_geocodable • ActsAsLocatable
  • 30. Rails plugins An initial comparison • GeoKit • Graticule & acts_as_geocodable • ActsAsLocatable • YM4R
  • 32. GeoKit Lab Exercise • Install GeoKit
  • 33. GeoKit Lab Exercise • Install GeoKit • Get API Keys
  • 34. GeoKit Lab Exercise • Install GeoKit • Get API Keys • Try Geocoding (in Rails console)
  • 35. GeoKit install $ rails geokit $ cd geokit $ ruby script/plugin install svn://rubyforge.org/var/svn/geokit/trunk
  • 36. 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__'
  • 37. Geocoding from multiple providers GeoKit::Geocoders::provider_order = [:google, :yahoo]
  • 38. 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
  • 39. 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>
  • 41. Advance Geocoding geocoding with ActiveRecord • setup database tables
  • 42. Advance Geocoding geocoding with ActiveRecord • setup database tables • auto-geocode your model
  • 43. 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
  • 44. app/models/location.rb class Location < ActiveRecord::Base acts_as_mappable :auto_geocode => true end
  • 45. 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>
  • 46. 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"}> ]
  • 47. 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"}> ]
  • 48. 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"}> ]
  • 49. end of geocoding exercise (break?)
  • 51. More GeoKit features • Bounding queries
  • 52. More GeoKit features • Bounding queries • Eager loading
  • 53. More GeoKit features • Bounding queries • Eager loading • IP geocoding
  • 54. GeoKit::Bounds >> bounds = GeoKit::Bounds.new( sq_sw_point, sq_ne_point ) >> locs = Location.find :all, :bounds => bounds
  • 55. Eager Loading >> locs = Location.find :all, :origin => home, :include => [:reviews], :within => 5, :order => 'distance'
  • 56. 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>
  • 57. 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
  • 59. Graticule and its Rails plugin • Automatically geocodes your models when they are saved
  • 60. Graticule and its Rails plugin • Automatically geocodes your models when they are saved • Comes as a gem plus a plugin for your Rails app
  • 61. Inital config $ ruby script/generate geocodable_migration add_geocodable_tables $ rake db:migrate
  • 62. 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' ) )
  • 63. 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
  • 64. app/model/location.rb class Location < ActiveRecord::Base acts_as_geocodable end
  • 65. 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)>
  • 66. 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
  • 67. 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"}>]
  • 68. IP geocoding def index @nearest = Location.find( :nearest, :origin => remote_location) if remote_location @locations = Location.find(:all) end
  • 69. 02 Location data in ActiveRecord (PostGIS/PostgreSQL)
  • 70. Geospatial Domain What’s so special about spatial?
  • 71. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi*
  • 75. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi*
  • 76. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi* • Raster Data
  • 79. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi* • Raster Data
  • 80. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi* • Raster Data • Spatial Reference System (SRS), SRIDs
  • 81. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi* • Raster Data • Spatial Reference System (SRS), SRIDs • Spatial Indices
  • 82. Geospatial Domain What’s so special about spatial? • Point, Line, Polygon, Multi* • Raster Data • Spatial Reference System (SRS), SRIDs • Spatial Indices • Map Projections
  • 83. Free desktop GIS (uDig) http://udig.refractions.net
  • 84. Free desktop GIS (uDig) http://udig.refractions.net
  • 85. Free desktop GIS (uDig) http://udig.refractions.net
  • 87. GeoRails Stack Overview
  • 88. GeoRails Stack Overview PostGIS Database
  • 89. GeoRails Stack Overview GeoRuby PostGIS Database
  • 90. GeoRails Stack Overview Spatial Adapter GeoRuby PostGIS Database
  • 91. GeoRails Stack Overview YM4R Spatial Adapter GeoRuby PostGIS Database
  • 93. Prerequisites Installing PostGIS • Install PostgreSQL without PostGIS extension
  • 94. Prerequisites Installing PostGIS • Install PostgreSQL without PostGIS extension • Install PostGIS separately
  • 95. Prerequisites Installing PostGIS • Install PostgreSQL without PostGIS extension • Install PostGIS separately • Ensures you get the latest PostGIS release
  • 97. Prerequisites template_postgis • create a clone of template1
  • 98. Prerequisites template_postgis • create a clone of template1 • add pl/pgsql language
  • 99. Prerequisites template_postgis • create a clone of template1 • add pl/pgsql language • install the postgis functions/libraries
  • 100. Prerequisites template_postgis • create a clone of template1 • add pl/pgsql language • install the postgis functions/libraries • grant role-based permissions
  • 101. Prerequisites template_postgis • create a clone of template1 • add pl/pgsql language • install the postgis functions/libraries • grant role-based permissions • vacuum freeze
  • 102. 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;
  • 103. 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
  • 104. using template_postgis createdb -T template_postgis my_database
  • 106. Prequisites GeoRuby • Data structs to hold Spatial Data from PostGIS/MySQL-Spatial
  • 107. Prequisites GeoRuby • Data structs to hold Spatial Data from PostGIS/MySQL-Spatial • Based on OGC Simple Features Spec
  • 108. Prequisites GeoRuby • Data structs to hold Spatial Data from PostGIS/MySQL-Spatial • Based on OGC Simple Features Spec • No geometry operations or reprojections
  • 109. 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
  • 110. GeoRuby $ sudo gem install georuby
  • 112. GeoStack Running on Edge Rails • Installing Edge Rails from Git
  • 113. GeoStack Running on Edge Rails • Installing Edge Rails from Git • Add Geo-plugins as Git Submodules
  • 114. GeoStack Running on Edge Rails • Installing Edge Rails from Git • Add Geo-plugins as Git Submodules • Git it up and running
  • 115. 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
  • 116. 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
  • 117. 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
  • 118. Complete Mapping Solutons using Rails (1) Web Mapping Application using Google Maps
  • 119. Magic of GeoScaffold $ ruby script/generate geo_scaffold Location name:string description:text geom:point
  • 120. 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
  • 121. $ rake db:migrate $ mongrel_rails start
  • 123. WTF! okay okay! lets dig in and see what just happened
  • 124. Housekeeping was done Migrations Spatial Indices Test Fixtures Mime-Types
  • 125. 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
  • 126. 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
  • 127. 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
  • 128. 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
  • 131.
  • 132.
  • 133. 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) )
  • 134. 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"
  • 135. 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
  • 136. Tests (unit/functional) $ rake test:units $ rake test:functionals http://www.flickr.com/photo_zoom.gne? id=317182464&size=l
  • 137. Tests (JavaScript) $ ruby script/plugin install http://dev.rubyonrails.org/svn/rails/plugins/ javascript_test $ ruby script/generate javascript_test application
  • 138. 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");
  • 139. Tests (JavaScript) $ rake test:javascripts
  • 140. GeoRSS & KML Mime-Types config/initializers.rb Mime::Type.register "application/vnd.google-earth.kml+xml", :kml Mime::Type.register "application/rss+xml", :georss
  • 141. Views & Controllers were generated all that CRUD
  • 142. Read lets take it one step at a time
  • 143. 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
  • 144. 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) %> ...
  • 145. 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
  • 147. 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
  • 148. 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
  • 149. 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
  • 150. 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
  • 151. 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 -%>
  • 153. Create lets take it one step at a time
  • 154. 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); } }
  • 155. 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(); }); }); }
  • 156. CRUD location: (layouts/hotspots.rb) <%= stylesheet_link_tag 'geo_scaffold' %> <%= javascript_include_tag :defaults %> <%= javascript_include_tag 'geo_scaffold' %>
  • 157. 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
  • 158. 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 -%>
  • 159. 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
  • 164. Update lets take it one step at a time
  • 165. 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(); }); }
  • 166. 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
  • 167. 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 -%>
  • 168. 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
  • 172.
  • 173. Complete Mapping Solutons using Rails (2) instant RESTful mapping API with GeoScaffold
  • 174. 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>
  • 175. 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
  • 176. 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>
  • 177. 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
  • 178. 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>
  • 179. Text
  • 180. 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
  • 181. 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>
  • 182. Text
  • 183. DELETE curl -u shoaib:secret -X DELETE http://localhost:3000/hotspots/2.xml
  • 184. Holy Cow! that was hardcore! Lets use ActiveResource instead of low level cURL
  • 185. to do something Cool! I want to geocode the location of my Twitter Friends
  • 186. TwitterUser Model as ActiveResource $ ruby script/generate model TwitterUser --skip-migration
  • 187. 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
  • 188. Need GeoKit to Geocode $ ruby script/plugin install git://github.com/ptb/geokit.git edit config/environment.rb with you API keys
  • 189. 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">
  • 190. Exercise as an exercise you can add markers to the map for your twitter friends location
  • 191. Complete Mapping Solutons using Rails (3) Google Earth & Rails GeoStack
  • 192. 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
  • 193. 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]") } } } }
  • 194. 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 { ...
  • 195. 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
  • 196. 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 ...
  • 197. 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
  • 198. 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
  • 199. 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
  • 200. 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") ...
  • 201. 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
  • 202. 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") } } ...
  • 203. 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