implement topojson based map rendering
position devices based on location.lat/lng
use viewbox on topo root svg to support live scaling of map and nodes

Change-Id: I56c2b1e211ab63a694b817d04ee4bb62ac62cec4
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
index 4c09b4d..8934ec3 100644
--- a/web/gui/src/main/webapp/topo2.js
+++ b/web/gui/src/main/webapp/topo2.js
@@ -106,7 +106,9 @@
                     config.force.pad + ',' +
                     config.force.pad + ')';
             }
-        }
+        },
+        // see below in creation of viewBox on main svg
+        logicalSize: 1000
     };
 
     // radio buttons
@@ -174,6 +176,9 @@
         link,
         mask;
 
+    // the projection for the map background
+    var geoMapProjection;
+
     // ==============================
     // For Debugging / Development
 
@@ -763,6 +768,15 @@
             return;
         }
 
+        var location = node.location;
+        if (location && location.type === 'latlng') {
+            var coord = geoMapProjection([location.lng, location.lat]);
+            node.fixed = true;
+            node.x = coord[0];
+            node.y = coord[1];
+            return;
+        }
+
         // Note: Placing incoming unpinned nodes at exactly the same point
         //        (center of the view) causes them to explode outwards when
         //        the force layout kicks in. So, we spread them out a bit
@@ -1356,7 +1370,8 @@
         //trace = onos.exported.webSockTrace;
 
         // NOTE: view.$div is a D3 selection of the view's div
-        svg = view.$div.append('svg');
+        var viewBox = '0 0 ' + config.logicalSize + ' ' + config.logicalSize;
+        svg = view.$div.append('svg').attr('viewBox', viewBox);
         setSize(svg, view);
 
         // add blue glow filter to svg layer
@@ -1452,7 +1467,7 @@
     }
 
     // TODO: move these to config/state portion of script
-    var geoJsonUrl = 'geoUsa.json',     // TODO: Paul
+    var geoJsonUrl = 'json/map/continental_us.json',     // TODO: Paul
         geoJson;
 
     function loadGeoJsonData() {
@@ -1496,29 +1511,38 @@
 
     function loadGeoMap() {
         fnTrace('loadGeoMap', geoJsonUrl);
-        var w = network.view.width(),
-            h = network.view.height();
 
-        // TODO: load map layer from GeoJSON stored in 'geoJson' var...
-        // bgImg = svg.insert('<svg-element-type>', '#topo-G') ...
+        // extracts the topojson data into geocoordinate-based geometry
+        var topoData = topojson.feature(geoJson, geoJson.objects.states);
 
-        // TODO: Paul
-    }
+        // see: http://bl.ocks.org/mbostock/4707858
+        geoMapProjection = d3.geo.mercator();
+        var path = d3.geo.path().projection(geoMapProjection);
 
-    function resizeBg(view) {
-        if (geoJson) {
-            // TODO : resize GeoJSON map
+        geoMapProjection
+            .scale(1)
+            .translate([0, 0]);
 
-            // TODO: Paul
+        // [[x1,y1],[x2,y2]]
+        var b = path.bounds(topoData);
+        // TODO: why 1.75?
+        var s = 1.75 / Math.max((b[1][0] - b[0][0]) / config.logicalSize, (b[1][1] - b[0][1]) / config.logicalSize);
+        var t = [(config.logicalSize - s * (b[1][0] + b[0][0])) / 2, (config.logicalSize - s * (b[1][1] + b[0][1])) / 2];
 
-        } else if (bgImg) {
-            setSize(bgImg, view);
-        }
+        geoMapProjection
+            .scale(s)
+            .translate(t);
+
+        bgImg = svg.insert("g", '#topo-G');
+        bgImg.attr('id', 'map').selectAll('path')
+            .data(topoData.features)
+            .enter()
+            .append('path')
+            .attr('d', path);
     }
 
     function resize(view, ctx, flags) {
         setSize(svg, view);
-        resizeBg(view);
 
         // TODO: hook to recompute layout, perhaps? work with zoom/pan code
         // adjust force layout size