• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Async. and Realtime Geo Applications with Node.js
 

Async. and Realtime Geo Applications with Node.js

on

  • 14,693 views

 

Statistics

Views

Total Views
14,693
Views on SlideShare
14,167
Embed Views
526

Actions

Likes
29
Downloads
333
Comments
2

8 Embeds 526

http://blog.spacialdb.com 421
http://lanyrd.com 45
http://posterous.com 25
http://spacialdb.posterous.com 18
https://twitter.com 9
http://www.knowlead.co.za 5
http://www.genesiscontentnetwork.com 2
http://gcn.guidone.local 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

12 of 2 previous next

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • 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'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's and early 2000'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 "syntactic sugar", 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'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
  • 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’re making it really obvious to anyone reading your code exactly which global variables a script is creating.\n\n\n
  • 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
  • CoffeeScript removes the rather verbose function statement, and replaces it with a thin arrow: –>. 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
  • JavaScript doesn't have a class syntax, since it'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 "class" 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's an example from the Coffeescript site:\n\n
  • 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's initialize or Python's __init__.\n\n\nIn fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument'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
  • 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's initialize or Python's __init__.\n\nIn fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument'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
  • 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's initialize or Python's __init__.\n\n\nIn fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument'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
  • 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's initialize or Python's __init__.\n\n\nIn fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument'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
  • Behind the scenes, CoffeeScript is using JavaScript's native prototype to create classes; adding a bit of syntactic sugar for static property inheritance and context persistence. As a developer all that's exposed to you is the class keyword.\n
  • 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's initialize or Python's __init__.\n\n\nIn fact, CoffeeScript provides a shorthand for the common pattern of setting instance properties. By prefixing argument'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
  • that was very quick but you can get more here.\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • * 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'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
  • * 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'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
  • * 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'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
  • \n
  • Structure of the WebSocket Server\n
  • Structure of the WebSocket Server\n
  • Structure of the WebSocket Server\n
  • Structure of the WebSocket Server\n
  • Structure of the WebSocket Server\n
  • Structure of the WebSocket Server\n
  • Structure of the WebSocket Server\n
  • Structure of the WebSocket Server\n
  • \n
  • \n
  • 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
  • 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
  • \n
  • The first five events — "connect", "disconnect", "reconnecting", "reconnect", and "reconnect_failed" — are emitted by Socket.IO in response to changes in the connection status. We've registered a handler for each in order to expose this information to users.\n\nWe've also added handlers for "message" events. Our "message" handler will be called whenever the server receives a "publish" or "broadcast" event (io.sockets.send and socket.broadcast.send emit "message" events). \n\nAll that remains is to have the client emit appropriate custom events in response to input from users.\n\n
  • The first five events — "connect", "disconnect", "reconnecting", "reconnect", and "reconnect_failed" — are emitted by Socket.IO in response to changes in the connection status. We've registered a handler for each in order to expose this information to users.\n\nWe've also added handlers for "message" events. Our "message" handler will be called whenever the server receives a "publish" or "broadcast" event (io.sockets.send and socket.broadcast.send emit "message" events). \n\nAll that remains is to have the client emit appropriate custom events in response to input from users.\n\n
  • 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
  • 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
  • # 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
  • This is what we see..\n
  • 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 “getCurrentPosition” method which takes a callback function. So lets create a function “get_location()” 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
  • 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
  • Adds a marker to the map\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • 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
  • # 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 “Cloud”. We can do this with SpacialDB very easily. \n\nAlthough SpaciaDB'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
  • \n
  • \n
  • \n
  • \n
  • 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'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
  • \n
  • \n
  • \n
  • \nBack at the client we receive the broadcast and parse the message to find the user's name, location and the message. We can then display that in a popup.\n\n\n
  • \n
  • \n
  • \n
  • \n
  • Lets now look at ways to deploy our application live\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

Async. and Realtime Geo Applications with Node.js Async. and Realtime Geo Applications with Node.js Presentation Transcript

  • Async & RT Geoapps 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.gzResolving nodejs.org... 8.12.44.238Connecting to nodejs.org|8.12.44.238|:80... connected.HTTP request sent, awaiting response... 200 OKLength: 12419274 (12M) [application/octet-stream]Saving to: `node-v0.4.11.tar.gz100%[======================================>] 12,419,274 357K/s in 37s2011-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 -vv0.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.jsvar 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 OKConnection: keep-aliveTransfer-Encoding: chunkedhello 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/npmIt worked➜ npm -v1.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/cakecoffee-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...*** Installedhttp-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) & arraysragas = 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] } };
  • # functionsplay = (raga) -> console.log(ragas[raga][aaroha]) console.log(ragas[raga][avaroha]) nullplay(bhupali)var play;play = function(raga) { console.log(ragas[raga][aaroha]); console.log(ragas[raga][avaroha]); return null;};play(bhupali);
  • # Classesclass 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 outevent = new Party("Yo! The big party!", "Thu, 09/15/2011 6:30pm", "Hamilton Building, Denver Art Museum")console.log(event.hasBalloons())# falseevent.kind = "Birthday"console.log(event.hasBalloons())# true
  • http://screencasts.org/episodes/introduction-to-coffeescript
  • Node webdevelopment (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/expressexpress@2.4.6 /usr/local/lib/node_modules/express├── mime@1.2.2├── qs@0.3.1└── connect@1.7.0➜ cat hello.coffeeexpress = require(express)app = express.createServer()app.set(view engine, jade)# App Routesapp.get /, (request, response) -> response.send hello world# Listenapp.listen 3000console.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 errdb.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.createClientclient.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.coffeegeonode = require geosgeom = new geonode.Geometry()geom.fromWkt("POINT(0 0)")console.log(geom.toWkt())➜ coffee geos.coffeePOINT (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.coffeeLets write the GeoChat server
  • fs = require fs # to read static fileshttp = require http # http serverrequest = require request_ = require underscoreserver = http.createServer (req, res) -> fs.readFile "#{__dirname}/map.html", (err, data) -> res.writeHead 200, Content-Type: text/html res.end data, utf8server.listen(process.env.PORT || 3000)io = require(socket.io).listen serverio.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 fileshttp = require http # http server importsrequest = require request_ = require underscoreserver = http.createServer (req, res) -> fs.readFile "#{__dirname}/map.html", (err, data) -> res.writeHead 200, Content-Type: text/html res.end data, utf8server.listen(process.env.PORT || 3000)io = require(socket.io).listen serverio.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 fileshttp = require http # http server importsrequest = require request_ = require underscoreserver = 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, utf8server.listen(process.env.PORT || 3000)io = require(socket.io).listen serverio.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 fileshttp = require http # http server importsrequest = require request_ = require underscoreserver = 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, utf8server.listen(process.env.PORT || 3000) listening portio = require(socket.io).listen serverio.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 fileshttp = require http # http server importsrequest = require request_ = require underscoreserver = 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, utf8server.listen(process.env.PORT || 3000) listening portio = require(socket.io).listen server bind socket & storeio.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 fileshttp = require http # http server importsrequest = require request_ = require underscoreserver = 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, utf8server.listen(process.env.PORT || 3000) listening portio = require(socket.io).listen server bind socket & storeio.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 fileshttp = require http # http server importsrequest = require request_ = require underscoreserver = 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, utf8server.listen(process.env.PORT || 3000) listening portio = require(socket.io).listen server bind socket & storeio.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 fileshttp = require http # http server importsrequest = require request_ = require underscoreserver = 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, utf8server.listen(process.env.PORT || 3000) listening portio = require(socket.io).listen server bind socket & storeio.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 Disconnectedconnection 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 GeoLocationget_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 PostGISdatabase 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 Capfileload deploy if respond_to?(:namespace) # cap2 differentiatorload config/deploy # remove this line to skip loading any of the default tasks➜ cat config/deploy.rb...set :use_sudo, trueset :bluepill, /var/lib/gems/1.8/bin/bluepillnamespace :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" endendbefore deploy:setup, deploy:create_deploy_to_with_sudoafter deploy:finalize_update, deploy:npm_install...➜ cap deploy
  • ➜ cat nodeapp.pillBluepill.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] endend➜ bluepill load nodeapp.pill
  • Deploy to Heroku➜ cat .gitignorenode_modules➜ git init➜ git add .➜ git commit -m "init"➜ heroku create --stack cedarCreating sharp-rain-871... done, stack is cedarhttp://sharp-rain-871.herokuapp.com/ | git@heroku.com:sharp-rain-871.gitGit remote heroku added➜ git push heroku master...➜ heroku ps:scale web=1Scaling web processes... done, now running 1
  • HTML5 Mobile iOS Android Blackberry IE Opera Firefox webOS SymbianGeolocation ✓ ✓ ✓ ✓ ✓ ✓ ✓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