GUI2 Absolute locations for Devices and Hosts

Change-Id: I172020a19004b559ae740478d30a2cf9ce08091e
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.ts b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.ts
index d2a0b18..7b66f36 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.ts
@@ -28,7 +28,7 @@
     SimpleChanges,
     ViewChildren
 } from '@angular/core';
-import {LogService} from 'gui2-fw-lib';
+import {LogService, WebSocketService} from 'gui2-fw-lib';
 import {
     Device,
     ForceDirectedGraph,
@@ -38,6 +38,8 @@
     LayerType,
     Link,
     LinkHighlight,
+    Location, LocMeta,
+    MetaUi,
     ModelEventMemo,
     ModelEventType,
     Region,
@@ -50,6 +52,16 @@
     HostNodeSvgComponent,
     LinkSvgComponent
 } from './visuals';
+import {
+    BackgroundSvgComponent,
+    LocationType
+} from '../backgroundsvg/backgroundsvg.component';
+
+interface UpdateMeta {
+    id: string;
+    class: string;
+    memento: MetaUi;
+}
 
 /**
  * ONOS GUI -- Topology Forces Graph Layer View.
@@ -85,7 +97,8 @@
 
     constructor(
         protected log: LogService,
-        private ref: ChangeDetectorRef
+        private ref: ChangeDetectorRef,
+        protected wss: WebSocketService
     ) {
         this.selectedLink = null;
         this.log.debug('ForceSvgComponent constructed');
@@ -172,6 +185,21 @@
                 this.graph.nodes = this.graph.nodes.concat(subRegions);
             }
 
+            // If a node has a fixed location then assign it to fx and fy so
+            // that it doesn't get affected by forces
+            this.graph.nodes
+            .forEach((n) => {
+                const loc: Location = <Location>n['location'];
+                if (loc && loc.locType === LocationType.GEO) {
+                    const position: MetaUi =
+                        BackgroundSvgComponent.convertGeoToCanvas(
+                            <LocMeta>{lng: loc.longOrX, lat: loc.latOrY});
+                    n.fx = position.x;
+                    n.fy = position.y;
+                    this.log.debug('Found node', n.id, 'with', loc.locType);
+                }
+            });
+
             // Associate the endpoints of each link with a real node
             this.graph.links = [];
             for (const linkIdx of Object.keys(this.regionData.links)) {
@@ -394,5 +422,21 @@
             });
         }
     }
+
+    /**
+     * As nodes are dragged around the graph, their new location should be sent
+     * back to server
+     * @param klass The class of node e.g. 'host' or 'device'
+     * @param id - the ID of the node
+     * @param newLocation - the new Location of the node
+     */
+    nodeMoved(klass: string, id: string, newLocation: MetaUi) {
+        this.wss.sendEvent('updateMeta', <UpdateMeta>{
+            id: id,
+            class: klass,
+            memento: newLocation
+        });
+        this.log.debug(klass, id, 'has been moved to', newLocation);
+    }
 }