Permalink
Browse files

Major cleanup and new icons/tracking

  • Loading branch information...
1 parent 854ce52 commit b9f68c850862a672ba7a877cc2bdc9a85f37f1a3 @ghitchens committed Jul 24, 2011
Showing with 335 additions and 157 deletions.
  1. +13 −9 README.md
  2. +2 −1 web/index.html
  3. +232 −0 web/js/aistoy.bak
  4. +88 −128 web/js/aistoy.js
  5. +0 −19 web/js/jquery-1.3.2.min.js
View
@@ -3,29 +3,33 @@
- [coffeescript](http://coffeescript.org/)
- [nodejs](http://nodejs.org/)
-- [websockets](http://dev.w3.org/html5/websockets/)
+- [websockets](http://dev.w3.org/html5/websockets/)
-All the cool kids are playing with these, and I wanted to see what the fuss was about. Given that I work with marine navigation, **aistoy** seemed to be a natural fit.
-
-I also really needed something to get me immersed in javascript (I've blamed my resistance on the ugly syntax, but with coffee-script I finally relented).
+All the cool kids are playing with these, and I wanted to see what the fuss was about. Given that I work with marine navigation, **aistoy** seemed to be a natural fit. I also really needed something to get me immersed in javascript (I've blamed my resistance on the ugly syntax, but with coffee-script I finally relented).
### The Server ###
The **server/server.coffee** script (written in coffee-script and node.js) connects
to an AIS feed (specified on the command line), and creates a second connection to an
ais decoder server, which is unfortunately currently a python script, as the ais decoder
-library I found was for python (I plan to fix this if I have time some day). It opens a server a allowing web-socket connections which are used to stream the real-time data to the browser.
+library I found ([libais](https://github.com/schwehr/libais)) was for python (I plan to fix this if I have time some day). It opens a server a allowing web-socket connections which are used to stream the real-time data to the browser.
### The Client ###
The client is a quick hack for now - needs some more work, but you can get the
-idea pretty well in spite of my horrible google maps icons.
+idea pretty well. I do rotation using html5 canvas and [elabels](http://econym.org.uk/gmap/elabel.htm).
### Conclusions: ###
-- Coffee-script rocks the house. Makes Javascript beautiful, fun!
-- Node's way of writing servers is super clean and straightforward
-- Websockets are a mess, but the socket.io library for node isn't bad
+- Coffee-script rocks the house. Makes Javascript beautiful, fun, although
+ it's more of a pain to develop in on the client side, because you have to
+ add a script to compile/referesh.
+
+- Node's way of writing servers is super clean and straightforward, but
+ it could use some polish (an exception brings down the server, really?)
+
+- Websockets are a mess, but the socket.io library for node isn't a bad
+ workaround to all the fiasco about browser support.
### Installing ###
View
@@ -8,7 +8,7 @@
<link rel="stylesheet" href="css/meyer_reset.css" />
<link rel="stylesheet" href="css/style.css" type="text/css" />
<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;sensor=false&amp;key=ABQIAAAAS5qrJFIB5--iBQT33uH6_BQOL4TyltpR8aQPYLl5UQtYIm_4_BRbAVokUNM-6lzF-SQGMkmI9qUlEQ" type="text/javascript"></script>
- <script type="text/javascript" src="js/jquery-1.3.2.min.js"></script>
+ <script src="http://econym.org.uk/gmap/elabel.js" type="text/javascript"></script>
<script src="http://127.0.0.1:58080/socket.io/socket.io.js"></script>
<script type="text/javascript" src="js/aistoy.js"></script>
</head>
@@ -25,3 +25,4 @@
</body>
</html>
+
View
@@ -0,0 +1,232 @@
+// Binds a function to the jQuery document ready event utilising the jQuery .class selector.
+// The following code illustrates how the JavaScript can dynamically
+// pick up the number of charts laid out in the HTML and then make them
+// draggable (jQuery UI interaction). The draggable scatterplots are
+// nested bottom right. This is because their divs are positioned
+// absolute in order to easily display them over the background map.
+// The .bkgrndMapChartCanvas class needed an absolute location and each
+// chart would be directly above and therefore obscure the other charts if
+// their positions were not offset from one another.
+$(document).ready(function(){
+ scatterplots = $('.bkgrndMapChartCanvas');
+ nestOffset = "";
+});
+
+// The creation of custom icons and the initialize function
+// is based upon code found in 'Using PHP/MySQL with Google Maps'
+// http://code.google.com/apis/maps/articles/phpsqlajax.html
+var blue_icon = new GIcon();
+blue_icon.image = 'http://labs.google.com/ridefinder/images/mm_20_blue.png';
+blue_icon.shadow = 'http://labs.google.com/ridefinder/images/mm_20_shadow.png';
+blue_icon.iconSize = new GSize(12, 20);
+blue_icon.shadowSize = new GSize(22, 20);
+blue_icon.iconAnchor = new GPoint(6, 20);
+blue_icon.infoWindowAnchor = new GPoint(5, 1);
+
+var yellow_icon = new GIcon();
+yellow_icon.image = 'http://labs.google.com/ridefinder/images/mm_20_yellow.png';
+yellow_icon.shadow = 'http://labs.google.com/ridefinder/images/mm_20_shadow.png';
+yellow_icon.iconSize = new GSize(12, 20);
+yellow_icon.shadowSize = new GSize(22, 20);
+yellow_icon.iconAnchor = new GPoint(6, 20);
+yellow_icon.infoWindowAnchor = new GPoint(5, 1);
+
+var red_icon = new GIcon();
+red_icon.image = 'http://labs.google.com/ridefinder/images/mm_20_red.png';
+red_icon.shadow = 'http://labs.google.com/ridefinder/images/mm_20_shadow.png';
+red_icon.iconSize = new GSize(12, 20);
+red_icon.shadowSize = new GSize(22, 20);
+red_icon.iconAnchor = new GPoint(6, 20);
+red_icon.infoWindowAnchor = new GPoint(5, 1);
+
+var arrowIcon = new Image();
+arrowIcon.src = "arrow.png";
+
+
+var markers = [];
+var vessels = {}; // vessel data keyed on mmsi, holds array of data
+var next_mark_id = 0;
+var map;
+
+function initialize() {
+ if (GBrowserIsCompatible()) {
+ map = new GMap2(document.getElementById("bkgrndMap"));
+ map.setCenter(new GLatLng(37.82,-122.38), 13);
+ map.setMapType(G_SATELLITE_MAP);
+ map.setUIToDefault();
+ }
+ start_ais_connection();
+}
+
+/*----------------------------- handling ais data --------------------------*/
+
+// open a websocket connection, handle incoming infomration about the vessel
+function start_ais_connection() {
+ var socket = io.connect('http://127.0.0.1:58080/');
+ socket.on('ais', on_vessel_ais_data);
+}
+
+// handle ais data coming through
+function on_vessel_ais_data(data) {
+ if (vessels[data.mmsi]==null) { // never heard from this vessel before
+ vessels[data.mmsi]= {ais: [data], marker: createVesselMarker(data)};
+ }
+ else { // update track for this vessel
+ var vessel = vessels[data.mmsi];
+ vessel.marker.setPoint(new GLatLng(data.y, data.x));
+ vessel.ais = data;
+ }
+}
+
+/*--------------------------- vessel info display ---------------------------*/
+
+function vesselTR(hdr, value) {
+ return "<tr><th>" + hdr + "</th><td>" + value + "</td></tr>";
+}
+
+function vesselTABLE(ais) {
+ desc = "<table cellpadding='5'>"
+ desc += vesselTR('MMSI',ais.mmsi)
+ desc += vesselTR('SOG', Math.round(ais.sog));
+ desc += vesselTR('COG', Math.round(ais.cog));
+ if (ais.true_heading <= 360) {
+ desc += vesselTR('HDG', Math.round(ais.true_heading));
+ }
+ desc += "</table>"
+ return desc;
+}
+
+/*----------------------------- marker rotation -----------------------------*/
+
+
+// create and return a marker (actually, an ELabel)
+var create_marker(ais) {
+ var marker = new ELabel(
+ new GLatLng(ais.y, ais.x),
+ '<canvas id="vc'+ais.mmsi+'" width="32" height="32"><\/canvas>',
+ null, new GSize(-16, 16)
+ );
+ GEvent.addListener(marker, 'click', function() {
+ marker.openInfoWindowHtml(html);
+ });
+
+ map.addOverlay(marker);
+ draw_rotated_marker(ais);
+ return marker;
+}
+
+// given an ais info object, set the rotation of this marker. We can't
+// assume we can accesess vessels as this marker isn't in there yet.
+var draw_rotated_marker(ais)
+{
+ var angleDegrees = ais.cog;
+ var canvas = document.getElementById("vc"+ais.mmsi).getContext('2d');
+ var angleRadians = (angleDegrees / 180) * Math.PI;
+ var cosa = Math.cos(angleRadians);
+ var sina = Math.sin(angleRadians);
+
+ canvas.clearRect(0, 0, 32, 32);
+ canvas.save();
+ canvas.rotate(angleRadians);
+ canvas.translate(16 * sina + 16 * cosa, 16 * cosa - 16 * sina);
+ canvas.drawImage(red_icon, -16, -16);
+ canvas.restore();
+}
+
+
+/*-------------------------- vessel marker helpers --------------------------*/
+
+/*
+function create_marker(ais) {
+ var mmsi = data['mmsi'];
+ var sog = data['sog'];
+
+
+ var marker = new ELabel( new GLatLng(ais.y, ais.x),
+ '<canvas id="vc'+mmsi+'" width="32" height="32"><\/canvas>',
+ null,
+ new GSize(-16, 16));
+
+ arrowIcon.src = "arrow.png";
+
+
+ var point = new GLatLng();
+
+ var icon = (sog > 1.5 ? yellow_icon : red_icon);
+ var marker = new GMarker(point, icon);
+ var html = vesselTABLE(data);
+
+
+
+ GEvent.addListener(marker, 'click', function() {
+ marker.openInfoWindowHtml(html);
+ });
+
+ map.addOverlay(marker);
+ return marker;
+}
+
+ markers.push(marker);
+
+ var i = next_mark_id;
+ next_mark_id = next_mark_id + 1 ;
+ markers[i].markId = i;
+ markers[i].groupSelect = false;
+
+/*
+ GEvent.addListener(marker, 'dblclick', function() {
+ marker.openInfoWindowHtml(html);
+ });
+
+ // Switch icon on marker mouseover and mouseout
+ GEvent.addListener(marker, "mouseover", function() {
+ selectPoint(marker.markId);
+ });
+
+ GEvent.addListener(marker, "mouseout", function() {
+ deselectPoint(marker.markId);
+ });
+
+ // The click event is used for multiple marker selection
+ // by switching the groupSelect flag.
+ GEvent.addListener(marker, "click", function() {
+ if (marker.groupSelect == false) {
+ marker.groupSelect = true;
+ selectPoint(marker.markId);
+ }
+ else {
+ marker.groupSelect = false;
+ deselectPoint(marker.markId);
+ }
+ });
+ map.addOverlay(marker);
+ return marker;
+}
+
+// The selectPoint and deselectPoint functions alter the
+// appearance of the scatterplot points simultaneous with
+// the alteration of the appearance of markers according
+// to the mouseover, mouseout, and click events.
+// The points and markers have the same corresponding colours.
+// Each marker has its own id. In the first scatterplot these ids
+// correspond to the ids of the markers, but in the second
+// scatterplot their ids correspond to the (number of markers + i).
+// In the third scatterplot they correspond to ((2 * number of markers) + i),
+// and so on. This is because a single column array holds all the points.
+
+function selectPoint(id) {
+ if (markers[id].groupSelect == false) {
+ markers[id].setImage(yellow_icon.image);
+ }
+ else {
+ markers[id].setImage(red_icon.image);
+ }
+}
+
+function deselectPoint(id) {
+ if (markers[id].groupSelect == false) {
+ markers[id].setImage(blue_icon.image);
+ }
+} */
+
+
Oops, something went wrong.

0 comments on commit b9f68c8

Please sign in to comment.