Fixed node geometry so that [x,y] is now at the center of the rectangle. (Added debug circles).
Added radio buttons in mast head.
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index ae969be..b2675ec 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -25,9 +25,11 @@
   -->
 <html>
 <head>
+    <meta charset="utf-8">
     <title>ONOS GUI</title>
 
-    <script src="libs/d3.min.js"></script>
+    <!--TODO: use the minified version of d3, once debugging is complete -->
+    <script src="libs/d3.js"></script>
     <script src="libs/jquery-2.1.1.min.js"></script>
 
     <link rel="stylesheet" href="base.css">
@@ -42,10 +44,10 @@
         <div id="mast">
             <img id="logo" src="img/onos-logo.png" width="60" height="38">
             <span class="title">Open Network Operating System</span>
-            <span class="right">
-                <span class="radio">[All Layers]</span>
-                <span class="radio">[Packet Only]</span>
-                <span class="radio">[Optical Only]</span>
+            <span id="displayModes" class="right">
+                <span id="showAll" class="radio active">All Layers</span>
+                <span id="showPkt" class="radio">Packet Only</span>
+                <span id="showOpt" class="radio">Optical Only</span>
             </span>
         </div>
         <div id="view"></div>
diff --git a/web/gui/src/main/webapp/network.js b/web/gui/src/main/webapp/network.js
index 32b0f27..7c80616 100644
--- a/web/gui/src/main/webapp/network.js
+++ b/web/gui/src/main/webapp/network.js
@@ -31,7 +31,8 @@
     var config = {
             options: {
                 layering: true,
-                collisionPrevention: true
+                collisionPrevention: true,
+                showNodeXY: true
             },
             XjsonUrl: 'rs/topology/graph',
             jsonUrl: 'network.json',
@@ -91,7 +92,8 @@
         view = {},
         network = {},
         selected = {},
-        highlighted = null;
+        highlighted = null,
+        viewMode = 'showAll';
 
 
     function loadNetworkView() {
@@ -126,6 +128,21 @@
         });
 
         $(window).on('resize', resize);
+
+        // set up radio button behavior
+        d3.selectAll("#displayModes .radio").on('click', function() {
+            var id = d3.select(this).attr("id");
+            if (id !== viewMode) {
+                radioButton('displayModes', id);
+                viewMode = id;
+                alert("action: " + id);
+            }
+        });
+    }
+
+    function radioButton(group, id) {
+        d3.selectAll("#" + group + " .radio").classed("active", false);
+        d3.select("#" + group + " #" + id).classed("active", true);
     }
 
 
@@ -263,8 +280,6 @@
 //                    + " scale(" + d3.event.scale + ")");
 //        }
 
-        // TODO: svg.append('defs') for markers?
-
         // TODO: move glow/blur stuff to util script
         var glow = network.svg.append('filter')
             .attr('x', '-50%')
@@ -295,8 +310,7 @@
 //        });
 
 
-
-
+        // add links to the display
         network.link = network.svg.append('g').selectAll('.link')
             .data(network.force.links(), function(d) {return d.id})
             .enter().append('line')
@@ -350,6 +364,7 @@
         });
 
 
+        // add nodes to the display
         network.node = network.svg.selectAll('.node')
             .data(network.force.nodes(), function(d) {return d.id})
             .enter().append('g')
@@ -386,79 +401,105 @@
             });
 
         network.nodeRect = network.node.append('rect')
-            .attr('rx', 5)
-            .attr('ry', 5);
-        // note that width/height are adjusted to fit the label text
+            .attr({
+                rx: 5,
+                ry: 5,
+                width: 100,
+                height: 12
+            });
+            // note that width/height are adjusted to fit the label text
 
         network.node.each(function(d) {
             var node = d3.select(this),
-                rect = node.select('rect'),
-                icon = iconUrl(d),
-                text = node.append('text')
-                    // TODO: add label cycle behavior
-                    .text(d.id)
-                    .attr('dy', '1.1em');
+                icon = iconUrl(d);
+
+            node.append('text')
+            // TODO: add label cycle behavior
+                .text(d.id)
+                .attr('dy', '1.1em');
 
             if (icon) {
                 var cfg = config.icons;
                 node.append('svg:image')
-                    .attr('width', cfg.w)
-                    .attr('height', cfg.h)
-                    .attr('xlink:href', icon);
+                    .attr({
+                        width: cfg.w,
+                        height: cfg.h,
+                        'xlink:href': icon
+                    });
                 // note, icon relative positioning (x,y) is done after we have
                 // adjusted the bounds of the rectangle...
             }
 
+            // for debugging...
+            if (config.options.showNodeXY) {
+                node.append('circle')
+                    .attr({
+                        class: 'debug',
+                        cx: 0,
+                        cy: 0,
+                        r: '3px'
+                    });
+            }
         });
 
+
+        // returns the newly computed bounding box
+        function adjustRectToFitText(n) {
+            var text = n.select('text'),
+                box = text.node().getBBox(),
+                lab = config.labels;
+
+            text.attr('text-anchor', 'middle')
+                .attr('y', '-0.8em')
+                .attr('x', lab.imgPad/2)
+            ;
+
+            // TODO: figure out how to access the data on selection
+            console.log("\nadjust rect for " + n.data().id);
+            console.log(box);
+
+            // translate the bbox so that it is centered on [x,y]
+            box.x = -box.width / 2;
+            box.y = -box.height / 2;
+
+            // add padding
+            box.x -= (lab.padLR + lab.imgPad/2);
+            box.width += lab.padLR * 2 + lab.imgPad;
+            box.y -= lab.padTB;
+            box.height += lab.padTB * 2;
+
+            return box;
+        }
+
+        function boundsFromBox(box) {
+            return {
+                x1: box.x,
+                y1: box.y,
+                x2: box.x + box.width,
+                y2: box.y + box.height
+            };
+        }
+
         // this function is scheduled to happen soon after the given thread ends
         setTimeout(function() {
             network.node.each(function(d) {
                 // for every node, recompute size, padding, etc. so text fits
                 var node = d3.select(this),
-                    text = node.selectAll('text'),
-                    bounds = {},
-                    first = true;
+                    text = node.select('text'),
+                    box = adjustRectToFitText(node),
+                    lab = config.labels;
 
-                // NOTE: probably unnecessary code if we only have one line.
-                text.each(function() {
-                    var box = this.getBBox();
-                    if (first || box.x < bounds.x1) {
-                        bounds.x1 = box.x;
-                    }
-                    if (first || box.y < bounds.y1) {
-                        bounds.y1 = box.y;
-                    }
-                    if (first || box.x + box.width < bounds.x2) {
-                        bounds.x2 = box.x + box.width;
-                    }
-                    if (first || box.y + box.height < bounds.y2) {
-                        bounds.y2 = box.y + box.height;
-                    }
-                    first = false;
-                }).attr('text-anchor', 'middle');
-
-                var lab = config.labels,
-                    oldWidth = bounds.x2 - bounds.x1;
-
-                bounds.x1 -= oldWidth / 2;
-                bounds.x2 -= oldWidth / 2;
-
-                bounds.x1 -= (lab.padLR + lab.imgPad);
-                bounds.y1 -= lab.padTB;
-                bounds.x2 += lab.padLR;
-                bounds.y2 += lab.padTB;
-
+                // now make the computed adjustment
                 node.select('rect')
-                    .attr('x', bounds.x1)
-                    .attr('y', bounds.y1)
-                    .attr('width', bounds.x2 - bounds.x1)
-                    .attr('height', bounds.y2 - bounds.y1);
+                    .attr(box);
 
                 node.select('image')
-                    .attr('x', bounds.x1 + config.icons.xoff)
-                    .attr('y', bounds.y1 + config.icons.yoff);
+                    .attr('x', box.x + config.icons.xoff)
+                    .attr('y', box.y + config.icons.yoff);
 
+                var bounds = boundsFromBox(box);
+
+                // todo: clean up extent and edge work..
                 d.extent = {
                     left: bounds.x1 - lab.marginLR,
                     right: bounds.x2 + lab.marginLR,
@@ -473,7 +514,6 @@
                     bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2)
                 };
 
-                // ====
             });
 
             network.numTicks = 0;
diff --git a/web/gui/src/main/webapp/onos.css b/web/gui/src/main/webapp/onos.css
index 5db4271..eea7d9d 100644
--- a/web/gui/src/main/webapp/onos.css
+++ b/web/gui/src/main/webapp/onos.css
@@ -48,6 +48,26 @@
 }
 
 /*
+ * Radio Buttons
+ */
+
+span.radio {
+    margin: 4px 0;
+    border: 1px dotted #222;
+    padding: 1px 6px;
+    color: #eee;
+    cursor: pointer;
+}
+
+span.radio.active {
+    background-color: #bbb;
+    border: 1px solid #eee;
+    padding: 1px 6px;
+    color: #666;
+    font-weight: bold;
+}
+
+/*
  * === DEBUGGING ======
  */
 svg {
@@ -71,12 +91,6 @@
     -moz-transition: opacity 250ms;
 }
 
-marker#end {
-    fill: #666;
-    stroke: #666;
-    stroke-width: 1.5px;
-}
-
 svg .node rect {
     stroke-width: 1.5px;
 
@@ -88,6 +102,7 @@
 svg .node.device.roadm rect {
     fill: #229;
 }
+
 svg .node.device.switch rect {
     fill: #55f;
 }
@@ -102,6 +117,18 @@
     pointer-events: none;
 }
 
+/* for debugging */
+svg .node circle.debug {
+    fill: white;
+    stroke: red;
+}
+svg .node rect.debug {
+    fill: yellow;
+    stroke: red;
+    opacity: 0.35;
+}
+
+
 svg .node.selected rect {
     filter: url(#blue-glow);
 }
@@ -119,6 +146,16 @@
     opacity: .6;
 }
 
+/*
+ * === currently unused ===============================================
+ */
+
+svg marker#end {
+    fill: #666;
+    stroke: #666;
+    stroke-width: 1.5px;
+}
+
 svg .legend {
     position: fixed;
 }