GUI2 Absolute locations for Devices and Hosts

Change-Id: I172020a19004b559ae740478d30a2cf9ce08091e
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html
index 95d5d9e..b6f511a 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html
@@ -55,7 +55,8 @@
         whose contents are supplied through the Topology Service, and whose positions
         are driven by the d3.force engine
     -->
-    <svg:svg #svgZoom xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" id="topo2">
+    <svg:svg #svgZoom xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" id="topo2"
+        preserveAspectRatio="xMaxYMax none">
         <svg:desc>The main SVG canvas of the Topology View</svg:desc>
         <svg:g *ngIf="force.regionData?.devices[0].length +
                         force.regionData?.devices[1].length +
@@ -63,6 +64,14 @@
                onos-nodeviceconnected />
         <svg:g id="topo-zoomlayer" onosZoomableOf [zoomableOf]="svgZoom">
             <svg:desc>A logical layer that allows the main SVG canvas to be zoomed and panned</svg:desc>
+            <svg:g #gridFull *ngIf="prefsState.grid == 1 || prefsState.grid == 3" onos-gridsvg>
+            </svg:g>
+            <svg:g #geoGrid *ngIf="prefsState.grid == 2 || prefsState.grid == 3"
+                   onos-gridsvg [horizLowerLimit]="-180" [horizUpperLimit]="180"
+                   [vertLowerLimit]="-75" [vertUpperLimit]="75" [spacing]="15"
+                   [invertVertical]="true" [fit]="'fit1000high'" [aspectRatio]="0.83333"
+                   [gridcolor]="'#bfe7fb'">
+            </svg:g>
             <svg:g *ngIf="prefsState.bg" onos-backgroundsvg [map]="mapIdState">
                 <svg:desc>The Background SVG component - contains maps</svg:desc>
             </svg:g>
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts
index 0c0a6dd..ed65f5a 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts
@@ -49,6 +49,7 @@
 import {BackgroundSvgComponent} from '../layer/backgroundsvg/backgroundsvg.component';
 import {FormsModule, ReactiveFormsModule} from '@angular/forms';
 import {MapSvgComponent} from '../layer/mapsvg/mapsvg.component';
+import {GridsvgComponent} from '../layer/gridsvg/gridsvg.component';
 
 
 class MockActivatedRoute extends ActivatedRoute {
@@ -119,6 +120,10 @@
         this.listeners = this.listeners.filter((obj) => obj !== listener);
     }
 
+    setPrefs(name: string, obj: Object) {
+
+    }
+
 }
 
 /**
@@ -185,7 +190,8 @@
                 SubRegionNodeSvgComponent,
                 MapSelectorComponent,
                 BackgroundSvgComponent,
-                MapSvgComponent
+                MapSvgComponent,
+                GridsvgComponent
             ],
             providers: [
                 { provide: FnService, useValue: fs },
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
index 750162f..2de3c8b 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
@@ -35,6 +35,7 @@
 import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component';
 import {TopologyService} from '../topology.service';
 import {
+    GridDisplayToggle,
     HostLabelToggle,
     LabelToggle,
     UiElement
@@ -43,7 +44,7 @@
     INSTANCE_TOGGLE, SUMMARY_TOGGLE, DETAILS_TOGGLE,
     HOSTS_TOGGLE, OFFLINE_TOGGLE, PORTS_TOGGLE,
     BKGRND_TOGGLE, CYCLELABELS_BTN, CYCLEHOSTLABEL_BTN,
-    RESETZOOM_BTN, EQMASTER_BTN,
+    CYCLEGRIDDISPLAY_BTN, RESETZOOM_BTN, EQMASTER_BTN,
     CANCEL_TRAFFIC, ALL_TRAFFIC, QUICKHELP_BTN, BKGRND_SELECT
 } from '../panel/toolbar/toolbar.component';
 import {TrafficService} from '../traffic.service';
@@ -57,6 +58,7 @@
 const PREF_DETAIL = 'detail';
 const PREF_DLBLS = 'dlbls';
 const PREF_HLBLS = 'hlbls';
+const PREF_GRID = 'grid';
 const PREF_HOSTS = 'hosts';
 const PREF_INSTS = 'insts';
 const PREF_OFFDEV = 'offdev';
@@ -81,6 +83,7 @@
     ovid: string;
     summary: number;
     toolbar: number;
+    grid: number;
 }
 
 /**
@@ -135,6 +138,7 @@
         spr: 0,
         summary: 1,
         toolbar: 0,
+        grid: 0
     };
 
     mapIdState: MapObject = <MapObject>{
@@ -144,6 +148,9 @@
     mapSelShown: boolean = false;
     lionFn; // Function
 
+    gridShown: boolean = true;
+    geoGridShown: boolean = true;
+
     constructor(
         protected log: LogService,
         protected fs: FnService,
@@ -177,6 +184,7 @@
         this.is.loadIconDef('m_map');
         this.is.loadIconDef('m_selectMap');
         this.is.loadIconDef('m_cycleLabels');
+        this.is.loadIconDef('m_cycleGridDisplay');
         this.is.loadIconDef('m_resetZoom');
         this.is.loadIconDef('m_eqMaster');
         this.is.loadIconDef('m_unknown');
@@ -211,6 +219,15 @@
         }
     }
 
+    private static gridDisplayFlashMessage(index: number): string {
+        switch (index) {
+            case 0: return 'fl_grid_display_hide';
+            case 1: return 'fl_grid_display_1000';
+            case 2: return 'fl_grid_display_geo';
+            case 3: return 'fl_grid_display_both';
+        }
+    }
+
     /**
      * Pass the list of Key Commands to the KeyService, and initialize the Topology
      * Service - which communicates with through the WebSocket to the ONOS server
@@ -241,9 +258,6 @@
         if (data[TOPO2_PREFS]) {
             this.prefsState = data[TOPO2_PREFS];
         }
-        if (data[TOPO_MAPID_PREFS]) {
-            this.mapIdState = data[TOPO_MAPID_PREFS];
-        }
         this.log.debug('Updated topo2 prefs', this.prefsState, this.mapIdState);
     }
 
@@ -294,6 +308,9 @@
             case CYCLEHOSTLABEL_BTN:
                 this.cycleHostLabels();
                 break;
+            case CYCLEGRIDDISPLAY_BTN:
+                this.cycleGridDisplay();
+                break;
             case RESETZOOM_BTN:
                 this.resetZoom();
                 break;
@@ -323,19 +340,20 @@
     actionMap() {
         return {
             A: [() => {this.monitorAllTraffic(); }, 'Monitor all traffic'],
-            L: [() => {this.cycleDeviceLabels(); }, 'Cycle device labels'],
             B: [(token) => {this.toggleBackground(token); }, 'Toggle background'],
             D: [(token) => {this.toggleDetails(token); }, 'Toggle details panel'],
+            E: [() => {this.equalizeMasters(); }, 'Equalize mastership roles'],
+            H: [() => {this.toggleHosts(); }, 'Toggle host visibility'],
             I: [(token) => {this.toggleInstancePanel(token); }, 'Toggle ONOS Instance Panel'],
             G: [() => {this.mapSelShown = !this.mapSelShown; }, 'Show map selection dialog'],
-            O: [() => {this.toggleSummary(); }, 'Toggle the Summary Panel'],
-            R: [() => {this.resetZoom(); }, 'Reset pan / zoom'],
-            P: [(token) => {this.togglePorts(token); }, 'Toggle Port Highlighting'],
-            E: [() => {this.equalizeMasters(); }, 'Equalize mastership roles'],
-            X: [() => {this.resetNodeLocation(); }, 'Reset Node Location'],
-            U: [() => {this.unpinNode(); }, 'Unpin node (mouse over)'],
-            H: [() => {this.toggleHosts(); }, 'Toggle host visibility'],
+            L: [() => {this.cycleDeviceLabels(); }, 'Cycle device labels'],
             M: [() => {this.toggleOfflineDevices(); }, 'Toggle offline visibility'],
+            O: [() => {this.toggleSummary(); }, 'Toggle the Summary Panel'],
+            P: [(token) => {this.togglePorts(token); }, 'Toggle Port Highlighting'],
+            Q: [() => {this.cycleGridDisplay(); }, 'Cycle grid display'],
+            R: [() => {this.resetZoom(); }, 'Reset pan / zoom'],
+            U: [() => {this.unpinNode(); }, 'Unpin node (mouse over)'],
+            X: [() => {this.resetNodeLocation(); }, 'Reset Node Location'],
             dot: [() => {this.toggleToolbar(); }, 'Toggle Toolbar'],
             0: [() => {this.cancelTraffic(); }, 'Cancel traffic monitoring'],
             'shift-L': [() => {this.cycleHostLabels(); }, 'Cycle host labels'],
@@ -443,6 +461,14 @@
         this.log.debug('Cycling host labels', old, next);
     }
 
+    protected cycleGridDisplay() {
+        const old: GridDisplayToggle = this.prefsState.grid;
+        const next = GridDisplayToggle.next(old);
+        this.flashMsg = this.lionFn(TopologyComponent.gridDisplayFlashMessage(next));
+        this.updatePrefsState(PREF_GRID, next);
+        this.log.debug('Cycling grid display', old, next);
+    }
+
     /**
      * When the button is clicked on the toolbar or the B key is pressed
      * 1) Find the inverse of the current state (held as 1 or 0)