Skip to content
Browse files

real-time sparkfun.com visitors plotted on nasa's BlueMarble imagery

  • Loading branch information...
1 parent 1484bed commit 6b9aa7f24d0a5166071e33cc5bd10f7d14d70672 @benlemasurier committed May 27, 2011
View
20 example/css/map.css
@@ -0,0 +1,20 @@
+body {
+ background-color: #111;
+ color: #fff;
+ font-family: helvetica, arial;
+ margin: 30px;
+}
+
+h1 {
+ font-size: 72px;
+ font-weight: bold;
+ margin-bottom: 0;
+}
+
+#visualization {
+ width: 100%;
+ margin-top: 0;
+}
+
+#size_up { cursor: pointer; }
+#size_down { cursor: pointer; }
View
BIN example/images/world_map.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
2 example/js/blode_graph.js
@@ -6,7 +6,7 @@ var BlodeGraph = Class.create({
this._bar_padding = 1;
this._tick = 100; // 100ms
this._log_buffer = [];
- this._socket = new BlodeSocket().listen('localhost', '8008');
+ this._socket = new BlodeSocket().listen('10.10.10.2', '8008');
this.show_legend = false;
// color scheme
View
131 example/js/blode_map.js
@@ -0,0 +1,131 @@
+var Point = Class.create({
+ initialize: function(x, y) {
+ this.x = x;
+ this.y = y;
+ },
+
+ toString: function() {
+ return "(" + this.x + ", " + this.y + ")";
+ }
+});
+
+// Blode message graphing
+var BlodeMap = Class.create({
+ initialize: function(container_id) {
+ this._container = $(container_id);
+ this._socket = new BlodeSocket().listen('10.10.10.2', '8008');
+ this._geo_url = "http://ben-dev/geoip/lookup.php?jsonp=window.blode_map.log_visitor&ip=";
+ this._map_image = "images/world_map.jpg";
+ this._background = this.create_canvas(this._container, 0);
+ this._foreground = this.create_canvas(this._container, 1);
+
+ this._point_size = 2;
+ this._point_buffer = [];
+ this._point_buffer_size = 1000;
+ this._point_color = "rgba(255, 0, 0, 0.5)";
+
+ // prime the point buffer
+ this.initialize_point_buffer();
+
+ this.render_background();
+
+ this.listen();
+ },
+
+ initialize_point_buffer: function() {
+ this._point_buffer = [];
+
+ for(var i = 0; i < this._point_buffer_size; i++)
+ this._point_buffer[i] = new Point(0, 0);
+ },
+
+ listen: function() {
+ this._socket.observe('blode:message', function(data) {
+ var message = data.memo.message();
+ if(message.include('app.run')) {
+ var ip = message.evalJSON().remote_addr;
+ new Ajax.JSONRequest(this._geo_url + ip, {
+ });
+ }
+ }.bind(this));
+ },
+
+ create_canvas: function(container, layer) {
+ var canvas = new Element('canvas'),
+ layer = layer || 0;
+
+ // each canvas (layer) is the exact same size as its parent container
+ canvas.style.position = "absolute";
+ canvas.clonePosition(container);
+ canvas.width = container.getWidth();
+ canvas.height = container.getWidth() / 2;
+ canvas.style.zIndex = layer;
+
+ container.insert(canvas);
+
+ return canvas;
+ },
+
+ render_background: function() {
+ // The background layer consists of each bar's background color
+ var context = this._background.getContext('2d');
+
+ // clear layer
+ context.clearRect(0, 0, this._background.width, this._background.height);
+
+ var context = this._background.getContext('2d');
+ var img = new Image();
+ img.src = this._map_image;
+ img.onload = function() {
+ context.drawImage(img, 0, 0, img.width, img.height, 0, 0, context.canvas.width, context.canvas.height);
+ }.bind(this);
+ },
+
+ render_foreground: function() {
+ var context = this._foreground.getContext('2d');
+
+ // clear layer
+ context.clearRect(0, 0, this._foreground.width, this._foreground.height);
+
+ // set layer color
+ context.fillStyle = this._point_color;
+
+ for(i = 0, j = this._point_buffer_size; i < j; i++) {
+ if(this._point_buffer[i].x != 0 && this._point_buffer[i].y != 0) {
+ var point_size = (i == 0) ? this._point_size * 2 : this._point_size;
+ context.beginPath();
+ context.arc(this._point_buffer[i].x - (point_size / 2),
+ this._point_buffer[i].y - (point_size / 2),
+ point_size,
+ 0, Math.PI * 2, true);
+ context.closePath();
+ context.fill();
+ }
+ }
+ },
+
+ log_visitor: function(geo_data) {
+ var context = this._foreground.getContext('2d');
+ var minX = -180,
+ minY = -90,
+ maxX = 180,
+ maxY = 90;
+
+ var lon = geo_data.longitude,
+ lat = geo_data.latitude;
+
+ if(lon == 0 || lat == 0)
+ return;
+
+ var x = (this._foreground.width * (lon - minX)) / (maxX - minX),
+ y = this._foreground.height - ((this._foreground.height * (lat - minY)) / (maxY - minY));
+
+ // insert the new piont into the buffer
+ this._point_buffer.unshift(new Point(x, y));
+
+ // remove the last item from the buffer
+ this._point_buffer = this._point_buffer.slice(0, -1);
+
+ this.render_foreground();
+ }
+});
View
115 example/js/jsonp.js
@@ -0,0 +1,115 @@
+Ajax.JSONRequest = Class.create(Ajax.Base, (function() {
+ var id = 0, head = document.getElementsByTagName('head')[0] || document.body;
+ return {
+ initialize: function($super, url, options) {
+ $super(options);
+ this.options.url = url;
+ this.options.callbackParamName = this.options.callbackParamName || 'callback';
+ this.options.timeout = this.options.timeout || 10; // Default timeout: 10 seconds
+ this.options.invokeImmediately = (!Object.isUndefined(this.options.invokeImmediately)) ? this.options.invokeImmediately : true ;
+
+ if (!Object.isUndefined(this.options.parameters) && Object.isString(this.options.parameters)) {
+ this.options.parameters = this.options.parameters.toQueryParams();
+ }
+
+ if (this.options.invokeImmediately) {
+ this.request();
+ }
+ },
+
+ /**
+ * Ajax.JSONRequest#_cleanup() -> undefined
+ * Cleans up after the request
+ **/
+ _cleanup: function() {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ this.timeout = null;
+ }
+ if (this.transport && Object.isElement(this.transport)) {
+ this.transport.remove();
+ this.transport = null;
+ }
+ },
+
+ /**
+ * Ajax.JSONRequest#request() -> undefined
+ * Invokes the JSON-P request lifecycle
+ **/
+ request: function() {
+
+ // Define local vars
+ var response = new Ajax.JSONResponse(this);
+ var key = this.options.callbackParamName,
+ name = '_prototypeJSONPCallback_' + (id++),
+ complete = function() {
+ if (Object.isFunction(this.options.onComplete)) {
+ this.options.onComplete.call(this, response);
+ }
+ Ajax.Responders.dispatch('onComplete', this, response);
+ }.bind(this);
+
+ // Add callback as a parameter and build request URL
+ this.options.parameters[key] = name;
+ var url = this.options.url + ((this.options.url.include('?') ? '&' : '?') + Object.toQueryString(this.options.parameters));
+
+ // Define callback function
+ window[name] = function(json) {
+ this._cleanup(); // Garbage collection
+ window[name] = undefined;
+
+ response.status = 200;
+ response.statusText = "OK";
+ response.setResponseContent(json);
+
+ if (Object.isFunction(this.options.onSuccess)) {
+ this.options.onSuccess.call(this, response);
+ }
+ Ajax.Responders.dispatch('onSuccess', this, response);
+
+ complete();
+
+ }.bind(this);
+
+ this.transport = new Element('script', { type: 'text/javascript', src: url });
+
+ if (Object.isFunction(this.options.onCreate)) {
+ this.options.onCreate.call(this, response);
+ }
+ Ajax.Responders.dispatch('onCreate', this);
+
+ head.appendChild(this.transport);
+
+ this.timeout = setTimeout(function() {
+ this._cleanup();
+ window[name] = Prototype.emptyFunction;
+ if (Object.isFunction(this.options.onFailure)) {
+ response.status = 504;
+ response.statusText = "Gateway Timeout";
+ this.options.onFailure.call(this, response);
+ }
+ complete();
+ }.bind(this), this.options.timeout * 1000);
+ },
+ toString: function() { return "[object Ajax.JSONRequest]"; }
+ };
+})());
+
+Ajax.JSONResponse = Class.create({
+ initialize: function(request) {
+ this.request = request;
+ },
+ request: undefined,
+ status: 0,
+ statusText: '',
+ responseJSON: undefined,
+ responseText: undefined,
+ setResponseContent: function(json) {
+ this.responseJSON = json;
+ this.responseText = Object.toJSON(json);
+ },
+ getTransport: function() {
+ if (this.request) return this.request.transport;
+ },
+ toString: function() { return "[object Ajax.JSONResponse]"; }
+});
View
39 example/map.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Blode - hits are pretties</title>
+ <link rel="stylesheet" type="text/css" media="screen" href="css/map.css" />
+ <script src="js/prototype.js" type="text/javascript"></script>
+ <script src="js/jsonp.js" type="text/javascript"></script>
+ <script src="js/blode_helpers.js" type="text/javascript"></script>
+ <script src="js/blode_message.js" type="text/javascript"></script>
+ <script src="js/blode_socket.js" type="text/javascript"></script>
+ <script src="js/blode_map.js" type="text/javascript"></script>
+ <script language="javascript">
+ window.onload = function() {
+ $('visualization').setStyle({ height: $('visualization').getWidth() / 2 + "px"});
+ window.blode_map = new BlodeMap('visualization');
+
+ $('size_up').onclick = function() {
+ if(window.blode_map._point_size < 50)
+ window.blode_map._point_size++;
+ }
+
+ $('size_down').onclick = function() {
+ if(window.blode_map._point_size > 1)
+ window.blode_map._point_size--;
+ }
+ };
+ </script>
+ </head>
+ <body>
+ <h1>real-time visitors</h1>
+ <p id="visualization">
+ </p>
+ <p id="controls">
+ <div class="point_size">
+ point size: [ <span id="size_up">+</span> / <span id="size_down">-</span> ]
+ </div>
+ </p>
+ </body>
+</html>
View
4 example/watch.html
@@ -1,8 +1,8 @@
<html>
<head>
<title>test.</title>
- <script src="prototype.js" type="text/javascript"></script>
- <script src="scriptaculous.js?load=effects" type="text/javascript"></script>
+ <script src="js/prototype.js" type="text/javascript"></script>
+ <script src="js/scriptaculous.js?load=effects" type="text/javascript"></script>
<style type="text/css">
body {
font-family: helvetica, arial;
View
4 example/websocket.html
@@ -1,8 +1,8 @@
<html>
<head>
<title>test.</title>
- <script src="prototype.js" type="text/javascript"></script>
- <script src="scriptaculous.js?load=effects" type="text/javascript"></script>
+ <script src="js/prototype.js" type="text/javascript"></script>
+ <script src="js/scriptaculous.js?load=effects" type="text/javascript"></script>
<style type="text/css">
body {
font-family: helvetica, arial;

0 comments on commit 6b9aa7f

Please sign in to comment.
Something went wrong with that request. Please try again.