SlideShare une entreprise Scribd logo
1  sur  109
Async & RT Geo
apps with Node.js
   Kashif Rasul @krasul
   Shoaib Burq @sabman




   http://spacialdb.com
      (@SpacialDB)
Checkout tomorrow
• Techniques for distributed high-speed map
  tile generation using Mapnik & Node.js by
  Simon & Javier @ 13:00 Silver Room
• The State of GeoCouch by Volker @ 15:00
  Silver Room
• The new GeoData tool set: CouchDB and
  NodeJS by Mick @ 15:30 Silver Room
By the end of this you
     will know...
• what Node.js is.
• how to write and deploy Node.js servers.
• a bit about coffeescript.
• about the Geostack for Node.js.
• see how to write and deploy a RT geo app.
Build something useless
       but cool :-)


   By the end of this..
By the end of this..
What’s node.js?
Node.js

• Event based server side JavaScript based on
  Google’s V8 Engine
• Event based: good at handling a high
  number of concurrent connections
• Trick: make network I/O non-blocking and
  make most file I/O asynchronous
Blocking vs. non-
    blocking
  var result = db.query("select...");

  // use result
  ...

  db.query("select..", function (result) {
      // use result
      ...
  });
Non-blocking
•   Allows the app to return to the event loop
    immediately
•   But why isn’t everyone using event loops?
    •   Require I/O to be non-blocking
    •   Most libs are blocking
    •   POSIX async I/O not available
    •   db bindings have no support for async queries
    •   async DNS resolution not standard etc.
I/O is expensive
•   L1 cache 0.5 ns
•   Mutex lock/unlock 25 ns
•   Main mem. ref. 100 ns
•   Send 2K bytes over 1Gbps 20,000 ns
•   Read 1MB seq. from mem. 250,000 ns
•   Disk seek 10,000,000 ns
•   Read 1MB seq. from disk 20,000,000 ns
Rough calc.

• Read 30 images serially each 256K:
 • 30 seek × 10 ms/seek + 30 × 256K ÷
    30MB/s = 560 ms
• Read them in parallel:
 • 10 ms/seek + 256K ÷ 30MB/s = 18 ms
Speed is important

• Amazon: 100ms extra causes 1% drop in
  sales
• Google: 500ms extra causes 20% fewer
  searches
• Yahoo: 400ms extra causes 5-9% more
  “Back” button clicks
Node.js: components

• V8
• libuv: networking layer for all platforms
  contains libev, libeio and c-ares
• http_parser: parser for HTTP messages
• openssl
Node.js design

• JS designed to be used with an event loop
• Provide a purely evented non-blocking
  infrastructure to script highly concurrent
  servers
• No direct I/O: there must be a callback
Node.js: goals
• Low-level API
• Stream everything
• Built-in support for important protocols
  like TCP, DNS, HTTP
• Support HTTP features like Chuncked
  requests, Keep-alive, Hang requests etc.
HelloWorld.js
           by kashif (@krasul)
Node.js: install (Linux)
➜ wget http://nodejs.org/dist/node-v0.4.11.tar.gz
--2011-09-01 15:38:07-- http://nodejs.org/dist/node-v0.4.11.tar.gz
Resolving nodejs.org... 8.12.44.238
Connecting to nodejs.org|8.12.44.238|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 12419274 (12M) [application/octet-stream]
Saving to: `node-v0.4.11.tar.gz'

100%[======================================>] 12,419,274   357K/s    in 37s

2011-09-01 15:38:45 (332 KB/s) - `node-v0.4.11.tar.gz' saved [12419274/12419274]

➜ tar xzf node-v0.4.11.tar.gz
➜ cd node-v0.4.11
➜ ./configure
...
➜ sudo make install
...
➜ node -v
v0.4.11
Node.js: install (OS X)
 ➜ /usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"
 ...
 ➜ brew install node
 ...
 ➜ node -v
 v0.4.11




 ➜ sudo port selfupdate
 ...
 ➜ sudo port install nodejs
 ...
 ➜ node -v
 v0.4.11
Node.js: install (Win)

• Grab the installer from http://nodejs.org/
  dist/v0.5.5/node.exe
• Unstable version
• Can be built from source by using Cygwin
  but it’s a pain in the ass
➜ cat hello.js
var http = require('http');
var net = require('net');
var c = 0;

http.createServer(function(req, res) {
    c++;
    res.writeHead(200);
    res.end('hello worldn');
}).listen(8000);

net.createServer(function(socket) {
    socket.write('connections: ' + c);
    socket.end();
}).listen(8001);

➜ node hello.js &
[1] 1879

➜ curl -i http://localhost:8000/
HTTP/1.1 200 OK
Connection: keep-alive
Transfer-Encoding: chunked

hello world
Node.js: Modules

• File
• Streams
• Crypto
• HTTP
• DNS
Node Package Manager
➜ curl http://npmjs.org/install.sh | sh
...
npm@1.0.27 /usr/local/lib/node_modules/npm
It worked

➜ npm -v
1.0.27

➜ npm -g install coffee-script
/usr/local/bin/coffee -> /usr/local/lib/node_modules/coffee-script/bin/coffee
/usr/local/bin/cake -> /usr/local/lib/node_modules/coffee-script/bin/cake
coffee-script@1.1.2 /usr/local/lib/node_modules/coffee-script

➜ export NODE_PATH=/usr/local/lib/node_modules
➜ cat package.json
{ "name": "pow"
, "description": "Zero-configuration Rack server for Mac OS X"
, "version": "0.3.2"
, "author": "Sam Stephenson"
, "repository":
  { "type": "git"
  , "url": "http://github.com/sstephenson/pow.git"
  }
, "bin": { "pow": "./bin/pow" }
, "main": "./lib/index.js"
, "dependencies": {
    "async": "0.1.8"
  , "coffee-script": ">= 1.1.0"
  , "connect": ">= 1.0.3"
  , "http-proxy": ">= 0.5.8"
  , "log": ">= 1.1.1"
  , "nack": ">= 0.12.2"
  , "ndns": ">= 0.1.2"
  }
, "bundleDependencies" : [ "ndns" ]
, "devDependencies": {
    "eco": "= 1.0.3"
,   "nodeunit": ">= 0.5.0"
  }
, "engines" : { "node" : ">=0.4.1" }
, "scripts": {
    "install": "[[ -z "$BUNDLE_ONLY" ]] && cake install || true"
,   "test": "cake test"
,   "start": "cake start"
,   "stop": "cake stop"
  }
}
➜ npm install

> pow@0.3.2 install /Users/kashif/node/pow
> [[ -z "$BUNDLE_ONLY" ]] && cake install || true

*** Installing local configuration files...
*** Installed
http-proxy@0.6.6 ./node_modules/http-proxy
├── colors@0.5.0
├── pkginfo@0.2.2
└── optimist@0.2.6
CoffeeScript Quickie!

                 by shoaib
                (@sabman)
CoffeeScript a quick
         intro
• Compiles *.coffee down to JavaScript *.js
• Adds to the good parts of JS and gives
  familiarity, safety and readability
• Usually need shorter code to generate the
  JS you need.
music_style = "Indian"
 ask = "Do you like #{music_style} music?"




var ask, music_style;
music_style = "Indian";
ask = "Do you like " + music_style + " music?";
# hashes (json objects) & arrays
ragas =
  bhupali:
    aaroha: [ 'sa', 're','ga', 'pa', 'dha' ]
    avaroha: [ 'dha', 'pa','ga', 're', 'sa' ]
  durga:
    aaroha: [ 'sa', 're','ma', 'pa', 'dha', 'sa' ]
    avaroha: [ 'sa', 'dha', 'pa', 'ma', 're', 'sa']




 var ragas;
 ragas = {
    bhupali: {
       aaroha: ['sa', 're', 'ga', 'pa', 'dha'],
       avaroha: ['dha', 'pa', 'ga', 're', 'sa']
    },
    durga: {
       aaroha: ['sa', 're', 'ma', 'pa', 'dha', 'sa'],
       avaroha: ['sa', 'dha', 'pa', 'ma', 're', 'sa']
    }
 };
# functions
play = (raga) ->
  console.log(ragas[raga]['aaroha'])
  console.log(ragas[raga]['avaroha'])
  null

play('bhupali')




var play;
play = function(raga) {
   console.log(ragas[raga]['aaroha']);
   console.log(ragas[raga]['avaroha']);
   return null;
};
play('bhupali');
# Classes
class Event
  constructor: (@name, @time, @address, @lat, @lon) ->




   var Event;
   Event = (function() {
     function Event(name, time, address, lat, lon) {
       this.name = name;
       this.time = time;
       this.address = address;
       this.lat = lat;
       this.lon = lon;
     }
     return Event;
   })();
class Event
  constructor: (@name, @time, @address, @lat, @lon) ->

class Party extends Event
  @celeberation: true
  @kind: null
  hasBalloons: ->
    @kind == "Birthday" ? true : false
Event is the name
 of the class and
                    class Event
   constructor
                      constructor: (@name, @time, @address, @lat, @lon) ->

                    class Party extends Event
                      @celeberation: true
                      @kind: null
                      hasBalloons: ->
                        @kind == "Birthday" ? true : false
Event is the name
 of the class and
                    class Event
   constructor
                      constructor: (@name, @time, @address, @lat, @lon) ->

  Inheritance via   class Party extends Event
     extends          @celeberation: true
                      @kind: null
                      hasBalloons: ->
                        @kind == "Birthday" ? true : false
Event is the name
 of the class and
                       class Event
   constructor
                         constructor: (@name, @time, @address, @lat, @lon) ->

  Inheritance via      class Party extends Event
     extends             @celeberation: true
                         @kind: null
                         hasBalloons: ->
     Inheritance via
        extends            @kind == "Birthday" ? true : false

  @ defines an instance
       variable
var Event, Party;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
   for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
   function ctor() { this.constructor = child; }
   ctor.prototype = parent.prototype;
   child.prototype = new ctor;
   child.__super__ = parent.prototype;
   return child;
};
Event = (function() {
   function Event(name, time, address, lat, lon) {
     this.name = name;
     this.time = time;
     this.address = address;
     this.lat = lat;
     this.lon = lon;
  }
   return Event;
})();
Party = (function() {
   __extends(Party, Event);
   function Party() {
     Party.__super__.constructor.apply(this, arguments);
  }
   Party.celeberation = true;
   Party.kind = null;
   Party.prototype.hasBalloons = function() {
     var _ref;
     return (_ref = this.kind === "Birthday") != null ? _ref : {
        "true": false
     };
  };
   return Party;
})();
try it out

event = new Party("Yo! The big party!",
                  "Thu, 09/15/2011 6:30pm",
                  "Hamilton Building, Denver Art Museum")

console.log(event.hasBalloons())
# false

event.kind = "Birthday"

console.log(event.hasBalloons())
# true
http://screencasts.org/episodes/introduction-to-coffeescript
Node web
development
 (Geo)stack

  by kashif
  (@krasul)

              flickr:darwinbell (cc)
Node.js: Web
         Frameworks

• Express: Sinatra inspired and built on
  Connect a middleware layer for Node.js
• SocketStream: a fast Real-time web
  framework
Express
➜ npm install -g express
/usr/local/bin/express -> /usr/local/lib/node_modules/express/bin/express
express@2.4.6 /usr/local/lib/node_modules/express
├── mime@1.2.2
├── qs@0.3.1
└── connect@1.7.0

➜ cat hello.coffee
express = require('express')
app = express.createServer()
app.set('view engine', 'jade')

# App Routes
app.get '/', (request, response) ->
  response.send 'hello world'

# Listen
app.listen 3000
console.log "Express server listening on port %d", app.address().port
Jade

• HTML5 template engine based on HAML
• Supports Express out of the box
• Has filters for coffee-script
<!DOCTYPE html>
!!! 5                                <html lang="en">
html(lang="en")                        <head>
  head                                   <title>Jade</title>
    title= pageTitle                     <script type="text/javascript">
    script(type='text/javascript')         if (foo) {
       if (foo) {                            bar()
          bar()                            }
       }                                 </script>
  body                                 </head>
    h1 Jade - node template engine     <body>
    #container                           <h1>Jade - node template engine</h1>
       - if (youAreUsingJade)            <div id="container">
         p You are amazing                 <p>You are amazing</p>
       - else                            </div>
         p Get on it!                  </body>
                                     </html>
Database connection

• PostgreSQL + PostGIS via node-pg
• Sqlite3 + Spatialite via node-sqlite3
• CouchDB + GeoCouch via cradle
• SpacialDB via HTTP Client libraries
PostGIS
➜ cat package.json
...
"dependencies": {
  ...
  "pg": "0.5.4"
}

➜ cat server.coffee
...
pg = require "pg"

DATABASE_URL = "pg://postgres:testing@localhost/your_db"

pg.connect DATABASE_URL, (err, client) ->
  query = client.query "SELECT * FROM your_table"

  query.on 'row', (row) ->
    console.log(JSON.stringify(row))
...
Spatialite
➜ cat package.json
...
"dependencies": {
  ...
  "sqlite3": "2.0.16"
}

➜ cat server.coffee
...
fs = require "fs"
sqlite3 = require "sqlite3"

spatialite_ext = "/usr/local/lib/libspatialite.dylib"

db = new sqlite3.Database "test.sqlite3"

db.loadExtension spatialite_ext, (err) ->
  if err throw err

db.each "SELECT...", (err, row) ->
  console.log row.id + ": " + row.info
Geocouch
➜ cat package.json
...
"dependencies": {
  ...
  "cradle": "0.5.5"
}

➜ cat server.coffee
...
cradle = require "cradle"

db = new(cradle.Connection)().database('starwars')

db.get "vader", (err, doc) ->
  console.log doc.name
...
Redis
➜ cat package.json
...
"dependencies": {
   ...
   "redis": "0.6.7"
  ,"hiredis": "0.1.12"
}

➜ cat server.coffee
...
redis = require("redis")
client = redis.createClient

client.on "error", (err) ->
    console.log("Error " + err);

client.set "string key","string val" ->
  redis.print
MongoDB
•   v2.0 just out with support for multi-location
    objects and polygon within searches
•   v1.4+ added support for 2d geo point indexes
    to do find near queries.
•   can query for points within a bounding box
•   v1.7+ added spherical distance query
•   use mongoose a MongoDB object tool for
    node.js
Geo bindings

• Geocoder
• Geos/Proj4
• Mapnik
Geocoder

➜ cat package.json
...
"dependencies": {
  ...
  "geocoder": "0.0.5"
}

➜ cat server.coffee
...
geocoder = require("geocoder")

geocoder.geocode "Denver, CO", (err, data) ->
  # do something with the data
node-geos

➜ cat geos.coffee
geonode = require 'geos'

geom = new geonode.Geometry()
geom.fromWkt("POINT(0 0)")

console.log(geom.toWkt())

➜ coffee geos.coffee
POINT (0.0000000000000000 0.0000000000000000)
node-mapnik

• Techniques for distributed high-speed map
  tile generation using Mapnik & Node.js by
  Simon & Javier. Thursday @ 13:00 Silver
  Room
HTTP Client
• github.com/coolaj86/abstract-http-request:
  Higher level wrapper around the HTTP
  request system
• github.com/danwrong/restler, github.com/
  maxpert/Reston & github.com/pfleidi/node-
  wwwdude: REST client libraries
• github.com/cloudhead/http-console: A
  useful interactive shell for HTTP requests
by shoaib
                    (@sabman)




Building “Chat on a Map”
Starting a node project
          package.json
      {
          "name": "example_2",
          "version": "0.0.0",
          "dependencies": {
            "socket.io" : "*",
            "coffee-script": "*",
            "request" : "*",
            "underscore": "*"
          }
      }
Starting a node project
                package.json
            {
                "name": "example_2",
minimum         "version": "0.0.0",
 required       "dependencies": {
                  "socket.io" : "*",
                  "coffee-script": "*",
                  "request" : "*",
                  "underscore": "*"
                }
            }
Starting a node project
      package.json
      {
          "name": "example_2",
          "version": "0.0.0",
          "dependencies": {
            "socket.io" : "*",
            "coffee-script": "*",
            "request" : "*",
            "underscore": "*"
          }
      }



      $ npm install
server.coffee
Lets write the GeoChat server
fs        =   require   'fs'   # to read static files
http      =   require   'http' # http server
request   =   require   'request'
_         =   require   'underscore'

server = http.createServer (req, res) ->
  fs.readFile "#{__dirname}/map.html", (err, data) ->
    res.writeHead 200, 'Content-Type': 'text/html'
    res.end data, 'utf8'

server.listen(process.env.PORT || 3000)

io = require('socket.io').listen server
io.sockets.on 'connection', (socket) ->
  clientId = socket.id

  socket.on 'disconnect', (message) ->
    # find the client & delete it ...

  socket.on 'publish', (message) ->
    # ...

  socket.on 'broadcast', (message) ->
    # find the client and broadcast their message
    socket.broadcast.send(chat_data)
    # forwards message to all the clients except the one
    # that emitted the "broadcast" event

  socket.on 'addUser', (message) ->
    # create a new user and broadcast their existance
    socket.broadcast.emit 'newUser', new_record
fs        =   require   'fs'   # to read static files
http      =   require   'http' # http server
                                                           imports
request   =   require   'request'
_         =   require   'underscore'

server = http.createServer (req, res) ->
  fs.readFile "#{__dirname}/map.html", (err, data) ->
    res.writeHead 200, 'Content-Type': 'text/html'
    res.end data, 'utf8'

server.listen(process.env.PORT || 3000)

io = require('socket.io').listen server
io.sockets.on 'connection', (socket) ->
  clientId = socket.id

  socket.on 'disconnect', (message) ->
    # find the client & delete it ...

  socket.on 'publish', (message) ->
    # ...

  socket.on 'broadcast', (message) ->
    # find the client and broadcast their message
    socket.broadcast.send(chat_data)
    # forwards message to all the clients except the one
    # that emitted the "broadcast" event

  socket.on 'addUser', (message) ->
    # create a new user and broadcast their existence
    socket.broadcast.emit 'newUser', new_record
fs        =   require   'fs'   # to read static files
http      =   require   'http' # http server
                                                           imports
request   =   require   'request'
_         =   require   'underscore'

server = http.createServer (req, res) ->
  fs.readFile "#{__dirname}/map.html", (err, data) ->      serve static http via
    res.writeHead 200, 'Content-Type': 'text/html'         node
    res.end data, 'utf8'

server.listen(process.env.PORT || 3000)

io = require('socket.io').listen server
io.sockets.on 'connection', (socket) ->
  clientId = socket.id

  socket.on 'disconnect', (message) ->
    # find the client & delete it ...

  socket.on 'publish', (message) ->
    # ...

  socket.on 'broadcast', (message) ->
    # find the client and broadcast their message
    socket.broadcast.send(chat_data)
    # forwards message to all the clients except the one
    # that emitted the "broadcast" event

  socket.on 'addUser', (message) ->
    # create a new user and broadcast their existence
    socket.broadcast.emit 'newUser', new_record
fs        =   require   'fs'   # to read static files
http      =   require   'http' # http server
                                                           imports
request   =   require   'request'
_         =   require   'underscore'

server = http.createServer (req, res) ->
  fs.readFile "#{__dirname}/map.html", (err, data) ->      serve static http via
    res.writeHead 200, 'Content-Type': 'text/html'         node
    res.end data, 'utf8'

server.listen(process.env.PORT || 3000)                    listening port
io = require('socket.io').listen server
io.sockets.on 'connection', (socket) ->
  clientId = socket.id

  socket.on 'disconnect', (message) ->
    # find the client & delete it ...

  socket.on 'publish', (message) ->
    # ...

  socket.on 'broadcast', (message) ->
    # find the client and broadcast their message
    socket.broadcast.send(chat_data)
    # forwards message to all the clients except the one
    # that emitted the "broadcast" event

  socket.on 'addUser', (message) ->
    # create a new user and broadcast their existence
    socket.broadcast.emit 'newUser', new_record
fs        =   require   'fs'   # to read static files
http      =   require   'http' # http server
                                                           imports
request   =   require   'request'
_         =   require   'underscore'

server = http.createServer (req, res) ->
  fs.readFile "#{__dirname}/map.html", (err, data) ->      serve static http via
    res.writeHead 200, 'Content-Type': 'text/html'         node
    res.end data, 'utf8'

server.listen(process.env.PORT || 3000)                    listening port
io = require('socket.io').listen server                    bind socket & store
io.sockets.on 'connection', (socket) ->
  clientId = socket.id
                                                           session

  socket.on 'disconnect', (message) ->
    # find the client & delete it ...

  socket.on 'publish', (message) ->
    # ...

  socket.on 'broadcast', (message) ->
    # find the client and broadcast their message
    socket.broadcast.send(chat_data)
    # forwards message to all the clients except the one
    # that emitted the "broadcast" event

  socket.on 'addUser', (message) ->
    # create a new user and broadcast their existence
    socket.broadcast.emit 'newUser', new_record
fs        =   require   'fs'   # to read static files
http      =   require   'http' # http server
                                                           imports
request   =   require   'request'
_         =   require   'underscore'

server = http.createServer (req, res) ->
  fs.readFile "#{__dirname}/map.html", (err, data) ->      serve static http via
    res.writeHead 200, 'Content-Type': 'text/html'         node
    res.end data, 'utf8'

server.listen(process.env.PORT || 3000)                    listening port
io = require('socket.io').listen server                    bind socket & store
io.sockets.on 'connection', (socket) ->
  clientId = socket.id
                                                           session

  socket.on 'disconnect', (message) ->
                                                           disconnect event
    # find the client & delete it ...                      delete the user
  socket.on 'publish', (message) ->
    # ...

  socket.on 'broadcast', (message) ->
    # find the client and broadcast their message
    socket.broadcast.send(chat_data)
    # forwards message to all the clients except the one
    # that emitted the "broadcast" event

  socket.on 'addUser', (message) ->
    # create a new user and broadcast their existence
    socket.broadcast.emit 'newUser', new_record
fs        =   require   'fs'   # to read static files
http      =   require   'http' # http server
                                                           imports
request   =   require   'request'
_         =   require   'underscore'

server = http.createServer (req, res) ->
  fs.readFile "#{__dirname}/map.html", (err, data) ->      serve static http via
    res.writeHead 200, 'Content-Type': 'text/html'         node
    res.end data, 'utf8'

server.listen(process.env.PORT || 3000)                    listening port
io = require('socket.io').listen server                    bind socket & store
io.sockets.on 'connection', (socket) ->
  clientId = socket.id
                                                           session

  socket.on 'disconnect', (message) ->
                                                           disconnect event
    # find the client & delete it ...                      delete the user
  socket.on 'publish', (message) ->
    # ...                                                  broadcast: verify the
                                                           client and broadcast
  socket.on 'broadcast', (message) ->
    # find the client and broadcast their message          the message with
    socket.broadcast.send(chat_data)                       user details
    # forwards message to all the clients except the one
    # that emitted the "broadcast" event

  socket.on 'addUser', (message) ->
    # create a new user and broadcast their existence
    socket.broadcast.emit 'newUser', new_record
fs        =   require   'fs'   # to read static files
http      =   require   'http' # http server
                                                           imports
request   =   require   'request'
_         =   require   'underscore'

server = http.createServer (req, res) ->
  fs.readFile "#{__dirname}/map.html", (err, data) ->      serve static http via
    res.writeHead 200, 'Content-Type': 'text/html'         node
    res.end data, 'utf8'

server.listen(process.env.PORT || 3000)                    listening port
io = require('socket.io').listen server                    bind socket & store
io.sockets.on 'connection', (socket) ->
  clientId = socket.id
                                                           session

  socket.on 'disconnect', (message) ->
                                                           disconnect event
    # find the client & delete it ...                      delete the user
  socket.on 'publish', (message) ->
    # ...                                                  broadcast: verify the
                                                           client and broadcast
  socket.on 'broadcast', (message) ->
    # find the client and broadcast their message          the message with
    socket.broadcast.send(chat_data)                       user details
    # forwards message to all the clients except the one
    # that emitted the "broadcast" event

  socket.on 'addUser', (message) ->                        respond to ‘addUser’
    # create a new user and broadcast their existence      custom event & emit
    socket.broadcast.emit 'newUser', new_record
                                                           ‘newUser’ custom
client code “map.html”
client code
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>GeoChat :: FOSS4G-2011</title>
    <script src="/socket.io/socket.io.js"></script>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script>

    <script type="text/coffeescript">
      #...
    </script>
  </head>
  <body id="socket.io.demo" onload="">
    <h1>Socket.IO chat demo</h1>
    <input type="text" autofocus="autofocus"></input>
    <button type="button">publish</button>
    <button type="button">broadcast</button>
    <p>Status: <span id="status">Undefined</span></p>
  </body>
</html>
client code
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>GeoChat :: FOSS4G-2011</title>
    <script src="/socket.io/socket.io.js"></script>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script>

    <script type="text/coffeescript">
      #...
    </script>
  </head>
  <body id="socket.io.demo" onload="">
    <h1>Socket.IO chat demo</h1>
                                                      websockets
                                                    via socket.io.js
    <input type="text" autofocus="autofocus"></input>
    <button type="button">publish</button>
    <button type="button">broadcast</button>
    <p>Status: <span id="status">Undefined</span></p>
  </body>
</html>
client code
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>GeoChat :: FOSS4G-2011</title>
    <script src="/socket.io/socket.io.js"></script>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script>

    <script type="text/coffeescript">
      #...
    </script>
  </head>
  <body id="socket.io.demo" onload="">
                                                              write
    <h1>Socket.IO chat demo</h1>                          coffee-script
    <input type="text" autofocus="autofocus"></input>
    <button type="button">publish</button>                  in client
    <button type="button">broadcast</button>
    <p>Status: <span id="status">Undefined</span></p>
  </body>
</html>
client code
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>GeoChat :: FOSS4G-2011</title>
    <script src="/socket.io/socket.io.js"></script>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script>

    <script type="text/coffeescript">
      #...
    </script>
  </head>
  <body id="socket.io.demo" onload="">
    <h1>Socket.IO chat demo</h1>
    <input type="text" autofocus="autofocus"></input>
    <button type="button">publish</button>
    <button type="button">broadcast</button>
    <p>Status: <span id="status">Undefined</span></p>
  </body>
</html>
<script type="text/coffeescript">
                                                  Lets handle
  jQuery ($) ->                                  the messaging
    $status = $ '#status'
    socket = io.connect()

    socket.on 'connect', ->
      $status.text 'Connected'

    socket.on 'disconnect', ->
      $status.text 'Disconnected'

    socket.on 'reconnecting', (seconds) ->
      $status.text "Reconnecting in #{seconds} seconds"

    socket.on 'reconnect', ->
      $status.text 'Reconnected'

    socket.on 'reconnect_failed', ->
      $status.text 'Failed to reconnect'

    socket.on 'message', (message) ->
      $('<li>').text(message).appendTo $('#messages')

    $input = $ 'input'

    $('button').click ->
      socket.emit $(this).text(), $input.val()
      $input.val('').focus()

</script>
<script type="text/coffeescript">
                                                               Lets handle
               jQuery ($) ->                                  the messaging
                 $status = $ '#status'
                 socket = io.connect()

                 socket.on 'connect', ->
                   $status.text 'Connected'

                 socket.on 'disconnect', ->
                   $status.text 'Disconnected'
connection       socket.on 'reconnecting', (seconds) ->
  status           $status.text "Reconnecting in #{seconds} seconds"

  events         socket.on 'reconnect', ->
                   $status.text 'Reconnected'

                 socket.on 'reconnect_failed', ->
                   $status.text 'Failed to reconnect'

                 socket.on 'message', (message) ->
                   $('<li>').text(message).appendTo $('#messages')

                 $input = $ 'input'

                 $('button').click ->
                   socket.emit $(this).text(), $input.val()
                   $input.val('').focus()

             </script>
Emit events based on
                            client input




$input = $ 'input'

$('button').click ->
  socket.emit $(this).text(), $input.val()
  $input.val('').focus()
Emit events based on
                            client input




$input = $ 'input'

$('button').click ->
  socket.emit $(this).text(), $input.val()
  $input.val('').focus()
Lets add “Geo”
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src=
"http://jashkenas.github.com/coffee-script/extras/coffee-script.js">
</script>


map = new L.Map("map")

cloudmadeUrl =
  "http://{s}.tile.cloudmade.com/API-KEY/997/256/{z}/{x}/{y}.png"

cloudmade = new L.TileLayer(cloudmadeUrl, { maxZoom: 18 })

town = new L.LatLng(52.52267, 13.41293)

map.setView(town, 13).addLayer(cloudmade)
add HTML5                                side
                                                   note

          GeoLocation
get_location = ->
  navigator.geolocation.getCurrentPosition(show_map)

show_map = (position) ->
  latitude = position.coords.latitude
  longitude = position.coords.longitude
  markerLocation = new L.LatLng(latitude, longitude)
  marker = new L.Marker(markerLocation)
  map.addLayer(marker)
  map.setView(markerLocation, 16)

get_location()
Add form for new user
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>

geocoder = new google.maps.Geocoder()

$("#dialog").dialog({
   autoOpen: false
   buttons:
     "Add User": () ->
       geocoder.geocode { 'address': $( "#location" ).val() }, (results, status) ->
         if (status == google.maps.GeocoderStatus.OK)
            lat = results[0].geometry.location.lat()
            lng = results[0].geometry.location.lng()
            markerLocation = new L.LatLng(lat, lng)
            marker = new L.Marker(markerLocation)
            map.addLayer(marker)
            map.setView(markerLocation, 16)
            socket.emit 'addUser',
              "name": $( "#name" ).val()
              "location": $( "#location" ).val()
              "lat": lat
              "lng": lng
         else
            alert("Geocode was not successful for the following reason: " + status)
       $( this ).dialog( "close" )
     "Cancel": () ->
       $( this ).dialog( "close" )
})
Add form for new user
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>

geocoder = new google.maps.Geocoder()

$("#dialog").dialog({
   autoOpen: false
   buttons:
     "Add User": () ->
       geocoder.geocode { 'address': $( "#location" ).val() }, (results, status) ->
         if (status == google.maps.GeocoderStatus.OK)
            lat = results[0].geometry.location.lat()
            lng = results[0].geometry.location.lng()
            markerLocation = new L.LatLng(lat, lng)
            marker = new L.Marker(markerLocation)
            map.addLayer(marker)
            map.setView(markerLocation, 16)
            socket.emit 'addUser',
              "name": $( "#name" ).val()
              "location": $( "#location" ).val()
                                                                  Emit an ‘addUser’
              "lat": lat                                          event from client
              "lng": lng
         else
            alert("Geocode was not successful for the following reason: " + status)
       $( this ).dialog( "close" )
     "Cancel": () ->
       $( this ).dialog( "close" )
})
Add form for new user



<button id="create-user">Create new user</button>

<div id="dialog" title="Create new user">
  <p class="validateTips">All form fields are required.</p>

  <form>
  <fieldset>
    <label for="name">Name</label>
    <input type="text" name="name" id="name" />
    <label for="location">Location</label>
    <input type="text" name="location" id="location" value="" />
  </fieldset>
  </form>
</div>
{
    "name": "addUser",
    "args": [{
        "name": "Kashif",
        "email": "kashif.rasul@gmail.com",
        "location": "Torstraße 104, Berlin, Germany",
        "lat": 52.52959,
        "lng": 13.403620000000046
    }]
}
Persisting your data in SpacialDB
Create a PostGIS
database in the cloud
  ➜    spacialdb create

  {
      "name": "spacialdb1_shoaib_burq",
      "port": 9999,
      "username": "shoaib_burq",
      "host": "beta.spacialdb.com",
      "password": "1436c20eca"
  }
List your databases
➜       spacialdb list

[
    {
         "username": "shoaib_burq",
         "password": "1436c20eca",
         "host": "beta.spacialdb.com",
         "name": "heroku_shoaib_burq_dac1",
         "connection_string":
           "postgres://shoaib_burq:1436c20eca@beta.spacialdb.com:9999/heroku_shoaib_burq_dac1",
         "port": 9999
    },
    {
         "username": "shoaib_burq",
         "password": "1436c20eca",
         "host": "beta.spacialdb.com",
         "name": "spacialdb1_shoaib_burq",
         "connection_string":
           "postgres://shoaib_burq:1436c20eca@beta.spacialdb.com:9999/spacialdb1_shoaib_burq",
         "port": 9999
    }
]
Lets create a ‘Layer’ &
                 an ‘API’
➜    spacialdb layers:add characters --db spacialdb1_shoaib_burq

{
    "name": "characters",
    "srid": 4326,
    "database": "spacialdb1_shoaib_burq",
    "table_name": "characters",
    "user": "shoaib_burq",
    "api_url":
      "https://api.spacialdb.com/1/users/shoaib_burq/layers/characters",
    "acl": {
      "get": "37f9e107e4a112eddcdc1c31fd12832a",
      "delete": "1b62758e5e2948e6ef0cd0930516e5c7",
      "post": "f0698e2664c04e2fe03702f07392e878",
      "put": "7378dcc7eb8f628ffc702d5d27c8c7db"
    }
}
List your API layers
➜       spacialdb layers

[
    {
        "name": "characters",
        "srid": 4326,
        "database": "spacialdb1_shoaib_burq",
        "table_name": "characters",
        "user": "shoaib_burq",
        "api_url":
          "https://api.spacialdb.com/1/users/shoaib_burq/layers/characters",
        "acl": {
          "get": "37f9e107e4a112eddcdc1c31fd12832a",
          "delete": "1b62758e5e2948e6ef0cd0930516e5c7",
          "post": "f0698e2664c04e2fe03702f07392e878",
          "put": "7378dcc7eb8f628ffc702d5d27c8c7db"
        }
]
The socket server
socket.on 'addUser', (message) ->
  # post the data to SpacialDB and
  input =
    geometry:   { type: "Point", coordinates: [message.lng, message.lat]}
    properties: { name : message.name, location: message.location }
  post_url = "https://.../layers/characters?key=#{lyr_conf.acl.post}"
  req =
    method: "POST", uri: post_url, body: JSON.stringify(input)
    headers: { "Content-Type": "application/json" }

  request req, (error, response, body) ->
    if error
      console.log error
    else
      # then broadcast it
      rec = JSON.parse(body)
      get_url = "https://.../layers/characters/#{rec.id}?key=#{lyr_conf.acl.get}"
      req =
         method: "GET", uri: get_url
         headers: {"Content-Type": "application/json"}
      request req, (error, response, body) ->
         if error
           console.log error
         else
           socket.emit('newUser', body)
socket.on 'addUser', (message) ->
  # post the data to SpacialDB and
  input =
    geometry:   { type: "Point", coordinates: [message.lng, message.lat]}
    properties: { name : message.name, location: message.location }
  post_url = "https://.../layers/characters?key=#{lyr_conf.acl.post}"
  req =                                    {
    method: "POST", uri: post_url, body: JSON.stringify(input)
                                              "type": "Feature",
                                              "geometry": {
    headers: { "Content-Type": "application/json" }
                                                 "type": "Point",
                                                 "coordinates": [13.4113, 52.5234]
  request req, (error, response, body) ->      },
    if error                                   "properties": {
      console.log error                            "name": "Shoaib",
    else                                           "location": "Berlin, Germany"
                                               },
      # then broadcast it                      "id": 19
      rec = JSON.parse(body)                }
      get_url = "https://.../layers/characters/#{rec.id}?key=#{lyr_conf.acl.get}"
      req =
         method: "GET", uri: get_url
         headers: {"Content-Type": "application/json"}
      request req, (error, response, body) ->
         if error
           console.log error
         else
           socket.emit('newUser', body) # <== emits GeoJSON with the event
The socket client
socket.on 'newUser', (message) ->
  geojsonObj = JSON.parse(message)
  users.concat geojsonObj
  circleLocation = new L.LatLng(geojsonObj.geometry.coordinates[1],
                                geojsonObj.geometry.coordinates[0])


  circleOptions = {color: '#f03', opacity: 0.7}
  userLayer = new L.Circle(circleLocation, 500, circleOptions)
  userLayer.bindPopup(JSON.stringify(geojsonObj.properties))
  map.setView(circleLocation, 15).addLayer(userLayer)
Some exercises

•   Real-world role-playing
    games

•   Disaster response apps

•   Location based social
    networking
Deployment
Deployment

• Server with port 22 (ssh) and 80 (http)
• Capistrano deployment tool
• Bluepill, God, Supervisord, Monit, Upstart,
  etc. process management tool
➜ cat Capfile
load 'deploy' if respond_to?(:namespace) # cap2 differentiator
load 'config/deploy' # remove this line to skip loading any of the default tasks

➜ cat config/deploy.rb
...
set :use_sudo, true

set :bluepill, '/var/lib/gems/1.8/bin/bluepill'

namespace :deploy do
  task :start, :roles => :app, :except => { :no_release => true } do
    run "#{try_sudo :as => 'root'} #{bluepill} start #{application}"
  end

 ...

 task :create_deploy_to_with_sudo, :roles => :app do
   run "#{try_sudo :as => 'root'} mkdir -p #{deploy_to}"
 end

  task :npm_install, :roles => :app, :except => { :no_release => true } do
    run "cd #{release_path} && npm install"
  end
end

before 'deploy:setup', 'deploy:create_deploy_to_with_sudo'
after 'deploy:finalize_update', 'deploy:npm_install'
...

➜ cap deploy
➜ cat nodeapp.pill
Bluepill.application("app") do |app|
  app.process("node") do |process|
    process.working_dir = "/var/www/node/current"
    process.start_command = "/usr/bin/env NODE_ENV=production app_port=80 node server.js"
    process.pid_file = "/var/www/node/shared/pids/node.pid"
    process.stdout = process.stderr = "/var/www/node/shared/log/node.log"
    process.daemonize = true

   process.start_grace_time = 10.seconds
   process.stop_grace_time = 10.seconds
   process.restart_grace_time = 20.seconds

    process.checks :cpu_usage, :every => 10.seconds, :below => 5, :times => 3
    process.checks :mem_usage, :every => 10.seconds, :below => 100.megabytes, :times => [3,5]
  end
end

➜ bluepill load nodeapp.pill
Deploy to Heroku
➜ cat .gitignore
node_modules

➜ git init
➜ git add .
➜ git commit -m "init"

➜ heroku create --stack cedar
Creating sharp-rain-871... done, stack is cedar
http://sharp-rain-871.herokuapp.com/ | git@heroku.com:sharp-rain-871.git
Git remote heroku added

➜ git push heroku master
...

➜ heroku ps:scale web=1
Scaling web processes... done, now running 1
HTML5 Mobile

              iOS Android Blackberry IE Opera Firefox webOS Symbian

Geolocation   ✓      ✓        ✓      ✓   ✓      ✓      ✓
Web Sockets ✓4.2+           ✓6.1+        ✓      ✓
Web Workers                 ✓6.0+        ✓      ✓
 Web SQL
              ✓     ✓2.0+   ✓6.0+        ✓             ✓
  Store
By the end of this you
     will know...
• what Node.js is.
• how to write and deploy Node.js servers.
• a bit about coffeescript.
• about the Geostack for Node.js.
• see how to write and deploy a RT geo app.
Live Demo:
geochatapp.herokuapp.com

Contenu connexe

Tendances

ScalaDays Amsterdam - Don't block yourself
ScalaDays Amsterdam - Don't block yourselfScalaDays Amsterdam - Don't block yourself
ScalaDays Amsterdam - Don't block yourselfFlavio W. Brasil
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KThomas Fuchs
 
Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016Ben Lesh
 
Chloe and the Realtime Web
Chloe and the Realtime WebChloe and the Realtime Web
Chloe and the Realtime WebTrotter Cashion
 
The promise of asynchronous php
The promise of asynchronous phpThe promise of asynchronous php
The promise of asynchronous phpWim Godden
 
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017Ryan Weaver
 
Symfony: Your Next Microframework (SymfonyCon 2015)
Symfony: Your Next Microframework (SymfonyCon 2015)Symfony: Your Next Microframework (SymfonyCon 2015)
Symfony: Your Next Microframework (SymfonyCon 2015)Ryan Weaver
 
Django - 次の一歩 gumiStudy#3
Django - 次の一歩 gumiStudy#3Django - 次の一歩 gumiStudy#3
Django - 次の一歩 gumiStudy#3makoto tsuyuki
 
Web Crawling with NodeJS
Web Crawling with NodeJSWeb Crawling with NodeJS
Web Crawling with NodeJSSylvain Zimmer
 
Got Logs? Get Answers with Elasticsearch ELK - PuppetConf 2014
Got Logs? Get Answers with Elasticsearch ELK - PuppetConf 2014Got Logs? Get Answers with Elasticsearch ELK - PuppetConf 2014
Got Logs? Get Answers with Elasticsearch ELK - PuppetConf 2014Puppet
 
Automated release management with team city & octopusdeploy - NDC 2013
Automated release management with team city & octopusdeploy - NDC 2013Automated release management with team city & octopusdeploy - NDC 2013
Automated release management with team city & octopusdeploy - NDC 2013Kristoffer Deinoff
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
Riak with node.js
Riak with node.jsRiak with node.js
Riak with node.jsSean Cribbs
 
Get Real: Adventures in realtime web apps
Get Real: Adventures in realtime web appsGet Real: Adventures in realtime web apps
Get Real: Adventures in realtime web appsdaviddemello
 
History of jQuery
History of jQueryHistory of jQuery
History of jQueryjeresig
 
Stop Worrying & Love the SQL - A Case Study
Stop Worrying & Love the SQL - A Case StudyStop Worrying & Love the SQL - A Case Study
Stop Worrying & Love the SQL - A Case StudyAll Things Open
 

Tendances (20)

ScalaDays Amsterdam - Don't block yourself
ScalaDays Amsterdam - Don't block yourselfScalaDays Amsterdam - Don't block yourself
ScalaDays Amsterdam - Don't block yourself
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
 
Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016
 
Chloe and the Realtime Web
Chloe and the Realtime WebChloe and the Realtime Web
Chloe and the Realtime Web
 
Async Frontiers
Async FrontiersAsync Frontiers
Async Frontiers
 
ES6: The Awesome Parts
ES6: The Awesome PartsES6: The Awesome Parts
ES6: The Awesome Parts
 
The promise of asynchronous php
The promise of asynchronous phpThe promise of asynchronous php
The promise of asynchronous php
 
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
 
Symfony: Your Next Microframework (SymfonyCon 2015)
Symfony: Your Next Microframework (SymfonyCon 2015)Symfony: Your Next Microframework (SymfonyCon 2015)
Symfony: Your Next Microframework (SymfonyCon 2015)
 
Django - 次の一歩 gumiStudy#3
Django - 次の一歩 gumiStudy#3Django - 次の一歩 gumiStudy#3
Django - 次の一歩 gumiStudy#3
 
Web Crawling with NodeJS
Web Crawling with NodeJSWeb Crawling with NodeJS
Web Crawling with NodeJS
 
Got Logs? Get Answers with Elasticsearch ELK - PuppetConf 2014
Got Logs? Get Answers with Elasticsearch ELK - PuppetConf 2014Got Logs? Get Answers with Elasticsearch ELK - PuppetConf 2014
Got Logs? Get Answers with Elasticsearch ELK - PuppetConf 2014
 
Automated release management with team city & octopusdeploy - NDC 2013
Automated release management with team city & octopusdeploy - NDC 2013Automated release management with team city & octopusdeploy - NDC 2013
Automated release management with team city & octopusdeploy - NDC 2013
 
Not your Grandma's XQuery
Not your Grandma's XQueryNot your Grandma's XQuery
Not your Grandma's XQuery
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Riak with node.js
Riak with node.jsRiak with node.js
Riak with node.js
 
Get Real: Adventures in realtime web apps
Get Real: Adventures in realtime web appsGet Real: Adventures in realtime web apps
Get Real: Adventures in realtime web apps
 
A Gentle Introduction to Event Loops
A Gentle Introduction to Event LoopsA Gentle Introduction to Event Loops
A Gentle Introduction to Event Loops
 
History of jQuery
History of jQueryHistory of jQuery
History of jQuery
 
Stop Worrying & Love the SQL - A Case Study
Stop Worrying & Love the SQL - A Case StudyStop Worrying & Love the SQL - A Case Study
Stop Worrying & Love the SQL - A Case Study
 

Similaire à Async. and Realtime Geo Applications with Node.js

Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applicationsTom Croucher
 
Crafting Evolvable Api Responses
Crafting Evolvable Api ResponsesCrafting Evolvable Api Responses
Crafting Evolvable Api Responsesdarrelmiller71
 
Make BDD great again
Make BDD great againMake BDD great again
Make BDD great againYana Gusti
 
Event-driven IO server-side JavaScript environment based on V8 Engine
Event-driven IO server-side JavaScript environment based on V8 EngineEvent-driven IO server-side JavaScript environment based on V8 Engine
Event-driven IO server-side JavaScript environment based on V8 EngineRicardo Silva
 
JSLT: JSON querying and transformation
JSLT: JSON querying and transformationJSLT: JSON querying and transformation
JSLT: JSON querying and transformationLars Marius Garshol
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on AndroidSven Haiges
 
Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Remy Sharp
 
Is html5-ready-workshop-110727181512-phpapp02
Is html5-ready-workshop-110727181512-phpapp02Is html5-ready-workshop-110727181512-phpapp02
Is html5-ready-workshop-110727181512-phpapp02PL dream
 
React Native Evening
React Native EveningReact Native Evening
React Native EveningTroy Miles
 
Practical Use of MongoDB for Node.js
Practical Use of MongoDB for Node.jsPractical Use of MongoDB for Node.js
Practical Use of MongoDB for Node.jsasync_io
 
Socket applications
Socket applicationsSocket applications
Socket applicationsJoão Moura
 
Emerging Languages: A Tour of the Horizon
Emerging Languages: A Tour of the HorizonEmerging Languages: A Tour of the Horizon
Emerging Languages: A Tour of the HorizonAlex Payne
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVCAlive Kuo
 
Symfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsIgnacio Martín
 
JavaScript!
JavaScript!JavaScript!
JavaScript!RTigger
 

Similaire à Async. and Realtime Geo Applications with Node.js (20)

huhu
huhuhuhu
huhu
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
Node.js - A Quick Tour
Node.js - A Quick TourNode.js - A Quick Tour
Node.js - A Quick Tour
 
Node.js
Node.jsNode.js
Node.js
 
Crafting Evolvable Api Responses
Crafting Evolvable Api ResponsesCrafting Evolvable Api Responses
Crafting Evolvable Api Responses
 
Make BDD great again
Make BDD great againMake BDD great again
Make BDD great again
 
Event-driven IO server-side JavaScript environment based on V8 Engine
Event-driven IO server-side JavaScript environment based on V8 EngineEvent-driven IO server-side JavaScript environment based on V8 Engine
Event-driven IO server-side JavaScript environment based on V8 Engine
 
JSLT: JSON querying and transformation
JSLT: JSON querying and transformationJSLT: JSON querying and transformation
JSLT: JSON querying and transformation
 
CouchDB on Android
CouchDB on AndroidCouchDB on Android
CouchDB on Android
 
Nodejs - A quick tour (v6)
Nodejs - A quick tour (v6)Nodejs - A quick tour (v6)
Nodejs - A quick tour (v6)
 
Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)
 
Is html5-ready-workshop-110727181512-phpapp02
Is html5-ready-workshop-110727181512-phpapp02Is html5-ready-workshop-110727181512-phpapp02
Is html5-ready-workshop-110727181512-phpapp02
 
React Native Evening
React Native EveningReact Native Evening
React Native Evening
 
Practical Use of MongoDB for Node.js
Practical Use of MongoDB for Node.jsPractical Use of MongoDB for Node.js
Practical Use of MongoDB for Node.js
 
Socket applications
Socket applicationsSocket applications
Socket applications
 
Emerging Languages: A Tour of the Horizon
Emerging Languages: A Tour of the HorizonEmerging Languages: A Tour of the Horizon
Emerging Languages: A Tour of the Horizon
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
 
Symfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worlds
 
JavaScript!
JavaScript!JavaScript!
JavaScript!
 
"Javascript" por Tiago Rodrigues
"Javascript" por Tiago Rodrigues"Javascript" por Tiago Rodrigues
"Javascript" por Tiago Rodrigues
 

Plus de Shoaib 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
 
Where20 2008 Ruby Tutorial
Where20 2008 Ruby TutorialWhere20 2008 Ruby Tutorial
Where20 2008 Ruby TutorialShoaib Burq
 
learning interoperability from web2.0
learning interoperability from web2.0learning interoperability from web2.0
learning interoperability from web2.0Shoaib Burq
 

Plus de Shoaib Burq (11)

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
 
Where20 2008 Ruby Tutorial
Where20 2008 Ruby TutorialWhere20 2008 Ruby Tutorial
Where20 2008 Ruby Tutorial
 
learning interoperability from web2.0
learning interoperability from web2.0learning interoperability from web2.0
learning interoperability from web2.0
 
Rails Gis Hacks
Rails Gis HacksRails Gis Hacks
Rails Gis Hacks
 

Dernier

Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
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
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESmohitsingh558521
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
"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
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
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
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 

Dernier (20)

Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
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)
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
"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
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
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
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 

Async. and Realtime Geo Applications with Node.js

  • 1. Async & RT Geo apps with Node.js Kashif Rasul @krasul Shoaib Burq @sabman http://spacialdb.com (@SpacialDB)
  • 2. Checkout tomorrow • Techniques for distributed high-speed map tile generation using Mapnik & Node.js by Simon & Javier @ 13:00 Silver Room • The State of GeoCouch by Volker @ 15:00 Silver Room • The new GeoData tool set: CouchDB and NodeJS by Mick @ 15:30 Silver Room
  • 3. By the end of this you will know... • what Node.js is. • how to write and deploy Node.js servers. • a bit about coffeescript. • about the Geostack for Node.js. • see how to write and deploy a RT geo app.
  • 4. Build something useless but cool :-) By the end of this..
  • 5. By the end of this..
  • 7. Node.js • Event based server side JavaScript based on Google’s V8 Engine • Event based: good at handling a high number of concurrent connections • Trick: make network I/O non-blocking and make most file I/O asynchronous
  • 8. Blocking vs. non- blocking var result = db.query("select..."); // use result ... db.query("select..", function (result) { // use result ... });
  • 9. Non-blocking • Allows the app to return to the event loop immediately • But why isn’t everyone using event loops? • Require I/O to be non-blocking • Most libs are blocking • POSIX async I/O not available • db bindings have no support for async queries • async DNS resolution not standard etc.
  • 10. I/O is expensive • L1 cache 0.5 ns • Mutex lock/unlock 25 ns • Main mem. ref. 100 ns • Send 2K bytes over 1Gbps 20,000 ns • Read 1MB seq. from mem. 250,000 ns • Disk seek 10,000,000 ns • Read 1MB seq. from disk 20,000,000 ns
  • 11. Rough calc. • Read 30 images serially each 256K: • 30 seek × 10 ms/seek + 30 × 256K ÷ 30MB/s = 560 ms • Read them in parallel: • 10 ms/seek + 256K ÷ 30MB/s = 18 ms
  • 12. Speed is important • Amazon: 100ms extra causes 1% drop in sales • Google: 500ms extra causes 20% fewer searches • Yahoo: 400ms extra causes 5-9% more “Back” button clicks
  • 13. Node.js: components • V8 • libuv: networking layer for all platforms contains libev, libeio and c-ares • http_parser: parser for HTTP messages • openssl
  • 14. Node.js design • JS designed to be used with an event loop • Provide a purely evented non-blocking infrastructure to script highly concurrent servers • No direct I/O: there must be a callback
  • 15. Node.js: goals • Low-level API • Stream everything • Built-in support for important protocols like TCP, DNS, HTTP • Support HTTP features like Chuncked requests, Keep-alive, Hang requests etc.
  • 16. HelloWorld.js by kashif (@krasul)
  • 17. Node.js: install (Linux) ➜ wget http://nodejs.org/dist/node-v0.4.11.tar.gz --2011-09-01 15:38:07-- http://nodejs.org/dist/node-v0.4.11.tar.gz Resolving nodejs.org... 8.12.44.238 Connecting to nodejs.org|8.12.44.238|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 12419274 (12M) [application/octet-stream] Saving to: `node-v0.4.11.tar.gz' 100%[======================================>] 12,419,274 357K/s in 37s 2011-09-01 15:38:45 (332 KB/s) - `node-v0.4.11.tar.gz' saved [12419274/12419274] ➜ tar xzf node-v0.4.11.tar.gz ➜ cd node-v0.4.11 ➜ ./configure ... ➜ sudo make install ... ➜ node -v v0.4.11
  • 18. Node.js: install (OS X) ➜ /usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)" ... ➜ brew install node ... ➜ node -v v0.4.11 ➜ sudo port selfupdate ... ➜ sudo port install nodejs ... ➜ node -v v0.4.11
  • 19. Node.js: install (Win) • Grab the installer from http://nodejs.org/ dist/v0.5.5/node.exe • Unstable version • Can be built from source by using Cygwin but it’s a pain in the ass
  • 20. ➜ cat hello.js var http = require('http'); var net = require('net'); var c = 0; http.createServer(function(req, res) { c++; res.writeHead(200); res.end('hello worldn'); }).listen(8000); net.createServer(function(socket) { socket.write('connections: ' + c); socket.end(); }).listen(8001); ➜ node hello.js & [1] 1879 ➜ curl -i http://localhost:8000/ HTTP/1.1 200 OK Connection: keep-alive Transfer-Encoding: chunked hello world
  • 21. Node.js: Modules • File • Streams • Crypto • HTTP • DNS
  • 22. Node Package Manager ➜ curl http://npmjs.org/install.sh | sh ... npm@1.0.27 /usr/local/lib/node_modules/npm It worked ➜ npm -v 1.0.27 ➜ npm -g install coffee-script /usr/local/bin/coffee -> /usr/local/lib/node_modules/coffee-script/bin/coffee /usr/local/bin/cake -> /usr/local/lib/node_modules/coffee-script/bin/cake coffee-script@1.1.2 /usr/local/lib/node_modules/coffee-script ➜ export NODE_PATH=/usr/local/lib/node_modules
  • 23. ➜ cat package.json { "name": "pow" , "description": "Zero-configuration Rack server for Mac OS X" , "version": "0.3.2" , "author": "Sam Stephenson" , "repository": { "type": "git" , "url": "http://github.com/sstephenson/pow.git" } , "bin": { "pow": "./bin/pow" } , "main": "./lib/index.js" , "dependencies": { "async": "0.1.8" , "coffee-script": ">= 1.1.0" , "connect": ">= 1.0.3" , "http-proxy": ">= 0.5.8" , "log": ">= 1.1.1" , "nack": ">= 0.12.2" , "ndns": ">= 0.1.2" } , "bundleDependencies" : [ "ndns" ] , "devDependencies": { "eco": "= 1.0.3" , "nodeunit": ">= 0.5.0" } , "engines" : { "node" : ">=0.4.1" } , "scripts": { "install": "[[ -z "$BUNDLE_ONLY" ]] && cake install || true" , "test": "cake test" , "start": "cake start" , "stop": "cake stop" } }
  • 24. ➜ npm install > pow@0.3.2 install /Users/kashif/node/pow > [[ -z "$BUNDLE_ONLY" ]] && cake install || true *** Installing local configuration files... *** Installed http-proxy@0.6.6 ./node_modules/http-proxy ├── colors@0.5.0 ├── pkginfo@0.2.2 └── optimist@0.2.6
  • 25. CoffeeScript Quickie! by shoaib (@sabman)
  • 26. CoffeeScript a quick intro • Compiles *.coffee down to JavaScript *.js • Adds to the good parts of JS and gives familiarity, safety and readability • Usually need shorter code to generate the JS you need.
  • 27. music_style = "Indian" ask = "Do you like #{music_style} music?" var ask, music_style; music_style = "Indian"; ask = "Do you like " + music_style + " music?";
  • 28. # hashes (json objects) & arrays ragas = bhupali: aaroha: [ 'sa', 're','ga', 'pa', 'dha' ] avaroha: [ 'dha', 'pa','ga', 're', 'sa' ] durga: aaroha: [ 'sa', 're','ma', 'pa', 'dha', 'sa' ] avaroha: [ 'sa', 'dha', 'pa', 'ma', 're', 'sa'] var ragas; ragas = { bhupali: { aaroha: ['sa', 're', 'ga', 'pa', 'dha'], avaroha: ['dha', 'pa', 'ga', 're', 'sa'] }, durga: { aaroha: ['sa', 're', 'ma', 'pa', 'dha', 'sa'], avaroha: ['sa', 'dha', 'pa', 'ma', 're', 'sa'] } };
  • 29. # functions play = (raga) -> console.log(ragas[raga]['aaroha']) console.log(ragas[raga]['avaroha']) null play('bhupali') var play; play = function(raga) { console.log(ragas[raga]['aaroha']); console.log(ragas[raga]['avaroha']); return null; }; play('bhupali');
  • 30. # Classes class Event constructor: (@name, @time, @address, @lat, @lon) -> var Event; Event = (function() { function Event(name, time, address, lat, lon) { this.name = name; this.time = time; this.address = address; this.lat = lat; this.lon = lon; } return Event; })();
  • 31. class Event constructor: (@name, @time, @address, @lat, @lon) -> class Party extends Event @celeberation: true @kind: null hasBalloons: -> @kind == "Birthday" ? true : false
  • 32. Event is the name of the class and class Event constructor constructor: (@name, @time, @address, @lat, @lon) -> class Party extends Event @celeberation: true @kind: null hasBalloons: -> @kind == "Birthday" ? true : false
  • 33. Event is the name of the class and class Event constructor constructor: (@name, @time, @address, @lat, @lon) -> Inheritance via class Party extends Event extends @celeberation: true @kind: null hasBalloons: -> @kind == "Birthday" ? true : false
  • 34. Event is the name of the class and class Event constructor constructor: (@name, @time, @address, @lat, @lon) -> Inheritance via class Party extends Event extends @celeberation: true @kind: null hasBalloons: -> Inheritance via extends @kind == "Birthday" ? true : false @ defines an instance variable
  • 35. var Event, Party; var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }; Event = (function() { function Event(name, time, address, lat, lon) { this.name = name; this.time = time; this.address = address; this.lat = lat; this.lon = lon; } return Event; })(); Party = (function() { __extends(Party, Event); function Party() { Party.__super__.constructor.apply(this, arguments); } Party.celeberation = true; Party.kind = null; Party.prototype.hasBalloons = function() { var _ref; return (_ref = this.kind === "Birthday") != null ? _ref : { "true": false }; }; return Party; })();
  • 36. try it out event = new Party("Yo! The big party!", "Thu, 09/15/2011 6:30pm", "Hamilton Building, Denver Art Museum") console.log(event.hasBalloons()) # false event.kind = "Birthday" console.log(event.hasBalloons()) # true
  • 38. Node web development (Geo)stack by kashif (@krasul) flickr:darwinbell (cc)
  • 39. Node.js: Web Frameworks • Express: Sinatra inspired and built on Connect a middleware layer for Node.js • SocketStream: a fast Real-time web framework
  • 40. Express ➜ npm install -g express /usr/local/bin/express -> /usr/local/lib/node_modules/express/bin/express express@2.4.6 /usr/local/lib/node_modules/express ├── mime@1.2.2 ├── qs@0.3.1 └── connect@1.7.0 ➜ cat hello.coffee express = require('express') app = express.createServer() app.set('view engine', 'jade') # App Routes app.get '/', (request, response) -> response.send 'hello world' # Listen app.listen 3000 console.log "Express server listening on port %d", app.address().port
  • 41. Jade • HTML5 template engine based on HAML • Supports Express out of the box • Has filters for coffee-script
  • 42. <!DOCTYPE html> !!! 5 <html lang="en"> html(lang="en") <head> head <title>Jade</title> title= pageTitle <script type="text/javascript"> script(type='text/javascript') if (foo) { if (foo) { bar() bar() } } </script> body </head> h1 Jade - node template engine <body> #container <h1>Jade - node template engine</h1> - if (youAreUsingJade) <div id="container"> p You are amazing <p>You are amazing</p> - else </div> p Get on it! </body> </html>
  • 43. Database connection • PostgreSQL + PostGIS via node-pg • Sqlite3 + Spatialite via node-sqlite3 • CouchDB + GeoCouch via cradle • SpacialDB via HTTP Client libraries
  • 44. PostGIS ➜ cat package.json ... "dependencies": { ... "pg": "0.5.4" } ➜ cat server.coffee ... pg = require "pg" DATABASE_URL = "pg://postgres:testing@localhost/your_db" pg.connect DATABASE_URL, (err, client) -> query = client.query "SELECT * FROM your_table" query.on 'row', (row) -> console.log(JSON.stringify(row)) ...
  • 45. Spatialite ➜ cat package.json ... "dependencies": { ... "sqlite3": "2.0.16" } ➜ cat server.coffee ... fs = require "fs" sqlite3 = require "sqlite3" spatialite_ext = "/usr/local/lib/libspatialite.dylib" db = new sqlite3.Database "test.sqlite3" db.loadExtension spatialite_ext, (err) -> if err throw err db.each "SELECT...", (err, row) -> console.log row.id + ": " + row.info
  • 46. Geocouch ➜ cat package.json ... "dependencies": { ... "cradle": "0.5.5" } ➜ cat server.coffee ... cradle = require "cradle" db = new(cradle.Connection)().database('starwars') db.get "vader", (err, doc) -> console.log doc.name ...
  • 47. Redis ➜ cat package.json ... "dependencies": { ... "redis": "0.6.7" ,"hiredis": "0.1.12" } ➜ cat server.coffee ... redis = require("redis") client = redis.createClient client.on "error", (err) -> console.log("Error " + err); client.set "string key","string val" -> redis.print
  • 48. MongoDB • v2.0 just out with support for multi-location objects and polygon within searches • v1.4+ added support for 2d geo point indexes to do find near queries. • can query for points within a bounding box • v1.7+ added spherical distance query • use mongoose a MongoDB object tool for node.js
  • 49. Geo bindings • Geocoder • Geos/Proj4 • Mapnik
  • 50. Geocoder ➜ cat package.json ... "dependencies": { ... "geocoder": "0.0.5" } ➜ cat server.coffee ... geocoder = require("geocoder") geocoder.geocode "Denver, CO", (err, data) -> # do something with the data
  • 51. node-geos ➜ cat geos.coffee geonode = require 'geos' geom = new geonode.Geometry() geom.fromWkt("POINT(0 0)") console.log(geom.toWkt()) ➜ coffee geos.coffee POINT (0.0000000000000000 0.0000000000000000)
  • 52. node-mapnik • Techniques for distributed high-speed map tile generation using Mapnik & Node.js by Simon & Javier. Thursday @ 13:00 Silver Room
  • 53. HTTP Client • github.com/coolaj86/abstract-http-request: Higher level wrapper around the HTTP request system • github.com/danwrong/restler, github.com/ maxpert/Reston & github.com/pfleidi/node- wwwdude: REST client libraries • github.com/cloudhead/http-console: A useful interactive shell for HTTP requests
  • 54. by shoaib (@sabman) Building “Chat on a Map”
  • 55. Starting a node project package.json { "name": "example_2", "version": "0.0.0", "dependencies": { "socket.io" : "*", "coffee-script": "*", "request" : "*", "underscore": "*" } }
  • 56. Starting a node project package.json { "name": "example_2", minimum "version": "0.0.0", required "dependencies": { "socket.io" : "*", "coffee-script": "*", "request" : "*", "underscore": "*" } }
  • 57. Starting a node project package.json { "name": "example_2", "version": "0.0.0", "dependencies": { "socket.io" : "*", "coffee-script": "*", "request" : "*", "underscore": "*" } } $ npm install
  • 59. fs = require 'fs' # to read static files http = require 'http' # http server request = require 'request' _ = require 'underscore' server = http.createServer (req, res) -> fs.readFile "#{__dirname}/map.html", (err, data) -> res.writeHead 200, 'Content-Type': 'text/html' res.end data, 'utf8' server.listen(process.env.PORT || 3000) io = require('socket.io').listen server io.sockets.on 'connection', (socket) -> clientId = socket.id socket.on 'disconnect', (message) -> # find the client & delete it ... socket.on 'publish', (message) -> # ... socket.on 'broadcast', (message) -> # find the client and broadcast their message socket.broadcast.send(chat_data) # forwards message to all the clients except the one # that emitted the "broadcast" event socket.on 'addUser', (message) -> # create a new user and broadcast their existance socket.broadcast.emit 'newUser', new_record
  • 60. fs = require 'fs' # to read static files http = require 'http' # http server imports request = require 'request' _ = require 'underscore' server = http.createServer (req, res) -> fs.readFile "#{__dirname}/map.html", (err, data) -> res.writeHead 200, 'Content-Type': 'text/html' res.end data, 'utf8' server.listen(process.env.PORT || 3000) io = require('socket.io').listen server io.sockets.on 'connection', (socket) -> clientId = socket.id socket.on 'disconnect', (message) -> # find the client & delete it ... socket.on 'publish', (message) -> # ... socket.on 'broadcast', (message) -> # find the client and broadcast their message socket.broadcast.send(chat_data) # forwards message to all the clients except the one # that emitted the "broadcast" event socket.on 'addUser', (message) -> # create a new user and broadcast their existence socket.broadcast.emit 'newUser', new_record
  • 61. fs = require 'fs' # to read static files http = require 'http' # http server imports request = require 'request' _ = require 'underscore' server = http.createServer (req, res) -> fs.readFile "#{__dirname}/map.html", (err, data) -> serve static http via res.writeHead 200, 'Content-Type': 'text/html' node res.end data, 'utf8' server.listen(process.env.PORT || 3000) io = require('socket.io').listen server io.sockets.on 'connection', (socket) -> clientId = socket.id socket.on 'disconnect', (message) -> # find the client & delete it ... socket.on 'publish', (message) -> # ... socket.on 'broadcast', (message) -> # find the client and broadcast their message socket.broadcast.send(chat_data) # forwards message to all the clients except the one # that emitted the "broadcast" event socket.on 'addUser', (message) -> # create a new user and broadcast their existence socket.broadcast.emit 'newUser', new_record
  • 62. fs = require 'fs' # to read static files http = require 'http' # http server imports request = require 'request' _ = require 'underscore' server = http.createServer (req, res) -> fs.readFile "#{__dirname}/map.html", (err, data) -> serve static http via res.writeHead 200, 'Content-Type': 'text/html' node res.end data, 'utf8' server.listen(process.env.PORT || 3000) listening port io = require('socket.io').listen server io.sockets.on 'connection', (socket) -> clientId = socket.id socket.on 'disconnect', (message) -> # find the client & delete it ... socket.on 'publish', (message) -> # ... socket.on 'broadcast', (message) -> # find the client and broadcast their message socket.broadcast.send(chat_data) # forwards message to all the clients except the one # that emitted the "broadcast" event socket.on 'addUser', (message) -> # create a new user and broadcast their existence socket.broadcast.emit 'newUser', new_record
  • 63. fs = require 'fs' # to read static files http = require 'http' # http server imports request = require 'request' _ = require 'underscore' server = http.createServer (req, res) -> fs.readFile "#{__dirname}/map.html", (err, data) -> serve static http via res.writeHead 200, 'Content-Type': 'text/html' node res.end data, 'utf8' server.listen(process.env.PORT || 3000) listening port io = require('socket.io').listen server bind socket & store io.sockets.on 'connection', (socket) -> clientId = socket.id session socket.on 'disconnect', (message) -> # find the client & delete it ... socket.on 'publish', (message) -> # ... socket.on 'broadcast', (message) -> # find the client and broadcast their message socket.broadcast.send(chat_data) # forwards message to all the clients except the one # that emitted the "broadcast" event socket.on 'addUser', (message) -> # create a new user and broadcast their existence socket.broadcast.emit 'newUser', new_record
  • 64. fs = require 'fs' # to read static files http = require 'http' # http server imports request = require 'request' _ = require 'underscore' server = http.createServer (req, res) -> fs.readFile "#{__dirname}/map.html", (err, data) -> serve static http via res.writeHead 200, 'Content-Type': 'text/html' node res.end data, 'utf8' server.listen(process.env.PORT || 3000) listening port io = require('socket.io').listen server bind socket & store io.sockets.on 'connection', (socket) -> clientId = socket.id session socket.on 'disconnect', (message) -> disconnect event # find the client & delete it ... delete the user socket.on 'publish', (message) -> # ... socket.on 'broadcast', (message) -> # find the client and broadcast their message socket.broadcast.send(chat_data) # forwards message to all the clients except the one # that emitted the "broadcast" event socket.on 'addUser', (message) -> # create a new user and broadcast their existence socket.broadcast.emit 'newUser', new_record
  • 65. fs = require 'fs' # to read static files http = require 'http' # http server imports request = require 'request' _ = require 'underscore' server = http.createServer (req, res) -> fs.readFile "#{__dirname}/map.html", (err, data) -> serve static http via res.writeHead 200, 'Content-Type': 'text/html' node res.end data, 'utf8' server.listen(process.env.PORT || 3000) listening port io = require('socket.io').listen server bind socket & store io.sockets.on 'connection', (socket) -> clientId = socket.id session socket.on 'disconnect', (message) -> disconnect event # find the client & delete it ... delete the user socket.on 'publish', (message) -> # ... broadcast: verify the client and broadcast socket.on 'broadcast', (message) -> # find the client and broadcast their message the message with socket.broadcast.send(chat_data) user details # forwards message to all the clients except the one # that emitted the "broadcast" event socket.on 'addUser', (message) -> # create a new user and broadcast their existence socket.broadcast.emit 'newUser', new_record
  • 66. fs = require 'fs' # to read static files http = require 'http' # http server imports request = require 'request' _ = require 'underscore' server = http.createServer (req, res) -> fs.readFile "#{__dirname}/map.html", (err, data) -> serve static http via res.writeHead 200, 'Content-Type': 'text/html' node res.end data, 'utf8' server.listen(process.env.PORT || 3000) listening port io = require('socket.io').listen server bind socket & store io.sockets.on 'connection', (socket) -> clientId = socket.id session socket.on 'disconnect', (message) -> disconnect event # find the client & delete it ... delete the user socket.on 'publish', (message) -> # ... broadcast: verify the client and broadcast socket.on 'broadcast', (message) -> # find the client and broadcast their message the message with socket.broadcast.send(chat_data) user details # forwards message to all the clients except the one # that emitted the "broadcast" event socket.on 'addUser', (message) -> respond to ‘addUser’ # create a new user and broadcast their existence custom event & emit socket.broadcast.emit 'newUser', new_record ‘newUser’ custom
  • 68. client code <!DOCTYPE html> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>GeoChat :: FOSS4G-2011</title> <script src="/socket.io/socket.io.js"></script> <script src="http://code.jquery.com/jquery-latest.js"></script> <script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script> <script type="text/coffeescript"> #... </script> </head> <body id="socket.io.demo" onload=""> <h1>Socket.IO chat demo</h1> <input type="text" autofocus="autofocus"></input> <button type="button">publish</button> <button type="button">broadcast</button> <p>Status: <span id="status">Undefined</span></p> </body> </html>
  • 69. client code <!DOCTYPE html> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>GeoChat :: FOSS4G-2011</title> <script src="/socket.io/socket.io.js"></script> <script src="http://code.jquery.com/jquery-latest.js"></script> <script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script> <script type="text/coffeescript"> #... </script> </head> <body id="socket.io.demo" onload=""> <h1>Socket.IO chat demo</h1> websockets via socket.io.js <input type="text" autofocus="autofocus"></input> <button type="button">publish</button> <button type="button">broadcast</button> <p>Status: <span id="status">Undefined</span></p> </body> </html>
  • 70. client code <!DOCTYPE html> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>GeoChat :: FOSS4G-2011</title> <script src="/socket.io/socket.io.js"></script> <script src="http://code.jquery.com/jquery-latest.js"></script> <script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script> <script type="text/coffeescript"> #... </script> </head> <body id="socket.io.demo" onload=""> write <h1>Socket.IO chat demo</h1> coffee-script <input type="text" autofocus="autofocus"></input> <button type="button">publish</button> in client <button type="button">broadcast</button> <p>Status: <span id="status">Undefined</span></p> </body> </html>
  • 71. client code <!DOCTYPE html> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>GeoChat :: FOSS4G-2011</title> <script src="/socket.io/socket.io.js"></script> <script src="http://code.jquery.com/jquery-latest.js"></script> <script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script> <script type="text/coffeescript"> #... </script> </head> <body id="socket.io.demo" onload=""> <h1>Socket.IO chat demo</h1> <input type="text" autofocus="autofocus"></input> <button type="button">publish</button> <button type="button">broadcast</button> <p>Status: <span id="status">Undefined</span></p> </body> </html>
  • 72. <script type="text/coffeescript"> Lets handle jQuery ($) -> the messaging $status = $ '#status' socket = io.connect() socket.on 'connect', -> $status.text 'Connected' socket.on 'disconnect', -> $status.text 'Disconnected' socket.on 'reconnecting', (seconds) -> $status.text "Reconnecting in #{seconds} seconds" socket.on 'reconnect', -> $status.text 'Reconnected' socket.on 'reconnect_failed', -> $status.text 'Failed to reconnect' socket.on 'message', (message) -> $('<li>').text(message).appendTo $('#messages') $input = $ 'input' $('button').click -> socket.emit $(this).text(), $input.val() $input.val('').focus() </script>
  • 73. <script type="text/coffeescript"> Lets handle jQuery ($) -> the messaging $status = $ '#status' socket = io.connect() socket.on 'connect', -> $status.text 'Connected' socket.on 'disconnect', -> $status.text 'Disconnected' connection socket.on 'reconnecting', (seconds) -> status $status.text "Reconnecting in #{seconds} seconds" events socket.on 'reconnect', -> $status.text 'Reconnected' socket.on 'reconnect_failed', -> $status.text 'Failed to reconnect' socket.on 'message', (message) -> $('<li>').text(message).appendTo $('#messages') $input = $ 'input' $('button').click -> socket.emit $(this).text(), $input.val() $input.val('').focus() </script>
  • 74. Emit events based on client input $input = $ 'input' $('button').click -> socket.emit $(this).text(), $input.val() $input.val('').focus()
  • 75. Emit events based on client input $input = $ 'input' $('button').click -> socket.emit $(this).text(), $input.val() $input.val('').focus()
  • 76. Lets add “Geo” <script src="http://code.jquery.com/jquery-latest.js"></script> <script src= "http://jashkenas.github.com/coffee-script/extras/coffee-script.js"> </script> map = new L.Map("map") cloudmadeUrl = "http://{s}.tile.cloudmade.com/API-KEY/997/256/{z}/{x}/{y}.png" cloudmade = new L.TileLayer(cloudmadeUrl, { maxZoom: 18 }) town = new L.LatLng(52.52267, 13.41293) map.setView(town, 13).addLayer(cloudmade)
  • 77.
  • 78. add HTML5 side note GeoLocation get_location = -> navigator.geolocation.getCurrentPosition(show_map) show_map = (position) -> latitude = position.coords.latitude longitude = position.coords.longitude markerLocation = new L.LatLng(latitude, longitude) marker = new L.Marker(markerLocation) map.addLayer(marker) map.setView(markerLocation, 16) get_location()
  • 79.
  • 80.
  • 81. Add form for new user <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script> geocoder = new google.maps.Geocoder() $("#dialog").dialog({ autoOpen: false buttons: "Add User": () -> geocoder.geocode { 'address': $( "#location" ).val() }, (results, status) -> if (status == google.maps.GeocoderStatus.OK) lat = results[0].geometry.location.lat() lng = results[0].geometry.location.lng() markerLocation = new L.LatLng(lat, lng) marker = new L.Marker(markerLocation) map.addLayer(marker) map.setView(markerLocation, 16) socket.emit 'addUser', "name": $( "#name" ).val() "location": $( "#location" ).val() "lat": lat "lng": lng else alert("Geocode was not successful for the following reason: " + status) $( this ).dialog( "close" ) "Cancel": () -> $( this ).dialog( "close" ) })
  • 82. Add form for new user <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script> geocoder = new google.maps.Geocoder() $("#dialog").dialog({ autoOpen: false buttons: "Add User": () -> geocoder.geocode { 'address': $( "#location" ).val() }, (results, status) -> if (status == google.maps.GeocoderStatus.OK) lat = results[0].geometry.location.lat() lng = results[0].geometry.location.lng() markerLocation = new L.LatLng(lat, lng) marker = new L.Marker(markerLocation) map.addLayer(marker) map.setView(markerLocation, 16) socket.emit 'addUser', "name": $( "#name" ).val() "location": $( "#location" ).val() Emit an ‘addUser’ "lat": lat event from client "lng": lng else alert("Geocode was not successful for the following reason: " + status) $( this ).dialog( "close" ) "Cancel": () -> $( this ).dialog( "close" ) })
  • 83. Add form for new user <button id="create-user">Create new user</button> <div id="dialog" title="Create new user"> <p class="validateTips">All form fields are required.</p> <form> <fieldset> <label for="name">Name</label> <input type="text" name="name" id="name" /> <label for="location">Location</label> <input type="text" name="location" id="location" value="" /> </fieldset> </form> </div>
  • 84.
  • 85.
  • 86. { "name": "addUser", "args": [{ "name": "Kashif", "email": "kashif.rasul@gmail.com", "location": "Torstraße 104, Berlin, Germany", "lat": 52.52959, "lng": 13.403620000000046 }] }
  • 87. Persisting your data in SpacialDB
  • 88. Create a PostGIS database in the cloud ➜ spacialdb create { "name": "spacialdb1_shoaib_burq", "port": 9999, "username": "shoaib_burq", "host": "beta.spacialdb.com", "password": "1436c20eca" }
  • 89. List your databases ➜ spacialdb list [ { "username": "shoaib_burq", "password": "1436c20eca", "host": "beta.spacialdb.com", "name": "heroku_shoaib_burq_dac1", "connection_string": "postgres://shoaib_burq:1436c20eca@beta.spacialdb.com:9999/heroku_shoaib_burq_dac1", "port": 9999 }, { "username": "shoaib_burq", "password": "1436c20eca", "host": "beta.spacialdb.com", "name": "spacialdb1_shoaib_burq", "connection_string": "postgres://shoaib_burq:1436c20eca@beta.spacialdb.com:9999/spacialdb1_shoaib_burq", "port": 9999 } ]
  • 90. Lets create a ‘Layer’ & an ‘API’ ➜ spacialdb layers:add characters --db spacialdb1_shoaib_burq { "name": "characters", "srid": 4326, "database": "spacialdb1_shoaib_burq", "table_name": "characters", "user": "shoaib_burq", "api_url": "https://api.spacialdb.com/1/users/shoaib_burq/layers/characters", "acl": { "get": "37f9e107e4a112eddcdc1c31fd12832a", "delete": "1b62758e5e2948e6ef0cd0930516e5c7", "post": "f0698e2664c04e2fe03702f07392e878", "put": "7378dcc7eb8f628ffc702d5d27c8c7db" } }
  • 91. List your API layers ➜ spacialdb layers [ { "name": "characters", "srid": 4326, "database": "spacialdb1_shoaib_burq", "table_name": "characters", "user": "shoaib_burq", "api_url": "https://api.spacialdb.com/1/users/shoaib_burq/layers/characters", "acl": { "get": "37f9e107e4a112eddcdc1c31fd12832a", "delete": "1b62758e5e2948e6ef0cd0930516e5c7", "post": "f0698e2664c04e2fe03702f07392e878", "put": "7378dcc7eb8f628ffc702d5d27c8c7db" } ]
  • 93. socket.on 'addUser', (message) -> # post the data to SpacialDB and input = geometry: { type: "Point", coordinates: [message.lng, message.lat]} properties: { name : message.name, location: message.location } post_url = "https://.../layers/characters?key=#{lyr_conf.acl.post}" req = method: "POST", uri: post_url, body: JSON.stringify(input) headers: { "Content-Type": "application/json" } request req, (error, response, body) -> if error console.log error else # then broadcast it rec = JSON.parse(body) get_url = "https://.../layers/characters/#{rec.id}?key=#{lyr_conf.acl.get}" req = method: "GET", uri: get_url headers: {"Content-Type": "application/json"} request req, (error, response, body) -> if error console.log error else socket.emit('newUser', body)
  • 94. socket.on 'addUser', (message) -> # post the data to SpacialDB and input = geometry: { type: "Point", coordinates: [message.lng, message.lat]} properties: { name : message.name, location: message.location } post_url = "https://.../layers/characters?key=#{lyr_conf.acl.post}" req = { method: "POST", uri: post_url, body: JSON.stringify(input) "type": "Feature", "geometry": { headers: { "Content-Type": "application/json" } "type": "Point", "coordinates": [13.4113, 52.5234] request req, (error, response, body) -> }, if error "properties": { console.log error "name": "Shoaib", else "location": "Berlin, Germany" }, # then broadcast it "id": 19 rec = JSON.parse(body) } get_url = "https://.../layers/characters/#{rec.id}?key=#{lyr_conf.acl.get}" req = method: "GET", uri: get_url headers: {"Content-Type": "application/json"} request req, (error, response, body) -> if error console.log error else socket.emit('newUser', body) # <== emits GeoJSON with the event
  • 96. socket.on 'newUser', (message) -> geojsonObj = JSON.parse(message) users.concat geojsonObj circleLocation = new L.LatLng(geojsonObj.geometry.coordinates[1], geojsonObj.geometry.coordinates[0]) circleOptions = {color: '#f03', opacity: 0.7} userLayer = new L.Circle(circleLocation, 500, circleOptions) userLayer.bindPopup(JSON.stringify(geojsonObj.properties)) map.setView(circleLocation, 15).addLayer(userLayer)
  • 97.
  • 98.
  • 99.
  • 100. Some exercises • Real-world role-playing games • Disaster response apps • Location based social networking
  • 102. Deployment • Server with port 22 (ssh) and 80 (http) • Capistrano deployment tool • Bluepill, God, Supervisord, Monit, Upstart, etc. process management tool
  • 103. ➜ cat Capfile load 'deploy' if respond_to?(:namespace) # cap2 differentiator load 'config/deploy' # remove this line to skip loading any of the default tasks ➜ cat config/deploy.rb ... set :use_sudo, true set :bluepill, '/var/lib/gems/1.8/bin/bluepill' namespace :deploy do task :start, :roles => :app, :except => { :no_release => true } do run "#{try_sudo :as => 'root'} #{bluepill} start #{application}" end ... task :create_deploy_to_with_sudo, :roles => :app do run "#{try_sudo :as => 'root'} mkdir -p #{deploy_to}" end task :npm_install, :roles => :app, :except => { :no_release => true } do run "cd #{release_path} && npm install" end end before 'deploy:setup', 'deploy:create_deploy_to_with_sudo' after 'deploy:finalize_update', 'deploy:npm_install' ... ➜ cap deploy
  • 104. ➜ cat nodeapp.pill Bluepill.application("app") do |app| app.process("node") do |process| process.working_dir = "/var/www/node/current" process.start_command = "/usr/bin/env NODE_ENV=production app_port=80 node server.js" process.pid_file = "/var/www/node/shared/pids/node.pid" process.stdout = process.stderr = "/var/www/node/shared/log/node.log" process.daemonize = true process.start_grace_time = 10.seconds process.stop_grace_time = 10.seconds process.restart_grace_time = 20.seconds process.checks :cpu_usage, :every => 10.seconds, :below => 5, :times => 3 process.checks :mem_usage, :every => 10.seconds, :below => 100.megabytes, :times => [3,5] end end ➜ bluepill load nodeapp.pill
  • 105. Deploy to Heroku ➜ cat .gitignore node_modules ➜ git init ➜ git add . ➜ git commit -m "init" ➜ heroku create --stack cedar Creating sharp-rain-871... done, stack is cedar http://sharp-rain-871.herokuapp.com/ | git@heroku.com:sharp-rain-871.git Git remote heroku added ➜ git push heroku master ... ➜ heroku ps:scale web=1 Scaling web processes... done, now running 1
  • 106. HTML5 Mobile iOS Android Blackberry IE Opera Firefox webOS Symbian Geolocation ✓ ✓ ✓ ✓ ✓ ✓ ✓ Web Sockets ✓4.2+ ✓6.1+ ✓ ✓ Web Workers ✓6.0+ ✓ ✓ Web SQL ✓ ✓2.0+ ✓6.0+ ✓ ✓ Store
  • 107. By the end of this you will know... • what Node.js is. • how to write and deploy Node.js servers. • a bit about coffeescript. • about the Geostack for Node.js. • see how to write and deploy a RT geo app.
  • 108.

Notes de l'éditeur

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. What is CoffeeScript?\n\nCoffeeScript is a language that compiles down to JavaScript. This means that code in .coffee files are not interpreted at run time, but are compiled beforehand into .js files.\n\nCoffeeScript can be written for all implementations of JavaScript, whether it&apos;s for Node.js or the browser.\n\nWhy CoffeeScript?\n\nFirst of all, JavaScript got a bad wrap in the past for the copy-and-paste mentality of the 90&apos;s and early 2000&apos;s, where no one really knew what was going on. But JavaScript as a language really is great and has a lot to offer.\n\nSaying that, you can get lost in the curly brackets and semi-colons - It can get messy, and sometimes a tad unreadable.\n\nCoffeeScript gets around these issues by adding &quot;syntactic sugar&quot;, similar to what Ruby and Python have to offer, which helps us write less code faster, which is also easier to read. Even when it&apos;s compiled down, CoffeeScript performs as well as JavaScript, and in some instances the compiled JavaScript has even better performance over hand-written code, due to optimizations CoffeeScript uses that some people may not be aware of. You can actually learn a lot about JavaScript by looking at what CoffeeScript compiles down to, so we encourage you to do that, and ask why CoffeeScript has done things a particular way.\n\n\n
  27. CoffeeScript simply removes global variables. Behind the scenes, CoffeeScript wraps up scripts with anonymous function, keeping the local context, and automatically prefixes all variables assignments with var. For example, take this simple variable assignment in CoffeeScript:\n\nGlobal Variables are possible too.\nIn the root context, this is equal to the global object, and by creating a local exports variable you&amp;#x2019;re making it really obvious to anyone reading your code exactly which global variables a script is creating.\n\n\n
  28. Object literals can be specified exactly as in JavaScript. However, CoffeeScript makes it easier.\nLikewise, arrays can use whitespace instead of comma separators, although the square brackets ([]) are still required.\n\n
  29. CoffeeScript removes the rather verbose function statement, and replaces it with a thin arrow: &amp;#x2013;&gt;. Functions can be one liners, or indented on multiple lines. The last expression is implicitly returned. The following two code snippets are equivalent to the third JavaScript code snippet:\nYou can also specify arguments in a pair of parenthesis before the arrow.\nCoffeeScript supports default arguments too, for example:\n
  30. JavaScript doesn&apos;t have a class syntax, since it&apos;s a class-free, prototypal language. There are a variety of techniques for building classes in JavaScript, which are often verbose and/or wrong in subtle ways. CoffeeScript takes advantage of the fact that &quot;class&quot; is a reserved but unused word in JavaScript, allowing making it very easy to write classes with terse, readable code that implement a class pattern with inheritance. Here&apos;s an example from the Coffeescript site:\n\n
  31. In the example above, Event is the name of the class, and also the name of the resultant variable that you can use to create instances. Behind the scenes CoffeeScript is using construction functions, which means you can instantiate classes using the new operator.\n\nDefining constructors (functions that get invoked upon instantiation) is simple, just use a function named constructor. This is akin to using Ruby&apos;s initialize or Python&apos;s __init__.\n\n\nIn fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument&apos;s with @, CoffeeScript will automatically set the arguments as instance properties in the constructor. Indeed, this shorthand will also work for normal functions outside classes. The example below is equivalent to the last example, where we set the instance properties manually.\n\n
  32. In the example above, Event is the name of the class, and also the name of the resultant variable that you can use to create instances. Behind the scenes CoffeeScript is using construction functions, which means you can instantiate classes using the new operator.\n\nDefining constructors (functions that get invoked upon instantiation) is simple, just use a function named constructor. This is akin to using Ruby&apos;s initialize or Python&apos;s __init__.\n\nIn fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument&apos;s with @, CoffeeScript will automatically set the arguments as instance properties in the constructor. Indeed, this shorthand will also work for normal functions outside classes. The example below is equivalent to the last example, where we set the instance properties manually.\n\n
  33. In the example above, Event is the name of the class, and also the name of the resultant variable that you can use to create instances. Behind the scenes CoffeeScript is using construction functions, which means you can instantiate classes using the new operator.\n\nDefining constructors (functions that get invoked upon instantiation) is simple, just use a function named constructor. This is akin to using Ruby&apos;s initialize or Python&apos;s __init__.\n\n\nIn fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument&apos;s with @, CoffeeScript will automatically set the arguments as instance properties in the constructor. Indeed, this shorthand will also work for normal functions outside classes. The example below is equivalent to the last example, where we set the instance properties manually.\n\n
  34. In the example above, Event is the name of the class, and also the name of the resultant variable that you can use to create instances. Behind the scenes CoffeeScript is using construction functions, which means you can instantiate classes using the new operator.\n\nDefining constructors (functions that get invoked upon instantiation) is simple, just use a function named constructor. This is akin to using Ruby&apos;s initialize or Python&apos;s __init__.\n\n\nIn fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument&apos;s with @, CoffeeScript will automatically set the arguments as instance properties in the constructor. Indeed, this shorthand will also work for normal functions outside classes. The example below is equivalent to the last example, where we set the instance properties manually.\n\n
  35. Behind the scenes, CoffeeScript is using JavaScript&apos;s native prototype to create classes; adding a bit of syntactic sugar for static property inheritance and context persistence. As a developer all that&apos;s exposed to you is the class keyword.\n
  36. In the example above, Event is the name of the class, and also the name of the resultant variable that you can use to create instances. Behind the scenes CoffeeScript is using construction functions, which means you can instantiate classes using the new operator.\n\nDefining constructors (functions that get invoked upon instantiation) is simple, just use a function named constructor. This is akin to using Ruby&apos;s initialize or Python&apos;s __init__.\n\n\nIn fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument&apos;s with @, CoffeeScript will automatically set the arguments as instance properties in the constructor. Indeed, this shorthand will also work for normal functions outside classes. The example below is equivalent to the last example, where we set the instance properties manually.\n\n
  37. that was very quick but you can get more here.\n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. * Think of package.json as an application manifest for a node project. \n\n* Use by npm (node package manager) to describe you application and allow automatic installation of dependencies needed to run you application.\n\n* It describes verions, name and dependencies.\n\n* The most **important** things in your package.json are the *name* and *version* fields. Those are actually required, and your package won&apos;t install without them.\n\n### name \n\n* Check the [npm registry](http://registry.npmjs.org) if you are creating a public project.\n\n* Non-url-safe characters will be rejected.\n\n* We are going add socket.io as a dependency.\n
  56. * Think of package.json as an application manifest for a node project. \n\n* Use by npm (node package manager) to describe your application and allow automatic installation of dependencies needed to run your application.\n\n* It describes versions, name and dependencies.\n\n* The most **important** things in your package.json are the *name* and *version* fields. Those are actually required, and your package won&apos;t install without them.\n\n### name \n\n* Check the [npm registry](http://registry.npmjs.org) if you are creating a public project.\n\n* Non-url-safe characters will be rejected.\n\n* We are going add socket.io as a dependency.\n
  57. * It describes version, name and dependencies.\n* The most **important** things in your package.json are the *name* and *version* fields. Those are actually required, and your package won&apos;t install without them.\n### name \n* Check the [npm registry](http://registry.npmjs.org) if you are creating a public project.\n* Non-url-safe characters will be rejected.\n* We are going add socket.io as a dependency.\n
  58. \n
  59. Structure of the WebSocket Server\n
  60. Structure of the WebSocket Server\n
  61. Structure of the WebSocket Server\n
  62. Structure of the WebSocket Server\n
  63. Structure of the WebSocket Server\n
  64. Structure of the WebSocket Server\n
  65. Structure of the WebSocket Server\n
  66. Structure of the WebSocket Server\n
  67. \n
  68. \n
  69. Socket.IO is the last critical piece in this toolchain. Socket.IO abstracts away the pain of providing realtime connections to almost any browser. It will detect the capabilities of the client and use a variety of transports to facilitate a connection. All we have to do is handle the messaging.\n\nCoffeeScript can be used both on the server, as a command-line compiler based on Node.js/V8, or to run CoffeeScripts directly in the browser. This module contains the main entry functions for tokenizing, parsing, and compiling source CoffeeScript into JavaScript.\n\nIf included on a webpage, it will automatically sniff out, compile, and execute all scripts present in text/coffeescript tags.\n\n\n
  70. CoffeeScript can be used both on the server, as a command-line compiler based on Node.js/V8, or to run CoffeeScripts directly in the browser. This module contains the main entry functions for tokenizing, parsing, and compiling source CoffeeScript into JavaScript.\n\nIf included on a webpage, it will automatically sniff out, compile, and execute all scripts present in text/coffeescript tags.\n\n\n
  71. \n
  72. The first five events&amp;#x2009;&amp;#x2014;&amp;#x2009;&quot;connect&quot;, &quot;disconnect&quot;, &quot;reconnecting&quot;, &quot;reconnect&quot;, and &quot;reconnect_failed&quot;&amp;#x2009;&amp;#x2014;&amp;#x2009;are emitted by Socket.IO in response to changes in the connection status. We&apos;ve registered a handler for each in order to expose this information to users.\n\nWe&apos;ve also added handlers for &quot;message&quot; events. Our &quot;message&quot; handler will be called whenever the server receives a &quot;publish&quot; or &quot;broadcast&quot; event (io.sockets.send and socket.broadcast.send emit &quot;message&quot; events). \n\nAll that remains is to have the client emit appropriate custom events in response to input from users.\n\n
  73. The first five events&amp;#x2009;&amp;#x2014;&amp;#x2009;&quot;connect&quot;, &quot;disconnect&quot;, &quot;reconnecting&quot;, &quot;reconnect&quot;, and &quot;reconnect_failed&quot;&amp;#x2009;&amp;#x2014;&amp;#x2009;are emitted by Socket.IO in response to changes in the connection status. We&apos;ve registered a handler for each in order to expose this information to users.\n\nWe&apos;ve also added handlers for &quot;message&quot; events. Our &quot;message&quot; handler will be called whenever the server receives a &quot;publish&quot; or &quot;broadcast&quot; event (io.sockets.send and socket.broadcast.send emit &quot;message&quot; events). \n\nAll that remains is to have the client emit appropriate custom events in response to input from users.\n\n
  74. All that remains is to have the client emit appropriate custom events in response to input from users. This will give us the basics chat client.\n
  75. All that remains is to have the client emit appropriate custom events in response to input from users. This will give us the basics chat client.\n
  76. # put leaflet in your html\n\n1. leaflet.css, 2. Leaflet JavaScript, 3. mapdiv \n\ninit map\n1. create a map instance, \n2. set its view to the center of \n - Berlin (52.52267, 13.41293)\n - Denver (39.73904, -104.98482)\n3. add a pretty CloudMade tile layer to it\n\nThis will give us something with a map...\n\n
  77. This is what we see..\n
  78. Later I will throw up some ideas on what additional things you can build with the basic building block of an application that we describe in this tutorial. And in this vain I just want to mention that you can get user location via HTML5. Which may be useful for App ideas later.\n\nHTML5 GeoLocation enabled browsers will allow you to access geoLocation via a Javascript API. the simplist way to get location is using a &amp;#x201C;getCurrentPosition&amp;#x201D; method which takes a callback function. So lets create a function &amp;#x201C;get_location()&amp;#x201D; which simply uses the HTML5 GeoLocation API. We write a callback function too to create a marker with the new position. The callback function receives the device position as parameters.\n\n
  79. This prompts the user for their location in a non-modal way. This makes sure their browsing experience is not interrupted. They can ignore it if they which or say Allow. Allowing will prompt the callback function. Which...\n
  80. Adds a marker to the map\n
  81. \n
  82. \n
  83. \n
  84. \n
  85. \n
  86. Socket.IO is the last critical piece in this toolchain. Socket.IO abstracts away the pain of providing realtime connections to almost any browser. It will detect the capabilities of the client and use a variety of transports to facilitate a connection. All we have to do is handle the messaging.\n\n\n
  87. # Why do this? \nAt the end of this we will deploy our application... and we need a persistence storage with spatial capabilities out in the &amp;#x201C;Cloud&amp;#x201D;. We can do this with SpacialDB very easily. \n\nAlthough SpaciaDB&apos;s REST API lets you push data directly to the database, sometimes you want to do things on the server before saving to the data. One example would be creating a Real-time websocket powered appilication. Where the a websocket client communicates with the server and the server manages the message broacasting.\n\nSo lets get started. First you need an account on SpacialDB.\n\n# I am signed up lets get a spatial database\n\nAssuming you have the spcialdb gem installed and are signed up to spacialdb (if not its easy `spacaildb signup`). \n\n
  88. \n
  89. \n
  90. \n
  91. \n
  92. On the server side we want to listen for the `addUser` event. In response to the event we want to store the new user in SpacialDB and then emit a `newUser` event with the saved user&apos;s details back to the client.\n\nBack at the client we will have to listen for the `newUser` event and take appropriate action.\n\n
  93. \n
  94. \n
  95. \n
  96. \nBack at the client we receive the broadcast and parse the message to find the user&apos;s name, location and the message. We can then display that in a popup.\n\n\n
  97. \n
  98. \n
  99. \n
  100. \n
  101. Lets now look at ways to deploy our application live\n
  102. \n
  103. \n
  104. \n
  105. \n
  106. \n
  107. \n
  108. \n
  109. \n