Added native Bazel build to GUI2. Reduced a lot of the unused Angular CLI structures

Reviewers should look at the changes in WORKSPACE, BUILD, BUILD.bazel, README.md files
This is only possible now as rules_nodejs went to 1.0.0 on December 20
gui2 has now been made the entry point (rather than gui2-fw-lib)
No tests or linting are functional yet for Typescript
Each NgModule now has its own BUILD.bazel file with ng_module
gui2-fw-lib is all one module and has been refactored to simplify the directory structure
gui2-topo-lib is also all one module - its directory structure has had 3 layers removed
The big bash script in web/gui2/BUILD has been removed - all is done through ng_module rules
in web/gui2/src/main/webapp/BUILD.bazel and web/gui2/src/main/webapp/app/BUILD.bazel

Change-Id: Ifcfcc23a87be39fe6d6c8324046cc8ebadb90551
diff --git a/web/gui2-topo-lib/lib/gui2-topo-lib.module.ts b/web/gui2-topo-lib/lib/gui2-topo-lib.module.ts
new file mode 100644
index 0000000..26afcc3
--- /dev/null
+++ b/web/gui2-topo-lib/lib/gui2-topo-lib.module.ts
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { TopologyRoutingModule } from './topology-routing.module';
+import { TopologyComponent } from './topology/topology.component';
+import { NoDeviceConnectedSvgComponent } from './layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component';
+import { InstanceComponent } from './panel/instance/instance.component';
+import { SummaryComponent } from './panel/summary/summary.component';
+import { ToolbarComponent } from './panel/toolbar/toolbar.component';
+import { DetailsComponent } from './panel/details/details.component';
+import { Gui2FwLibModule } from '../../gui2-fw-lib/public_api';
+import { BackgroundSvgComponent } from './layer/backgroundsvg/backgroundsvg.component';
+import { ForceSvgComponent } from './layer/forcesvg/forcesvg.component';
+import { MapSvgComponent } from './layer/mapsvg/mapsvg.component';
+import { TopologyService } from './topology.service';
+import { DraggableDirective } from './layer/forcesvg/draggable/draggable.directive';
+import { MapSelectorComponent } from './panel/mapselector/mapselector.component';
+import { DeviceNodeSvgComponent} from './layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component';
+import { HostNodeSvgComponent } from './layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component';
+import { SubRegionNodeSvgComponent } from './layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component';
+import { LinkSvgComponent} from './layer/forcesvg/visuals/linksvg/linksvg.component';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import { GridsvgComponent } from './layer/gridsvg/gridsvg.component';
+import {TrafficService} from './traffic.service';
+import {LayoutService} from './layout.service';
+import { BadgeSvgComponent } from './layer/forcesvg/visuals/badgesvg/badgesvg.component';
+
+/**
+ * ONOS GUI -- Topology View Module
+ *
+ * The main entry point is the TopologyComponent
+ *
+ * Note: This has been updated from onos-gui-1.0.0 where it was called 'topo2'
+ * whereas here it is now called 'topology'. This also merges in the old 'topo'
+ */
+@NgModule({
+    imports: [
+        CommonModule,
+        FormsModule,
+        ReactiveFormsModule,
+        TopologyRoutingModule,
+        Gui2FwLibModule
+    ],
+    declarations: [
+        BackgroundSvgComponent,
+        DetailsComponent,
+        DeviceNodeSvgComponent,
+        ForceSvgComponent,
+        GridsvgComponent,
+        HostNodeSvgComponent,
+        InstanceComponent,
+        LinkSvgComponent,
+        MapSelectorComponent,
+        MapSvgComponent,
+        NoDeviceConnectedSvgComponent,
+        SubRegionNodeSvgComponent,
+        SummaryComponent,
+        ToolbarComponent,
+        TopologyComponent,
+        DraggableDirective,
+        BadgeSvgComponent,
+    ],
+    providers: [
+        TopologyService,
+        TrafficService,
+        LayoutService
+    ],
+    exports: [
+        BackgroundSvgComponent,
+        DetailsComponent,
+        DeviceNodeSvgComponent,
+        ForceSvgComponent,
+        GridsvgComponent,
+        HostNodeSvgComponent,
+        InstanceComponent,
+        LinkSvgComponent,
+        MapSelectorComponent,
+        MapSvgComponent,
+        NoDeviceConnectedSvgComponent,
+        SubRegionNodeSvgComponent,
+        SummaryComponent,
+        ToolbarComponent,
+        TopologyComponent,
+        DraggableDirective,
+        BadgeSvgComponent
+    ]
+})
+export class Gui2TopoLibModule { }
diff --git a/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.css b/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.css
new file mode 100644
index 0000000..642c1c4
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.css
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology View (background) -- CSS file
+ */
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.html b/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.html
new file mode 100644
index 0000000..9f8faf8
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.html
@@ -0,0 +1,29 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<!-- The transform here goes from a 0,0 centred grid of -180 to 180 of
+    longitude to -75 to 75 of latitude
+     It is mapped to a 2000x1000 SVG grid with -500,0 at the top left
+     (The SVG viewbox of ONOS is 1000x1000 - for the geo grid we wanted
+     to keep it the same height 1000 representing +75 latitude down to
+     -75 latitude, but double the width. Why 75? There's no city in the
+     world above 70 - Murmansk)
+     The 6.66 represents 1000/150 and the 5.55 represents 2000/360
+     The reason for the difference is that mercator projection widens
+     countries in the northern and southern extremities, and so
+     the map is squashed horizontally slightly here to compensate
+     (with no squashing the width would be 2400)-->
+<svg:g xmlns:svg="http://www.w3.org/2000/svg" onos-mapsvg [map]="map" (mapBounds)="updatedBounds($event)"
+       transform="translate(500,500), scale(5.5555,6.666666)"/>
diff --git a/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.spec.ts
new file mode 100644
index 0000000..1063017
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.spec.ts
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed, getTestBed } from '@angular/core/testing';
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { BackgroundSvgComponent } from './backgroundsvg.component';
+import {MapSvgComponent, TopoData} from '../mapsvg/mapsvg.component';
+import {from} from 'rxjs';
+import {HttpClient} from '@angular/common/http';
+import {LocMeta, LogService, ZoomUtils} from '../../../gui2-fw-lib/public_api';
+import {MapObject} from '../maputils';
+import {ForceSvgComponent} from '../forcesvg/forcesvg.component';
+
+import {DraggableDirective} from '../forcesvg/draggable/draggable.directive';
+import {DeviceNodeSvgComponent} from '../forcesvg/visuals/devicenodesvg/devicenodesvg.component';
+import {SubRegionNodeSvgComponent} from '../forcesvg/visuals/subregionnodesvg/subregionnodesvg.component';
+import {LinkSvgComponent} from '../forcesvg/visuals/linksvg/linksvg.component';
+import {HostNodeSvgComponent} from '../forcesvg/visuals/hostnodesvg/hostnodesvg.component';
+import {BadgeSvgComponent} from '../forcesvg/visuals/badgesvg/badgesvg.component';
+
+
+describe('BackgroundSvgComponent', () => {
+    let httpMock: HttpTestingController;
+
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: BackgroundSvgComponent;
+    let fixture: ComponentFixture<BackgroundSvgComponent>;
+
+    const testmap: MapObject = <MapObject>{
+        scale: 1.0,
+        id: 'bayareaGEO',
+        description: 'test map',
+        filePath: 'testmap'
+    };
+
+    const sampleTopoData = <TopoData>require('../mapsvg/tests/bayarea.json');
+
+    beforeEach(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+
+
+        TestBed.configureTestingModule({
+            imports: [HttpClientTestingModule],
+            declarations: [
+                BackgroundSvgComponent,
+                MapSvgComponent,
+                ForceSvgComponent,
+                DeviceNodeSvgComponent,
+                HostNodeSvgComponent,
+                SubRegionNodeSvgComponent,
+                LinkSvgComponent,
+                DraggableDirective,
+                BadgeSvgComponent
+            ],
+            providers: [
+                { provide: LogService, useValue: logSpy },
+            ]
+        })
+        .compileComponents();
+
+        logServiceSpy = TestBed.get(LogService);
+        httpMock = TestBed.get(HttpTestingController);
+        fixture = TestBed.createComponent(BackgroundSvgComponent);
+
+        component = fixture.componentInstance;
+        component.map = testmap;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        httpMock.expectOne('testmap.topojson').flush(sampleTopoData);
+
+        expect(component).toBeTruthy();
+
+        httpMock.verify();
+    });
+
+    it('should convert latlong to xy', () => {
+        const result = ZoomUtils.convertGeoToCanvas(<LocMeta>{lat: 52, lng: -8});
+        expect(Math.round(result.x * 100)).toEqual(45556);
+        expect(Math.round(result.y * 100)).toEqual(15333);
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.ts b/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.ts
new file mode 100644
index 0000000..1af98d6
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/backgroundsvg/backgroundsvg.component.ts
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, EventEmitter, Input, Output} from '@angular/core';
+import {MapObject} from '../maputils';
+import {MapBounds, TopoZoomPrefs, LogService, ZoomUtils} from '../../../../gui2-fw-lib/public_api';
+
+/**
+ * model of the topo2CurrentLayout attrs from BgZoom below
+ */
+export interface BgZoomAttrs {
+    offsetX: number;
+    offsetY: number;
+    scale: number;
+}
+
+/**
+ * model of the topo2CurrentLayout background zoom attrs from Layout below
+ */
+export interface BgZoom {
+    cfg: BgZoomAttrs;
+    usr?: BgZoomAttrs;
+}
+
+/**
+ * model of the topo2CurrentLayout breadcrumb from Layout below
+ */
+export interface LayoutCrumb {
+    id: string;
+    name: string;
+}
+
+/**
+ * Enum of the topo2CurrentRegion location type from Location below
+ */
+export enum LocationType {
+    NONE = 'none',
+    GEO = 'geo',
+    GRID = 'grid'
+}
+
+/**
+ * model of the topo2CurrentLayout WebSocket response
+ */
+export interface Layout {
+    id: string;
+    bgDefaultScale: number;
+    bgDesc: string;
+    bgFilePath: string;
+    bgId: string;
+    bgType: LocationType;
+    bgWarn: string;
+    bgZoom: BgZoom;
+    crumbs: LayoutCrumb[];
+    parent: string;
+    region: string;
+    regionName: string;
+}
+
+/**
+ * ONOS GUI -- Topology Background Layer View.
+ *
+ * TODO: consider that this layer has only one component the MapSvg and hence
+ * might be able to be eliminated
+ */
+@Component({
+    selector: '[onos-backgroundsvg]',
+    templateUrl: './backgroundsvg.component.html',
+    styleUrls: ['./backgroundsvg.component.css']
+})
+export class BackgroundSvgComponent {
+    @Input() map: MapObject;
+    @Output() zoomlevel = new EventEmitter<TopoZoomPrefs>();
+
+    layoutData: Layout = <Layout>{};
+
+    constructor(
+        private log: LogService
+    ) {
+        this.log.debug('BackgroundSvg constructed');
+    }
+
+    /**
+     * Called when ever the mapBounds event is raised by the MapSvgComponent
+     *
+     * @param bounds - the bounds of the newly loaded map in terms of Lat and Long
+     */
+    updatedBounds(bounds: MapBounds): void {
+        const zoomPrefs: TopoZoomPrefs =
+            ZoomUtils.convertBoundsToZoomLevel(bounds, this.log);
+
+        this.zoomlevel.emit(zoomPrefs);
+    }
+
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/draggable/draggable.directive.spec.ts b/web/gui2-topo-lib/lib/layer/forcesvg/draggable/draggable.directive.spec.ts
new file mode 100644
index 0000000..bb7dfbf
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/draggable/draggable.directive.spec.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { DraggableDirective } from './draggable.directive';
+import {inject, TestBed} from '@angular/core/testing';
+import {ElementRef} from '@angular/core';
+import {LogService} from '../../../../../gui2-fw-lib/public_api';
+
+export class MockElementRef extends ElementRef {
+    nativeElement = {};
+}
+
+describe('DraggableDirective', () => {
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let mockWindow: Window;
+
+    beforeEach(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        mockWindow = <any>{
+            navigator: {
+                userAgent: 'HeadlessChrome',
+                vendor: 'Google Inc.'
+            }
+        };
+
+        TestBed.configureTestingModule({
+            providers: [DraggableDirective,
+                { provide: LogService, useValue: logSpy },
+                { provide: 'Window', useFactory: (() => mockWindow ) },
+                { provide: ElementRef, useValue: mockWindow }
+            ]
+        });
+        logServiceSpy = TestBed.get(LogService);
+    });
+
+    it('should create an instance', inject([DraggableDirective], (directive: DraggableDirective) => {
+
+        expect(directive).toBeTruthy();
+    }));
+});
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/draggable/draggable.directive.ts b/web/gui2-topo-lib/lib/layer/forcesvg/draggable/draggable.directive.ts
new file mode 100644
index 0000000..f476e46
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/draggable/draggable.directive.ts
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    Directive,
+    ElementRef,
+    EventEmitter,
+    Input,
+    OnChanges, Output
+} from '@angular/core';
+import {ForceDirectedGraph, Node} from '../models';
+import * as d3 from 'd3';
+import {LogService, MetaUi, ZoomUtils} from '../../../../../gui2-fw-lib/public_api';
+import {BackgroundSvgComponent} from '../../backgroundsvg/backgroundsvg.component';
+
+@Directive({
+  selector: '[onosDraggableNode]'
+})
+export class DraggableDirective implements OnChanges {
+    @Input() draggableNode: Node;
+    @Input() draggableInGraph: ForceDirectedGraph;
+    @Output() newLocation = new EventEmitter<MetaUi>();
+
+    constructor(
+        private _element: ElementRef,
+        private log: LogService
+    ) {
+        this.log.debug('DraggableDirective constructed');
+    }
+
+    ngOnChanges() {
+        this.applyDraggableBehaviour(
+            this._element.nativeElement,
+            this.draggableNode,
+            this.draggableInGraph,
+            this.newLocation);
+    }
+
+    /**
+     * A method to bind a draggable behaviour to an svg element
+     */
+    applyDraggableBehaviour(element, node: Node, graph: ForceDirectedGraph, newLocation: EventEmitter<MetaUi>) {
+        const d3element = d3.select(element);
+
+        function started() {
+            /** Preventing propagation of dragstart to parent elements */
+            d3.event.sourceEvent.stopPropagation();
+
+            if (!d3.event.active) {
+                graph.simulation.alphaTarget(0.3).restart();
+            }
+
+            d3.event.on('drag', () => dragged()).on('end', () => ended());
+
+            function dragged() {
+                node.fx = d3.event.x;
+                node.fy = d3.event.y;
+            }
+
+            function ended() {
+                if (!d3.event.active) {
+                    graph.simulation.alphaTarget(0);
+                }
+                newLocation.emit(ZoomUtils.convertXYtoGeo(node.fx, node.fy));
+
+                // node.fx = null;
+                // node.fy = null;
+            }
+        }
+
+        d3element.call(d3.drag()
+            .on('start', started));
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.css b/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.css
new file mode 100644
index 0000000..addd41c
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.css
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology View (forces) -- CSS file
+ */
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.html b/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.html
new file mode 100644
index 0000000..026ef87
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.html
@@ -0,0 +1,104 @@
+<!--
+~ Copyright 2019-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<svg:desc xmlns:svg="http://www.w3.org/2000/svg">The force layout layer. This is
+    an SVG component that displays Nodes (Devices, Hosts and SubRegions) and
+    Links. Positions of each are driven by a forces computation engine</svg:desc>
+<svg:g xmlns:svg="http://www.w3.org/2000/svg" class="topo2-links">
+    <svg:desc>Topology links</svg:desc>
+    <!-- Template explanation: Creates an SVG Group and in
+        line 1) use the svg component onos-linksvg, setting it's link
+         Input parameter to the link item from the next line
+        line 2) Use the built in NgFor directive to iterate through the
+         set of links filtered by the filteredLinks() function.
+        line 3) feed the highlightPorts of this (forcesvg) component in to
+         the highlightsEnabled of the link component
+        line 5) when the onos-linksvg component emits the selectedEvent,
+         call the updateSelected() method of this (forcesvg) component
+        line 6) feed the scale of this (forcesvg) component in to the scale
+         of the link
+    -->
+    <svg:g onos-linksvg [link]="link"
+           *ngFor="let link of filteredLinks()"
+           [highlightsEnabled]="highlightPorts"
+           (selectedEvent)="updateSelected($event)"
+           [scale]="scale">
+    </svg:g>
+</svg:g>
+<svg:g xmlns:svg="http://www.w3.org/2000/svg" class="topo2-nodes">
+    <svg:desc>Topology nodes</svg:desc>
+    <!-- Template explanation - create an SVG Group and
+        line 1) use the svg component onos-devicenodesvg, setting it's device
+         Input parameter to the device item from the next line
+        line 2) Use the built in NgFor directive to iterate through all
+         of the devices in the chosen layer index. The current iteration
+         is in the device variable
+        line 3) Use the onosDraggable directive and pass this device in to
+         its draggableNode Input parameter and setting the draggableInGraph
+         Input parameter to 'graph'
+        line 4) event handler of the draggable directive - causes the new location
+         to be written back to the server
+        line 5) when the onos-devicenodesvg component emits the selectedEvent,
+         call the updateSelected() method of this (forcesvg) component
+        line 6) feed the devicelabeltoggle of this (forcesvg) component in to
+         the labelToggle of the device
+        line 7) feed the scale of this (forcesvg) component in to the scale
+         of the device
+    -->
+    <svg:g onos-devicenodesvg [device]="device"
+           *ngFor="let device of regionData.devices[visibleLayerIdx()]"
+           onosDraggableNode [draggableNode]="device" [draggableInGraph]="graph"
+               (newLocation)="nodeMoved('device', device.id, $event)"
+           (selectedEvent)="updateSelected($event)"
+            [labelToggle]="deviceLabelToggle"
+            [scale]="scale">
+        <svg:desc>Device nodes</svg:desc>
+    </svg:g>
+    <!-- Template explanation - only display the hosts if 'showHosts' is set true -->
+    <svg:g *ngIf="showHosts">
+        <!-- Template explanation - create an SVG Group and
+            line 1) use the svg component onos-hostnodesvg, setting it's host
+             Input parameter to the host item from the next line
+            line 2) Use the built in NgFor directive to iterate through all
+             of the hosts in the chosen layer index. The current iteration
+             is in the 'host' variable
+            line 3) Use the onosDraggable directive and pass this host in to
+             its draggableNode Input parameter and setting the draggableInGraph
+             Input parameter to 'graph'
+            line 4) event handler of the draggable directive - causes the new location
+             to be written back to the server
+            line 5) when the onos-hostnodesvg component emits the selectedEvent
+             call the updateSelected() method of this (forcesvg) component
+            line 6) feed the hostLabelToggle of this (forcesvg) component in to
+             the labelToggle of the host
+            line 7) feed the scale of this (forcesvg) component in to the scale
+             of the host
+        -->
+        <svg:g onos-hostnodesvg [host]="host"
+               *ngFor="let host of regionData.hosts[visibleLayerIdx()]"
+               onosDraggableNode [draggableNode]="host" [draggableInGraph]="graph"
+                   (newLocation)="nodeMoved('host', host.id, $event)"
+               (selectedEvent)="updateSelected($event)"
+               [labelToggle]="hostLabelToggle"
+               [scale]="scale">
+            <svg:desc>Host nodes</svg:desc>
+        </svg:g>
+    </svg:g>
+    <svg:g onos-subregionnodesvg [subRegion]="subRegion"
+           *ngFor="let subRegion of regionData.subregions"
+           onosDraggableNode [draggableNode]="subRegion" [draggableInGraph]="graph">
+        <svg:desc>Subregion nodes</svg:desc>
+    </svg:g>
+</svg:g>
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.spec.ts
new file mode 100644
index 0000000..6ee354a
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.spec.ts
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {ForceSvgComponent} from './forcesvg.component';
+import {
+    FnService, IconService,
+    LionService,
+    LogService, SvgUtilService,
+    UrlFnService,
+    WebSocketService
+} from '../../../gui2-fw-lib/public_api';
+import {DraggableDirective} from './draggable/draggable.directive';
+import {ActivatedRoute, Params} from '@angular/router';
+import {of} from 'rxjs';
+import {DeviceNodeSvgComponent} from './visuals/devicenodesvg/devicenodesvg.component';
+import {SubRegionNodeSvgComponent} from './visuals/subregionnodesvg/subregionnodesvg.component';
+import {HostNodeSvgComponent} from './visuals/hostnodesvg/hostnodesvg.component';
+import {LinkSvgComponent} from './visuals/linksvg/linksvg.component';
+import {Device, Host, Link, LinkType, Region} from './models';
+import {ChangeDetectorRef, SimpleChange} from '@angular/core';
+import {TopologyService} from '../../topology.service';
+import {BadgeSvgComponent} from './visuals/badgesvg/badgesvg.component';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+class MockIconService {
+    loadIconDef() { }
+}
+
+class MockSvgUtilService {
+
+    cat7() {
+        const tcid = 'd3utilTestCard';
+
+        function getColor(id, muted, theme) {
+            // NOTE: since we are lazily assigning domain ids, we need to
+            //       get the color from all 4 scales, to keep the domains
+            //       in sync.
+            const ln = '#5b99d2';
+            const lm = '#9ebedf';
+            const dn = '#5b99d2';
+            const dm = '#9ebedf';
+            if (theme === 'dark') {
+                return muted ? dm : dn;
+            } else {
+                return muted ? lm : ln;
+            }
+        }
+
+        return {
+            // testCard: testCard,
+            getColor: getColor,
+        };
+    }
+}
+
+class MockUrlFnService { }
+
+class MockWebSocketService {
+    createWebSocket() { }
+    isConnected() { return false; }
+    unbindHandlers() { }
+    bindHandlers() { }
+}
+
+class MockTopologyService {
+    public instancesIndex: Map<string, number>;
+    constructor() {
+        this.instancesIndex = new Map();
+    }
+}
+
+describe('ForceSvgComponent', () => {
+    let fs: FnService;
+    let ar: MockActivatedRoute;
+    let windowMock: Window;
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: ForceSvgComponent;
+    let fixture: ComponentFixture<ForceSvgComponent>;
+    const openflowSampleData = require('./tests/test-module-topo2CurrentRegion.json');
+    const openflowRegionData: Region = <Region><unknown>(openflowSampleData.payload);
+
+    const odtnSampleData = require('./tests/test-OdtnConfig-topo2CurrentRegion.json');
+    const odtnRegionData: Region = <Region><unknown>(odtnSampleData.payload);
+
+    const emptyRegion: Region = <Region>{devices: [ [], [], [] ], hosts: [ [], [], [] ], links: []};
+
+    beforeEach(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+        windowMock = <any>{
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+
+        const bundleObj = {
+            'core.view.Topo': {
+                test: 'test1'
+            }
+        };
+        const mockLion = (key) => {
+            return bundleObj[key] || '%' + key + '%';
+        };
+
+        fs = new FnService(ar, logSpy, windowMock);
+
+        TestBed.configureTestingModule({
+            declarations: [
+                ForceSvgComponent,
+                DeviceNodeSvgComponent,
+                HostNodeSvgComponent,
+                SubRegionNodeSvgComponent,
+                LinkSvgComponent,
+                DraggableDirective,
+                BadgeSvgComponent
+            ],
+            providers: [
+                { provide: LogService, useValue: logSpy },
+                { provide: ActivatedRoute, useValue: ar },
+                { provide: FnService, useValue: fs },
+                { provide: ChangeDetectorRef, useClass: ChangeDetectorRef },
+                { provide: UrlFnService, useClass: MockUrlFnService },
+                { provide: WebSocketService, useClass: MockWebSocketService },
+                { provide: LionService, useFactory: (() => {
+                        return {
+                            bundle: ((bundleId) => mockLion),
+                            ubercache: new Array(),
+                            loadCbs: new Map<string, () => void>([])
+                        };
+                    })
+                },
+                { provide: IconService, useClass: MockIconService },
+                { provide: SvgUtilService, useClass: MockSvgUtilService },
+                { provide: TopologyService, useClass: MockTopologyService },
+                { provide: 'Window', useValue: windowMock },
+            ]
+        })
+        .compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+
+        fixture = TestBed.createComponent(ForceSvgComponent);
+        component = fixture.debugElement.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+
+    it('load sample files', () => {
+        expect(openflowSampleData).toBeTruthy();
+        expect(openflowSampleData.payload).toBeTruthy();
+        expect(openflowSampleData.payload.id).toBe('(root)');
+
+        expect(odtnSampleData).toBeTruthy();
+        expect(odtnSampleData.payload).toBeTruthy();
+        expect(odtnSampleData.payload.id).toBe('(root)');
+    });
+
+    it('should read sample data payload as Region', () => {
+        expect(openflowRegionData).toBeTruthy();
+        // console.log(regionData);
+        expect(openflowRegionData.id).toBe('(root)');
+        expect(openflowRegionData.devices).toBeTruthy();
+        expect(openflowRegionData.devices.length).toBe(3);
+        expect(openflowRegionData.devices[2].length).toBe(10);
+        expect(openflowRegionData.hosts.length).toBe(3);
+        expect(openflowRegionData.hosts[2].length).toBe(20);
+        expect(openflowRegionData.links.length).toBe(44);
+    });
+
+    it('should read device246 correctly', () => {
+        const device246: Device = openflowRegionData.devices[2][0];
+        expect(device246.id).toBe('of:0000000000000246');
+        expect(device246.nodeType).toBe('device');
+        expect(device246.type).toBe('switch');
+        expect(device246.online).toBe(true);
+        expect(device246.master).toBe('10.192.19.68');
+        expect(device246.layer).toBe('def');
+
+        expect(device246.props.managementAddress).toBe('10.192.19.69');
+        expect(device246.props.protocol).toBe('OF_13');
+        expect(device246.props.driver).toBe('ofdpa-ovs');
+        expect(device246.props.latitude).toBe('40.15');
+        expect(device246.props.name).toBe('s246');
+        expect(device246.props.locType).toBe('geo');
+        expect(device246.props.channelId).toBe('10.192.19.69:59980');
+        expect(device246.props.longitude).toBe('-121.679');
+
+        expect(device246.location.locType).toBe('geo');
+        expect(device246.location.latOrY).toBe(40.15);
+        expect(device246.location.longOrX).toBe(-121.679);
+    });
+
+    it('should read host 3 correctly', () => {
+        const host3: Host = openflowRegionData.hosts[2][0];
+        expect(host3.id).toBe('00:88:00:00:00:03/110');
+        expect(host3.nodeType).toBe('host');
+        expect(host3.layer).toBe('def');
+        expect(host3.configured).toBe(false);
+        expect(host3.ips.length).toBe(3);
+        expect(host3.ips[0]).toBe('fe80::288:ff:fe00:3');
+        expect(host3.ips[1]).toBe('2000::102');
+        expect(host3.ips[2]).toBe('10.0.1.2');
+    });
+
+    it('should read link 3-205 correctly', () => {
+        const link3_205: Link = openflowRegionData.links[0];
+        expect(link3_205.id).toBe('00:AA:00:00:00:03/None~of:0000000000000205/6');
+        expect(link3_205.epA).toBe('00:AA:00:00:00:03/None');
+        expect(link3_205.epB).toBe('of:0000000000000205');
+        expect(String(LinkType[link3_205.type])).toBe('2');
+        expect(link3_205.portA).toBe(undefined);
+        expect(link3_205.portB).toBe('6');
+
+        expect(link3_205.rollup).toBeTruthy();
+        expect(link3_205.rollup.length).toBe(1);
+        expect(link3_205.rollup[0].id).toBe('00:AA:00:00:00:03/None~of:0000000000000205/6');
+        expect(link3_205.rollup[0].epA).toBe('00:AA:00:00:00:03/None');
+        expect(link3_205.rollup[0].epB).toBe('of:0000000000000205');
+        expect(String(LinkType[link3_205.rollup[0].type])).toBe('2');
+        expect(link3_205.rollup[0].portA).toBe(undefined);
+        expect(link3_205.rollup[0].portB).toBe('6');
+
+    });
+
+    it('should handle regionData change - empty Region', () => {
+        component.ngOnChanges(
+            {'regionData' : new SimpleChange(<Region>{}, emptyRegion, true)});
+
+        expect(component.graph.nodes.length).toBe(0);
+    });
+
+    it('should know how to format names', () => {
+        expect(ForceSvgComponent.extractNodeName('00:AA:00:00:00:03/None', undefined))
+            .toEqual('00:AA:00:00:00:03/None');
+
+        expect(ForceSvgComponent.extractNodeName('00:AA:00:00:00:03/161', '161'))
+            .toEqual('00:AA:00:00:00:03');
+
+        // Like epB of first example in sampleData file - endPtStr contains port number
+        expect(ForceSvgComponent.extractNodeName('of:0000000000000206/6', '6'))
+            .toEqual('of:0000000000000206');
+
+        // Like epB of second example in sampleData file - endPtStr does not contain port number
+        expect(ForceSvgComponent.extractNodeName('of:0000000000000206', '6'))
+            .toEqual('of:0000000000000206');
+    });
+
+    it('should handle openflow regionData change - sample Region', () => {
+        component.regionData = openflowRegionData;
+        component.ngOnChanges(
+            {'regionData' : new SimpleChange(<Region>{}, openflowRegionData, true)});
+
+        expect(component.graph.nodes.length).toBe(30);
+
+        expect(component.graph.links.length).toBe(44);
+
+    });
+
+    it('should handle odtn regionData change - sample odtn Region', () => {
+        component.regionData = odtnRegionData;
+        component.ngOnChanges(
+            {'regionData' : new SimpleChange(<Region>{}, odtnRegionData, true)});
+
+        expect(component.graph.nodes.length).toBe(2);
+
+        expect(component.graph.links.length).toBe(6);
+
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.ts b/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.ts
new file mode 100644
index 0000000..ec3e57d
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.ts
@@ -0,0 +1,715 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    ChangeDetectionStrategy,
+    ChangeDetectorRef,
+    Component,
+    EventEmitter,
+    HostListener,
+    Input,
+    OnChanges, OnDestroy,
+    OnInit,
+    Output,
+    QueryList,
+    SimpleChange,
+    SimpleChanges,
+    ViewChildren
+} from '@angular/core';
+import {LocMeta, LogService, MetaUi, WebSocketService, ZoomUtils} from '../../../../gui2-fw-lib/public_api';
+import {
+    Badge,
+    Device, DeviceHighlight,
+    DeviceProps,
+    ForceDirectedGraph,
+    Host, HostHighlight,
+    HostLabelToggle,
+    LabelToggle,
+    LayerType,
+    Link,
+    LinkHighlight,
+    Location,
+    ModelEventMemo,
+    ModelEventType,
+    Node,
+    Options,
+    Region,
+    RegionLink,
+    SubRegion,
+    UiElement
+} from './models';
+import {LocationType} from '../backgroundsvg/backgroundsvg.component';
+import {DeviceNodeSvgComponent} from './visuals/devicenodesvg/devicenodesvg.component';
+import {HostNodeSvgComponent} from './visuals/hostnodesvg/hostnodesvg.component';
+import {LinkSvgComponent} from './visuals/linksvg/linksvg.component';
+import {SelectedEvent} from './visuals/nodevisual';
+
+interface UpdateMeta {
+    id: string;
+    class: string;
+    memento: MetaUi;
+}
+
+const SVGCANVAS = <Options>{
+    width: 1000,
+    height: 1000
+};
+
+interface ChangeSummary {
+    numChanges: number;
+    locationChanged: boolean;
+}
+
+/**
+ * ONOS GUI -- Topology Forces Graph Layer View.
+ *
+ * The regionData is set by Topology Service on WebSocket topo2CurrentRegion callback
+ * This drives the whole Force graph
+ */
+@Component({
+    selector: '[onos-forcesvg]',
+    templateUrl: './forcesvg.component.html',
+    styleUrls: ['./forcesvg.component.css'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ForceSvgComponent implements OnInit, OnDestroy, OnChanges {
+    @Input() deviceLabelToggle: LabelToggle.Enum = LabelToggle.Enum.NONE;
+    @Input() hostLabelToggle: HostLabelToggle.Enum = HostLabelToggle.Enum.NONE;
+    @Input() showHosts: boolean = false;
+    @Input() showAlarms: boolean = false;
+    @Input() highlightPorts: boolean = true;
+    @Input() onosInstMastership: string = '';
+    @Input() visibleLayer: LayerType = LayerType.LAYER_DEFAULT;
+    @Input() selectedLink: RegionLink = null;
+    @Input() scale: number = 1;
+    @Input() regionData: Region = <Region>{devices: [ [], [], [] ], hosts: [ [], [], [] ], links: []};
+    @Output() linkSelected = new EventEmitter<RegionLink>();
+    @Output() selectedNodeEvent = new EventEmitter<UiElement[]>();
+    public graph: ForceDirectedGraph;
+    private selectedNodes: UiElement[] = [];
+    viewInitialized: boolean = false;
+
+    // References to the children of this component - these are created in the
+    // template view with the *ngFor and we get them by a query here
+    @ViewChildren(DeviceNodeSvgComponent) devices: QueryList<DeviceNodeSvgComponent>;
+    @ViewChildren(HostNodeSvgComponent) hosts: QueryList<HostNodeSvgComponent>;
+    @ViewChildren(LinkSvgComponent) links: QueryList<LinkSvgComponent>;
+
+    constructor(
+        protected log: LogService,
+        private ref: ChangeDetectorRef,
+        protected wss: WebSocketService
+    ) {
+        this.selectedLink = null;
+        this.log.debug('ForceSvgComponent constructed');
+    }
+
+    /**
+     * Utility for extracting a node name from an endpoint string
+     * In some cases - have to remove the port number from the end of a device
+     * name
+     * @param endPtStr The end point name
+     */
+    static extractNodeName(endPtStr: string, portStr: string): string {
+        if (portStr === undefined || endPtStr === undefined) {
+            return endPtStr;
+        } else if (endPtStr.includes('/')) {
+            return endPtStr.substr(0, endPtStr.length - portStr.length - 1);
+        }
+        return endPtStr;
+    }
+
+    /**
+     * Recursive method to compare 2 objects attribute by attribute and update
+     * the first where a change is detected
+     * @param existingNode 1st object
+     * @param updatedNode 2nd object
+     */
+    private static updateObject(existingNode: Object, updatedNode: Object): ChangeSummary {
+        const changed = <ChangeSummary>{numChanges: 0, locationChanged: false};
+        for (const key of Object.keys(updatedNode)) {
+            const o = updatedNode[key];
+            if (['id', 'x', 'y', 'fx', 'fy', 'vx', 'vy', 'index'].some(k => k === key)) {
+                continue;
+            } else if (o && typeof o === 'object' && o.constructor === Object) {
+                const subChanged = ForceSvgComponent.updateObject(existingNode[key], updatedNode[key]);
+                changed.numChanges += subChanged.numChanges;
+                changed.locationChanged = subChanged.locationChanged ? true : changed.locationChanged;
+            } else if (existingNode === undefined) {
+                // Copy the whole object
+                existingNode = updatedNode;
+                changed.locationChanged = true;
+                changed.numChanges++;
+            } else if (existingNode[key] !== updatedNode[key]) {
+                if (['locType', 'latOrY', 'longOrX', 'latitude', 'longitude', 'gridX', 'gridY'].some(k => k === key)) {
+                    changed.locationChanged = true;
+                }
+                changed.numChanges++;
+                existingNode[key] = updatedNode[key];
+            }
+        }
+        return changed;
+    }
+
+    @HostListener('window:resize', ['$event'])
+    onResize(event) {
+        this.graph.restartSimulation();
+        this.log.debug('Simulation restart after resize', event);
+    }
+
+    /**
+     * After the component is initialized create the Force simulation
+     * The list of devices, hosts and links will not have been receieved back
+     * from the WebSocket yet as this time - they will be updated later through
+     * ngOnChanges()
+     */
+    ngOnInit() {
+        // Receiving an initialized simulated graph from our custom d3 service
+        this.graph = new ForceDirectedGraph(SVGCANVAS, this.log);
+
+        /** Binding change detection check on each tick
+         * This along with an onPush change detection strategy should enforce
+         * checking only when relevant! This improves scripting computation
+         * duration in a couple of tests I've made, consistently. Also, it makes
+         * sense to avoid unnecessary checks when we are dealing only with
+         * simulations data binding.
+         */
+        this.graph.ticker.subscribe((simulation) => {
+            // this.log.debug("Force simulation has ticked. Alpha",
+            //     Math.round(simulation.alpha() * 1000) / 1000);
+            this.ref.markForCheck();
+        });
+
+        this.log.debug('ForceSvgComponent initialized - waiting for nodes and links');
+
+    }
+
+    /**
+     * When any one of the inputs get changed by a containing component, this
+     * gets called automatically. In addition this is called manually by
+     * topology.service when a response is received from the WebSocket from the
+     * server
+     *
+     * The Devices, Hosts and SubRegions are all added to the Node list for the simulation
+     * The Links are added to the Link list of the simulation.
+     * Before they are added the Links are associated with Nodes based on their endPt
+     *
+     * @param changes - a list of changed @Input(s)
+     */
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes['regionData']) {
+            const devices: Device[] =
+                changes['regionData'].currentValue.devices[this.visibleLayerIdx()];
+            const hosts: Host[] =
+                changes['regionData'].currentValue.hosts[this.visibleLayerIdx()];
+            const subRegions: SubRegion[] = changes['regionData'].currentValue.subRegion;
+            this.graph.nodes = [];
+            if (devices) {
+                this.graph.nodes = devices;
+            }
+            if (hosts) {
+                this.graph.nodes = this.graph.nodes.concat(hosts);
+            }
+            if (subRegions) {
+                this.graph.nodes = this.graph.nodes.concat(subRegions);
+            }
+
+            this.graph.nodes.forEach((n) => this.fixPosition(n));
+
+            // Associate the endpoints of each link with a real node
+            this.graph.links = [];
+            for (const linkIdx of Object.keys(this.regionData.links)) {
+                const link = this.regionData.links[linkIdx];
+                const epA = ForceSvgComponent.extractNodeName(link.epA, link.portA);
+                if (!this.graph.nodes.find((node) => node.id === epA)) {
+                    this.log.error('ngOnChange Could not find endpoint A', epA, 'for', link);
+                    continue;
+                }
+                const epB = ForceSvgComponent.extractNodeName(
+                    link.epB, link.portB);
+                if (!this.graph.nodes.find((node) => node.id === epB)) {
+                    this.log.error('ngOnChange Could not find endpoint B', epB, 'for', link);
+                    continue;
+                }
+                this.regionData.links[linkIdx].source =
+                    this.graph.nodes.find((node) =>
+                        node.id === epA);
+                this.regionData.links[linkIdx].target =
+                    this.graph.nodes.find((node) =>
+                        node.id === epB);
+                this.regionData.links[linkIdx].index = Number(linkIdx);
+            }
+
+            this.graph.links = this.regionData.links;
+            if (this.graph.nodes.length > 0) {
+                this.graph.reinitSimulation();
+            }
+            this.log.debug('ForceSvgComponent input changed',
+                this.graph.nodes.length, 'nodes,', this.graph.links.length, 'links');
+            if (!this.viewInitialized) {
+                this.viewInitialized = true;
+                if (this.showAlarms) {
+                    this.wss.sendEvent('alarmTopovDisplayStart', {});
+                }
+            }
+        }
+
+        if (changes['showAlarms'] && this.viewInitialized) {
+            if (this.showAlarms) {
+                this.wss.sendEvent('alarmTopovDisplayStart', {});
+            } else {
+                this.wss.sendEvent('alarmTopovDisplayStop', {});
+                this.cancelAllDeviceHighlightsNow();
+            }
+        }
+    }
+
+    ngOnDestroy(): void {
+        if (this.showAlarms) {
+            this.wss.sendEvent('alarmTopovDisplayStop', {});
+            this.cancelAllDeviceHighlightsNow();
+        }
+        this.viewInitialized = false;
+    }
+
+    /**
+     * If instance has a value then mute colors of devices not connected to it
+     * Otherwise if instance does not have a value unmute all
+     * @param instanceName name of the selected instance
+     */
+    changeInstSelection(instanceName: string) {
+        this.log.debug('Mastership changed', instanceName);
+        this.devices.filter((d) => d.device.master !== instanceName)
+            .forEach((d) => {
+                const isMuted = Boolean(instanceName);
+                d.ngOnChanges({'colorMuted': new SimpleChange(!isMuted, isMuted, true)});
+            }
+        );
+    }
+
+    /**
+     * If a node has a fixed location then assign it to fx and fy so
+     * that it doesn't get affected by forces
+     * @param graphNode The node whose location should be processed
+     */
+    private fixPosition(graphNode: Node): void {
+        const loc: Location = <Location>graphNode['location'];
+        const props: DeviceProps = <DeviceProps>graphNode['props'];
+        const metaUi = <MetaUi>graphNode['metaUi'];
+        if (loc && loc.locType === LocationType.GEO) {
+            const position: MetaUi =
+                ZoomUtils.convertGeoToCanvas(
+                    <LocMeta>{lng: loc.longOrX, lat: loc.latOrY});
+            graphNode.fx = position.x;
+            graphNode.fy = position.y;
+            this.log.debug('Found node', graphNode.id, 'with', loc.locType);
+        } else if (loc && loc.locType === LocationType.GRID) {
+            graphNode.fx = loc.longOrX;
+            graphNode.fy = loc.latOrY;
+            this.log.debug('Found node', graphNode.id, 'with', loc.locType);
+        } else if (props && props.locType === LocationType.NONE && metaUi) {
+            graphNode.fx = metaUi.x;
+            graphNode.fy = metaUi.y;
+            this.log.debug('Found node', graphNode.id, 'with locType=none and metaUi');
+        } else {
+            graphNode.fx = null;
+            graphNode.fy = null;
+        }
+    }
+
+    /**
+     * Get the index of LayerType so it can drive the visibility of nodes and
+     * hosts on layers
+     */
+    visibleLayerIdx(): number {
+        const layerKeys: string[] = Object.keys(LayerType);
+        for (const idx in layerKeys) {
+            if (LayerType[layerKeys[idx]] === this.visibleLayer) {
+                return Number(idx);
+            }
+        }
+        return -1;
+    }
+
+    selectLink(link: RegionLink): void {
+        this.selectedLink = link;
+        this.linkSelected.emit(link);
+    }
+
+    /**
+     * Iterate through all hosts and devices and links to deselect the previously selected
+     * node. The emit an event to the parent that lets it know the selection has
+     * changed.
+     *
+     * This function collates all of the nodes that have been selected and passes
+     * a collection of nodes up to the topology component
+     *
+     * @param selectedNode the newly selected node
+     */
+    updateSelected(selectedNode: SelectedEvent): void {
+        this.log.debug('Node or link ',
+            selectedNode.uiElement ? selectedNode.uiElement.id : '--',
+            selectedNode.deselecting ? 'deselected' : 'selected',
+            selectedNode.isShift ? 'Multiple' : '');
+
+        if (selectedNode.isShift && selectedNode.deselecting) {
+            const idx = this.selectedNodes.findIndex((n) =>
+                n.id === selectedNode.uiElement.id
+            );
+            this.selectedNodes.splice(idx, 1);
+            this.log.debug('Removed node', idx);
+
+        } else if (selectedNode.isShift) {
+            this.selectedNodes.push(selectedNode.uiElement);
+
+        } else if (selectedNode.deselecting) {
+            this.devices
+                .forEach((d) => d.deselect());
+            this.hosts
+                .forEach((h) => h.deselect());
+            this.links
+                .forEach((l) => l.deselect());
+            this.selectedNodes = [];
+
+        } else {
+            const selNodeId = selectedNode.uiElement.id;
+            // Otherwise if shift was not pressed deselect previous
+            this.devices
+                .filter((d) => d.device.id !== selNodeId)
+                .forEach((d) => d.deselect());
+            this.hosts
+                .filter((h) => h.host.id !== selNodeId)
+                .forEach((h) => h.deselect());
+
+            this.links
+                .filter((l) => l.link.id !== selNodeId)
+                .forEach((l) => l.deselect());
+
+            this.selectedNodes = [selectedNode.uiElement];
+        }
+        // Push the changes back up to parent (Topology Component)
+        this.selectedNodeEvent.emit(this.selectedNodes);
+    }
+
+    /**
+     * We want to filter links to show only those not related to hosts if the
+     * 'showHosts' flag has been switched off. If 'showHosts' is true, then
+     * display all links.
+     */
+    filteredLinks(): Link[] {
+        return this.regionData.links.filter((h) =>
+            this.showHosts ||
+            ((<Host>h.source).nodeType !== 'host' &&
+            (<Host>h.target).nodeType !== 'host'));
+    }
+
+    /**
+     * When changes happen in the model, then model events are sent up through the
+     * Web Socket
+     * @param type - the type of the change
+     * @param memo - a qualifier on the type
+     * @param subject - the item that the update is for
+     * @param data - the new definition of the item
+     */
+    handleModelEvent(type: ModelEventType, memo: ModelEventMemo, subject: string, data: UiElement): void {
+        switch (type) {
+            case ModelEventType.DEVICE_ADDED_OR_UPDATED:
+                if (memo === ModelEventMemo.ADDED) {
+                    this.fixPosition(<Device>data);
+                    this.graph.nodes.push(<Device>data);
+                    this.regionData.devices[this.visibleLayerIdx()].push(<Device>data);
+                    this.log.debug('Device added', (<Device>data).id);
+                } else if (memo === ModelEventMemo.UPDATED) {
+                    const oldDevice: Device =
+                        this.regionData.devices[this.visibleLayerIdx()]
+                            .find((d) => d.id === subject);
+                    const changes = ForceSvgComponent.updateObject(oldDevice, <Device>data);
+                    if (changes.numChanges > 0) {
+                        this.log.debug('Device ', oldDevice.id, memo, ' - ', changes, 'changes');
+                        if (changes.locationChanged) {
+                            this.fixPosition(oldDevice);
+                        }
+                        const svgDevice: DeviceNodeSvgComponent =
+                            this.devices.find((svgdevice) => svgdevice.device.id === subject);
+                        svgDevice.ngOnChanges({'device':
+                                new SimpleChange(<Device>{}, oldDevice, true)
+                        });
+                    }
+                } else {
+                    this.log.warn('Device ', memo, ' - not yet implemented', data);
+                }
+                break;
+            case ModelEventType.HOST_ADDED_OR_UPDATED:
+                if (memo === ModelEventMemo.ADDED) {
+                    this.fixPosition(<Host>data);
+                    this.graph.nodes.push(<Host>data);
+                    this.regionData.hosts[this.visibleLayerIdx()].push(<Host>data);
+                    this.log.debug('Host added', (<Host>data).id);
+                } else if (memo === ModelEventMemo.UPDATED) {
+                    const oldHost: Host = this.regionData.hosts[this.visibleLayerIdx()]
+                        .find((h) => h.id === subject);
+                    const changes = ForceSvgComponent.updateObject(oldHost, <Host>data);
+                    if (changes.numChanges > 0) {
+                        this.log.debug('Host ', oldHost.id, memo, ' - ', changes, 'changes');
+                        if (changes.locationChanged) {
+                            this.fixPosition(oldHost);
+                        }
+                    }
+                } else {
+                    this.log.warn('Host change', memo, ' - unexpected');
+                }
+                break;
+            case ModelEventType.DEVICE_REMOVED:
+                if (memo === ModelEventMemo.REMOVED || memo === undefined) {
+                    const removeIdx: number =
+                        this.regionData.devices[this.visibleLayerIdx()]
+                            .findIndex((d) => d.id === subject);
+                    this.regionData.devices[this.visibleLayerIdx()].splice(removeIdx, 1);
+                    this.removeRelatedLinks(subject);
+                    this.log.debug('Device ', subject, 'removed. Links', this.regionData.links);
+                } else {
+                    this.log.warn('Device removed - unexpected memo', memo);
+                }
+                break;
+            case ModelEventType.HOST_REMOVED:
+                if (memo === ModelEventMemo.REMOVED || memo === undefined) {
+                    const removeIdx: number =
+                        this.regionData.hosts[this.visibleLayerIdx()]
+                            .findIndex((h) => h.id === subject);
+                    this.regionData.hosts[this.visibleLayerIdx()].splice(removeIdx, 1);
+                    this.removeRelatedLinks(subject);
+                    this.log.debug('Host ', subject, 'removed');
+                } else {
+                    this.log.warn('Host removed - unexpected memo', memo);
+                }
+                break;
+            case ModelEventType.LINK_ADDED_OR_UPDATED:
+                if (memo === ModelEventMemo.ADDED &&
+                    this.regionData.links.findIndex((l) => l.id === subject) === -1) {
+                    const newLink = <RegionLink>data;
+
+
+                    const epA = ForceSvgComponent.extractNodeName(
+                        newLink.epA, newLink.portA);
+                    if (!this.graph.nodes.find((node) => node.id === epA)) {
+                        this.log.error('Could not find endpoint A', epA, 'of', newLink);
+                        break;
+                    }
+                    const epB = ForceSvgComponent.extractNodeName(
+                        newLink.epB, newLink.portB);
+                    if (!this.graph.nodes.find((node) => node.id === epB)) {
+                        this.log.error('Could not find endpoint B', epB, 'of link', newLink);
+                        break;
+                    }
+
+                    const listLen = this.regionData.links.push(<RegionLink>data);
+                    this.regionData.links[listLen - 1].source =
+                        this.graph.nodes.find((node) => node.id === epA);
+                    this.regionData.links[listLen - 1].target =
+                        this.graph.nodes.find((node) => node.id === epB);
+                    this.log.debug('Link added', subject);
+                } else if (memo === ModelEventMemo.UPDATED) {
+                    const oldLink = this.regionData.links.find((l) => l.id === subject);
+                    const changes = ForceSvgComponent.updateObject(oldLink, <RegionLink>data);
+                    this.log.debug('Link ', subject, '. Updated', changes, 'items');
+                } else {
+                    this.log.warn('Link event ignored', subject, data);
+                }
+                break;
+            case ModelEventType.LINK_REMOVED:
+                if (memo === ModelEventMemo.REMOVED) {
+                    const removeIdx = this.regionData.links.findIndex((l) => l.id === subject);
+                    this.regionData.links.splice(removeIdx, 1);
+                    this.log.debug('Link ', subject, 'removed');
+                }
+                break;
+            default:
+                this.log.error('Unexpected model event', type, 'for', subject, 'Data', data);
+        }
+        this.graph.links = this.regionData.links;
+        this.graph.reinitSimulation();
+    }
+
+    private removeRelatedLinks(subject: string) {
+        const len = this.regionData.links.length;
+        for (let i = 0; i < len; i++) {
+            const linkIdx = this.regionData.links.findIndex((l) =>
+                (ForceSvgComponent.extractNodeName(l.epA, l.portA) === subject ||
+                    ForceSvgComponent.extractNodeName(l.epB, l.portB) === subject));
+            if (linkIdx >= 0) {
+                this.regionData.links.splice(linkIdx, 1);
+                this.log.debug('Link ', linkIdx, 'removed on attempt', i);
+            }
+        }
+    }
+
+    /**
+     * When traffic monitoring is turned on (A key) highlights will be sent back
+     * from the WebSocket through the Traffic Service
+     * Also handles Intent highlights in case one is selected
+     * @param devices - an array of device highlights
+     * @param hosts - an array of host highlights
+     * @param links - an array of link highlights
+     */
+    handleHighlights(devices: DeviceHighlight[], hosts: HostHighlight[], links: LinkHighlight[], fadeMs: number = 0): void {
+
+        if (devices.length > 0) {
+            this.log.debug(devices.length, 'Devices highlighted');
+            devices.forEach((dh: DeviceHighlight) => {
+                this.devices.forEach((d: DeviceNodeSvgComponent) => {
+                    if (d.device.id === dh.id) {
+                        d.badge = dh.badge;
+                        this.ref.markForCheck(); // Forces ngOnChange in the DeviceSvgComponent
+                        this.log.debug('Highlighting device', dh.id);
+                    }
+                });
+            });
+        }
+        if (hosts.length > 0) {
+            this.log.debug(hosts.length, 'Hosts highlighted');
+            hosts.forEach((hh: HostHighlight) => {
+                this.hosts.forEach((h) => {
+                    if (h.host.id === hh.id) {
+                        h.badge = hh.badge;
+                        this.ref.markForCheck(); // Forces ngOnChange in the HostSvgComponent
+                        this.log.debug('Highlighting host', hh.id);
+                    }
+                });
+            });
+        }
+        if (links.length > 0) {
+            this.log.debug(links.length, 'Links highlighted');
+            links.forEach((lh: LinkHighlight) => {
+                if (fadeMs > 0) {
+                    lh.fadems = fadeMs;
+                }
+                // Don't user .filter() above as it will create a copy of the component which will be discarded
+                this.links.forEach((l) => {
+                    if (l.link.id === Link.linkIdFromShowHighlights(lh.id)) {
+                        l.linkHighlight = lh;
+                        this.ref.markForCheck(); // Forces ngOnChange in the LinkSvgComponent
+                    }
+                });
+            });
+        }
+    }
+
+    cancelAllHostHighlightsNow() {
+        this.hosts.forEach((host: HostNodeSvgComponent) => {
+            host.badge = undefined;
+            this.ref.markForCheck(); // Forces ngOnChange in the HostSvgComponent
+        });
+    }
+
+    cancelAllDeviceHighlightsNow() {
+        this.devices.forEach((device: DeviceNodeSvgComponent) => {
+            device.badge = undefined;
+            this.ref.markForCheck(); // Forces ngOnChange in the DeviceSvgComponent
+        });
+    }
+
+    cancelAllLinkHighlightsNow() {
+        this.links.forEach((link: LinkSvgComponent) => {
+            link.linkHighlight = <LinkHighlight>{};
+            this.ref.markForCheck(); // Forces ngOnChange in the LinkSvgComponent
+        });
+    }
+
+    /**
+     * 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('updateMeta2', <UpdateMeta>{
+            id: id,
+            class: klass,
+            memento: newLocation
+        });
+        this.log.debug(klass, id, 'has been moved to', newLocation);
+    }
+
+    /**
+     * If any nodes with fixed positions had been dragged out of place
+     * then put back where they belong
+     * If there are some devices selected reset only these
+     */
+    resetNodeLocations(): number {
+        let numbernodes = 0;
+        if (this.selectedNodes.length > 0) {
+            this.devices
+                .filter((d) => this.selectedNodes.some((s) => s.id === d.device.id))
+                .forEach((dev) => {
+                    Node.resetNodeLocation(<Node>dev.device);
+                    numbernodes++;
+                });
+            this.hosts
+                .filter((h) => this.selectedNodes.some((s) => s.id === h.host.id))
+                .forEach((h) => {
+                    Host.resetNodeLocation(<Host>h.host);
+                    numbernodes++;
+                });
+        } else {
+            this.devices.forEach((dev) => {
+                Node.resetNodeLocation(<Node>dev.device);
+                numbernodes++;
+            });
+            this.hosts.forEach((h) => {
+                Host.resetNodeLocation(<Host>h.host);
+                numbernodes++;
+            });
+        }
+        this.graph.reinitSimulation();
+        return numbernodes;
+    }
+
+    /**
+     * Toggle floating nodes between unpinned and frozen
+     * There may be frozen and unpinned in the selection
+     *
+     * If there are nodes selected toggle only these
+     */
+    unpinOrFreezeNodes(freeze: boolean): number {
+        let numbernodes = 0;
+        if (this.selectedNodes.length > 0) {
+            this.devices
+                .filter((d) => this.selectedNodes.some((s) => s.id === d.device.id))
+                .forEach((d) => {
+                    Node.unpinOrFreezeNode(<Node>d.device, freeze);
+                    numbernodes++;
+                });
+            this.hosts
+                .filter((h) => this.selectedNodes.some((s) => s.id === h.host.id))
+                .forEach((h) => {
+                    Node.unpinOrFreezeNode(<Node>h.host, freeze);
+                    numbernodes++;
+                });
+        } else {
+            this.devices.forEach((d) => {
+                Node.unpinOrFreezeNode(<Node>d.device, freeze);
+                numbernodes++;
+            });
+            this.hosts.forEach((h) => {
+                Node.unpinOrFreezeNode(<Node>h.host, freeze);
+                numbernodes++;
+            });
+        }
+        this.graph.reinitSimulation();
+        return numbernodes;
+    }
+}
+
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/models/force-directed-graph.spec.ts b/web/gui2-topo-lib/lib/layer/forcesvg/models/force-directed-graph.spec.ts
new file mode 100644
index 0000000..2fb9155
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/models/force-directed-graph.spec.ts
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {ForceDirectedGraph, Options} from './force-directed-graph';
+import {Node} from './node';
+import {Link} from './link';
+import {LogService} from '../../../../../gui2-fw-lib/public_api';
+import {TestBed} from '@angular/core/testing';
+
+export class TestNode extends Node {
+    constructor(id: string) {
+        super(id);
+    }
+}
+
+export class TestLink extends Link {
+    constructor(source: Node, target: Node) {
+        super(source, target);
+    }
+}
+
+/**
+ * ONOS GUI -- ForceDirectedGraph - Unit Tests
+ */
+describe('ForceDirectedGraph', () => {
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let fdg: ForceDirectedGraph;
+    const options: Options = {width: 1000, height: 1000};
+
+    beforeEach(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        const nodes: Node[] = [];
+        const links: Link[] = [];
+        fdg = new ForceDirectedGraph(options, logSpy);
+
+        for (let i = 0; i < 10; i++) {
+            const newNode: TestNode = new TestNode('id' + i);
+            nodes.push(newNode);
+        }
+        for (let j = 1; j < 10; j++) {
+            const newLink = new TestLink(nodes[0], nodes[j]);
+            links.push(newLink);
+        }
+        fdg.nodes = nodes;
+        fdg.links = links;
+        fdg.reinitSimulation();
+        logServiceSpy = TestBed.get(LogService);
+    });
+
+    afterEach(() => {
+        fdg.stopSimulation();
+        fdg.nodes = [];
+        fdg.links = [];
+        fdg.reinitSimulation();
+    });
+
+    it('should be created', () => {
+        expect(fdg).toBeTruthy();
+    });
+
+    it('should have simulation', () => {
+        expect(fdg.simulation).toBeTruthy();
+    });
+
+    it('should have 10 nodes', () => {
+        expect(fdg.nodes.length).toEqual(10);
+    });
+
+    it('should have 10 links', () => {
+        expect(fdg.links.length).toEqual(9);
+    });
+
+    // TODO fix these up to listen for tick
+    // it('nodes should not be at zero', () => {
+    //     expect(nodes[0].x).toBeGreaterThan(0);
+    // });
+    // it('ticker should emit', () => {
+    //     let tickMe = jasmine.createSpy("tickMe() spy");
+    //     fdg.ticker.subscribe((simulation) => tickMe());
+    //     expect(tickMe).toHaveBeenCalled();
+    // });
+
+    // it('init links chould be called ', () => {
+    //     spyOn(fdg, 'initLinks');
+    //     // expect(fdg).toBeTruthy();
+    //     fdg.reinitSimulation(options);
+    //     expect(fdg.initLinks).toHaveBeenCalled();
+    // });
+});
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/models/force-directed-graph.ts b/web/gui2-topo-lib/lib/layer/forcesvg/models/force-directed-graph.ts
new file mode 100644
index 0000000..7bf44af
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/models/force-directed-graph.ts
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { EventEmitter } from '@angular/core';
+import { Link } from './link';
+import { Node } from './node';
+import * as d3 from 'd3-force';
+import {LogService} from '../../../../../gui2-fw-lib/public_api';
+
+const FORCES = {
+    COLLISION: 1,
+    GRAVITY: 0.4,
+    FRICTION: 0.7
+};
+
+const CHARGES = {
+    device: -800,
+    host: -2000,
+    region: -800,
+    _def_: -1200
+};
+
+const LINK_DISTANCE = {
+    // note: key is link.type
+    direct: 100,
+    optical: 120,
+    UiEdgeLink: 3,
+    UiDeviceLink: 100,
+    _def_: 50,
+};
+
+/**
+ * note: key is link.type
+ * range: {0.0 ... 1.0}
+ */
+const LINK_STRENGTH = {
+    _def_: 0.5
+};
+
+export interface Options {
+    width: number;
+    height: number;
+}
+
+/**
+ * The inspiration for this approach comes from
+ * https://medium.com/netscape/visualizing-data-with-angular-and-d3-209dde784aeb
+ *
+ * Do yourself a favour and read https://d3indepth.com/force-layout/
+ */
+export class ForceDirectedGraph {
+    public ticker: EventEmitter<d3.Simulation<Node, Link>> = new EventEmitter();
+    public simulation: d3.Simulation<any, any>;
+    public canvasOptions: Options;
+    public nodes: Node[] = [];
+    public links: Link[] = [];
+
+    constructor(options: Options, public log: LogService) {
+        this.canvasOptions = options;
+        const ticker = this.ticker;
+
+        // Creating the force simulation and defining the charges
+        this.simulation = d3.forceSimulation()
+            .force('charge',
+                d3.forceManyBody().strength(this.charges.bind(this)))
+            // .distanceMin(100).distanceMax(500))
+            .force('gravity',
+                d3.forceManyBody().strength(FORCES.GRAVITY))
+            .force('friction',
+                d3.forceManyBody().strength(FORCES.FRICTION))
+            .force('center',
+                d3.forceCenter(this.canvasOptions.width / 2, this.canvasOptions.height / 2))
+            .force('x', d3.forceX())
+            .force('y', d3.forceY())
+            .on('tick', () => {
+                ticker.emit(this.simulation); // ForceSvgComponent.ngOnInit listens
+            });
+
+    }
+
+    /**
+     * Assigning updated node and restarting the simulation
+     * Setting alpha to 0.3 and it will count down to alphaTarget=0
+     */
+    public reinitSimulation() {
+        this.simulation.nodes(this.nodes);
+        this.simulation.force('link',
+            d3.forceLink(this.links)
+                .strength(LINK_STRENGTH._def_)
+                .distance(this.distance.bind(this))
+        );
+        this.simulation.alpha(0.3).restart();
+    }
+
+    charges(node: Node) {
+        const nodeType = node.nodeType;
+        return CHARGES[nodeType] || CHARGES._def_;
+    }
+
+    distance(link: Link) {
+        const linkType = link.type;
+        return LINK_DISTANCE[linkType] || LINK_DISTANCE._def_;
+    }
+
+    stopSimulation() {
+        this.simulation.stop();
+        this.log.debug('Simulation stopped');
+    }
+
+    public restartSimulation(alpha: number = 0.3) {
+        this.simulation.alpha(alpha).restart();
+        this.log.debug('Simulation restarted. Alpha:', alpha);
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/models/index.ts b/web/gui2-topo-lib/lib/layer/forcesvg/models/index.ts
new file mode 100644
index 0000000..36fd2e7
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/models/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export * from './node';
+export * from './link';
+export * from './regions';
+
+export * from './force-directed-graph';
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/models/link.ts b/web/gui2-topo-lib/lib/layer/forcesvg/models/link.ts
new file mode 100644
index 0000000..d5ff2a7
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/models/link.ts
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Node, UiElement} from './node';
+import * as d3 from 'd3';
+
+export enum LinkType {
+    UiRegionLink,
+    UiDeviceLink,
+    UiEdgeLink
+}
+
+/**
+ * model of the topo2CurrentRegion region rollup from Region below
+ *
+ */
+export interface RegionRollup {
+    id: string;
+    epA: string;
+    epB: string;
+    portA: string;
+    portB: string;
+    type: LinkType;
+}
+
+/**
+ * Implementing SimulationLinkDatum interface into our custom Link class
+ */
+export class Link implements UiElement, d3.SimulationLinkDatum<Node> {
+    // Optional - defining optional implementation properties - required for relevant typing assistance
+    index?: number;
+    id: string; // The id of the link in the format epA/portA~epB/portB
+    epA: string; // The name of the device or host at one end
+    epB: string; // The name of the device or host at the other end
+    portA: string; // The number of the port at one end
+    portB: string; // The number of the port at the other end
+    type: LinkType;
+    rollup: RegionRollup[]; // Links in sub regions represented by this one link
+
+    // Must - defining enforced implementation properties
+    source: Node;
+    target: Node;
+
+    public static deviceNameFromEp(ep: string): string {
+        if (ep !== undefined && ep.lastIndexOf('/') > 0) {
+            return ep.substr(0, ep.lastIndexOf('/'));
+        }
+        return ep;
+    }
+
+    /**
+     * The WSS event showHighlights is sent up with a slightly different
+     * name format on the link id using the "-" separator rather than the "~"
+     * @param linkId The id of the link in either format
+     */
+    public static linkIdFromShowHighlights(linkId: string) {
+        if (linkId.includes('-')) {
+            const parts: string[] = linkId.split('-');
+            const part0 = Link.removeHostPortNum(parts[0]);
+            const part1 = Link.removeHostPortNum(parts[1]);
+            return part0 + '~' + part1;
+        }
+        return linkId;
+    }
+
+    private static removeHostPortNum(hostStr: string) {
+        if (hostStr.includes('/None/')) {
+            const subparts = hostStr.split('/');
+            return subparts[0] + '/' + subparts[1];
+        }
+        return hostStr;
+    }
+
+    constructor(source, target) {
+        this.source = source;
+        this.target = target;
+    }
+
+    linkTypeStr(): string {
+        return LinkType[this.type];
+    }
+}
+
+/**
+ * model of the topo2CurrentRegion region link from Region
+ */
+export class RegionLink extends Link {
+
+    constructor(type: LinkType, nodeA: Node, nodeB: Node) {
+        super(nodeA, nodeB);
+        this.type = type;
+    }
+}
+
+/**
+ * model of the highlights that are sent back from WebSocket when traffic is shown
+ */
+export interface LinkHighlight {
+    id: string;
+    css: string;
+    label: string;
+    fadems: number;
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/models/node.ts b/web/gui2-topo-lib/lib/layer/forcesvg/models/node.ts
new file mode 100644
index 0000000..5bcba8c
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/models/node.ts
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import * as d3 from 'd3';
+import {LocationType} from '../../backgroundsvg/backgroundsvg.component';
+import {LayerType, Location, NodeType, RegionProps} from './regions';
+import {LocMeta, MetaUi, ZoomUtils} from '../../../../../gui2-fw-lib/public_api';
+
+export interface UiElement {
+    index?: number;
+    id: string;
+}
+
+export namespace LabelToggle {
+    /**
+     * Toggle state for how device labels should be displayed
+     */
+    export enum Enum {
+        NONE,
+        ID,
+        NAME
+    }
+
+    /**
+     * Add the method 'next()' to the LabelToggle enum above
+     */
+    export function next(current: Enum) {
+        if (current === Enum.NONE) {
+            return Enum.ID;
+        } else if (current === Enum.ID) {
+            return Enum.NAME;
+        } else if (current === Enum.NAME) {
+            return Enum.NONE;
+        }
+    }
+}
+
+export namespace HostLabelToggle {
+    /**
+     * Toggle state for how host labels should be displayed
+     */
+    export enum Enum {
+        NONE,
+        NAME,
+        IP,
+        MAC
+    }
+
+    /**
+     * Add the method 'next()' to the HostLabelToggle enum above
+     */
+    export function next(current: Enum) {
+        if (current === Enum.NONE) {
+            return Enum.NAME;
+        } else if (current === Enum.NAME) {
+            return Enum.IP;
+        } else if (current === Enum.IP) {
+            return Enum.MAC;
+        } else if (current === Enum.MAC) {
+            return Enum.NONE;
+        }
+    }
+}
+
+export namespace GridDisplayToggle {
+    /**
+     * Toggle state for how the grid should be displayed
+     */
+    export enum Enum {
+        GRIDNONE,
+        GRID1000,
+        GRIDGEO,
+        GRIDBOTH
+    }
+
+    /**
+     * Add the method 'next()' to the GridDisplayToggle enum above
+     */
+    export function next(current: Enum) {
+        if (current === Enum.GRIDNONE) {
+            return Enum.GRID1000;
+        } else if (current === Enum.GRID1000) {
+            return Enum.GRIDGEO;
+        } else if (current === Enum.GRIDGEO) {
+            return Enum.GRIDBOTH;
+        } else if (current === Enum.GRIDBOTH) {
+            return Enum.GRIDNONE;
+        }
+    }
+}
+
+/**
+ * model of the topo2CurrentRegion device props from Device below
+ */
+export interface DeviceProps {
+    latitude: string;
+    longitude: string;
+    gridX: number;
+    gridY: number;
+    name: string;
+    locType: LocationType;
+    uiType: string;
+    channelId: string;
+    managementAddress: string;
+    protocol: string;
+    driver: string;
+}
+
+export interface HostProps {
+    gridX: number;
+    gridY: number;
+    latitude: number;
+    longitude: number;
+    locType: LocationType;
+    name: string;
+}
+
+/**
+ * Implementing SimulationNodeDatum interface into our custom Node class
+ */
+export class Node implements UiElement, d3.SimulationNodeDatum {
+    // Optional - defining optional implementation properties - required for relevant typing assistance
+    index?: number;
+    x: number;
+    y: number;
+    vx?: number;
+    vy?: number;
+    fx?: number | null;
+    fy?: number | null;
+    nodeType: NodeType;
+    location: Location;
+    id: string;
+
+    protected constructor(id) {
+        this.id = id;
+        this.x = 0;
+        this.y = 0;
+    }
+
+    /**
+     * Static method to reset the node's position to that specified in its
+     * coordinates
+     * This is overridden for host
+     * @param node The node to reset
+     */
+    static resetNodeLocation(node: Node): void {
+        let origLoc: MetaUi;
+
+        if (!node.location || node.location.locType === LocationType.NONE) {
+            // No location - nothing to do
+            return;
+        } else if (node.location.locType === LocationType.GEO) {
+            origLoc = ZoomUtils.convertGeoToCanvas(<LocMeta>{
+                lng: node.location.longOrX,
+                lat: node.location.latOrY
+            });
+        } else if (node.location.locType === LocationType.GRID) {
+            origLoc = ZoomUtils.convertXYtoGeo(
+                node.location.longOrX, node.location.latOrY);
+        }
+        Node.moveNodeTo(node, origLoc);
+    }
+
+    protected static moveNodeTo(node: Node, origLoc: MetaUi) {
+        const currentX = node.fx;
+        const currentY = node.fy;
+        const distX = origLoc.x - node.fx;
+        const distY = origLoc.y - node.fy;
+        let count = 0;
+        const task = setInterval(() => {
+            count++;
+            if (count >= 10) {
+                clearInterval(task);
+            }
+            node.fx = currentX + count * distX / 10;
+            node.fy = currentY + count * distY / 10;
+        }, 50);
+    }
+
+    static unpinOrFreezeNode(node: Node, freeze: boolean): void {
+        if (!node.location || node.location.locType === LocationType.NONE) {
+            if (freeze) {
+                node.fx = node.x;
+                node.fy = node.y;
+            } else {
+                node.fx = null;
+                node.fy = null;
+            }
+        }
+    }
+}
+
+export interface Badge {
+    status: string;
+    isGlyph: boolean;
+    txt: string;
+    msg: string;
+}
+
+/**
+ * model of the topo2CurrentRegion device from Region below
+ */
+export class Device extends Node {
+    id: string;
+    layer: LayerType;
+    metaUi: MetaUi;
+    master: string;
+    online: boolean;
+    props: DeviceProps;
+    type: string;
+
+    constructor(id: string) {
+        super(id);
+    }
+}
+
+export interface DeviceHighlight {
+    id: string;
+    badge: Badge;
+}
+
+export interface HostHighlight {
+    id: string;
+    badge: Badge;
+}
+
+/**
+ * Model of the ONOS Host element in the topology
+ */
+export class Host extends Node {
+    configured: boolean;
+    id: string;
+    ips: string[];
+    layer: LayerType;
+    props: HostProps;
+
+    constructor(id: string) {
+        super(id);
+    }
+
+    static resetNodeLocation(host: Host): void {
+        let origLoc: MetaUi;
+
+        if (!host.props || host.props.locType === LocationType.NONE) {
+            // No location - nothing to do
+            return;
+        } else if (host.props.locType === LocationType.GEO) {
+            origLoc = ZoomUtils.convertGeoToCanvas(<LocMeta>{
+                lng: host.props.longitude,
+                lat: host.props.latitude
+            });
+        } else if (host.props.locType === LocationType.GRID) {
+            origLoc = ZoomUtils.convertXYtoGeo(
+                host.props.gridX, host.props.gridY);
+        }
+        Node.moveNodeTo(host, origLoc);
+    }
+}
+
+
+/**
+ * model of the topo2CurrentRegion subregion from Region below
+ */
+export class SubRegion extends Node {
+    id: string;
+    location: Location;
+    nDevs: number;
+    nHosts: number;
+    name: string;
+    props: RegionProps;
+
+    constructor(id: string) {
+        super(id);
+    }
+}
+
+/**
+ * Enumerated values for topology update event types
+ */
+export enum ModelEventType {
+    HOST_ADDED_OR_UPDATED,
+    LINK_ADDED_OR_UPDATED,
+    DEVICE_ADDED_OR_UPDATED,
+    DEVICE_REMOVED,
+    HOST_REMOVED,
+    LINK_REMOVED,
+}
+
+/**
+ * Enumerated values for topology update event memo field
+ */
+export enum ModelEventMemo {
+    ADDED = 'added',
+    REMOVED = 'removed',
+    UPDATED = 'updated'
+}
+
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/models/regions.ts b/web/gui2-topo-lib/lib/layer/forcesvg/models/regions.ts
new file mode 100644
index 0000000..3c1894b
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/models/regions.ts
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Enum of the topo2CurrentRegion node type from SubRegion below
+ */
+import {LocationType} from '../../backgroundsvg/backgroundsvg.component';
+import {Device, Host, SubRegion} from './node';
+import {RegionLink} from './link';
+
+export enum NodeType {
+    REGION = 'region',
+    DEVICE = 'device',
+    HOST = 'host',
+}
+
+/**
+ * Enum of the topo2CurrentRegion layerOrder from Region below
+ */
+export enum LayerType {
+    LAYER_OPTICAL = 'opt',
+    LAYER_PACKET = 'pkt',
+    LAYER_DEFAULT = 'def'
+}
+
+/**
+ * model of the topo2CurrentRegion location from SubRegion below
+ */
+export interface Location {
+    locType: LocationType;
+    latOrY: number;
+    longOrX: number;
+}
+
+/**
+ * model of the topo2CurrentRegion props from SubRegion below
+ */
+export interface RegionProps {
+    latitude: number;
+    longitude: number;
+    name: string;
+    peerLocations: string;
+}
+
+/**
+ * model of the topo2CurrentRegion WebSocket response
+ *
+ * The Devices are in a 2D array - 1st order is layer type, 2nd order is
+ * devices in that layer
+ */
+export interface Region {
+    note?: string;
+    id: string;
+    devices: Device[][];
+    hosts: Host[][];
+    links: RegionLink[];
+    layerOrder: LayerType[];
+    peerLocations?: Location[];
+    subregions: SubRegion[];
+}
+
+/**
+ * model of the topo2PeerRegions WebSocket response
+ */
+export interface Peer {
+    peers: SubRegion[];
+}
+
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/tests/test-OdtnConfig-topo2CurrentRegion.json b/web/gui2-topo-lib/lib/layer/forcesvg/tests/test-OdtnConfig-topo2CurrentRegion.json
new file mode 100644
index 0000000..2087940
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/tests/test-OdtnConfig-topo2CurrentRegion.json
@@ -0,0 +1,165 @@
+{
+    "event": "topo2CurrentRegion",
+    "payload": {
+        "id": "(root)",
+        "subregions": [],
+        "links": [
+            {
+                "id": "netconf:127.0.0.1:11002/201~netconf:127.0.0.1:11003/201",
+                "epA": "netconf:127.0.0.1:11002/201",
+                "epB": "netconf:127.0.0.1:11003/201",
+                "type": "UiDeviceLink",
+                "portA": "201",
+                "portB": "201",
+                "rollup": [
+                    {
+                        "id": "netconf:127.0.0.1:11002/201~netconf:127.0.0.1:11003/201",
+                        "epA": "netconf:127.0.0.1:11002/201",
+                        "epB": "netconf:127.0.0.1:11003/201",
+                        "type": "UiDeviceLink",
+                        "portA": "201",
+                        "portB": "201"
+                    }
+                ]
+            },
+            {
+                "id": "netconf:127.0.0.1:11002/202~netconf:127.0.0.1:11003/202",
+                "epA": "netconf:127.0.0.1:11002/202",
+                "epB": "netconf:127.0.0.1:11003/202",
+                "type": "UiDeviceLink",
+                "portA": "202",
+                "portB": "202",
+                "rollup": [
+                    {
+                        "id": "netconf:127.0.0.1:11002/202~netconf:127.0.0.1:11003/202",
+                        "epA": "netconf:127.0.0.1:11002/202",
+                        "epB": "netconf:127.0.0.1:11003/202",
+                        "type": "UiDeviceLink",
+                        "portA": "202",
+                        "portB": "202"
+                    }
+                ]
+            },
+            {
+                "id": "netconf:127.0.0.1:11002/203~netconf:127.0.0.1:11003/203",
+                "epA": "netconf:127.0.0.1:11002/203",
+                "epB": "netconf:127.0.0.1:11003/203",
+                "type": "UiDeviceLink",
+                "portA": "203",
+                "portB": "203",
+                "rollup": [
+                    {
+                        "id": "netconf:127.0.0.1:11002/203~netconf:127.0.0.1:11003/203",
+                        "epA": "netconf:127.0.0.1:11002/203",
+                        "epB": "netconf:127.0.0.1:11003/203",
+                        "type": "UiDeviceLink",
+                        "portA": "203",
+                        "portB": "203"
+                    }
+                ]
+            },
+            {
+                "id": "netconf:127.0.0.1:11002/204~netconf:127.0.0.1:11003/204",
+                "epA": "netconf:127.0.0.1:11002/204",
+                "epB": "netconf:127.0.0.1:11003/204",
+                "type": "UiDeviceLink",
+                "portA": "204",
+                "portB": "204",
+                "rollup": [
+                    {
+                        "id": "netconf:127.0.0.1:11002/204~netconf:127.0.0.1:11003/204",
+                        "epA": "netconf:127.0.0.1:11002/204",
+                        "epB": "netconf:127.0.0.1:11003/204",
+                        "type": "UiDeviceLink",
+                        "portA": "204",
+                        "portB": "204"
+                    }
+                ]
+            },
+            {
+                "id": "netconf:127.0.0.1:11002/205~netconf:127.0.0.1:11003/205",
+                "epA": "netconf:127.0.0.1:11002/205",
+                "epB": "netconf:127.0.0.1:11003/205",
+                "type": "UiDeviceLink",
+                "portA": "205",
+                "portB": "205",
+                "rollup": [
+                    {
+                        "id": "netconf:127.0.0.1:11002/205~netconf:127.0.0.1:11003/205",
+                        "epA": "netconf:127.0.0.1:11002/205",
+                        "epB": "netconf:127.0.0.1:11003/205",
+                        "type": "UiDeviceLink",
+                        "portA": "205",
+                        "portB": "205"
+                    }
+                ]
+            },
+            {
+                "id": "netconf:127.0.0.1:11002/206~netconf:127.0.0.1:11003/206",
+                "epA": "netconf:127.0.0.1:11002/206",
+                "epB": "netconf:127.0.0.1:11003/206",
+                "type": "UiDeviceLink",
+                "portA": "206",
+                "portB": "206",
+                "rollup": [
+                    {
+                        "id": "netconf:127.0.0.1:11002/206~netconf:127.0.0.1:11003/206",
+                        "epA": "netconf:127.0.0.1:11002/206",
+                        "epB": "netconf:127.0.0.1:11003/206",
+                        "type": "UiDeviceLink",
+                        "portA": "206",
+                        "portB": "206"
+                    }
+                ]
+            }
+        ],
+        "devices": [
+            [],
+            [],
+            [
+                {
+                    "id": "netconf:127.0.0.1:11002",
+                    "nodeType": "device",
+                    "type": "terminal_device",
+                    "online": true,
+                    "master": "127.0.0.1",
+                    "layer": "def",
+                    "props": {
+                        "ipaddress": "127.0.0.1",
+                        "protocol": "NETCONF",
+                        "driver": "cassini-ocnos",
+                        "port": "11002",
+                        "name": "cassini2",
+                        "locType": "none"
+                    }
+                },
+                {
+                    "id": "netconf:127.0.0.1:11003",
+                    "nodeType": "device",
+                    "type": "terminal_device",
+                    "online": true,
+                    "master": "127.0.0.1",
+                    "layer": "def",
+                    "props": {
+                        "ipaddress": "127.0.0.1",
+                        "protocol": "NETCONF",
+                        "driver": "cassini-ocnos",
+                        "port": "11003",
+                        "name": "cassini1",
+                        "locType": "none"
+                    }
+                }
+            ]
+        ],
+        "hosts": [
+            [],
+            [],
+            []
+        ],
+        "layerOrder": [
+            "opt",
+            "pkt",
+            "def"
+        ]
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/tests/test-module-topo2CurrentRegion.json b/web/gui2-topo-lib/lib/layer/forcesvg/tests/test-module-topo2CurrentRegion.json
new file mode 100644
index 0000000..e8af22f
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/tests/test-module-topo2CurrentRegion.json
@@ -0,0 +1,1204 @@
+{
+  "event": "topo2CurrentRegion",
+  "payload": {
+    "id": "(root)",
+    "subregions": [],
+    "links": [
+      {
+        "id": "00:AA:00:00:00:03/None~of:0000000000000205/6",
+        "epA": "00:AA:00:00:00:03/None",
+        "epB": "of:0000000000000205",
+        "type": "UiEdgeLink",
+        "portB": "6",
+        "rollup": [
+          {
+            "id": "00:AA:00:00:00:03/None~of:0000000000000205/6",
+            "epA": "00:AA:00:00:00:03/None",
+            "epB": "of:0000000000000205",
+            "type": "UiEdgeLink",
+            "portB": "6"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000205/3~of:0000000000000227/5",
+        "epA": "of:0000000000000205/3",
+        "epB": "of:0000000000000227/5",
+        "type": "UiDeviceLink",
+        "portA": "3",
+        "portB": "5",
+        "rollup": [
+          {
+            "id": "of:0000000000000205/3~of:0000000000000227/5",
+            "epA": "of:0000000000000205/3",
+            "epB": "of:0000000000000227/5",
+            "type": "UiDeviceLink",
+            "portA": "3",
+            "portB": "5"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000206/2~of:0000000000000226/8",
+        "epA": "of:0000000000000206/2",
+        "epB": "of:0000000000000226/8",
+        "type": "UiDeviceLink",
+        "portA": "2",
+        "portB": "8",
+        "rollup": [
+          {
+            "id": "of:0000000000000206/2~of:0000000000000226/8",
+            "epA": "of:0000000000000206/2",
+            "epB": "of:0000000000000226/8",
+            "type": "UiDeviceLink",
+            "portA": "2",
+            "portB": "8"
+          }
+        ]
+      },
+      {
+        "id": "00:BB:00:00:00:05/None~of:0000000000000203/7",
+        "epA": "00:BB:00:00:00:05/None",
+        "epB": "of:0000000000000203",
+        "type": "UiEdgeLink",
+        "portB": "7",
+        "rollup": [
+          {
+            "id": "00:BB:00:00:00:05/None~of:0000000000000203/7",
+            "epA": "00:BB:00:00:00:05/None",
+            "epB": "of:0000000000000203",
+            "type": "UiEdgeLink",
+            "portB": "7"
+          }
+        ]
+      },
+      {
+        "id": "00:DD:00:00:00:01/None~of:0000000000000207/3",
+        "epA": "00:DD:00:00:00:01/None",
+        "epB": "of:0000000000000207",
+        "type": "UiEdgeLink",
+        "portB": "3",
+        "rollup": [
+          {
+            "id": "00:DD:00:00:00:01/None~of:0000000000000207/3",
+            "epA": "00:DD:00:00:00:01/None",
+            "epB": "of:0000000000000207",
+            "type": "UiEdgeLink",
+            "portB": "3"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000203/1~of:0000000000000226/1",
+        "epA": "of:0000000000000203/1",
+        "epB": "of:0000000000000226/1",
+        "type": "UiDeviceLink",
+        "portA": "1",
+        "portB": "1",
+        "rollup": [
+          {
+            "id": "of:0000000000000203/1~of:0000000000000226/1",
+            "epA": "of:0000000000000203/1",
+            "epB": "of:0000000000000226/1",
+            "type": "UiDeviceLink",
+            "portA": "1",
+            "portB": "1"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000207/2~of:0000000000000247/1",
+        "epA": "of:0000000000000207/2",
+        "epB": "of:0000000000000247/1",
+        "type": "UiDeviceLink",
+        "portA": "2",
+        "portB": "1",
+        "rollup": [
+          {
+            "id": "of:0000000000000207/2~of:0000000000000247/1",
+            "epA": "of:0000000000000207/2",
+            "epB": "of:0000000000000247/1",
+            "type": "UiDeviceLink",
+            "portA": "2",
+            "portB": "1"
+          }
+        ]
+      },
+      {
+        "id": "00:99:66:00:00:01/None~of:0000000000000205/10",
+        "epA": "00:99:66:00:00:01/None",
+        "epB": "of:0000000000000205",
+        "type": "UiEdgeLink",
+        "portB": "10",
+        "rollup": [
+          {
+            "id": "00:99:66:00:00:01/None~of:0000000000000205/10",
+            "epA": "00:99:66:00:00:01/None",
+            "epB": "of:0000000000000205",
+            "type": "UiEdgeLink",
+            "portB": "10"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000208/1~of:0000000000000246/2",
+        "epA": "of:0000000000000208/1",
+        "epB": "of:0000000000000246/2",
+        "type": "UiDeviceLink",
+        "portA": "1",
+        "portB": "2",
+        "rollup": [
+          {
+            "id": "of:0000000000000208/1~of:0000000000000246/2",
+            "epA": "of:0000000000000208/1",
+            "epB": "of:0000000000000246/2",
+            "type": "UiDeviceLink",
+            "portA": "1",
+            "portB": "2"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000206/1~of:0000000000000226/7",
+        "epA": "of:0000000000000206/1",
+        "epB": "of:0000000000000226/7",
+        "type": "UiDeviceLink",
+        "portA": "1",
+        "portB": "7",
+        "rollup": [
+          {
+            "id": "of:0000000000000206/1~of:0000000000000226/7",
+            "epA": "of:0000000000000206/1",
+            "epB": "of:0000000000000226/7",
+            "type": "UiDeviceLink",
+            "portA": "1",
+            "portB": "7"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000226/9~of:0000000000000246/3",
+        "epA": "of:0000000000000226/9",
+        "epB": "of:0000000000000246/3",
+        "type": "UiDeviceLink",
+        "portA": "9",
+        "portB": "3",
+        "rollup": [
+          {
+            "id": "of:0000000000000226/9~of:0000000000000246/3",
+            "epA": "of:0000000000000226/9",
+            "epB": "of:0000000000000246/3",
+            "type": "UiDeviceLink",
+            "portA": "9",
+            "portB": "3"
+          }
+        ]
+      },
+      {
+        "id": "00:AA:00:00:00:04/None~of:0000000000000205/7",
+        "epA": "00:AA:00:00:00:04/None",
+        "epB": "of:0000000000000205",
+        "type": "UiEdgeLink",
+        "portB": "7",
+        "rollup": [
+          {
+            "id": "00:AA:00:00:00:04/None~of:0000000000000205/7",
+            "epA": "00:AA:00:00:00:04/None",
+            "epB": "of:0000000000000205",
+            "type": "UiEdgeLink",
+            "portB": "7"
+          }
+        ]
+      },
+      {
+        "id": "00:88:00:00:00:03/110~of:0000000000000205/11",
+        "epA": "00:88:00:00:00:03/110",
+        "epB": "of:0000000000000205",
+        "type": "UiEdgeLink",
+        "portB": "11",
+        "rollup": [
+          {
+            "id": "00:88:00:00:00:03/110~of:0000000000000205/11",
+            "epA": "00:88:00:00:00:03/110",
+            "epB": "of:0000000000000205",
+            "type": "UiEdgeLink",
+            "portB": "11"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000204/1~of:0000000000000226/3",
+        "epA": "of:0000000000000204/1",
+        "epB": "of:0000000000000226/3",
+        "type": "UiDeviceLink",
+        "portA": "1",
+        "portB": "3",
+        "rollup": [
+          {
+            "id": "of:0000000000000204/1~of:0000000000000226/3",
+            "epA": "of:0000000000000204/1",
+            "epB": "of:0000000000000226/3",
+            "type": "UiDeviceLink",
+            "portA": "1",
+            "portB": "3"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000203/2~of:0000000000000226/2",
+        "epA": "of:0000000000000203/2",
+        "epB": "of:0000000000000226/2",
+        "type": "UiDeviceLink",
+        "portA": "2",
+        "portB": "2",
+        "rollup": [
+          {
+            "id": "of:0000000000000203/2~of:0000000000000226/2",
+            "epA": "of:0000000000000203/2",
+            "epB": "of:0000000000000226/2",
+            "type": "UiDeviceLink",
+            "portA": "2",
+            "portB": "2"
+          }
+        ]
+      },
+      {
+        "id": "00:88:00:00:00:01/None~of:0000000000000205/12",
+        "epA": "00:88:00:00:00:01/None",
+        "epB": "of:0000000000000205",
+        "type": "UiEdgeLink",
+        "portB": "12",
+        "rollup": [
+          {
+            "id": "00:88:00:00:00:01/None~of:0000000000000205/12",
+            "epA": "00:88:00:00:00:01/None",
+            "epB": "of:0000000000000205",
+            "type": "UiEdgeLink",
+            "portB": "12"
+          }
+        ]
+      },
+      {
+        "id": "00:88:00:00:00:04/160~of:0000000000000206/6",
+        "epA": "00:88:00:00:00:04/160",
+        "epB": "of:0000000000000206",
+        "type": "UiEdgeLink",
+        "portB": "6",
+        "rollup": [
+          {
+            "id": "00:88:00:00:00:04/160~of:0000000000000206/6",
+            "epA": "00:88:00:00:00:04/160",
+            "epB": "of:0000000000000206",
+            "type": "UiEdgeLink",
+            "portB": "6"
+          }
+        ]
+      },
+      {
+        "id": "00:DD:00:00:00:02/None~of:0000000000000208/3",
+        "epA": "00:DD:00:00:00:02/None",
+        "epB": "of:0000000000000208",
+        "type": "UiEdgeLink",
+        "portB": "3",
+        "rollup": [
+          {
+            "id": "00:DD:00:00:00:02/None~of:0000000000000208/3",
+            "epA": "00:DD:00:00:00:02/None",
+            "epB": "of:0000000000000208",
+            "type": "UiEdgeLink",
+            "portB": "3"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000203/3~of:0000000000000227/1",
+        "epA": "of:0000000000000203/3",
+        "epB": "of:0000000000000227/1",
+        "type": "UiDeviceLink",
+        "portA": "3",
+        "portB": "1",
+        "rollup": [
+          {
+            "id": "of:0000000000000203/3~of:0000000000000227/1",
+            "epA": "of:0000000000000203/3",
+            "epB": "of:0000000000000227/1",
+            "type": "UiDeviceLink",
+            "portA": "3",
+            "portB": "1"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000208/2~of:0000000000000247/2",
+        "epA": "of:0000000000000208/2",
+        "epB": "of:0000000000000247/2",
+        "type": "UiDeviceLink",
+        "portA": "2",
+        "portB": "2",
+        "rollup": [
+          {
+            "id": "of:0000000000000208/2~of:0000000000000247/2",
+            "epA": "of:0000000000000208/2",
+            "epB": "of:0000000000000247/2",
+            "type": "UiDeviceLink",
+            "portA": "2",
+            "portB": "2"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000205/1~of:0000000000000226/5",
+        "epA": "of:0000000000000205/1",
+        "epB": "of:0000000000000226/5",
+        "type": "UiDeviceLink",
+        "portA": "1",
+        "portB": "5",
+        "rollup": [
+          {
+            "id": "of:0000000000000205/1~of:0000000000000226/5",
+            "epA": "of:0000000000000205/1",
+            "epB": "of:0000000000000226/5",
+            "type": "UiDeviceLink",
+            "portA": "1",
+            "portB": "5"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000204/2~of:0000000000000226/4",
+        "epA": "of:0000000000000204/2",
+        "epB": "of:0000000000000226/4",
+        "type": "UiDeviceLink",
+        "portA": "2",
+        "portB": "4",
+        "rollup": [
+          {
+            "id": "of:0000000000000204/2~of:0000000000000226/4",
+            "epA": "of:0000000000000204/2",
+            "epB": "of:0000000000000226/4",
+            "type": "UiDeviceLink",
+            "portA": "2",
+            "portB": "4"
+          }
+        ]
+      },
+      {
+        "id": "00:AA:00:00:00:01/None~of:0000000000000204/6",
+        "epA": "00:AA:00:00:00:01/None",
+        "epB": "of:0000000000000204",
+        "type": "UiEdgeLink",
+        "portB": "6",
+        "rollup": [
+          {
+            "id": "00:AA:00:00:00:01/None~of:0000000000000204/6",
+            "epA": "00:AA:00:00:00:01/None",
+            "epB": "of:0000000000000204",
+            "type": "UiEdgeLink",
+            "portB": "6"
+          }
+        ]
+      },
+      {
+        "id": "00:BB:00:00:00:03/None~of:0000000000000205/8",
+        "epA": "00:BB:00:00:00:03/None",
+        "epB": "of:0000000000000205",
+        "type": "UiEdgeLink",
+        "portB": "8",
+        "rollup": [
+          {
+            "id": "00:BB:00:00:00:03/None~of:0000000000000205/8",
+            "epA": "00:BB:00:00:00:03/None",
+            "epB": "of:0000000000000205",
+            "type": "UiEdgeLink",
+            "portB": "8"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000206/4~of:0000000000000227/8",
+        "epA": "of:0000000000000206/4",
+        "epB": "of:0000000000000227/8",
+        "type": "UiDeviceLink",
+        "portA": "4",
+        "portB": "8",
+        "rollup": [
+          {
+            "id": "of:0000000000000206/4~of:0000000000000227/8",
+            "epA": "of:0000000000000206/4",
+            "epB": "of:0000000000000227/8",
+            "type": "UiDeviceLink",
+            "portA": "4",
+            "portB": "8"
+          }
+        ]
+      },
+      {
+        "id": "00:AA:00:00:00:05/None~of:0000000000000203/6",
+        "epA": "00:AA:00:00:00:05/None",
+        "epB": "of:0000000000000203",
+        "type": "UiEdgeLink",
+        "portB": "6",
+        "rollup": [
+          {
+            "id": "00:AA:00:00:00:05/None~of:0000000000000203/6",
+            "epA": "00:AA:00:00:00:05/None",
+            "epB": "of:0000000000000203",
+            "type": "UiEdgeLink",
+            "portB": "6"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000205/5~of:0000000000000206/5",
+        "epA": "of:0000000000000205/5",
+        "epB": "of:0000000000000206/5",
+        "type": "UiDeviceLink",
+        "portA": "5",
+        "portB": "5",
+        "rollup": [
+          {
+            "id": "of:0000000000000205/5~of:0000000000000206/5",
+            "epA": "of:0000000000000205/5",
+            "epB": "of:0000000000000206/5",
+            "type": "UiDeviceLink",
+            "portA": "5",
+            "portB": "5"
+          }
+        ]
+      },
+      {
+        "id": "00:BB:00:00:00:02/None~of:0000000000000204/9",
+        "epA": "00:BB:00:00:00:02/None",
+        "epB": "of:0000000000000204",
+        "type": "UiEdgeLink",
+        "portB": "9",
+        "rollup": [
+          {
+            "id": "00:BB:00:00:00:02/None~of:0000000000000204/9",
+            "epA": "00:BB:00:00:00:02/None",
+            "epB": "of:0000000000000204",
+            "type": "UiEdgeLink",
+            "portB": "9"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000204/3~of:0000000000000227/3",
+        "epA": "of:0000000000000204/3",
+        "epB": "of:0000000000000227/3",
+        "type": "UiDeviceLink",
+        "portA": "3",
+        "portB": "3",
+        "rollup": [
+          {
+            "id": "of:0000000000000204/3~of:0000000000000227/3",
+            "epA": "of:0000000000000204/3",
+            "epB": "of:0000000000000227/3",
+            "type": "UiDeviceLink",
+            "portA": "3",
+            "portB": "3"
+          }
+        ]
+      },
+      {
+        "id": "00:EE:00:00:00:01/None~of:0000000000000207/4",
+        "epA": "00:EE:00:00:00:01/None",
+        "epB": "of:0000000000000207",
+        "type": "UiEdgeLink",
+        "portB": "4",
+        "rollup": [
+          {
+            "id": "00:EE:00:00:00:01/None~of:0000000000000207/4",
+            "epA": "00:EE:00:00:00:01/None",
+            "epB": "of:0000000000000207",
+            "type": "UiEdgeLink",
+            "portB": "4"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000203/4~of:0000000000000227/2",
+        "epA": "of:0000000000000203/4",
+        "epB": "of:0000000000000227/2",
+        "type": "UiDeviceLink",
+        "portA": "4",
+        "portB": "2",
+        "rollup": [
+          {
+            "id": "of:0000000000000203/4~of:0000000000000227/2",
+            "epA": "of:0000000000000203/4",
+            "epB": "of:0000000000000227/2",
+            "type": "UiDeviceLink",
+            "portA": "4",
+            "portB": "2"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000205/2~of:0000000000000226/6",
+        "epA": "of:0000000000000205/2",
+        "epB": "of:0000000000000226/6",
+        "type": "UiDeviceLink",
+        "portA": "2",
+        "portB": "6",
+        "rollup": [
+          {
+            "id": "of:0000000000000205/2~of:0000000000000226/6",
+            "epA": "of:0000000000000205/2",
+            "epB": "of:0000000000000226/6",
+            "type": "UiDeviceLink",
+            "portA": "2",
+            "portB": "6"
+          }
+        ]
+      },
+      {
+        "id": "00:99:00:00:00:01/None~of:0000000000000205/10",
+        "epA": "00:99:00:00:00:01/None",
+        "epB": "of:0000000000000205",
+        "type": "UiEdgeLink",
+        "portB": "10",
+        "rollup": [
+          {
+            "id": "00:99:00:00:00:01/None~of:0000000000000205/10",
+            "epA": "00:99:00:00:00:01/None",
+            "epB": "of:0000000000000205",
+            "type": "UiEdgeLink",
+            "portB": "10"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000205/4~of:0000000000000227/6",
+        "epA": "of:0000000000000205/4",
+        "epB": "of:0000000000000227/6",
+        "type": "UiDeviceLink",
+        "portA": "4",
+        "portB": "6",
+        "rollup": [
+          {
+            "id": "of:0000000000000205/4~of:0000000000000227/6",
+            "epA": "of:0000000000000205/4",
+            "epB": "of:0000000000000227/6",
+            "type": "UiDeviceLink",
+            "portA": "4",
+            "portB": "6"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000206/3~of:0000000000000227/7",
+        "epA": "of:0000000000000206/3",
+        "epB": "of:0000000000000227/7",
+        "type": "UiDeviceLink",
+        "portA": "3",
+        "portB": "7",
+        "rollup": [
+          {
+            "id": "of:0000000000000206/3~of:0000000000000227/7",
+            "epA": "of:0000000000000206/3",
+            "epB": "of:0000000000000227/7",
+            "type": "UiDeviceLink",
+            "portA": "3",
+            "portB": "7"
+          }
+        ]
+      },
+      {
+        "id": "00:BB:00:00:00:04/None~of:0000000000000205/9",
+        "epA": "00:BB:00:00:00:04/None",
+        "epB": "of:0000000000000205",
+        "type": "UiEdgeLink",
+        "portB": "9",
+        "rollup": [
+          {
+            "id": "00:BB:00:00:00:04/None~of:0000000000000205/9",
+            "epA": "00:BB:00:00:00:04/None",
+            "epB": "of:0000000000000205",
+            "type": "UiEdgeLink",
+            "portB": "9"
+          }
+        ]
+      },
+      {
+        "id": "00:AA:00:00:00:02/None~of:0000000000000204/7",
+        "epA": "00:AA:00:00:00:02/None",
+        "epB": "of:0000000000000204",
+        "type": "UiEdgeLink",
+        "portB": "7",
+        "rollup": [
+          {
+            "id": "00:AA:00:00:00:02/None~of:0000000000000204/7",
+            "epA": "00:AA:00:00:00:02/None",
+            "epB": "of:0000000000000204",
+            "type": "UiEdgeLink",
+            "portB": "7"
+          }
+        ]
+      },
+      {
+        "id": "00:BB:00:00:00:01/None~of:0000000000000204/8",
+        "epA": "00:BB:00:00:00:01/None",
+        "epB": "of:0000000000000204",
+        "type": "UiEdgeLink",
+        "portB": "8",
+        "rollup": [
+          {
+            "id": "00:BB:00:00:00:01/None~of:0000000000000204/8",
+            "epA": "00:BB:00:00:00:01/None",
+            "epB": "of:0000000000000204",
+            "type": "UiEdgeLink",
+            "portB": "8"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000207/1~of:0000000000000246/1",
+        "epA": "of:0000000000000207/1",
+        "epB": "of:0000000000000246/1",
+        "type": "UiDeviceLink",
+        "portA": "1",
+        "portB": "1",
+        "rollup": [
+          {
+            "id": "of:0000000000000207/1~of:0000000000000246/1",
+            "epA": "of:0000000000000207/1",
+            "epB": "of:0000000000000246/1",
+            "type": "UiDeviceLink",
+            "portA": "1",
+            "portB": "1"
+          }
+        ]
+      },
+      {
+        "id": "00:88:00:00:00:02/None~of:0000000000000206/7",
+        "epA": "00:88:00:00:00:02/None",
+        "epB": "of:0000000000000206",
+        "type": "UiEdgeLink",
+        "portB": "7",
+        "rollup": [
+          {
+            "id": "00:88:00:00:00:02/None~of:0000000000000206/7",
+            "epA": "00:88:00:00:00:02/None",
+            "epB": "of:0000000000000206",
+            "type": "UiEdgeLink",
+            "portB": "7"
+          }
+        ]
+      },
+      {
+        "id": "00:EE:00:00:00:02/None~of:0000000000000208/4",
+        "epA": "00:EE:00:00:00:02/None",
+        "epB": "of:0000000000000208",
+        "type": "UiEdgeLink",
+        "portB": "4",
+        "rollup": [
+          {
+            "id": "00:EE:00:00:00:02/None~of:0000000000000208/4",
+            "epA": "00:EE:00:00:00:02/None",
+            "epB": "of:0000000000000208",
+            "type": "UiEdgeLink",
+            "portB": "4"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000204/4~of:0000000000000227/4",
+        "epA": "of:0000000000000204/4",
+        "epB": "of:0000000000000227/4",
+        "type": "UiDeviceLink",
+        "portA": "4",
+        "portB": "4",
+        "rollup": [
+          {
+            "id": "of:0000000000000204/4~of:0000000000000227/4",
+            "epA": "of:0000000000000204/4",
+            "epB": "of:0000000000000227/4",
+            "type": "UiDeviceLink",
+            "portA": "4",
+            "portB": "4"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000203/5~of:0000000000000204/5",
+        "epA": "of:0000000000000203/5",
+        "epB": "of:0000000000000204/5",
+        "type": "UiDeviceLink",
+        "portA": "5",
+        "portB": "5",
+        "rollup": [
+          {
+            "id": "of:0000000000000203/5~of:0000000000000204/5",
+            "epA": "of:0000000000000203/5",
+            "epB": "of:0000000000000204/5",
+            "type": "UiDeviceLink",
+            "portA": "5",
+            "portB": "5"
+          }
+        ]
+      },
+      {
+        "id": "of:0000000000000227/9~of:0000000000000247/3",
+        "epA": "of:0000000000000227/9",
+        "epB": "of:0000000000000247/3",
+        "type": "UiDeviceLink",
+        "portA": "9",
+        "portB": "3",
+        "rollup": [
+          {
+            "id": "of:0000000000000227/9~of:0000000000000247/3",
+            "epA": "of:0000000000000227/9",
+            "epB": "of:0000000000000247/3",
+            "type": "UiDeviceLink",
+            "portA": "9",
+            "portB": "3"
+          }
+        ]
+      }
+    ],
+    "devices": [
+      [],
+      [],
+      [
+        {
+          "id": "of:0000000000000246",
+          "nodeType": "device",
+          "type": "switch",
+          "online": true,
+          "master": "10.192.19.68",
+          "layer": "def",
+          "props": {
+            "managementAddress": "10.192.19.69",
+            "protocol": "OF_13",
+            "driver": "ofdpa-ovs",
+            "latitude": "40.15",
+            "name": "s246",
+            "locType": "geo",
+            "channelId": "10.192.19.69:59980",
+            "longitude": "-121.679"
+          },
+          "location": {
+            "locType": "geo",
+            "latOrY": 40.15,
+            "longOrX": -121.679
+          }
+        },
+        {
+          "id": "of:0000000000000206",
+          "nodeType": "device",
+          "type": "switch",
+          "online": true,
+          "master": "10.192.19.68",
+          "layer": "def",
+          "props": {
+            "managementAddress": "10.192.19.69",
+            "protocol": "OF_13",
+            "driver": "ofdpa-ovs",
+            "latitude": "36.766",
+            "name": "s206",
+            "locType": "geo",
+            "channelId": "10.192.19.69:59975",
+            "longitude": "-92.029"
+          },
+          "location": {
+            "locType": "geo",
+            "latOrY": 36.766,
+            "longOrX": -92.029
+          }
+        },
+        {
+          "id": "of:0000000000000227",
+          "nodeType": "device",
+          "type": "switch",
+          "online": true,
+          "master": "10.192.19.68",
+          "layer": "def",
+          "props": {
+            "managementAddress": "10.192.19.69",
+            "protocol": "OF_13",
+            "driver": "ofdpa-ovs",
+            "latitude": "44.205",
+            "name": "s227",
+            "locType": "geo",
+            "channelId": "10.192.19.69:59979",
+            "longitude": "-96.359"
+          },
+          "location": {
+            "locType": "geo",
+            "latOrY": 44.205,
+            "longOrX": -96.359
+          }
+        },
+        {
+          "id": "of:0000000000000208",
+          "nodeType": "device",
+          "type": "switch",
+          "online": true,
+          "master": "10.192.19.68",
+          "layer": "def",
+          "props": {
+            "managementAddress": "10.192.19.69",
+            "protocol": "OF_13",
+            "driver": "ofdpa-ovs",
+            "latitude": "36.766",
+            "name": "s208",
+            "locType": "geo",
+            "channelId": "10.192.19.69:59977",
+            "longitude": "-116.029"
+          },
+          "location": {
+            "locType": "geo",
+            "latOrY": 36.766,
+            "longOrX": -116.029
+          }
+        },
+        {
+          "id": "of:0000000000000205",
+          "nodeType": "device",
+          "type": "switch",
+          "online": true,
+          "master": "10.192.19.68",
+          "layer": "def",
+          "props": {
+            "managementAddress": "10.192.19.69",
+            "protocol": "OF_13",
+            "driver": "ofdpa-ovs",
+            "latitude": "36.766",
+            "name": "s205",
+            "locType": "geo",
+            "channelId": "10.192.19.69:59974",
+            "longitude": "-96.89"
+          },
+          "location": {
+            "locType": "geo",
+            "latOrY": 36.766,
+            "longOrX": -96.89
+          }
+        },
+        {
+          "id": "of:0000000000000247",
+          "nodeType": "device",
+          "type": "switch",
+          "online": true,
+          "master": "10.192.19.68",
+          "layer": "def",
+          "props": {
+            "managementAddress": "10.192.19.69",
+            "protocol": "OF_13",
+            "driver": "ofdpa-ovs",
+            "latitude": "40.205",
+            "name": "s247",
+            "locType": "geo",
+            "channelId": "10.192.19.69:59981",
+            "longitude": "-117.359"
+          },
+          "location": {
+            "locType": "geo",
+            "latOrY": 40.205,
+            "longOrX": -117.359
+          }
+        },
+        {
+          "id": "of:0000000000000226",
+          "nodeType": "device",
+          "type": "switch",
+          "online": true,
+          "master": "10.192.19.68",
+          "layer": "def",
+          "props": {
+            "managementAddress": "10.192.19.69",
+            "protocol": "OF_13",
+            "driver": "ofdpa-ovs",
+            "latitude": "44.15",
+            "name": "s226",
+            "locType": "geo",
+            "channelId": "10.192.19.69:59978",
+            "longitude": "-107.679"
+          },
+          "location": {
+            "locType": "geo",
+            "latOrY": 44.15,
+            "longOrX": -107.679
+          }
+        },
+        {
+          "id": "of:0000000000000203",
+          "nodeType": "device",
+          "type": "switch",
+          "online": true,
+          "master": "10.192.19.68",
+          "layer": "def",
+          "props": {
+            "managementAddress": "10.192.19.69",
+            "protocol": "OF_13",
+            "driver": "ofdpa-ovs",
+            "latitude": "36.766",
+            "name": "s203",
+            "locType": "geo",
+            "channelId": "10.192.19.69:59972",
+            "longitude": "-111.359"
+          },
+          "location": {
+            "locType": "geo",
+            "latOrY": 36.766,
+            "longOrX": -111.359
+          }
+        },
+        {
+          "id": "of:0000000000000204",
+          "nodeType": "device",
+          "type": "switch",
+          "online": true,
+          "master": "10.192.19.68",
+          "layer": "def",
+          "props": {
+            "managementAddress": "10.192.19.69",
+            "protocol": "OF_13",
+            "driver": "ofdpa-ovs",
+            "latitude": "36.766",
+            "name": "s204",
+            "locType": "geo",
+            "channelId": "10.192.19.69:59973",
+            "longitude": "-106.359"
+          },
+          "location": {
+            "locType": "geo",
+            "latOrY": 36.766,
+            "longOrX": -106.359
+          }
+        },
+        {
+          "id": "of:0000000000000207",
+          "nodeType": "device",
+          "type": "switch",
+          "online": true,
+          "master": "10.192.19.68",
+          "layer": "def",
+          "props": {
+            "managementAddress": "10.192.19.69",
+            "protocol": "OF_13",
+            "driver": "ofdpa-ovs",
+            "latitude": "36.766",
+            "name": "s207",
+            "locType": "geo",
+            "channelId": "10.192.19.69:59976",
+            "longitude": "-122.359"
+          },
+          "location": {
+            "locType": "geo",
+            "latOrY": 36.766,
+            "longOrX": -122.359
+          }
+        }
+      ]
+    ],
+    "hosts": [
+      [],
+      [],
+      [
+        {
+          "id": "00:88:00:00:00:03/110",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::288:ff:fe00:3",
+            "2000::102",
+            "10.0.1.2"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:DD:00:00:00:01/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:88:00:00:00:04/160",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::288:ff:fe00:4",
+            "10.0.6.2",
+            "2000::602"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:BB:00:00:00:02/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::2bb:ff:fe00:2"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:AA:00:00:00:05/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:88:00:00:00:01/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::288:ff:fe00:1",
+            "2000::101",
+            "10.0.1.1"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:AA:00:00:00:01/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:AA:00:00:00:03/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:BB:00:00:00:04/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::2bb:ff:fe00:4"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:EE:00:00:00:02/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::2ee:ff:fe00:2"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:99:00:00:00:01/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "10.0.3.253",
+            "fe80::299:ff:fe00:1"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:99:66:00:00:01/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::299:66ff:fe00:1",
+            "2000::3fd"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:EE:00:00:00:01/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::2ee:ff:fe00:1"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:BB:00:00:00:01/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::2bb:ff:fe00:1"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:BB:00:00:00:03/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::2bb:ff:fe00:3"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:AA:00:00:00:04/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:BB:00:00:00:05/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::2bb:ff:fe00:5"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:88:00:00:00:02/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [
+            "fe80::288:ff:fe00:2",
+            "2000::601",
+            "10.0.6.1"
+          ],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:AA:00:00:00:02/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [],
+          "props": {},
+          "configured": false
+        },
+        {
+          "id": "00:DD:00:00:00:02/None",
+          "nodeType": "host",
+          "layer": "def",
+          "ips": [],
+          "props": {},
+          "configured": false
+        }
+      ]
+    ],
+    "layerOrder": [
+      "opt",
+      "pkt",
+      "def"
+    ]
+  }
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.css b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.css
new file mode 100644
index 0000000..373b57b
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.css
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+.status.e {
+    fill: #c72930;
+}
+
+.text.e {
+    fill: white;
+    font-weight: bold;
+}
+
+.status.w {
+    fill: #db7773;
+}
+
+.text.w {
+    fill: white;
+    font-weight: bold;
+}
+
+.status.i {
+    fill: #007dc4;
+}
+
+.text.i {
+    fill: white;
+}
+
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.html b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.html
new file mode 100644
index 0000000..e8dfcb9
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.html
@@ -0,0 +1,19 @@
+<!--
+~ Copyright 2019-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<svg:g xmlns:svg="http://www.w3.org/2000/svg" [title]="badge.msg">
+    <svg:circle r="12" [ngClass]="['status', badge.status ? badge.status : '']" cx="-18" cy="-18"></svg:circle>
+    <svg:text x="-18" y="-11" text-anchor="middle" [ngClass]="['text', badge.status ? badge.status : '']">{{ badge.txt }}</svg:text>
+</svg:g>
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.spec.ts
new file mode 100644
index 0000000..2b457b4
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.spec.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {BadgeSvgComponent} from './badgesvg.component';
+
+describe('BadgeSvgComponent', () => {
+    let component: BadgeSvgComponent;
+    let fixture: ComponentFixture<BadgeSvgComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            declarations: [BadgeSvgComponent]
+        })
+        .compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(BadgeSvgComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.ts
new file mode 100644
index 0000000..f0bbc7a
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/badgesvg/badgesvg.component.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Component, Input, OnInit} from '@angular/core';
+import {Badge} from '../../models';
+
+@Component({
+    selector: '[onos-badgesvg]',
+    templateUrl: './badgesvg.component.html',
+    styleUrls: ['./badgesvg.component.css']
+})
+export class BadgeSvgComponent implements OnInit {
+    @Input() badge: Badge = <Badge>{};
+
+    constructor() {
+    }
+
+    ngOnInit() {
+    }
+
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.css b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.css
new file mode 100644
index 0000000..e7ce209
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.css
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology View (forces device visual) -- CSS file
+ */
+g.node.device rect {
+    fill: #f0f0f070;
+}
+g.node.device text {
+    fill: #bbb;
+}
+g.node.device use {
+    fill: #777;
+}
+
+
+g.node.device.online rect {
+    fill: #fafafad0;
+}
+g.node.device.online text {
+    fill: #3c3a3a;
+}
+g.node.device.online use {
+    /* NOTE: this gets overridden programatically */
+    fill: #ffffff;
+}
+
+g.node.selected .node-container {
+    stroke-width: 2.0;
+    stroke: #009fdb;
+}
+
+g.node.hovered .node-container {
+    stroke-width: 2.0;
+    stroke: #454545;
+}
+
+path.bracket {
+    stroke: white;
+    stroke-width: 1;
+    fill: none
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html
new file mode 100644
index 0000000..c4f5621
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html
@@ -0,0 +1,100 @@
+<!--
+~ Copyright 2019-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<svg:defs xmlns:svg="http://www.w3.org/2000/svg">
+    <!-- Template explanation: Define an SVG Filter that in
+        line 0) creates a box big enough to accommodate the drop shadow
+        line 1) render the target object in to a bit map and apply a blur to it
+            based on its alpha channel
+        line 2) take that blurred layer and shift it down and to the right by 4
+        line 3) Merge this blurred and shifted layer and overlay it with the
+            original target object
+    -->
+    <svg:filter id="drop-shadow" x="-25%" y="-25%" width="200%" height="200%">
+        <svg:feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur" />
+        <svg:feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>
+        <svg:feMerge >
+            <svg:feMergeNode in="offsetBlur" />
+            <svg:feMergeNode in="SourceGraphic" />
+        </svg:feMerge>
+    </svg:filter>
+    <!-- Template explanation: Define a colour gradient that can be used in icons -->
+    <svg:linearGradient id="diagonal_blue" x1="0%" y1="0%" x2="100%" y2="100%">
+        <svg:stop offset= "0%" style="stop-color: #7fabdb;" />
+        <svg:stop offset= "100%" style="stop-color: #5b99d2;" />
+    </svg:linearGradient>
+</svg:defs>
+<!-- Template explanation: Creates an SVG Group and in
+    line 1) transform it to the position calculated by the d3 force graph engine
+            and scale it inversely to the zoom level
+    line 2) Give it various CSS styles depending on attributes
+    line 3) When it is clicked, call the method that toggles the selection and
+        emits an event.
+    Other child objects have their own description
+-->
+<svg:g xmlns:svg="http://www.w3.org/2000/svg"
+       [attr.transform]="'translate(' + device?.x + ',' + device?.y + '), scale(' + scale + ')'"
+        [ngClass]="['node', 'device', device.online?'online':'', selected?'selected':'']"
+        (click)="toggleSelected(device, $event)">
+    <svg:desc>Device {{device.id}}</svg:desc>
+    <!-- Template explanation: Creates an SVG Rectangle and in
+        line 1) set a css style and shift so that it's centred
+        line 2) set the initial width and height - width changes with label
+        line 3) link to the animation 'deviceLabelToggle', pass in to it a width
+            calculated from the width of the text, and additional padding at the end
+        line 4) Apply the filter defined above to this rectangle (even as its
+            width changes
+    -->
+    <svg:rect
+            class="node-container" x="-18" y="-18"
+            width="36" height="36"
+            [@deviceLabelToggle]="{ value: labelToggle, params: {txtWidth: (36 + labelTextLen() * 1.1)+'px' }}"
+            filter= "url(#drop-shadow)">
+    </svg:rect>
+    <!-- Template explanation: Creates an SVG Rectangle slightly smaller and
+        overlaid on the above. This is the blue box, and its width and height does
+        not change
+    -->
+    <svg:rect x="-16" y="-16" width="32" height="32" [ngStyle]="{'fill': panelColor}">
+    </svg:rect>
+    <!-- Create an L shaped bracket on bottom left of icon if it has either grid or geo location-->
+    <svg:path *ngIf="device.location && device.location.locType != 'none'"
+              d="M-15 12 v3 h3" class="bracket">
+    </svg:path>
+    <!-- Create an L shaped bracket on top right of icon if it has been pinned or has fixed location-->
+    <svg:path *ngIf="device.fx != null"
+              d="M15 -12 v-3 h-3" class="bracket">
+    </svg:path>
+    <!-- Template explanation: Creates an SVG Text element and in
+        line 1) make it left aligned and slightly down and to the right of the last rect
+        line 2) set its text length to be the calculated value - see that function
+        line 3) because of kerning the actual text might be shorter or longer than
+            the pre-calculated value - if so change the spacing between the letters
+            (and not the letter width to compensate)
+        line 4) link to the animation deviceLabelToggleTxt, so that the text appears
+            in gently
+        line 5) The text will be one of 3 values - blank, the id or the name
+    -->
+    <svg:text
+            text-anchor="start" y="0.3em" x="22"
+            [attr.textLength]= "labelTextLen()"
+            lengthAdjust= "spacing"
+            [@deviceLabelToggleTxt]="labelToggle">
+        {{ labelToggle == 0 ? '': labelToggle == 1 ? device.id:device.props.name }}
+    </svg:text>
+    <svg:use [attr.xlink:href]="'#' + deviceIcon()" width="36" height="36" x="-18" y="-18">
+    </svg:use>
+    <svg:g *ngIf="badge" onos-badgesvg [badge]="badge"></svg:g>
+</svg:g>
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts
new file mode 100644
index 0000000..deb175e
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DeviceNodeSvgComponent } from './devicenodesvg.component';
+import {FnService, IconService, LogService, SvgUtilService} from '../../../../../../gui2-fw-lib/public_api';
+import {ActivatedRoute, Params} from '@angular/router';
+import {of} from 'rxjs';
+import {ChangeDetectorRef} from '@angular/core';
+import {Device} from '../../models';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {TopologyService} from '../../../../topology.service';
+import {BadgeSvgComponent} from '../badgesvg/badgesvg.component';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+class MockIconService {
+    loadIconDef() { }
+}
+
+class MockSvgUtilService {
+
+    cat7() {
+        const tcid = 'd3utilTestCard';
+
+        function getColor(id, muted, theme) {
+            // NOTE: since we are lazily assigning domain ids, we need to
+            //       get the color from all 4 scales, to keep the domains
+            //       in sync.
+            const ln = '#5b99d2';
+            const lm = '#9ebedf';
+            const dn = '#5b99d2';
+            const dm = '#9ebedf';
+            if (theme === 'dark') {
+                return muted ? dm : dn;
+            } else {
+                return muted ? lm : ln;
+            }
+        }
+
+        return {
+            // testCard: testCard,
+            getColor: getColor,
+        };
+    }
+}
+
+class MockTopologyService {
+    public instancesIndex: Map<string, number>;
+    constructor() {
+        this.instancesIndex = new Map();
+    }
+}
+
+describe('DeviceNodeSvgComponent', () => {
+    let fs: FnService;
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: DeviceNodeSvgComponent;
+    let fixture: ComponentFixture<DeviceNodeSvgComponent>;
+    let windowMock: Window;
+    let ar: MockActivatedRoute;
+    let testDevice: Device;
+
+
+    beforeEach(async(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+        testDevice = new Device('test:1');
+        testDevice.online = true;
+
+        windowMock = <any>{
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+        fs = new FnService(ar, logSpy, windowMock);
+
+        TestBed.configureTestingModule({
+            imports: [ BrowserAnimationsModule ],
+            declarations: [ DeviceNodeSvgComponent, BadgeSvgComponent ],
+            providers: [
+                { provide: LogService, useValue: logSpy },
+                { provide: ActivatedRoute, useValue: ar },
+                { provide: ChangeDetectorRef, useClass: ChangeDetectorRef },
+                { provide: IconService, useClass: MockIconService },
+                { provide: SvgUtilService, useClass: MockSvgUtilService },
+                { provide: TopologyService, useClass: MockTopologyService },
+                { provide: 'Window', useValue: windowMock },
+            ]
+        })
+        .compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(DeviceNodeSvgComponent);
+        component = fixture.componentInstance;
+        component.device = testDevice;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts
new file mode 100644
index 0000000..2243e20
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    ChangeDetectionStrategy,
+    ChangeDetectorRef,
+    Component,
+    EventEmitter,
+    Input,
+    OnChanges, OnInit, Output,
+    SimpleChanges,
+} from '@angular/core';
+import {
+    Badge,
+    Device,
+    LabelToggle,
+} from '../../models';
+import {IconService, LogService, SvgUtilService} from '../../../../../../gui2-fw-lib/public_api';
+import {NodeVisual, SelectedEvent} from '../nodevisual';
+import {animate, state, style, transition, trigger} from '@angular/animations';
+import {TopologyService} from '../../../../topology.service';
+
+/**
+ * The Device node in the force graph
+ *
+ * Note: here the selector is given square brackets [] so that it can be
+ * inserted in SVG element like a directive
+ */
+@Component({
+    selector: '[onos-devicenodesvg]',
+    templateUrl: './devicenodesvg.component.html',
+    styleUrls: ['./devicenodesvg.component.css'],
+    changeDetection: ChangeDetectionStrategy.Default,
+    animations: [
+        trigger('deviceLabelToggle', [
+            state('0', style({ // none
+                width: '36px',
+            })),
+            state('1, 2', // id
+                style({ width: '{{ txtWidth }}'}),
+                { params: {'txtWidth': '36px'}}
+            ), // default
+            transition('0 => 1', animate('250ms ease-in')),
+            transition('1 => 2', animate('250ms ease-in')),
+            transition('* => 0', animate('250ms ease-out'))
+        ]),
+        trigger('deviceLabelToggleTxt', [
+            state('0', style( {
+                opacity: 0,
+            })),
+            state( '1,2', style({
+                opacity: 1.0
+            })),
+            transition('0 => 1', animate('250ms ease-in')),
+            transition('* => 0', animate('250ms ease-out'))
+        ])
+    ]
+})
+export class DeviceNodeSvgComponent extends NodeVisual implements OnInit, OnChanges {
+    @Input() device: Device;
+    @Input() scale: number = 1.0;
+    @Input() labelToggle: LabelToggle.Enum = LabelToggle.Enum.NONE;
+    @Input() colorMuted: boolean = false;
+    @Input() colorTheme: string = 'light';
+    @Input() badge: Badge;
+    @Output() selectedEvent = new EventEmitter<SelectedEvent>();
+    textWidth: number = 36;
+    panelColor: string = '#9ebedf';
+
+    constructor(
+        protected log: LogService,
+        private is: IconService,
+        protected sus: SvgUtilService,
+        protected ts: TopologyService,
+        private ref: ChangeDetectorRef
+    ) {
+        super();
+    }
+
+    ngOnInit(): void {
+        this.panelColor = this.panelColour();
+    }
+
+    /**
+     * Called by parent (forcesvg) when a change happens
+     *
+     * There is a difficulty in passing the SVG text object to the animation
+     * directly, to get its width, so we capture it here and update textWidth
+     * local variable here and use it in the animation
+     */
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes['device']) {
+            if (!this.device.x) {
+                this.device.x = 0;
+                this.device.y = 0;
+            }
+            // The master might have changed - recalculate color
+            this.panelColor = this.panelColour();
+        }
+
+        if (changes['colorMuted']) {
+            this.colorMuted = changes['colorMuted'].currentValue;
+            this.panelColor = this.panelColour();
+        }
+
+        if (changes['badge']) {
+            this.badge = changes['badge'].currentValue;
+        }
+    }
+
+    /**
+     * Calculate the text length in advance as well as possible
+     *
+     * The length of SVG text cannot be exactly estimated, because depending on
+     * the letters kerning might mean that it is shorter or longer than expected
+     *
+     * This takes the approach of 8px width per letter of this size, that on average
+     * evens out over words. A word like 'ilj' will be much shorter than 'wm0'
+     * because of kerning
+     *
+     *
+     * In addition in the template, the <svg:text> properties
+     * textLength and lengthAdjust ensure that the text becomes long with extra
+     * wide spacing created as necessary.
+     *
+     * Other approaches like getBBox() of the text
+     */
+    labelTextLen() {
+        if (this.labelToggle === 1) {
+            return this.device.id.length * 8;
+        } else if (this.labelToggle === 2 && this.device &&
+            this.device.props.name && this.device.props.name.trim().length > 0) {
+            return this.device.props.name.length * 8;
+        } else {
+            return 0;
+        }
+    }
+
+    deviceIcon(): string {
+        if (this.device.props && this.device.props.uiType) {
+            this.is.loadIconDef(this.device.props.uiType);
+            return this.device.props.uiType;
+        } else {
+            return 'm_' + this.device.type;
+        }
+    }
+
+    /**
+     * Get a colour for the banner of the nth panel
+     * @param idx The index of the panel (0-6)
+     */
+    panelColour(): string {
+        const idx = this.ts.instancesIndex.get(this.device.master);
+        return this.sus.cat7().getColor(idx, this.colorMuted, this.colorTheme);
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.css b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.css
new file mode 100644
index 0000000..92a114f
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.css
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology View (forces host visual) -- CSS file
+ */
+.node.host text {
+    stroke: none;
+    font-size: 13px;
+    fill: #846;
+}
+
+.node.host circle {
+    stroke: #a3a596;
+    fill: #e0dfd6;
+}
+
+.node.host.selected > circle {
+    stroke-width: 2.0;
+    stroke: #009fdb;
+}
+
+.node.host use {
+    fill: #3c3a3a;
+}
+
+.node.host rect {
+    fill: #ffffff;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.html b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.html
new file mode 100644
index 0000000..bdaf54a
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.html
@@ -0,0 +1,71 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<svg:defs xmlns:svg="http://www.w3.org/2000/svg">
+    <!-- Template explanation: Define an SVG Filter that in
+        line 1) render the target object in to a bit map and apply a blur to it
+            based on its alpha channel
+        line 2) take that blurred layer and shift it down and to the right by 4
+        line 3) Merge this blurred and shifted layer and overlay it with the
+            original target object
+    -->
+    <svg:filter id="drop-shadow-host" x="-25%" y="-25%" width="200%" height="200%">
+        <svg:feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur" />
+        <svg:feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>
+        <svg:feMerge >
+            <svg:feMergeNode in="offsetBlur" />
+            <svg:feMergeNode in="SourceGraphic" />
+        </svg:feMerge>
+    </svg:filter>
+    <svg:radialGradient id="three_stops_radial">
+        <svg:stop offset= "0%" style="stop-color: #e3e5d6;" />
+        <svg:stop offset= "70%" style="stop-color: #c3c5b6;" />
+        <svg:stop offset="100%" style="stop-color: #a3a596;" />
+    </svg:radialGradient>
+</svg:defs>
+<!-- Template explanation: Creates an SVG Group and in
+    line 1) transform it to the position calculated by the d3 force graph engine
+    line 2) Give it various CSS styles depending on attributes
+    line 3) When it is clicked, call the method that toggles the selection and
+        emits an event.
+    Other child objects have their own description
+-->
+<svg:g  xmlns:svg="http://www.w3.org/2000/svg"
+        [attr.transform]="'translate(' + host?.x + ',' + host?.y + '), scale(' + scale + ')'"
+        [ngClass]="['node', 'host', 'endstation', 'fixed', selected?'selected':'', 'hovered']"
+        (click)="toggleSelected(host, $event)">
+    <svg:desc>Host {{host.id}}</svg:desc>
+    <!-- Template explanation: Creates an SVG Circle and in
+        line 1) Apply the drop shadow defined above to this circle
+        line 2) Apply the radial gradient defined above to the circle
+    -->
+    <svg:circle r="15"
+        filter="url(#drop-shadow-host)"
+        style="fill: url(#three_stops_radial)">
+    </svg:circle>
+    <svg:use xlink:href="#m_endstation" width="22.5" height="22.5" x="-11.25" y="-11.25">
+    </svg:use>
+    <!-- Template explanation: Creates an SVG Text
+        line 1) if the labelToggle is not 0
+        line 2) shift it below the circle, and have it centred with the circle
+        line 3) apply a scale and call on the hostName(0 method to get the
+            displayed value
+    -->
+    <svg:text
+        *ngIf="labelToggle != 0"
+        dy="30" text-anchor="middle"
+        >{{hostName()}}</svg:text>
+    <svg:g *ngIf="badge" onos-badgesvg [badge]="badge"></svg:g>
+</svg:g>
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.spec.ts
new file mode 100644
index 0000000..cf697bb
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.spec.ts
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HostNodeSvgComponent } from './hostnodesvg.component';
+import {ActivatedRoute, Params} from '@angular/router';
+import {of} from 'rxjs';
+import {LogService} from '../../../../../../gui2-fw-lib/public_api';
+import {Host} from '../../models';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {ChangeDetectorRef} from '@angular/core';
+import {BadgeSvgComponent} from '../badgesvg/badgesvg.component';
+
+class MockActivatedRoute extends ActivatedRoute {
+  constructor(params: Params) {
+    super();
+    this.queryParams = of(params);
+  }
+}
+
+describe('HostNodeSvgComponent', () => {
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: HostNodeSvgComponent;
+    let fixture: ComponentFixture<HostNodeSvgComponent>;
+    let ar: MockActivatedRoute;
+    let testHost: Host;
+
+    beforeEach(async(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+        testHost = new Host('host:1');
+        testHost.ips = ['10.205.86.123', '192.168.56.10'];
+
+        TestBed.configureTestingModule({
+            imports: [ BrowserAnimationsModule ],
+            declarations: [ HostNodeSvgComponent, BadgeSvgComponent ],
+            providers: [
+              { provide: LogService, useValue: logSpy },
+              { provide: ActivatedRoute, useValue: ar },
+              { provide: ChangeDetectorRef, useClass: ChangeDetectorRef }
+            ]
+        })
+        .compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(HostNodeSvgComponent);
+        component = fixture.componentInstance;
+        component.host = testHost;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.ts
new file mode 100644
index 0000000..decd099
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component.ts
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    Component,
+    EventEmitter,
+    Input,
+    OnChanges,
+    Output,
+    SimpleChanges
+} from '@angular/core';
+import {Badge, Host, HostLabelToggle, Node} from '../../models';
+import {LogService} from '../../../../../../gui2-fw-lib/public_api';
+import {NodeVisual, SelectedEvent} from '../nodevisual';
+
+/**
+ * The Host node in the force graph
+ *
+ * Note: here the selector is given square brackets [] so that it can be
+ * inserted in SVG element like a directive
+ */
+@Component({
+    selector: '[onos-hostnodesvg]',
+    templateUrl: './hostnodesvg.component.html',
+    styleUrls: ['./hostnodesvg.component.css']
+})
+export class HostNodeSvgComponent extends NodeVisual implements OnChanges {
+    @Input() host: Host;
+    @Input() scale: number = 1.0;
+    @Input() labelToggle: HostLabelToggle.Enum = HostLabelToggle.Enum.IP;
+    @Input() badge: Badge;
+    @Output() selectedEvent = new EventEmitter<SelectedEvent>();
+
+    constructor(
+        protected log: LogService
+    ) {
+        super();
+    }
+
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes['host']) {
+            if (!this.host.x) {
+                this.host.x = 0;
+                this.host.y = 0;
+            }
+        }
+
+        if (changes['badge']) {
+            this.badge = changes['badge'].currentValue;
+        }
+    }
+
+    hostName(): string {
+        if (this.host === undefined) {
+            return undefined;
+        } else if (this.labelToggle === HostLabelToggle.Enum.IP) {
+            return this.host.ips.join(',');
+        } else if (this.labelToggle === HostLabelToggle.Enum.MAC) {
+            return this.host.id;
+        } else {
+            return this.host.id; // Todo - replace with a friendly name
+        }
+
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.css b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.css
new file mode 100644
index 0000000..e5f12ae
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.css
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology View (forces link svg) -- CSS file
+ */
+/* --- Topo Links --- */
+line {
+    stroke: #888888;
+    stroke-width: 2px;
+}
+
+.link {
+    opacity: .9;
+}
+
+.link.selected {
+    stroke: #009fdb;
+}
+.link.enhanced {
+    stroke: #009fdb;
+    stroke-width: 4px;
+    cursor: pointer;
+}
+
+.link.inactive {
+    opacity: .5;
+    stroke-dasharray: 4 2;
+}
+/* TODO: Review for not-permitted links */
+.link.not-permitted {
+    stroke: rgb(255,0,0);
+    stroke-dasharray: 8 4;
+}
+
+.link.secondary {
+    stroke: rgba(0,153,51,0.5);
+}
+
+.link.secondary.port-traffic-green {
+    stroke: rgb(0,153,51);
+}
+
+.link.secondary.port-traffic-yellow {
+    stroke: rgb(128,145,27);
+}
+
+.link.secondary.port-traffic-orange {
+    stroke: rgb(255, 137, 3);
+}
+
+.link.secondary.port-traffic-red {
+    stroke: rgb(183, 30, 21);
+}
+
+/* Port traffic color visualization for Kbps, Mbps, and Gbps */
+
+.link.secondary.port-traffic-Kbps {
+    stroke: rgb(0,153,51);
+}
+
+.link.secondary.port-traffic-Mbps {
+    stroke: rgb(128,145,27);
+}
+
+.link.secondary.port-traffic-Gbps {
+    stroke: rgb(255, 137, 3);
+}
+
+.link.secondary.port-traffic-Gbps-choked {
+    stroke: rgb(183, 30, 21);
+}
+
+.link.animated {
+    stroke-dasharray: 8;
+    animation: ants 5s infinite linear;
+    /* below line could be added via Javascript, based on path, if we cared
+     * enough about the direction of ant-flow
+     */
+    /*animation-direction: reverse;*/
+}
+@keyframes ants {
+    from {
+        stroke-dashoffset: 0;
+    }
+    to {
+        stroke-dashoffset: 400;
+    }
+}
+
+.link.primary {
+    stroke-width: 4px;
+    stroke: #ffA300;
+}
+
+.link.secondary.optical {
+    stroke-width: 4px;
+    stroke: rgba(128,64,255,0.5);
+}
+
+.link.primary.optical {
+    stroke-width: 6px;
+    stroke: #74f;
+}
+
+/* Link Labels */
+.linkLabel rect {
+    stroke: none;
+    fill: #ffffff;
+}
+
+.linkLabel text {
+    fill: #444;
+    text-anchor: middle;
+}
+
+
+/* Port Labels */
+.portLabel rect {
+    stroke: #a3a596;
+    fill: #ffffff;
+}
+
+.portLabel {
+    fill: #444;
+    alignment-baseline: middle;
+    dominant-baseline: middle;
+}
+
+/* Number of Links Labels */
+
+
+#ov-topo2 text.numLinkText {
+    fill: #444;
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.html b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.html
new file mode 100644
index 0000000..ec3afae
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.html
@@ -0,0 +1,101 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<svg:defs xmlns:svg="http://www.w3.org/2000/svg">
+    <svg:filter id="glow">
+        <svg:feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0.9 0 0 0 0 0.9 0 0 0 0 1 0" />
+        <svg:feGaussianBlur stdDeviation="2.5" result="coloredBlur" />
+        <svg:feMerge>
+            <svg:feMergeNode in="coloredBlur" />
+            <svg:feMergeNode in="SourceGraphic"/>
+        </svg:feMerge>
+    </svg:filter>
+</svg:defs>
+<!-- Template explanation: Creates an SVG Line and in
+    line 1) transform end A to the position calculated by the d3 force graph engine
+    line 2) transform end B to the position calculated by the d3 force graph engine
+    line 3) Give it various CSS styles depending on attributes
+    ling 4) Change the line width depending on the scale
+    line 4) When it is clicked, call the method that toggles the selection and
+        emits an event.
+    line 5) When the mouse is moved over call on enhance() function. This will
+        flash up the port labels, and display the link in blue for 1 second
+    Other child objects have their own description
+-->
+<svg:line xmlns:svg="http://www.w3.org/2000/svg"
+        [attr.x1]="link.source?.x" [attr.y1]="link.source?.y"
+        [attr.x2]="link.target?.x" [attr.y2]="link.target?.y"
+        [ngClass]="['link', selected?'selected':'', enhanced?'enhanced':'', highlightAsString()]"
+        [ngStyle]="{'stroke-width': (enhanced ? 4 : 2) * scale + 'px'}"
+        (click)="toggleSelected(link, $event)"
+        (mouseover)="enhance()">
+<!--        [attr.filter]="highlighted?'url(#glow)':'none'">-->
+    <svg:desc>{{link.id}} {{linkHighlight?.css}} {{isHighlighted}}</svg:desc>
+</svg:line>
+<svg:g xmlns:svg="http://www.w3.org/2000/svg"
+       [ngClass]="['linkLabel']"
+       [attr.transform]="'scale(' + scale + ')'">
+    <!-- Template explanation: Creates SVG Text in the middle of the link to
+          show traffic and in:
+        line 1) Performs the animation 'linkLabelVisible' whenever the isHighlighted
+            boolean value changes
+        line 2 & 3) Sets the text at half way between the 2 end points of the line
+        Note: we do not use an *ngIf to enable or disable this, because that would
+        cause the fade out of the text to not work
+    -->
+    <svg:text xmlns:svg="http://www.w3.org/2000/svg"
+              [@linkLabelVisible]="isHighlighted"
+              [attr.x]="link.source?.x + (link.target?.x - link.source?.x)/2"
+              [attr.y]="link.source?.y + (link.target?.y - link.source?.y)/2"
+    >{{ linkHighlight?.label }}</svg:text>
+</svg:g>
+<!-- Template explanation: Creates an SVG Group if
+    line 1) 'enhanced' is active and port text exists
+    line 2) assigns classes to it
+-->
+<svg:g xmlns:svg="http://www.w3.org/2000/svg"
+       *ngIf="enhanced && link.portA"
+       class="portLabel"
+       [attr.transform]="'translate(' + labelPosSrc.x + ',' + labelPosSrc.y + '),scale(' + scale + ')'">
+    <!-- Template explanation: Creates an SVG Rectangle and in
+        line 1) transform end A to the position calculated by the d3 force graph engine
+        line 2) assigns classes to it
+    -->
+    <svg:rect
+            [attr.x]="2 - textLength(link.portA)/2" y="-8"
+            [attr.width]="4 + textLength(link.portA)" height="16" >
+    </svg:rect>
+    <!-- Template explanation: Creates SVG Text and in
+        line 1) transform it to the position calculated by the method labelPosSrc()
+        line 2) centre aligns it
+        line 3) ensures that the text fills the rectangle by adjusting spacing
+    -->
+    <svg:text y="2" text-anchor="middle"
+            [attr.textLength]= "textLength(link.portA)" lengthAdjust="spacing"
+    >{{ link.portA }}</svg:text>
+</svg:g>
+<!-- A repeat of the above, but for the other end of the line -->
+<svg:g xmlns:svg="http://www.w3.org/2000/svg"
+       *ngIf="enhanced && link.portB"
+       class="portLabel"
+       [attr.transform]="'translate(' + labelPosTgt.x + ',' + labelPosTgt.y + '),scale(' + scale + ')'">
+    <svg:rect
+            [attr.x]="2 - textLength(link.portB)/2" y="-8"
+            [attr.width]="4 + textLength(link.portB)" height="16">
+    </svg:rect>
+    <svg:text x="2" y="2" text-anchor="middle"
+            [attr.textLength]= "textLength(link.portB)" lengthAdjust="spacing"
+    >{{ link.portB }}</svg:text>
+</svg:g>
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.spec.ts
new file mode 100644
index 0000000..6418fb4
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.spec.ts
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LinkSvgComponent } from './linksvg.component';
+import {LogService} from '../../../../../../gui2-fw-lib/public_api';
+import {ActivatedRoute, Params} from '@angular/router';
+import {of} from 'rxjs';
+import {Device, Link, RegionLink, LinkType} from '../../models';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+describe('LinkVisualComponent', () => {
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: LinkSvgComponent;
+    let fixture: ComponentFixture<LinkSvgComponent>;
+    let ar: MockActivatedRoute;
+    let testLink: Link;
+    let testDeviceA: Device;
+    let testDeviceB: Device;
+
+    beforeEach(async(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+        testDeviceA = new Device('test:A');
+        testDeviceA.online = true;
+
+        testDeviceB = new Device('test:B');
+        testDeviceB.online = true;
+
+        testLink = new RegionLink(LinkType.UiDeviceLink, testDeviceA, testDeviceB);
+        testLink.id = 'test:A/1-test:B/1';
+
+        TestBed.configureTestingModule({
+            imports: [ BrowserAnimationsModule ],
+            declarations: [ LinkSvgComponent ],
+            providers: [
+                { provide: LogService, useValue: logSpy },
+                { provide: ActivatedRoute, useValue: ar },
+            ]
+        })
+        .compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(LinkSvgComponent);
+        component = fixture.componentInstance;
+        component.link = testLink;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.ts
new file mode 100644
index 0000000..9997897
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/linksvg/linksvg.component.ts
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    ChangeDetectorRef,
+    Component, EventEmitter,
+    Input, OnChanges, Output, SimpleChanges,
+} from '@angular/core';
+import {Link, LinkHighlight, UiElement} from '../../models';
+import {LogService} from '../../../../../../gui2-fw-lib/public_api';
+import {NodeVisual, SelectedEvent} from '../nodevisual';
+import {animate, state, style, transition, trigger} from '@angular/animations';
+
+interface Point {
+    x: number;
+    y: number;
+}
+
+/*
+ * LinkSvgComponent gets its data from 2 sources - the force SVG regionData (which
+ * gives the Link below), and other state data here.
+ */
+@Component({
+    selector: '[onos-linksvg]',
+    templateUrl: './linksvg.component.html',
+    styleUrls: ['./linksvg.component.css'],
+    animations: [
+        trigger('linkLabelVisible', [
+            state('true', style( {
+                opacity: 1.0,
+            })),
+            state( 'false', style({
+                opacity: 0
+            })),
+            transition('false => true', animate('500ms ease-in')),
+            transition('true => false', animate('1000ms ease-out'))
+        ])
+    ]
+})
+export class LinkSvgComponent extends NodeVisual implements OnChanges {
+    @Input() link: Link;
+    @Input() linkHighlight: LinkHighlight;
+    @Input() highlightsEnabled: boolean = true;
+    @Input() scale = 1.0;
+    isHighlighted: boolean = false;
+    @Output() selectedEvent = new EventEmitter<SelectedEvent>();
+    @Output() enhancedEvent = new EventEmitter<Link>();
+    enhanced: boolean = false;
+    labelPosSrc: Point = {x: 0, y: 0};
+    labelPosTgt: Point = {x: 0, y: 0};
+    lastTimer: any;
+
+    constructor(
+        protected log: LogService,
+        private ref: ChangeDetectorRef
+    ) {
+        super();
+    }
+
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes['linkHighlight']) {
+            const hl: LinkHighlight = changes['linkHighlight'].currentValue;
+            clearTimeout(this.lastTimer);
+            this.isHighlighted = true;
+            this.log.debug('Link highlighted', this.link.id);
+
+            if (hl.fadems > 0) {
+                this.lastTimer = setTimeout(() => {
+                    this.isHighlighted = false;
+                    this.linkHighlight = <LinkHighlight>{};
+                    this.ref.markForCheck();
+                }, this.linkHighlight.fadems); // Disappear slightly before next one comes in
+            }
+        }
+
+        this.ref.markForCheck();
+    }
+
+    highlightAsString(): string {
+        if (this.linkHighlight && this.linkHighlight.css) {
+            return this.linkHighlight.css;
+        }
+        return '';
+    }
+
+    enhance() {
+        if (!this.highlightsEnabled) {
+            return;
+        }
+        this.enhancedEvent.emit(this.link);
+        this.enhanced = true;
+        this.repositionLabels();
+        setTimeout(() => {
+            this.enhanced = false;
+            this.ref.markForCheck();
+        }, 1000);
+    }
+
+    /**
+     * We want to place the label for the port about 40 px from the node.
+     * If the distance between the nodes is less than 100, then just place the
+     * label 1/3 of the way from the node
+     */
+    repositionLabels(): void {
+        const x1: number = this.link.source.x;
+        const y1: number = this.link.source.y;
+        const x2: number = this.link.target.x;
+        const y2: number = this.link.target.y;
+
+        const dist = Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));
+        const offset = dist > 100 ? 40 : dist / 3;
+        this.labelPosSrc = <Point>{
+            x: x1 + (x2 - x1) * offset / dist,
+            y: y1 + (y2 - y1) * offset / dist
+        };
+
+        this.labelPosTgt = <Point>{
+            x: x2 - (x2 - x1) * offset / dist,
+            y: y2 - (y2 - y1) * offset / dist
+        };
+    }
+
+    /**
+     * For the 14pt font we are using, the average width seems to be about 8px
+     * @param text The string we want to calculate a width for
+     */
+    textLength(text: string) {
+        return text.length * 8;
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/nodevisual.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/nodevisual.ts
new file mode 100644
index 0000000..10bf3d8
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/nodevisual.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {EventEmitter} from '@angular/core';
+import {UiElement} from '../models';
+
+export interface SelectedEvent {
+    uiElement: UiElement;
+    deselecting: boolean;
+    isShift: boolean;
+    isCtrl: boolean;
+    isAlt: boolean;
+}
+
+/**
+ * A base class for the Host and Device components
+ */
+export abstract class NodeVisual {
+    selected: boolean;
+    selectedEvent = new EventEmitter<SelectedEvent>();
+
+    toggleSelected(uiElement: UiElement, event: MouseEvent) {
+        this.selected = !this.selected;
+        this.selectedEvent.emit(<SelectedEvent>{
+            uiElement: uiElement,
+            deselecting: !this.selected,
+            isShift: event.shiftKey,
+            isCtrl: event.ctrlKey,
+            isAlt: event.altKey
+        });
+    }
+
+    deselect() {
+        this.selected = false;
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.css b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.css
new file mode 100644
index 0000000..87c23bd
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.css
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology View (forces subRegion visual) -- CSS file
+ */
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.html b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.html
new file mode 100644
index 0000000..5760634
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.html
@@ -0,0 +1,23 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<svg:g  xmlns:svg="http://www.w3.org/2000/svg" [attr.transform]="'translate(' + subRegion?.x + ',' + subRegion?.y + ')'">>
+  <svg:circle
+          cx="0"
+          cy="0"
+          r="5">
+  </svg:circle>
+  <svg:text>{{subRegion?.id}}</svg:text>
+</svg:g>
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.spec.ts
new file mode 100644
index 0000000..d6f6446
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.spec.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SubRegionNodeSvgComponent } from './subregionnodesvg.component';
+import {SubRegion} from '../../models';
+
+describe('SubRegionNodeSvgComponent', () => {
+    let component: SubRegionNodeSvgComponent;
+    let fixture: ComponentFixture<SubRegionNodeSvgComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            declarations: [ SubRegionNodeSvgComponent ]
+        })
+        .compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(SubRegionNodeSvgComponent);
+        component = fixture.debugElement.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+
+    it('should create with an input', () => {
+        component.subRegion = new SubRegion('testId');
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.ts b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.ts
new file mode 100644
index 0000000..5dd4736
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
+import {SubRegion} from '../../models';
+
+/**
+ * The SubRegion node in the force graph
+ *
+ * Note 1: here the selector is given square brackets [] so that it can be
+ * inserted in SVG element like a directive
+ * Note 2: the selector is exactly the same as the @Input alias to make this
+ * directive trick work
+ */
+@Component({
+    selector: '[onos-subregionnodesvg]',
+    templateUrl: './subregionnodesvg.component.html',
+    styleUrls: ['./subregionnodesvg.component.css']
+})
+export class SubRegionNodeSvgComponent implements OnChanges {
+    @Input() subRegion: SubRegion;
+
+    ngOnChanges(changes: SimpleChanges) {
+        if (!this.subRegion.x) {
+            this.subRegion.x = 0;
+            this.subRegion.y = 0;
+        }
+    }
+
+}
diff --git a/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.css b/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.css
new file mode 100644
index 0000000..056a0a0
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.css
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+.gridrect {
+    stroke-width: 1;
+    fill: none;
+}
+
+.gridtext {
+    fill: lightgray;
+    text-anchor: middle;
+    dominant-baseline: middle;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.html b/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.html
new file mode 100644
index 0000000..c183ac1
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.html
@@ -0,0 +1,48 @@
+<!--
+~ Copyright 2019-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<svg:g *ngFor="let pt of gridPointsHoriz" xmlns:svg="http://www.w3.org/2000/svg"
+       [attr.transform]="'translate(' + horizCentreOffset + ',' + vertCentreOffset + '), ' +
+        'scale(' + gridScaleX + ',' + gridScaleY +')'">
+    <svg:desc>Vertical grid lines</svg:desc>
+    <svg:rect id="gridRectVert" class="gridrect"
+              [ngStyle]="{'stroke': gridcolor, 'stroke-width': 1/gridScaleX }"
+            [attr.width]="spacing"
+            [attr.height]="vertUpperLimit-vertLowerLimit"
+            [attr.x]="pt"
+            [attr.y]="vertLowerLimit">
+    </svg:rect>
+    <svg:text id="gridTextVert" class="gridtext"
+              [ngStyle]="{'stroke': gridcolor, 'font-size': 100/gridScaleX+'%', 'stroke-width': 1/gridScaleX }"
+            [attr.x]="pt"
+            [attr.y]="(vertUpperLimit - vertLowerLimit)/2">{{pt}}</svg:text>
+</svg:g>
+
+<svg:g *ngFor="let pt of gridPointsVert" xmlns:svg="http://www.w3.org/2000/svg"
+       [attr.transform]="'translate(' + horizCentreOffset + ',' + vertCentreOffset + '), ' +
+        'scale(' + gridScaleX + ',' + gridScaleY + ')'">
+    <svg:desc>Horizontal grid lines</svg:desc>
+    <svg:rect id="gridRectHoriz" class="gridrect"
+              [ngStyle]="{'stroke': gridcolor, 'stroke-width': 1/gridScaleY }"
+            [attr.width]="horizUpperLimit-horizLowerLimit"
+            [attr.height]="spacing"
+            [attr.x]="horizLowerLimit"
+            [attr.y]="pt">
+    </svg:rect>
+    <svg:text id="gridTextHoriz" class="gridtext"
+              [ngStyle]="{'stroke': gridcolor, 'font-size': 100/gridScaleY+'%', 'stroke-width': 1/gridScaleY }"
+            [attr.x]="(horizUpperLimit - horizLowerLimit)/2"
+            [attr.y]="invertVertical ? -pt : pt">{{pt}}</svg:text>
+</svg:g>
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.spec.ts
new file mode 100644
index 0000000..48a031a
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.spec.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { GridsvgComponent } from './gridsvg.component';
+
+describe('GridsvgComponent', () => {
+    let component: GridsvgComponent;
+    let fixture: ComponentFixture<GridsvgComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            declarations: [ GridsvgComponent ]
+        })
+        .compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(GridsvgComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.ts b/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.ts
new file mode 100644
index 0000000..e0934be
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/gridsvg/gridsvg.component.ts
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    Component,
+    Input,
+    OnChanges,
+    OnInit,
+    SimpleChanges
+} from '@angular/core';
+
+/**
+ * How to fit in to the 1000 by 100 SVG viewbox
+ */
+export enum FitOption {
+    FIT1000WIDE = 'fit1000wide',
+    FIT1000HIGH = 'fit1000high',
+    FITNONE = 'fitnone'// 1:1 ratio
+}
+
+const SVG_VIEWBOX_CENTRE = 500; // View box is 0,0,1000,1000
+
+@Component({
+    selector: '[onos-gridsvg]',
+    templateUrl: './gridsvg.component.html',
+    styleUrls: ['./gridsvg.component.css']
+})
+export class GridsvgComponent implements OnInit, OnChanges {
+    @Input() horizLowerLimit: number = 0;
+    @Input() horizUpperLimit: number = 1000;
+    @Input() vertLowerLimit: number = 0;
+    @Input() vertUpperLimit: number = 1000;
+    @Input() spacing: number = 100;
+    @Input() invertVertical: boolean = false;
+    @Input() gridcolor: string = '#e8e7e1'; // If specifying this in a template use [gridcolor]="'#e8e7e1'"
+    @Input() centre: boolean = true;
+    @Input() fit: FitOption = FitOption.FITNONE;
+    @Input() aspectRatio: number = 1.0;
+
+    gridPointsHoriz: number[];
+    gridPointsVert: number[];
+    horizCentreOffset: number = 0;
+    vertCentreOffset: number = 0;
+    gridScaleX: number = 1.0;
+    gridScaleY: number = 1.0;
+
+    public static calculateGridPoints(lwr: number, upper: number, step: number): number[] {
+        const gridPoints = new Array<number>();
+        for (let i = lwr; i < upper; i += step) {
+            gridPoints.push(i);
+        }
+        return gridPoints;
+    }
+
+    public static calcOffset(lwr: number, upper: number): number {
+        return -((upper + lwr) * (upper - lwr) / ((upper - lwr) * 2) - SVG_VIEWBOX_CENTRE);
+    }
+
+    public static calcScale(lwr: number, upper: number): number {
+        return SVG_VIEWBOX_CENTRE * 2 / Math.abs(upper - lwr);
+    }
+
+    constructor() { }
+
+    ngOnInit() {
+        this.gridPointsHoriz = GridsvgComponent.calculateGridPoints(
+            this.horizLowerLimit, this.horizUpperLimit, this.spacing);
+        this.gridPointsVert = GridsvgComponent.calculateGridPoints(
+            this.vertLowerLimit, this.vertUpperLimit, this.spacing);
+        this.horizCentreOffset = GridsvgComponent.calcOffset(this.horizUpperLimit, this.horizLowerLimit);
+        this.vertCentreOffset = GridsvgComponent.calcOffset(this.vertUpperLimit, this.vertLowerLimit);
+        this.gridScaleX = this.whichScale(this.fit, true);
+        this.gridScaleY = this.whichScale(this.fit, false);
+    }
+
+    ngOnChanges(changes: SimpleChanges) {
+        if (changes['horizLowerLimit'] ||
+            changes['horizUpperLimit'] ||
+            changes['horizSpacing']) {
+            this.gridPointsHoriz = GridsvgComponent.calculateGridPoints(
+                this.horizLowerLimit, this.horizUpperLimit, this.spacing);
+            this.horizCentreOffset = GridsvgComponent.calcOffset(this.horizUpperLimit, this.horizLowerLimit);
+        }
+        if (changes['vertLowerLimit'] ||
+            changes['vertUpperLimit'] ||
+            changes['vertSpacing'] ) {
+            this.gridPointsVert = GridsvgComponent.calculateGridPoints(
+                this.vertLowerLimit, this.vertUpperLimit, this.spacing);
+            this.vertCentreOffset = GridsvgComponent.calcOffset(this.vertUpperLimit, this.vertLowerLimit);
+        }
+    }
+
+    whichScale(fit: FitOption, isX: boolean): number {
+        if (fit === FitOption.FIT1000HIGH) {
+            return GridsvgComponent.calcScale(
+                    this.vertUpperLimit, this.vertLowerLimit) * (isX ? this.aspectRatio : 1.0);
+        } else if (fit === FitOption.FIT1000WIDE) {
+            return GridsvgComponent.calcScale(
+                this.horizUpperLimit, this.horizLowerLimit) * (isX ? 1.0 : this.aspectRatio);
+        } else {
+            return 1.0;
+        }
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.css b/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.css
new file mode 100644
index 0000000..d35aa7b
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.css
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ONOS GUI -- Topology Map Layer -- CSS file
+ */
+/* --- Topo Map --- */
+
+path.topo-map {
+    stroke-width: 0.05px;
+    stroke: #f4f4f4;
+    fill: #e5e5e6;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.html b/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.html
new file mode 100644
index 0000000..b399732
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.html
@@ -0,0 +1,21 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<svg:desc xmlns:svg="http://www.w3.org/2000/svg">Map of {{map.id}} in SVG format</svg:desc>
+<svg:path xmlns:svg="http://www.w3.org/2000/svg" id="bgmap" class="topo-map" *ngFor="let f of geodata?.features"  [attr.d]="pathGenerator(f)">
+<!-- Something about 'title' disagrees with Angular 8   <svg:title>{{ f.id }} {{f.properties?.name}}</svg:title>-->
+    <svg:desc>{{ f.id }} {{f.properties?.name}}</svg:desc>
+</svg:path>
+
diff --git a/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.spec.ts
new file mode 100644
index 0000000..454aad0
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.spec.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MapSvgComponent } from './mapsvg.component';
+import {HttpClient} from '@angular/common/http';
+import {from} from 'rxjs';
+import {LogService} from '../../../gui2-fw-lib/public_api';
+
+class MockHttpClient {
+    get() {
+        return from(['{"id":"app","icon":"nav_apps","cat":"PLATFORM","label":"Applications"}']);
+    }
+
+    subscribe() {}
+}
+
+describe('MapSvgComponent', () => {
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: MapSvgComponent;
+    let fixture: ComponentFixture<MapSvgComponent>;
+
+    beforeEach(async(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+
+        TestBed.configureTestingModule({
+            declarations: [ MapSvgComponent ],
+            providers: [
+                { provide: LogService, useValue: logSpy },
+                { provide: HttpClient, useClass: MockHttpClient },
+            ]
+        })
+        .compileComponents();
+
+        logServiceSpy = TestBed.get(LogService);
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(MapSvgComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.ts b/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.ts
new file mode 100644
index 0000000..993aebf
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.ts
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    Component, EventEmitter,
+    Input,
+    OnChanges, Output,
+    SimpleChanges
+} from '@angular/core';
+import { MapObject } from '../maputils';
+import {LogService, MapBounds} from '../../../../gui2-fw-lib/public_api';
+import {HttpClient} from '@angular/common/http';
+import * as d3 from 'd3';
+import * as topojson from 'topojson-client';
+
+const BUNDLED_URL_PREFIX = 'data/map/';
+
+/**
+ * Model of the transform attribute of a topojson file
+ */
+interface TopoDataTransform {
+    scale: number[];
+    translate: number[];
+}
+
+/**
+ * Model of the Feature returned prom topojson library
+ */
+interface Feature {
+    geometry: Object;
+    id: string;
+    properties: Object;
+    type: string;
+}
+
+/**
+ * Model of the Features Collection returned by the topojson.features function
+ */
+interface FeatureCollection {
+    type: string;
+    features: Feature[];
+}
+
+/**
+ * Model of the topojson file
+ */
+export interface TopoData {
+    type: string; // Usually "Topology"
+    objects: Object; // Can be a list of countries or individual countries
+    arcs: number[][][]; // Coordinates
+    bbox: number[]; // Bounding box
+    transform: TopoDataTransform; // scale and translate
+}
+
+@Component({
+    selector: '[onos-mapsvg]',
+    templateUrl: './mapsvg.component.html',
+    styleUrls: ['./mapsvg.component.css']
+})
+export class MapSvgComponent implements  OnChanges {
+    @Input() map: MapObject = <MapObject>{id: 'none'};
+    @Output() mapBounds = new EventEmitter<MapBounds>();
+
+    geodata: FeatureCollection;
+    pathgen: any; // (Feature) => string; have to leave it general, as there is the bounds method used below
+    // testPath: string;
+    // testFeature = <Feature>{
+    //     id: 'test',
+    //     type: 'Feature',
+    //     geometry: {
+    //         coordinates: [
+    //             [[-15, 60], [45, 60], [45, 45], [-15, 45], [-15, 60]],
+    //             [[-10, 55], [45, 55], [45, 50], [-10, 50], [-10, 55]],
+    //         ],
+    //         type: 'Polygon'
+    //     },
+    //     properties: { name: 'Test'}
+    // };
+
+    constructor(
+        private log: LogService,
+        private httpClient: HttpClient,
+    ) {
+        // Scale everything to 360 degrees wide and 150 high
+        // See background.component.html for more details
+        this.pathgen = d3.geoPath().projection(d3.geoIdentity().reflectY(true)
+            // MapSvgComponent.scale()
+        );
+
+        // this.log.debug('Feature Test',this.testFeature);
+        // this.testPath = this.pathgen(this.testFeature);
+        // this.log.debug('Feature Path', this.testPath);
+        this.log.debug('MapSvgComponent constructed');
+    }
+
+    static getUrl(id: string): string {
+        if (id && id[0] === '*') {
+            return BUNDLED_URL_PREFIX + id.slice(1) + '.topojson';
+        }
+        return id + '.topojson';
+    }
+
+    /**
+     * Wrapper for the path generator function
+     * @param feature The county or state within the map
+     */
+    pathGenerator(feature: Feature): string {
+        return this.pathgen(feature);
+    }
+
+    ngOnChanges(changes: SimpleChanges): void {
+        this.log.debug('Change detected', changes);
+        if (changes['map']) {
+            const map: MapObject = <MapObject>(changes['map'].currentValue);
+            if (map.id) {
+                this.httpClient
+                    .get(MapSvgComponent.getUrl(map.filePath))
+                    .subscribe((topoData: TopoData) => {
+                        // this.mapPathGenerator =
+                        this.handleTopoJson(map, topoData);
+                        this.log.debug('Path Generated for', map.id,
+                            'from', MapSvgComponent.getUrl(map.filePath));
+                    });
+            }
+        }
+    }
+
+    /**
+     * Handle the topojson file stream as it arrives back from the server
+     *
+     * The topojson library converts the topojson file in to a FeatureCollection
+     * d3.geo then further converts this in to a Path
+     *
+     * @param map The Map chosen in the GUI
+     * @param topoData The data in the TopoJson file
+     */
+    handleTopoJson(map: MapObject, topoData: TopoData): void {
+
+        let topoObject = topoData.objects[map.id];
+        if (!topoObject) {
+            topoObject = topoData.objects['states'];
+        }
+        this.log.debug('Topo obj', topoObject, 'topodata', topoData);
+        this.geodata = <FeatureCollection>topojson.feature(topoData, topoObject);
+        const bounds = this.pathgen.bounds(this.geodata);
+        this.mapBounds.emit(<MapBounds>{
+            lngMin: bounds[0][0],
+            latMin: -bounds[0][1], // Y was inverted in the transform
+            lngMax: bounds[1][0],
+            latMax: -bounds[1][1] // Y was inverted in the transform
+        });
+        this.log.debug('Map retrieved', topoData, this.geodata);
+
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layer/mapsvg/tests/bayarea.json b/web/gui2-topo-lib/lib/layer/mapsvg/tests/bayarea.json
new file mode 100644
index 0000000..f42f01e
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/mapsvg/tests/bayarea.json
@@ -0,0 +1,7951 @@
+{
+  "type": "Topology",
+  "objects": {
+    "bayareaGEO": {
+      "type": "GeometryCollection",
+      "geometries": [
+        {
+          "type": "Polygon",
+          "properties": {
+            "id": "1",
+            "id_2": null
+          },
+          "arcs": [
+            [
+              0
+            ]
+          ]
+        },
+        {
+          "type": "Polygon",
+          "properties": {
+            "id": "1",
+            "id_2": null
+          },
+          "arcs": [
+            [
+              1
+            ],
+            [
+              2
+            ],
+            [
+              3
+            ],
+            [
+              4
+            ]
+          ]
+        },
+        {
+          "type": "Polygon",
+          "properties": {
+            "id": null,
+            "id_2": null
+          },
+          "arcs": [
+            [
+              5
+            ]
+          ]
+        },
+        {
+          "type": "Polygon",
+          "properties": {
+            "id": null,
+            "id_2": null
+          },
+          "arcs": [
+            [
+              6
+            ]
+          ]
+        },
+        {
+          "type": "Polygon",
+          "properties": {
+            "id": null,
+            "id_2": null
+          },
+          "arcs": [
+            [
+              7
+            ]
+          ]
+        }
+      ]
+    }
+  },
+  "arcs": [
+    [
+      [
+        -121.88778346897,
+        37.470932004314
+      ],
+      [
+        -121.88441331969,
+        37.639946684024
+      ],
+      [
+        -121.69574230232,
+        37.610112237286
+      ],
+      [
+        -121.5591846074,
+        37.482407003812
+      ],
+      [
+        -121.65620306458,
+        37.372876810855
+      ],
+      [
+        -121.88025019411,
+        37.414543587788
+      ],
+      [
+        -121.88778346897,
+        37.470932004314
+      ]
+    ],
+    [
+      [
+        -122.41917671268,
+        37.241614091443
+      ],
+      [
+        -122.41848285842,
+        37.24869087536
+      ],
+      [
+        -122.41382412265,
+        37.25825206214
+      ],
+      [
+        -122.41481534302,
+        37.259757760846
+      ],
+      [
+        -122.41362587857,
+        37.260134185523
+      ],
+      [
+        -122.41293202431,
+        37.266985114633
+      ],
+      [
+        -122.41134607171,
+        37.266533405022
+      ],
+      [
+        -122.40648909186,
+        37.291377433665
+      ],
+      [
+        -122.40708382409,
+        37.296572094199
+      ],
+      [
+        -122.40490313926,
+        37.311553796321
+      ],
+      [
+        -122.40401104092,
+        37.32480394493
+      ],
+      [
+        -122.40093825776,
+        37.339635077181
+      ],
+      [
+        -122.40232596628,
+        37.348217559803
+      ],
+      [
+        -122.40093825776,
+        37.349196263962
+      ],
+      [
+        -122.39984791534,
+        37.356800042426
+      ],
+      [
+        -122.40143386795,
+        37.360639574125
+      ],
+      [
+        -122.40827328854,
+        37.364102681148
+      ],
+      [
+        -122.40886802077,
+        37.375621276246
+      ],
+      [
+        -122.41104870559,
+        37.377202259887
+      ],
+      [
+        -122.41085046152,
+        37.378933813399
+      ],
+      [
+        -122.41471622099,
+        37.379987802493
+      ],
+      [
+        -122.41630217359,
+        37.383902619128
+      ],
+      [
+        -122.41541007525,
+        37.385483602768
+      ],
+      [
+        -122.41848285842,
+        37.386914016539
+      ],
+      [
+        -122.42274510603,
+        37.392259246944
+      ],
+      [
+        -122.42284422807,
+        37.399637170602
+      ],
+      [
+        -122.42770120792,
+        37.406262244907
+      ],
+      [
+        -122.42750296384,
+        37.409800636865
+      ],
+      [
+        -122.43176521146,
+        37.41130633557
+      ],
+      [
+        -122.43632482519,
+        37.423653064957
+      ],
+      [
+        -122.43999234058,
+        37.427567881592
+      ],
+      [
+        -122.44098356096,
+        37.431859122903
+      ],
+      [
+        -122.44306512375,
+        37.432988396932
+      ],
+      [
+        -122.44455195431,
+        37.437656062919
+      ],
+      [
+        -122.44564229673,
+        37.461446102469
+      ],
+      [
+        -122.44960717823,
+        37.476728944331
+      ],
+      [
+        -122.45783430735,
+        37.490506087488
+      ],
+      [
+        -122.47012544002,
+        37.500594268816
+      ],
+      [
+        -122.47299997911,
+        37.500820123622
+      ],
+      [
+        -122.48757091864,
+        37.494496189058
+      ],
+      [
+        -122.49341911886,
+        37.492915205417
+      ],
+      [
+        -122.49520331554,
+        37.495776032958
+      ],
+      [
+        -122.49946556316,
+        37.495550178152
+      ],
+      [
+        -122.50075414965,
+        37.497733441275
+      ],
+      [
+        -122.49867258686,
+        37.500518983881
+      ],
+      [
+        -122.50045678353,
+        37.503379811421
+      ],
+      [
+        -122.51175669582,
+        37.513166853008
+      ],
+      [
+        -122.51413562473,
+        37.519566072507
+      ],
+      [
+        -122.51720840789,
+        37.522125760307
+      ],
+      [
+        -122.51810050623,
+        37.528148555129
+      ],
+      [
+        -122.51701016382,
+        37.529428399029
+      ],
+      [
+        -122.51988470291,
+        37.536204043205
+      ],
+      [
+        -122.51899260457,
+        37.537333317234
+      ],
+      [
+        -122.51522596714,
+        37.546066369727
+      ],
+      [
+        -122.5135408925,
+        37.555326416767
+      ],
+      [
+        -122.51582069936,
+        37.558563668984
+      ],
+      [
+        -122.51413562473,
+        37.560295222495
+      ],
+      [
+        -122.51532508918,
+        37.562478485618
+      ],
+      [
+        -122.51453211288,
+        37.566318017318
+      ],
+      [
+        -122.51800138419,
+        37.567673146153
+      ],
+      [
+        -122.51621718752,
+        37.569254129794
+      ],
+      [
+        -122.51829875031,
+        37.571136253176
+      ],
+      [
+        -122.51740665197,
+        37.573018376558
+      ],
+      [
+        -122.52047943513,
+        37.574147650587
+      ],
+      [
+        -122.51800138419,
+        37.576707338387
+      ],
+      [
+        -122.51730752993,
+        37.586795519715
+      ],
+      [
+        -122.51740665197,
+        37.591312615832
+      ],
+      [
+        -122.52087592328,
+        37.594399298178
+      ],
+      [
+        -122.51492860103,
+        37.595754427013
+      ],
+      [
+        -122.51463123491,
+        37.598690539489
+      ],
+      [
+        -122.50689971598,
+        37.59613085169
+      ],
+      [
+        -122.50105151576,
+        37.600422093001
+      ],
+      [
+        -122.49986205131,
+        37.603584060283
+      ],
+      [
+        -122.50204273613,
+        37.606670742629
+      ],
+      [
+        -122.49857346482,
+        37.6081011564
+      ],
+      [
+        -122.49728487833,
+        37.610510274329
+      ],
+      [
+        -122.49718575629,
+        37.618339907598
+      ],
+      [
+        -122.49896995297,
+        37.62022203098
+      ],
+      [
+        -122.49609541388,
+        37.622857003715
+      ],
+      [
+        -122.49490594943,
+        37.631590056208
+      ],
+      [
+        -122.4951041935,
+        37.664339003056
+      ],
+      [
+        -122.49639277999,
+        37.666371696309
+      ],
+      [
+        -122.49649190203,
+        37.683084951942
+      ],
+      [
+        -122.50075414965,
+        37.700852196668
+      ],
+      [
+        -122.50283571244,
+        37.708154835391
+      ],
+      [
+        -122.49827609871,
+        37.708154835391
+      ],
+      [
+        -122.49847434278,
+        37.714855194631
+      ],
+      [
+        -122.50055590557,
+        37.720576849712
+      ],
+      [
+        -122.50650322783,
+        37.72795477337
+      ],
+      [
+        -122.50680059394,
+        37.735407981963
+      ],
+      [
+        -122.50818830247,
+        37.750991963567
+      ],
+      [
+        -122.51046810933,
+        37.764091542306
+      ],
+      [
+        -122.5111619636,
+        37.771318896093
+      ],
+      [
+        -122.51314440435,
+        37.770791901546
+      ],
+      [
+        -122.51334264842,
+        37.777341690916
+      ],
+      [
+        -122.51492860103,
+        37.779750808845
+      ],
+      [
+        -122.51264879416,
+        37.784117335091
+      ],
+      [
+        -122.50987337711,
+        37.784794899509
+      ],
+      [
+        -122.50600761764,
+        37.788182721596
+      ],
+      [
+        -122.49341911886,
+        37.787655727049
+      ],
+      [
+        -122.48598496604,
+        37.790817694331
+      ],
+      [
+        -122.47755959284,
+        37.810994056987
+      ],
+      [
+        -122.47478417579,
+        37.80918721854
+      ],
+      [
+        -122.46992719594,
+        37.809413073346
+      ],
+      [
+        -122.46883685353,
+        37.807003955417
+      ],
+      [
+        -122.46358338554,
+        37.804895977229
+      ],
+      [
+        -122.44812034767,
+        37.806928670482
+      ],
+      [
+        -122.44871507989,
+        37.808660223993
+      ],
+      [
+        -122.43959585243,
+        37.808886078799
+      ],
+      [
+        -122.44088443892,
+        37.806928670482
+      ],
+      [
+        -122.43444150647,
+        37.807003955417
+      ],
+      [
+        -122.42988189274,
+        37.807832089705
+      ],
+      [
+        -122.42680910958,
+        37.808133229446
+      ],
+      [
+        -122.42670998754,
+        37.809638928152
+      ],
+      [
+        -122.42482666882,
+        37.810768202181
+      ],
+      [
+        -122.42522315698,
+        37.812650325563
+      ],
+      [
+        -122.4210600314,
+        37.812876180369
+      ],
+      [
+        -122.40748031224,
+        37.812725610499
+      ],
+      [
+        -122.39846020682,
+        37.807229810223
+      ],
+      [
+        -122.3889444912,
+        37.796614634348
+      ],
+      [
+        -122.38686292841,
+        37.790064844978
+      ],
+      [
+        -122.38170858246,
+        37.783515055609
+      ],
+      [
+        -122.37794194503,
+        37.753626936302
+      ],
+      [
+        -122.37605862631,
+        37.752572947208
+      ],
+      [
+        -122.37278759907,
+        37.745496163291
+      ],
+      [
+        -122.36763325312,
+        37.740150932886
+      ],
+      [
+        -122.37397706352,
+        37.739473368468
+      ],
+      [
+        -122.36743500904,
+        37.738193524569
+      ],
+      [
+        -122.37288672111,
+        37.737064250539
+      ],
+      [
+        -122.37635599243,
+        37.738344094439
+      ],
+      [
+        -122.37486916186,
+        37.737290105345
+      ],
+      [
+        -122.37566213816,
+        37.732773009228
+      ],
+      [
+        -122.37189550073,
+        37.734203422999
+      ],
+      [
+        -122.36971481591,
+        37.732396584552
+      ],
+      [
+        -122.36713764293,
+        37.735633836769
+      ],
+      [
+        -122.36941744979,
+        37.732170729746
+      ],
+      [
+        -122.36525432421,
+        37.733826998322
+      ],
+      [
+        -122.36614642255,
+        37.73194487494
+      ],
+      [
+        -122.36505608014,
+        37.732848294163
+      ],
+      [
+        -122.36584905644,
+        37.731719020134
+      ],
+      [
+        -122.36287539531,
+        37.732170729746
+      ],
+      [
+        -122.36188417493,
+        37.730062751558
+      ],
+      [
+        -122.35881139177,
+        37.729761611817
+      ],
+      [
+        -122.36228066308,
+        37.728557052852
+      ],
+      [
+        -122.35692807305,
+        37.728707622723
+      ],
+      [
+        -122.36039734437,
+        37.708305405261
+      ],
+      [
+        -122.39360322697,
+        37.708230120326
+      ],
+      [
+        -122.39241376252,
+        37.705821002397
+      ],
+      [
+        -122.39300849475,
+        37.701529761086
+      ],
+      [
+        -122.38785414879,
+        37.67864314076
+      ],
+      [
+        -122.38111385023,
+        37.67736329686
+      ],
+      [
+        -122.38101472819,
+        37.671867496584
+      ],
+      [
+        -122.38725941656,
+        37.671792211649
+      ],
+      [
+        -122.38795327083,
+        37.668856099173
+      ],
+      [
+        -122.39241376252,
+        37.669910088267
+      ],
+      [
+        -122.39489181346,
+        37.664489572927
+      ],
+      [
+        -122.39043132177,
+        37.665995271632
+      ],
+      [
+        -122.38716029453,
+        37.664564857862
+      ],
+      [
+        -122.38567346396,
+        37.665167137344
+      ],
+      [
+        -122.38597083008,
+        37.667651540209
+      ],
+      [
+        -122.38379014525,
+        37.668329104626
+      ],
+      [
+        -122.38101472819,
+        37.668027964885
+      ],
+      [
+        -122.38121297227,
+        37.664564857862
+      ],
+      [
+        -122.37427442964,
+        37.664037863315
+      ],
+      [
+        -122.37477003982,
+        37.661177035774
+      ],
+      [
+        -122.37992438578,
+        37.660574756292
+      ],
+      [
+        -122.37546389409,
+        37.655003671081
+      ],
+      [
+        -122.37784282299,
+        37.654476676534
+      ],
+      [
+        -122.37754545688,
+        37.652218128476
+      ],
+      [
+        -122.38022175189,
+        37.64815274197
+      ],
+      [
+        -122.39132342011,
+        37.64815274197
+      ],
+      [
+        -122.3934049829,
+        37.641904092342
+      ],
+      [
+        -122.39043132177,
+        37.645216629494
+      ],
+      [
+        -122.38973746751,
+        37.640398393636
+      ],
+      [
+        -122.38408751136,
+        37.64017253883
+      ],
+      [
+        -122.38240243672,
+        37.63618243726
+      ],
+      [
+        -122.37903228744,
+        37.635429587907
+      ],
+      [
+        -122.38755678268,
+        37.634827308425
+      ],
+      [
+        -122.3889444912,
+        37.633246324784
+      ],
+      [
+        -122.38825063694,
+        37.630234927373
+      ],
+      [
+        -122.38527697581,
+        37.628804513603
+      ],
+      [
+        -122.3789331654,
+        37.631514771273
+      ],
+      [
+        -122.37387794149,
+        37.628126949185
+      ],
+      [
+        -122.36535344625,
+        37.62820223412
+      ],
+      [
+        -122.36485783606,
+        37.626847105285
+      ],
+      [
+        -122.36882271757,
+        37.620749025527
+      ],
+      [
+        -122.35504475434,
+        37.614952085511
+      ],
+      [
+        -122.35881139177,
+        37.609456285235
+      ],
+      [
+        -122.37219286685,
+        37.615027370446
+      ],
+      [
+        -122.37804106707,
+        37.6068213125
+      ],
+      [
+        -122.37437355167,
+        37.604562764442
+      ],
+      [
+        -122.37486916186,
+        37.60411105483
+      ],
+      [
+        -122.36852535145,
+        37.601852506771
+      ],
+      [
+        -122.3657499344,
+        37.59741069559
+      ],
+      [
+        -122.36386661569,
+        37.598389399748
+      ],
+      [
+        -122.36029822233,
+        37.592065465185
+      ],
+      [
+        -122.33442737051,
+        37.59214075012
+      ],
+      [
+        -122.33422912644,
+        37.587849508809
+      ],
+      [
+        -122.33303966198,
+        37.587849508809
+      ],
+      [
+        -122.33343615013,
+        37.59628142156
+      ],
+      [
+        -122.33036336697,
+        37.593119454279
+      ],
+      [
+        -122.32144238358,
+        37.596206136625
+      ],
+      [
+        -122.31291788835,
+        37.596356706496
+      ],
+      [
+        -122.30954773907,
+        37.589957486997
+      ],
+      [
+        -122.31014247129,
+        37.584536971656
+      ],
+      [
+        -122.30419514903,
+        37.579869305669
+      ],
+      [
+        -122.2948776775,
+        37.579267026186
+      ],
+      [
+        -122.29309348082,
+        37.577234332934
+      ],
+      [
+        -122.28843474505,
+        37.579417596057
+      ],
+      [
+        -122.28774089079,
+        37.575427494487
+      ],
+      [
+        -122.28466810762,
+        37.575803919163
+      ],
+      [
+        -122.27951376167,
+        37.573846510846
+      ],
+      [
+        -122.2788199074,
+        37.571362107982
+      ],
+      [
+        -122.27336819533,
+        37.570232833953
+      ],
+      [
+        -122.26256389323,
+        37.573846510846
+      ],
+      [
+        -122.25175959113,
+        37.567597861218
+      ],
+      [
+        -122.24511841461,
+        37.556907400407
+      ],
+      [
+        -122.24630787906,
+        37.55351957832
+      ],
+      [
+        -122.25066924872,
+        37.550282326103
+      ],
+      [
+        -122.24938066223,
+        37.549228337009
+      ],
+      [
+        -122.2440280722,
+        37.553594863255
+      ],
+      [
+        -122.24184738737,
+        37.550357611038
+      ],
+      [
+        -122.22777205803,
+        37.550056471297
+      ],
+      [
+        -122.22578961727,
+        37.545388805309
+      ],
+      [
+        -122.22291507818,
+        37.546216939597
+      ],
+      [
+        -122.23332289213,
+        37.532138656699
+      ],
+      [
+        -122.24293772978,
+        37.527621560582
+      ],
+      [
+        -122.24868680796,
+        37.521071771213
+      ],
+      [
+        -122.24809207574,
+        37.512263433785
+      ],
+      [
+        -122.24501929257,
+        37.508122762344
+      ],
+      [
+        -122.24601051295,
+        37.504734940256
+      ],
+      [
+        -122.24006319069,
+        37.499916704398
+      ],
+      [
+        -122.23976582458,
+        37.508800326762
+      ],
+      [
+        -122.23857636013,
+        37.508725041826
+      ],
+      [
+        -122.22915976655,
+        37.505563074545
+      ],
+      [
+        -122.22420366467,
+        37.501121263363
+      ],
+      [
+        -122.22232034596,
+        37.505036079998
+      ],
+      [
+        -122.21369672868,
+        37.509703745985
+      ],
+      [
+        -122.2111195557,
+        37.516554675096
+      ],
+      [
+        -122.20646081994,
+        37.522728039789
+      ],
+      [
+        -122.19674686025,
+        37.529503683965
+      ],
+      [
+        -122.19456617542,
+        37.532966790988
+      ],
+      [
+        -122.19664773821,
+        37.524233738495
+      ],
+      [
+        -122.20309067066,
+        37.521899905501
+      ],
+      [
+        -122.17860752736,
+        37.506240638962
+      ],
+      [
+        -122.17751718495,
+        37.503229241551
+      ],
+      [
+        -122.17959874774,
+        37.487795829818
+      ],
+      [
+        -122.17761630699,
+        37.487720544883
+      ],
+      [
+        -122.17751718495,
+        37.498185150887
+      ],
+      [
+        -122.17355230344,
+        37.496077172699
+      ],
+      [
+        -122.16919093379,
+        37.496077172699
+      ],
+      [
+        -122.16512693025,
+        37.498862715304
+      ],
+      [
+        -122.15729628927,
+        37.49893800024
+      ],
+      [
+        -122.15402526203,
+        37.497432301534
+      ],
+      [
+        -122.15521472648,
+        37.494270334252
+      ],
+      [
+        -122.15194369924,
+        37.490506087488
+      ],
+      [
+        -122.15293491962,
+        37.489979092941
+      ],
+      [
+        -122.14639286514,
+        37.491484791647
+      ],
+      [
+        -122.14222973956,
+        37.489226243588
+      ],
+      [
+        -122.14351832604,
+        37.4871182654
+      ],
+      [
+        -122.1510516009,
+        37.4871182654
+      ],
+      [
+        -122.15144808905,
+        37.482601169283
+      ],
+      [
+        -122.15065511275,
+        37.486892410594
+      ],
+      [
+        -122.14272534974,
+        37.486666555789
+      ],
+      [
+        -122.14123851918,
+        37.488021684624
+      ],
+      [
+        -122.14232886159,
+        37.490506087488
+      ],
+      [
+        -122.14530252272,
+        37.491936501258
+      ],
+      [
+        -122.15293491962,
+        37.492538780741
+      ],
+      [
+        -122.15352965184,
+        37.498712145434
+      ],
+      [
+        -122.16007170633,
+        37.501196548298
+      ],
+      [
+        -122.15333140777,
+        37.502551677133
+      ],
+      [
+        -122.15352965184,
+        37.500142559204
+      ],
+      [
+        -122.15263755351,
+        37.502777531939
+      ],
+      [
+        -122.14847442793,
+        37.502401107263
+      ],
+      [
+        -122.14847442793,
+        37.504584370386
+      ],
+      [
+        -122.13925607843,
+        37.507972192474
+      ],
+      [
+        -122.1346964647,
+        37.506842918444
+      ],
+      [
+        -122.13003772893,
+        37.503455096357
+      ],
+      [
+        -122.12795616614,
+        37.499239139981
+      ],
+      [
+        -122.12904650855,
+        37.498109865952
+      ],
+      [
+        -122.12478426093,
+        37.486741840724
+      ],
+      [
+        -122.13122719338,
+        37.484182152924
+      ],
+      [
+        -122.13251577987,
+        37.479966196548
+      ],
+      [
+        -122.12666757965,
+        37.475072675755
+      ],
+      [
+        -122.12141411165,
+        37.476879514202
+      ],
+      [
+        -122.12339655241,
+        37.473190552373
+      ],
+      [
+        -122.11616064366,
+        37.466189053392
+      ],
+      [
+        -122.11328610457,
+        37.467544182227
+      ],
+      [
+        -122.11150190789,
+        37.466264338327
+      ],
+      [
+        -122.09643535817,
+        37.466113768456
+      ],
+      [
+        -122.09673272429,
+        37.459940403763
+      ],
+      [
+        -122.09058715795,
+        37.456853721417
+      ],
+      [
+        -122.0862257883,
+        37.450303932047
+      ],
+      [
+        -122.08295476106,
+        37.452110770494
+      ],
+      [
+        -122.08027846604,
+        37.451960200623
+      ],
+      [
+        -122.07809778121,
+        37.448195953859
+      ],
+      [
+        -122.07720568287,
+        37.450078077241
+      ],
+      [
+        -122.07363728952,
+        37.448798233341
+      ],
+      [
+        -122.06739260115,
+        37.450454501918
+      ],
+      [
+        -122.06412157391,
+        37.445636266059
+      ],
+      [
+        -122.05995844833,
+        37.445636266059
+      ],
+      [
+        -122.05966108221,
+        37.464081075204
+      ],
+      [
+        -122.05123570902,
+        37.45903698454
+      ],
+      [
+        -122.04459453249,
+        37.460693253116
+      ],
+      [
+        -122.0361691593,
+        37.464984494427
+      ],
+      [
+        -121.99671858832,
+        37.467243042486
+      ],
+      [
+        -121.99265458478,
+        37.464608069751
+      ],
+      [
+        -121.97996696396,
+        37.460919107922
+      ],
+      [
+        -121.97471349597,
+        37.460768538051
+      ],
+      [
+        -121.9684688076,
+        37.462801231304
+      ],
+      [
+        -121.96727934315,
+        37.466941902744
+      ],
+      [
+        -121.97401964171,
+        37.468598171321
+      ],
+      [
+        -121.97421788578,
+        37.480794330837
+      ],
+      [
+        -121.99334843904,
+        37.499464994787
+      ],
+      [
+        -121.99711507647,
+        37.500443698945
+      ],
+      [
+        -122.00028698168,
+        37.494872613734
+      ],
+      [
+        -122.01029830748,
+        37.493366915029
+      ],
+      [
+        -122.00970357525,
+        37.489000388782
+      ],
+      [
+        -122.01376757879,
+        37.481622465125
+      ],
+      [
+        -122.02318417237,
+        37.484709147471
+      ],
+      [
+        -122.02704993183,
+        37.478611067713
+      ],
+      [
+        -122.03081656926,
+        37.481547180189
+      ],
+      [
+        -122.0309156913,
+        37.490129662812
+      ],
+      [
+        -122.035376183,
+        37.494722043864
+      ],
+      [
+        -122.04340506804,
+        37.495851317893
+      ],
+      [
+        -122.05242517347,
+        37.50021784414
+      ],
+      [
+        -122.0577777635,
+        37.501121263363
+      ],
+      [
+        -122.05549795663,
+        37.50706877325
+      ],
+      [
+        -122.05827337369,
+        37.515726540808
+      ],
+      [
+        -122.06322947557,
+        37.515651255872
+      ],
+      [
+        -122.06719435707,
+        37.513467992749
+      ],
+      [
+        -122.06907767579,
+        37.51775923406
+      ],
+      [
+        -122.0725469471,
+        37.516855814837
+      ],
+      [
+        -122.07294343526,
+        37.518135658737
+      ],
+      [
+        -122.06124703482,
+        37.522728039789
+      ],
+      [
+        -122.06907767579,
+        37.524534878236
+      ],
+      [
+        -122.06749172319,
+        37.530105963447
+      ],
+      [
+        -122.06283298742,
+        37.528976689418
+      ],
+      [
+        -122.06729347911,
+        37.540194144775
+      ],
+      [
+        -122.07353816748,
+        37.535902903464
+      ],
+      [
+        -122.09375906316,
+        37.529654253835
+      ],
+      [
+        -122.10188707024,
+        37.522652754854
+      ],
+      [
+        -122.11031244344,
+        37.511887009108
+      ],
+      [
+        -122.11725098607,
+        37.506692348574
+      ],
+      [
+        -122.11853957256,
+        37.526417001618
+      ],
+      [
+        -122.12042289128,
+        37.532816221117
+      ],
+      [
+        -122.13578680711,
+        37.558864808725
+      ],
+      [
+        -122.1410402751,
+        37.562177345877
+      ],
+      [
+        -122.15352965184,
+        37.582203138663
+      ],
+      [
+        -122.15501648241,
+        37.591086761026
+      ],
+      [
+        -122.15243930943,
+        37.599066964166
+      ],
+      [
+        -122.15580945871,
+        37.611564263423
+      ],
+      [
+        -122.16245063523,
+        37.614726230705
+      ],
+      [
+        -122.15759365539,
+        37.621577159815
+      ],
+      [
+        -122.15838663169,
+        37.63219233569
+      ],
+      [
+        -122.15975602629,
+        37.637743456844
+      ],
+      [
+        -122.16691112692,
+        37.666748120985
+      ],
+      [
+        -122.17216459492,
+        37.673147340484
+      ],
+      [
+        -122.19724247044,
+        37.692495568852
+      ],
+      [
+        -122.1979363247,
+        37.698518363674
+      ],
+      [
+        -122.20546959956,
+        37.70258375018
+      ],
+      [
+        -122.20913711495,
+        37.696109245745
+      ],
+      [
+        -122.21617477962,
+        37.695506966263
+      ],
+      [
+        -122.25651744894,
+        37.72125441413
+      ],
+      [
+        -122.26910594772,
+        37.737967669763
+      ],
+      [
+        -122.25949111007,
+        37.752572947208
+      ],
+      [
+        -122.32154150562,
+        37.771243611158
+      ],
+      [
+        -122.34027557073,
+        37.800604735918
+      ],
+      [
+        -122.34671850318,
+        37.810994056987
+      ],
+      [
+        -122.33393176032,
+        37.81031649257
+      ],
+      [
+        -122.32302833618,
+        37.810165922699
+      ],
+      [
+        -122.31767574615,
+        37.815059443492
+      ],
+      [
+        -122.33284141791,
+        37.814908873622
+      ],
+      [
+        -122.33284141791,
+        37.818898975192
+      ],
+      [
+        -122.3288765364,
+        37.819275399868
+      ],
+      [
+        -122.3288765364,
+        37.820555243768
+      ],
+      [
+        -122.33066073308,
+        37.820404673897
+      ],
+      [
+        -122.32996687882,
+        37.821609232862
+      ],
+      [
+        -122.31767574615,
+        37.824846485079
+      ],
+      [
+        -122.29358909101,
+        37.828535446908
+      ],
+      [
+        -122.2948776775,
+        37.833654822507
+      ],
+      [
+        -122.32025291913,
+        37.838473058365
+      ],
+      [
+        -122.32005467505,
+        37.843291294223
+      ],
+      [
+        -122.30954773907,
+        37.845549842282
+      ],
+      [
+        -122.31083632556,
+        37.854057039969
+      ],
+      [
+        -122.32233448192,
+        37.853605330357
+      ],
+      [
+        -122.33125546531,
+        37.857219007251
+      ],
+      [
+        -122.33264317383,
+        37.862639522591
+      ],
+      [
+        -122.32927302455,
+        37.871222005213
+      ],
+      [
+        -122.32590287527,
+        37.874383972495
+      ],
+      [
+        -122.32758794991,
+        37.877320084971
+      ],
+      [
+        -122.30835827462,
+        37.8822888907
+      ],
+      [
+        -122.30835827462,
+        37.890043239034
+      ],
+      [
+        -122.31182754593,
+        37.897496447627
+      ],
+      [
+        -122.335715957,
+        37.892301787093
+      ],
+      [
+        -122.33472473662,
+        37.900583129974
+      ],
+      [
+        -122.33799576387,
+        37.90351924245
+      ],
+      [
+        -122.34116766907,
+        37.903368672579
+      ],
+      [
+        -122.34443869631,
+        37.90223939855
+      ],
+      [
+        -122.34661938114,
+        37.899228001139
+      ],
+      [
+        -122.34374484205,
+        37.891398367869
+      ],
+      [
+        -122.35930700195,
+        37.891172513063
+      ],
+      [
+        -122.36456046995,
+        37.895313184504
+      ],
+      [
+        -122.37605862631,
+        37.895012044763
+      ],
+      [
+        -122.38101472819,
+        37.898776291527
+      ],
+      [
+        -122.38220419265,
+        37.903368672579
+      ],
+      [
+        -122.39092693196,
+        37.904497946608
+      ],
+      [
+        -122.3965768881,
+        37.90765991389
+      ],
+      [
+        -122.39330586086,
+        37.918124519895
+      ],
+      [
+        -122.39905493904,
+        37.922942755753
+      ],
+      [
+        -122.41094958356,
+        37.92745985187
+      ],
+      [
+        -122.41778900415,
+        37.932503942534
+      ],
+      [
+        -122.41987056694,
+        37.935967049557
+      ],
+      [
+        -122.41977144491,
+        37.940333575803
+      ],
+      [
+        -122.42780032995,
+        37.944624817114
+      ],
+      [
+        -122.42839506218,
+        37.947862069331
+      ],
+      [
+        -122.42651174346,
+        37.951701601031
+      ],
+      [
+        -122.4342432624,
+        37.959305379494
+      ],
+      [
+        -122.435829215,
+        37.964349470158
+      ],
+      [
+        -122.4342432624,
+        37.967812577181
+      ],
+      [
+        -122.40321806462,
+        37.967737292246
+      ],
+      [
+        -122.39984791534,
+        37.972555528104
+      ],
+      [
+        -122.3926120066,
+        37.974437651486
+      ],
+      [
+        -122.38488048766,
+        37.980309876438
+      ],
+      [
+        -122.3773472128,
+        37.980460446309
+      ],
+      [
+        -122.36822798534,
+        37.991301476989
+      ],
+      [
+        -122.37100340239,
+        37.995969142977
+      ],
+      [
+        -122.3733823313,
+        38.008617012105
+      ],
+      [
+        -122.37080515832,
+        38.014263382251
+      ],
+      [
+        -122.36783149719,
+        38.016898354986
+      ],
+      [
+        -122.36366837161,
+        38.017274779662
+      ],
+      [
+        -122.33769839775,
+        38.007713592881
+      ],
+      [
+        -122.33185019753,
+        38.008918151846
+      ],
+      [
+        -122.33125546531,
+        38.006433748981
+      ],
+      [
+        -122.32639848546,
+        38.009821571069
+      ],
+      [
+        -122.31926169875,
+        38.015994935762
+      ],
+      [
+        -122.30469075922,
+        38.01539265628
+      ],
+      [
+        -122.3025100744,
+        38.017651204338
+      ],
+      [
+        -122.29656275214,
+        38.019909752397
+      ],
+      [
+        -122.29477855546,
+        38.024351563579
+      ],
+      [
+        -122.28873211117,
+        38.026760681508
+      ],
+      [
+        -122.28674967041,
+        38.030374358401
+      ],
+      [
+        -122.28119883631,
+        38.031654202301
+      ],
+      [
+        -122.28090147019,
+        38.039559120506
+      ],
+      [
+        -122.27604449035,
+        38.04347393714
+      ],
+      [
+        -122.26950243587,
+        38.044452641299
+      ],
+      [
+        -122.26861033753,
+        38.047087614034
+      ],
+      [
+        -122.26989892402,
+        38.058305069391
+      ],
+      [
+        -122.2696015579,
+        38.060413047579
+      ],
+      [
+        -122.26533931029,
+        38.059886053032
+      ],
+      [
+        -122.24521753665,
+        38.063951439537
+      ],
+      [
+        -122.23203430564,
+        38.061843461349
+      ],
+      [
+        -122.26099075155,
+        38.095244490583
+      ],
+      [
+        -122.28139708038,
+        38.119963431388
+      ],
+      [
+        -122.2748550259,
+        38.121469130093
+      ],
+      [
+        -122.26335686953,
+        38.13110560181
+      ],
+      [
+        -122.25592271671,
+        38.132385445709
+      ],
+      [
+        -122.25532798449,
+        38.12598622621
+      ],
+      [
+        -122.24987627242,
+        38.128018919463
+      ],
+      [
+        -122.24848856389,
+        38.122523119187
+      ],
+      [
+        -122.22935801063,
+        38.124329957634
+      ],
+      [
+        -122.21726512204,
+        38.143377046261
+      ],
+      [
+        -122.21468794906,
+        38.154895641359
+      ],
+      [
+        -122.19535915172,
+        38.15504621123
+      ],
+      [
+        -122.19902666711,
+        38.159337452541
+      ],
+      [
+        -122.19446705338,
+        38.164682682946
+      ],
+      [
+        -122.19803544674,
+        38.16844692971
+      ],
+      [
+        -122.20190120621,
+        38.168296359839
+      ],
+      [
+        -122.18068909015,
+        38.185461325084
+      ],
+      [
+        -122.17652596457,
+        38.184859045602
+      ],
+      [
+        -122.16195502504,
+        38.191408834971
+      ],
+      [
+        -122.15719716724,
+        38.187719873142
+      ],
+      [
+        -122.15590858075,
+        38.180191379614
+      ],
+      [
+        -122.15362877388,
+        38.178384541167
+      ],
+      [
+        -122.1518445772,
+        38.172662886086
+      ],
+      [
+        -122.16611815062,
+        38.14706600809
+      ],
+      [
+        -122.16988478805,
+        38.137203681568
+      ],
+      [
+        -122.1655234184,
+        38.126061511146
+      ],
+      [
+        -122.16651463877,
+        38.119210582035
+      ],
+      [
+        -122.16463132006,
+        38.114994625659
+      ],
+      [
+        -122.16542429636,
+        38.110778669283
+      ],
+      [
+        -122.16364009968,
+        38.108218981484
+      ],
+      [
+        -122.16284712338,
+        38.101217482502
+      ],
+      [
+        -122.16086468263,
+        38.10046463315
+      ],
+      [
+        -122.16344185561,
+        38.106336858102
+      ],
+      [
+        -122.16324361153,
+        38.120264571129
+      ],
+      [
+        -122.15779189946,
+        38.12199612464
+      ],
+      [
+        -122.1547191163,
+        38.120114001258
+      ],
+      [
+        -122.14897003811,
+        38.120490425935
+      ],
+      [
+        -122.14827618385,
+        38.124329957634
+      ],
+      [
+        -122.14738408551,
+        38.121243275288
+      ],
+      [
+        -122.14014817677,
+        38.117704883329
+      ],
+      [
+        -122.14153588529,
+        38.121544415029
+      ],
+      [
+        -122.13806661398,
+        38.124179387764
+      ],
+      [
+        -122.13241665783,
+        38.126136796081
+      ],
+      [
+        -122.1278570441,
+        38.125007522052
+      ],
+      [
+        -122.12161235573,
+        38.129750472975
+      ],
+      [
+        -122.12428865075,
+        38.137580106244
+      ],
+      [
+        -122.12240533203,
+        38.14194663249
+      ],
+      [
+        -122.11507030125,
+        38.146840153284
+      ],
+      [
+        -122.1102133214,
+        38.145861449125
+      ],
+      [
+        -122.11130366382,
+        38.14849642186
+      ],
+      [
+        -122.11259225031,
+        38.148195282119
+      ],
+      [
+        -122.11526854532,
+        38.156476625
+      ],
+      [
+        -122.12736143391,
+        38.171383042186
+      ],
+      [
+        -122.11388083679,
+        38.170931332574
+      ],
+      [
+        -122.11378171476,
+        38.16844692971
+      ],
+      [
+        -122.10852824676,
+        38.164080403464
+      ],
+      [
+        -122.11407908087,
+        38.162122995146
+      ],
+      [
+        -122.11427732495,
+        38.158208178512
+      ],
+      [
+        -122.11189839604,
+        38.1551967811
+      ],
+      [
+        -122.10833000269,
+        38.154970926294
+      ],
+      [
+        -122.10297741266,
+        38.158961027864
+      ],
+      [
+        -122.10059848375,
+        38.156551909935
+      ],
+      [
+        -122.0846398357,
+        38.150980824724
+      ],
+      [
+        -122.09554325983,
+        38.137881245985
+      ],
+      [
+        -122.08662227645,
+        38.135170988315
+      ],
+      [
+        -122.08245915087,
+        38.136300262344
+      ],
+      [
+        -122.0825582729,
+        38.127416639981
+      ],
+      [
+        -122.08008022196,
+        38.12455581244
+      ],
+      [
+        -122.07264606914,
+        38.121318560223
+      ],
+      [
+        -122.06590577058,
+        38.120791565676
+      ],
+      [
+        -122.06451806206,
+        38.117403743588
+      ],
+      [
+        -122.06580664855,
+        38.10731556226
+      ],
+      [
+        -122.06768996726,
+        38.103927740173
+      ],
+      [
+        -122.10317565673,
+        38.075319464765
+      ],
+      [
+        -122.11804396237,
+        38.060940042126
+      ],
+      [
+        -122.11933254886,
+        38.062897450443
+      ],
+      [
+        -122.12002640313,
+        38.062144601091
+      ],
+      [
+        -122.12438777278,
+        38.057853359779
+      ],
+      [
+        -122.1278570441,
+        38.047388753775
+      ],
+      [
+        -122.13142543745,
+        38.043398652205
+      ],
+      [
+        -122.13826485805,
+        38.039784975312
+      ],
+      [
+        -122.13330875617,
+        38.042871657658
+      ],
+      [
+        -122.15253843147,
+        38.042796372723
+      ],
+      [
+        -122.15967521818,
+        38.033385755813
+      ],
+      [
+        -122.16453219802,
+        38.034740884648
+      ],
+      [
+        -122.17612947642,
+        38.047765178452
+      ],
+      [
+        -122.18386099536,
+        38.05401382808
+      ],
+      [
+        -122.1947644195,
+        38.056498230944
+      ],
+      [
+        -122.22519488505,
+        38.061015327061
+      ],
+      [
+        -122.22420366467,
+        38.057702789909
+      ],
+      [
+        -122.20596520975,
+        38.055368956915
+      ],
+      [
+        -122.20368540288,
+        38.05401382808
+      ],
+      [
+        -122.19070041595,
+        38.052432844439
+      ],
+      [
+        -122.18584343611,
+        38.04987315664
+      ],
+      [
+        -122.17890489348,
+        38.04460321117
+      ],
+      [
+        -122.1747417679,
+        38.038806271153
+      ],
+      [
+        -122.17345318141,
+        38.034740884648
+      ],
+      [
+        -122.17464264586,
+        38.035945443612
+      ],
+      [
+        -122.17662508661,
+        38.033988035295
+      ],
+      [
+        -122.17067776435,
+        38.031051922819
+      ],
+      [
+        -122.16215326912,
+        38.02608311709
+      ],
+      [
+        -122.16582078451,
+        38.024200993708
+      ],
+      [
+        -122.17454352382,
+        38.024200993708
+      ],
+      [
+        -122.17414703567,
+        38.022620010067
+      ],
+      [
+        -122.1779136731,
+        38.017425349533
+      ],
+      [
+        -122.17761630699,
+        38.015091516539
+      ],
+      [
+        -122.16542429636,
+        38.006509033917
+      ],
+      [
+        -122.15511560445,
+        38.00470219547
+      ],
+      [
+        -122.14758232959,
+        38.000260384288
+      ],
+      [
+        -122.14530252272,
+        37.997023132071
+      ],
+      [
+        -122.13985081065,
+        37.997926551294
+      ],
+      [
+        -122.14520340068,
+        38.0007120939
+      ],
+      [
+        -122.14421218031,
+        38.005906754434
+      ],
+      [
+        -122.14312183789,
+        38.005831469499
+      ],
+      [
+        -122.14460866846,
+        38.007487738075
+      ],
+      [
+        -122.15095247887,
+        38.009746286134
+      ],
+      [
+        -122.15214194332,
+        38.011477839645
+      ],
+      [
+        -122.15075423479,
+        38.012757683545
+      ],
+      [
+        -122.15491736037,
+        38.013359963027
+      ],
+      [
+        -122.14857354996,
+        38.017575919403
+      ],
+      [
+        -122.14748320755,
+        38.019006333174
+      ],
+      [
+        -122.14847442793,
+        38.020888456556
+      ],
+      [
+        -122.15848575373,
+        38.024878558126
+      ],
+      [
+        -122.14669023125,
+        38.023598714226
+      ],
+      [
+        -122.14688847532,
+        38.031729487236
+      ],
+      [
+        -122.12399128463,
+        38.035719588806
+      ],
+      [
+        -122.09336257501,
+        38.049120307287
+      ],
+      [
+        -122.07740392695,
+        38.05401382808
+      ],
+      [
+        -122.06114791278,
+        38.062144601091
+      ],
+      [
+        -122.05083922086,
+        38.060187192773
+      ],
+      [
+        -122.02923061666,
+        38.05928377355
+      ],
+      [
+        -122.02923061666,
+        38.05657351588
+      ],
+      [
+        -122.03369110836,
+        38.050550721057
+      ],
+      [
+        -122.03071744723,
+        38.043775076882
+      ],
+      [
+        -122.04846029196,
+        38.03150363243
+      ],
+      [
+        -122.03884545431,
+        38.030148503595
+      ],
+      [
+        -122.03894457635,
+        38.028191095278
+      ],
+      [
+        -122.0325016439,
+        38.027513530861
+      ],
+      [
+        -122.03260076594,
+        38.029772078919
+      ],
+      [
+        -122.03151042353,
+        38.029621509048
+      ],
+      [
+        -122.03111393538,
+        38.032557621524
+      ],
+      [
+        -122.02843764036,
+        38.032858761266
+      ],
+      [
+        -122.02972622685,
+        38.031277777625
+      ],
+      [
+        -122.02605871146,
+        38.024351563579
+      ],
+      [
+        -122.01123526801,
+        38.016975195765
+      ],
+      [
+        -122.00593693782,
+        38.015769080956
+      ],
+      [
+        -122.00355800892,
+        38.01667250018
+      ],
+      [
+        -121.99959312741,
+        38.014940946668
+      ],
+      [
+        -121.99493439164,
+        38.018554623562
+      ],
+      [
+        -121.99671858832,
+        38.018855763303
+      ],
+      [
+        -122.00028698168,
+        38.028040525407
+      ],
+      [
+        -121.99681771036,
+        38.030223788531
+      ],
+      [
+        -121.99721419851,
+        38.036848862836
+      ],
+      [
+        -121.99235721867,
+        38.036848862836
+      ],
+      [
+        -121.99225809663,
+        38.033310470877
+      ],
+      [
+        -121.98759936086,
+        38.033385755813
+      ],
+      [
+        -121.98740111678,
+        38.036171298418
+      ],
+      [
+        -121.98095818434,
+        38.034213890101
+      ],
+      [
+        -121.97947135377,
+        38.03391275036
+      ],
+      [
+        -121.99602473406,
+        38.038806271153
+      ],
+      [
+        -121.99602473406,
+        38.041290674017
+      ],
+      [
+        -121.99731332055,
+        38.041290674017
+      ],
+      [
+        -121.99731332055,
+        38.045205490652
+      ],
+      [
+        -121.98799584901,
+        38.044076216623
+      ],
+      [
+        -121.98759936086,
+        38.052131704698
+      ],
+      [
+        -121.98343623528,
+        38.04987315664
+      ],
+      [
+        -121.98482394381,
+        38.047614608581
+      ],
+      [
+        -121.9832379912,
+        38.047765178452
+      ],
+      [
+        -121.98294062509,
+        38.049647301834
+      ],
+      [
+        -121.97758803506,
+        38.047840463387
+      ],
+      [
+        -121.97729066895,
+        38.045732485199
+      ],
+      [
+        -121.97709242487,
+        38.047915748322
+      ],
+      [
+        -121.95736713938,
+        38.048442742869
+      ],
+      [
+        -121.95697065123,
+        38.040161399988
+      ],
+      [
+        -121.95994431236,
+        38.040387254794
+      ],
+      [
+        -121.96043992255,
+        38.038505131412
+      ],
+      [
+        -121.93268575202,
+        38.031127207754
+      ],
+      [
+        -121.93248750794,
+        38.033536325683
+      ],
+      [
+        -121.92852262644,
+        38.034439744907
+      ],
+      [
+        -121.9284235044,
+        38.047087614034
+      ],
+      [
+        -121.91018504947,
+        38.044979635846
+      ],
+      [
+        -121.88520629599,
+        38.047614608581
+      ],
+      [
+        -121.87113096665,
+        38.053035123921
+      ],
+      [
+        -121.86448979013,
+        38.061015327061
+      ],
+      [
+        -121.86250734938,
+        38.066059417725
+      ],
+      [
+        -121.86161525104,
+        38.069898949425
+      ],
+      [
+        -121.87668180076,
+        38.072985631771
+      ],
+      [
+        -121.88272824505,
+        38.076072314118
+      ],
+      [
+        -121.88520629599,
+        38.080137700623
+      ],
+      [
+        -121.88322385524,
+        38.088795468181
+      ],
+      [
+        -121.88768434693,
+        38.094140698586
+      ],
+      [
+        -121.89591147606,
+        38.097453235738
+      ],
+      [
+        -121.88966678769,
+        38.104755874461
+      ],
+      [
+        -121.89095537418,
+        38.11243493786
+      ],
+      [
+        -121.88857644527,
+        38.120942135546
+      ],
+      [
+        -121.88966678769,
+        38.124932237116
+      ],
+      [
+        -121.89382991327,
+        38.126663790628
+      ],
+      [
+        -121.89690269643,
+        38.12598622621
+      ],
+      [
+        -121.90512982556,
+        38.118834157359
+      ],
+      [
+        -121.90968943929,
+        38.119210582035
+      ],
+      [
+        -121.91365432079,
+        38.121694984899
+      ],
+      [
+        -121.91543851747,
+        38.131557311421
+      ],
+      [
+        -121.90859909687,
+        38.140139794044
+      ],
+      [
+        -121.90899558502,
+        38.143904040808
+      ],
+      [
+        -121.91038329355,
+        38.146689583413
+      ],
+      [
+        -121.92832438236,
+        38.160542011505
+      ],
+      [
+        -121.93407346054,
+        38.167919935163
+      ],
+      [
+        -121.91801569045,
+        38.178459826103
+      ],
+      [
+        -121.91861042267,
+        38.180191379614
+      ],
+      [
+        -121.92782877217,
+        38.183805056508
+      ],
+      [
+        -121.92991033496,
+        38.18696702379
+      ],
+      [
+        -121.92881999255,
+        38.183805056508
+      ],
+      [
+        -121.92069198546,
+        38.178685680908
+      ],
+      [
+        -121.92128671769,
+        38.177405837009
+      ],
+      [
+        -121.92326915844,
+        38.178610395973
+      ],
+      [
+        -121.9215840838,
+        38.177179982203
+      ],
+      [
+        -121.92891911459,
+        38.173641590244
+      ],
+      [
+        -121.93199189775,
+        38.17371687518
+      ],
+      [
+        -121.93565941314,
+        38.176351847915
+      ],
+      [
+        -121.93615502333,
+        38.181471223514
+      ],
+      [
+        -121.93893044039,
+        38.183654486637
+      ],
+      [
+        -121.9392278065,
+        38.18711759366
+      ],
+      [
+        -121.94349005412,
+        38.189903136266
+      ],
+      [
+        -121.94428303042,
+        38.187494018337
+      ],
+      [
+        -121.94210234559,
+        38.184106196249
+      ],
+      [
+        -121.94527425079,
+        38.181245368708
+      ],
+      [
+        -121.94854527804,
+        38.184633190796
+      ],
+      [
+        -121.95191542731,
+        38.183805056508
+      ],
+      [
+        -121.95389786807,
+        38.185084900407
+      ],
+      [
+        -121.95736713938,
+        38.184332051055
+      ],
+      [
+        -121.95825923772,
+        38.180492519355
+      ],
+      [
+        -121.96192675312,
+        38.183353346896
+      ],
+      [
+        -121.96400831591,
+        38.182449927673
+      ],
+      [
+        -121.96172850904,
+        38.172286461409
+      ],
+      [
+        -121.96222411923,
+        38.172662886086
+      ],
+      [
+        -121.9632153396,
+        38.172662886086
+      ],
+      [
+        -121.96361182775,
+        38.172587601151
+      ],
+      [
+        -121.96371094979,
+        38.172512316215
+      ],
+      [
+        -121.96093553274,
+        38.170404338027
+      ],
+      [
+        -121.95538469863,
+        38.170479622963
+      ],
+      [
+        -121.95260928158,
+        38.167091800875
+      ],
+      [
+        -121.9532040138,
+        38.164080403464
+      ],
+      [
+        -121.95736713938,
+        38.160316156699
+      ],
+      [
+        -121.96549514647,
+        38.160843151246
+      ],
+      [
+        -121.96450392609,
+        38.159864447088
+      ],
+      [
+        -121.96569339054,
+        38.159262167605
+      ],
+      [
+        -121.9660898787,
+        38.158584603188
+      ],
+      [
+        -121.95677240716,
+        38.160165586829
+      ],
+      [
+        -121.9508250849,
+        38.156326055129
+      ],
+      [
+        -121.95310489177,
+        38.154820356424
+      ],
+      [
+        -121.95013123064,
+        38.156250770194
+      ],
+      [
+        -121.94487776264,
+        38.154443931747
+      ],
+      [
+        -121.95151893916,
+        38.148421136925
+      ],
+      [
+        -121.94914001026,
+        38.142172487296
+      ],
+      [
+        -121.95221279343,
+        38.138483525467
+      ],
+      [
+        -121.94517512876,
+        38.135999122603
+      ],
+      [
+        -121.93674975556,
+        38.128922338686
+      ],
+      [
+        -121.93219014183,
+        38.122824258929
+      ],
+      [
+        -121.9416067354,
+        38.113263072148
+      ],
+      [
+        -121.94448127449,
+        38.117253173718
+      ],
+      [
+        -121.95330313584,
+        38.121469130093
+      ],
+      [
+        -121.97629944857,
+        38.129675188039
+      ],
+      [
+        -121.98056169619,
+        38.128997623622
+      ],
+      [
+        -121.98105730638,
+        38.130653892198
+      ],
+      [
+        -121.99126687625,
+        38.138935235079
+      ],
+      [
+        -121.98145379453,
+        38.130277467522
+      ],
+      [
+        -121.98135467249,
+        38.125760371405
+      ],
+      [
+        -121.97857925544,
+        38.122824258929
+      ],
+      [
+        -121.97709242487,
+        38.117629598394
+      ],
+      [
+        -121.98125555045,
+        38.112660792665
+      ],
+      [
+        -121.98789672697,
+        38.111456233701
+      ],
+      [
+        -121.98393184547,
+        38.114241776306
+      ],
+      [
+        -121.98948267957,
+        38.125760371405
+      ],
+      [
+        -121.99959312741,
+        38.136977826762
+      ],
+      [
+        -122.00702728023,
+        38.140742073526
+      ],
+      [
+        -122.02021051124,
+        38.137354251438
+      ],
+      [
+        -122.04419804434,
+        38.137053111697
+      ],
+      [
+        -122.05301990569,
+        38.133138295062
+      ],
+      [
+        -122.05629093293,
+        38.13381585948
+      ],
+      [
+        -122.05976020425,
+        38.138709380273
+      ],
+      [
+        -122.05837249572,
+        38.143979325743
+      ],
+      [
+        -122.03646652541,
+        38.170103198286
+      ],
+      [
+        -122.03289813205,
+        38.170404338027
+      ],
+      [
+        -122.0285367624,
+        38.168221074904
+      ],
+      [
+        -122.02080524346,
+        38.169124494128
+      ],
+      [
+        -122.00970357525,
+        38.176125993109
+      ],
+      [
+        -121.99632210017,
+        38.179589100132
+      ],
+      [
+        -121.99721419851,
+        38.175824853368
+      ],
+      [
+        -122.00028698168,
+        38.172587601151
+      ],
+      [
+        -122.00425186318,
+        38.170630192833
+      ],
+      [
+        -122.00454922929,
+        38.169952628416
+      ],
+      [
+        -122.0032606428,
+        38.168371644775
+      ],
+      [
+        -122.0000887376,
+        38.166338951522
+      ],
+      [
+        -121.99315019497,
+        38.164381543205
+      ],
+      [
+        -121.99394317127,
+        38.161671285535
+      ],
+      [
+        -121.99533087979,
+        38.162122995146
+      ],
+      [
+        -121.99761068666,
+        38.160993721117
+      ],
+      [
+        -121.99404229331,
+        38.161144290988
+      ],
+      [
+        -121.99176248644,
+        38.163628693852
+      ],
+      [
+        -121.99374492719,
+        38.164908537752
+      ],
+      [
+        -122.00107995798,
+        38.167091800875
+      ],
+      [
+        -122.00435098522,
+        38.170404338027
+      ],
+      [
+        -122.00107995798,
+        38.171759466862
+      ],
+      [
+        -121.99919663926,
+        38.173340450503
+      ],
+      [
+        -121.99622297813,
+        38.176803557526
+      ],
+      [
+        -121.99572736795,
+        38.179739670002
+      ],
+      [
+        -121.98175116064,
+        38.186364744307
+      ],
+      [
+        -121.9768941808,
+        38.18696702379
+      ],
+      [
+        -121.97957047581,
+        38.187795158078
+      ],
+      [
+        -121.98809497105,
+        38.184934330537
+      ],
+      [
+        -121.98700462863,
+        38.189150286913
+      ],
+      [
+        -121.9961238561,
+        38.192613393936
+      ],
+      [
+        -121.98829321512,
+        38.189225571848
+      ],
+      [
+        -121.98829321512,
+        38.186590599113
+      ],
+      [
+        -121.9969168324,
+        38.181922933126
+      ],
+      [
+        -122.01495704324,
+        38.177255267138
+      ],
+      [
+        -122.02496836904,
+        38.170630192833
+      ],
+      [
+        -122.03577267115,
+        38.172286461409
+      ],
+      [
+        -122.03557442707,
+        38.17898682065
+      ],
+      [
+        -122.03716037967,
+        38.178384541167
+      ],
+      [
+        -122.03815160005,
+        38.178459826103
+      ],
+      [
+        -122.04152174933,
+        38.180191379614
+      ],
+      [
+        -122.03706125763,
+        38.178535111038
+      ],
+      [
+        -122.03002359296,
+        38.181772363255
+      ],
+      [
+        -122.02962710481,
+        38.178610395973
+      ],
+      [
+        -122.02298592829,
+        38.177179982203
+      ],
+      [
+        -122.01317284657,
+        38.180643089226
+      ],
+      [
+        -122.01951665698,
+        38.179212675455
+      ],
+      [
+        -122.01872368067,
+        38.181019513902
+      ],
+      [
+        -122.02070612143,
+        38.180793659096
+      ],
+      [
+        -122.02268856218,
+        38.178384541167
+      ],
+      [
+        -122.02546397923,
+        38.179664385067
+      ],
+      [
+        -122.02407627071,
+        38.181998218061
+      ],
+      [
+        -122.02744641999,
+        38.18184764819
+      ],
+      [
+        -122.02883412851,
+        38.183805056508
+      ],
+      [
+        -122.02576134535,
+        38.188773862236
+      ],
+      [
+        -122.03369110836,
+        38.194796657059
+      ],
+      [
+        -122.03755686782,
+        38.193290958353
+      ],
+      [
+        -122.0361691593,
+        38.19110769523
+      ],
+      [
+        -122.03468232873,
+        38.193592098094
+      ],
+      [
+        -122.03636740337,
+        38.190279560942
+      ],
+      [
+        -122.0377551119,
+        38.189225571848
+      ],
+      [
+        -122.04370243416,
+        38.190053706136
+      ],
+      [
+        -122.05440761422,
+        38.185913034696
+      ],
+      [
+        -122.05411024811,
+        38.183202777025
+      ],
+      [
+        -122.0469734614,
+        38.181772363255
+      ],
+      [
+        -122.04637872917,
+        38.178459826103
+      ],
+      [
+        -122.05767864146,
+        38.179513815197
+      ],
+      [
+        -122.05896722795,
+        38.177255267138
+      ],
+      [
+        -122.05728215331,
+        38.172211176474
+      ],
+      [
+        -122.06372508576,
+        38.173039310762
+      ],
+      [
+        -122.06838382152,
+        38.179287960391
+      ],
+      [
+        -122.07185309284,
+        38.180266664549
+      ],
+      [
+        -122.07215045895,
+        38.181471223514
+      ],
+      [
+        -122.06788821134,
+        38.183503916767
+      ],
+      [
+        -122.0662031367,
+        38.188246867689
+      ],
+      [
+        -122.06679786892,
+        38.190580700683
+      ],
+      [
+        -122.06590577058,
+        38.191484119907
+      ],
+      [
+        -122.0662031367,
+        38.19238753913
+      ],
+      [
+        -122.0677890893,
+        38.192312254195
+      ],
+      [
+        -122.06630225873,
+        38.191182980165
+      ],
+      [
+        -122.06927591986,
+        38.191032410295
+      ],
+      [
+        -122.04231472563,
+        38.241849741611
+      ],
+      [
+        -122.03815160005,
+        38.246592692534
+      ],
+      [
+        -122.01485792121,
+        38.259767556208
+      ],
+      [
+        -122.01485792121,
+        38.25765957802
+      ],
+      [
+        -122.01237987027,
+        38.25765957802
+      ],
+      [
+        -122.01198338212,
+        38.261273254914
+      ],
+      [
+        -122.00524308356,
+        38.265037501678
+      ],
+      [
+        -122.00722552431,
+        38.263682372843
+      ],
+      [
+        -122.00663079208,
+        38.253293051774
+      ],
+      [
+        -121.98819409309,
+        38.252991912033
+      ],
+      [
+        -121.98809497105,
+        38.264435222196
+      ],
+      [
+        -121.98492306584,
+        38.264736361937
+      ],
+      [
+        -121.97966959785,
+        38.264585792066
+      ],
+      [
+        -121.98155291656,
+        38.26149910972
+      ],
+      [
+        -121.98135467249,
+        38.252991912033
+      ],
+      [
+        -121.96936090594,
+        38.252841342162
+      ],
+      [
+        -121.9692617839,
+        38.254422325803
+      ],
+      [
+        -121.97401964171,
+        38.254422325803
+      ],
+      [
+        -121.97491174004,
+        38.267296049736
+      ],
+      [
+        -121.93347872832,
+        38.266392630513
+      ],
+      [
+        -121.92247618214,
+        38.275953817294
+      ],
+      [
+        -121.92654018568,
+        38.279191069511
+      ],
+      [
+        -121.91484378524,
+        38.279191069511
+      ],
+      [
+        -121.91474466321,
+        38.273544699365
+      ],
+      [
+        -121.89660533032,
+        38.286493708233
+      ],
+      [
+        -121.86032666455,
+        38.286117283557
+      ],
+      [
+        -121.86032666455,
+        38.271888430789
+      ],
+      [
+        -121.85606441693,
+        38.269328742989
+      ],
+      [
+        -121.84902675226,
+        38.268651178571
+      ],
+      [
+        -121.84248469778,
+        38.271361436242
+      ],
+      [
+        -121.8422864537,
+        38.278362935223
+      ],
+      [
+        -121.82900410066,
+        38.278362935223
+      ],
+      [
+        -121.8238497547,
+        38.271512006112
+      ],
+      [
+        -121.80977442536,
+        38.258036002697
+      ],
+      [
+        -121.80551217774,
+        38.260068695949
+      ],
+      [
+        -121.80184466235,
+        38.258111287632
+      ],
+      [
+        -121.77190980699,
+        38.261423824784
+      ],
+      [
+        -121.77121595272,
+        38.263230663231
+      ],
+      [
+        -121.77567644442,
+        38.261950819331
+      ],
+      [
+        -121.77954220388,
+        38.263381233102
+      ],
+      [
+        -121.77874922758,
+        38.284385730046
+      ],
+      [
+        -121.76011428451,
+        38.284988009528
+      ],
+      [
+        -121.75912306413,
+        38.293645777085
+      ],
+      [
+        -121.74891349426,
+        38.296732459432
+      ],
+      [
+        -121.74306529404,
+        38.294473911373
+      ],
+      [
+        -121.74098373125,
+        38.291763653703
+      ],
+      [
+        -121.72690840191,
+        38.290107385127
+      ],
+      [
+        -121.7221505441,
+        38.285063294463
+      ],
+      [
+        -121.71848302871,
+        38.283783450563
+      ],
+      [
+        -121.71035502162,
+        38.2761796721
+      ],
+      [
+        -121.70926467921,
+        38.272490710271
+      ],
+      [
+        -121.69945159748,
+        38.264886931807
+      ],
+      [
+        -121.6944954956,
+        38.257057298538
+      ],
+      [
+        -121.69647793635,
+        38.247345541886
+      ],
+      [
+        -121.70272262473,
+        38.247646681628
+      ],
+      [
+        -121.70242525861,
+        38.245990413051
+      ],
+      [
+        -121.69717179062,
+        38.245990413051
+      ],
+      [
+        -121.69518934987,
+        38.243807149928
+      ],
+      [
+        -121.68854817334,
+        38.244108289669
+      ],
+      [
+        -121.68398855961,
+        38.238236064717
+      ],
+      [
+        -121.67853684754,
+        38.243731864993
+      ],
+      [
+        -121.67179654899,
+        38.254648180609
+      ],
+      [
+        -121.67169742695,
+        38.265940920901
+      ],
+      [
+        -121.66545273858,
+        38.274673973394
+      ],
+      [
+        -121.66426327413,
+        38.282578891599
+      ],
+      [
+        -121.6668404471,
+        38.292892927733
+      ],
+      [
+        -121.66674132507,
+        38.313521
+      ],
+      [
+        -121.63066090337,
+        38.313144575324
+      ],
+      [
+        -121.63085914745,
+        38.291763653703
+      ],
+      [
+        -121.64513272086,
+        38.289053396033
+      ],
+      [
+        -121.64354676826,
+        38.282277751858
+      ],
+      [
+        -121.63809505619,
+        38.277760655741
+      ],
+      [
+        -121.64235730381,
+        38.272114285594
+      ],
+      [
+        -121.64087047325,
+        38.267973614154
+      ],
+      [
+        -121.65187301942,
+        38.26164967959
+      ],
+      [
+        -121.6520712635,
+        38.255551599832
+      ],
+      [
+        -121.65593702297,
+        38.254121186062
+      ],
+      [
+        -121.65692824334,
+        38.249152380333
+      ],
+      [
+        -121.65910892817,
+        38.246667977469
+      ],
+      [
+        -121.65881156206,
+        38.244108289669
+      ],
+      [
+        -121.66535361654,
+        38.239892333293
+      ],
+      [
+        -121.66634483692,
+        38.235074097435
+      ],
+      [
+        -121.67328337955,
+        38.232213269895
+      ],
+      [
+        -121.67467108808,
+        38.228674877936
+      ],
+      [
+        -121.67159830491,
+        38.218285556867
+      ],
+      [
+        -121.65891068409,
+        38.206014112416
+      ],
+      [
+        -121.65732473149,
+        38.195173081735
+      ],
+      [
+        -121.66257819949,
+        38.183278061961
+      ],
+      [
+        -121.66812903359,
+        38.175222573885
+      ],
+      [
+        -121.67913157977,
+        38.163854548658
+      ],
+      [
+        -121.68557451222,
+        38.159713877217
+      ],
+      [
+        -121.67645528475,
+        38.1551967811
+      ],
+      [
+        -121.68190699682,
+        38.15120667953
+      ],
+      [
+        -121.68616924444,
+        38.138031815856
+      ],
+      [
+        -121.6860701224,
+        38.132234875839
+      ],
+      [
+        -121.69062973613,
+        38.123050113734
+      ],
+      [
+        -121.66584922673,
+        38.123501823346
+      ],
+      [
+        -121.66138873504,
+        38.118307162812
+      ],
+      [
+        -121.65990190447,
+        38.119888146453
+      ],
+      [
+        -121.65108004312,
+        38.116650894235
+      ],
+      [
+        -121.6512782872,
+        38.095947537033
+      ],
+      [
+        -121.64265466992,
+        38.090903446369
+      ],
+      [
+        -121.63640998155,
+        38.090000027145
+      ],
+      [
+        -121.63413017469,
+        38.092710284815
+      ],
+      [
+        -121.6328415882,
+        38.099485928991
+      ],
+      [
+        -121.63036353726,
+        38.102346756532
+      ],
+      [
+        -121.61737855033,
+        38.105734578619
+      ],
+      [
+        -121.6136119129,
+        38.108821260966
+      ],
+      [
+        -121.60260936672,
+        38.107993126678
+      ],
+      [
+        -121.59319277315,
+        38.102798466143
+      ],
+      [
+        -121.57445870804,
+        38.100314063279
+      ],
+      [
+        -121.56950260616,
+        38.096926241191
+      ],
+      [
+        -121.58129812863,
+        38.09391484378
+      ],
+      [
+        -121.58526301014,
+        38.096399246644
+      ],
+      [
+        -121.60488917359,
+        38.099862353667
+      ],
+      [
+        -121.62738987613,
+        38.098356654962
+      ],
+      [
+        -121.63264334412,
+        38.08864489831
+      ],
+      [
+        -121.63809505619,
+        38.086160495446
+      ],
+      [
+        -121.6452318429,
+        38.087440339346
+      ],
+      [
+        -121.66188434522,
+        38.095646397291
+      ],
+      [
+        -121.67308513548,
+        38.093538419104
+      ],
+      [
+        -121.67923070181,
+        38.089322462728
+      ],
+      [
+        -121.68190699682,
+        38.082019824005
+      ],
+      [
+        -121.67962718996,
+        38.068092110978
+      ],
+      [
+        -121.68200611886,
+        38.060638902385
+      ],
+      [
+        -121.70093842805,
+        38.044452641299
+      ],
+      [
+        -121.73255835805,
+        38.029019229566
+      ],
+      [
+        -121.73226099194,
+        38.014715091862
+      ],
+      [
+        -121.73077416137,
+        38.015016231604
+      ],
+      [
+        -121.73027855118,
+        38.013209393157
+      ],
+      [
+        -121.72621454764,
+        38.012983538351
+      ],
+      [
+        -121.71322956071,
+        38.005530329758
+      ],
+      [
+        -121.71422078109,
+        38.006584318852
+      ],
+      [
+        -121.69618057024,
+        38.007186598334
+      ],
+      [
+        -121.69667618043,
+        38.006057324305
+      ],
+      [
+        -121.70906643513,
+        38.005680899628
+      ],
+      [
+        -121.70906643513,
+        38.001314373382
+      ],
+      [
+        -121.70500243159,
+        38.001389658317
+      ],
+      [
+        -121.70480418752,
+        37.997926551294
+      ],
+      [
+        -121.69558583802,
+        37.998378260906
+      ],
+      [
+        -121.68944027168,
+        38.014037527445
+      ],
+      [
+        -121.68438504776,
+        38.014338667186
+      ],
+      [
+        -121.67863596958,
+        38.011026130034
+      ],
+      [
+        -121.66733605729,
+        38.013209393157
+      ],
+      [
+        -121.63571612729,
+        38.011703694451
+      ],
+      [
+        -121.62699338798,
+        38.009294576522
+      ],
+      [
+        -121.62709251002,
+        38.00613260924
+      ],
+      [
+        -121.62401972685,
+        38.006283179111
+      ],
+      [
+        -121.62372236074,
+        38.008165302493
+      ],
+      [
+        -121.62074869961,
+        38.008165302493
+      ],
+      [
+        -121.62035221146,
+        38.009445146393
+      ],
+      [
+        -121.62471358111,
+        38.010649705357
+      ],
+      [
+        -121.6196583572,
+        38.012908253416
+      ],
+      [
+        -121.6167838181,
+        38.012004834192
+      ],
+      [
+        -121.61658557403,
+        38.013886957574
+      ],
+      [
+        -121.61341366882,
+        38.014338667186
+      ],
+      [
+        -121.60706985842,
+        38.018780478368
+      ],
+      [
+        -121.60736722453,
+        38.020135607203
+      ],
+      [
+        -121.6020146345,
+        38.022243585391
+      ],
+      [
+        -121.59666204447,
+        38.026835966443
+      ],
+      [
+        -121.58466827791,
+        38.030299073466
+      ],
+      [
+        -121.58387530161,
+        38.016597215244
+      ],
+      [
+        -121.58228934901,
+        38.016747785115
+      ],
+      [
+        -121.5804060303,
+        38.006509033917
+      ],
+      [
+        -121.5775314912,
+        38.003572921441
+      ],
+      [
+        -121.56880875189,
+        38.002217792605
+      ],
+      [
+        -121.57059294857,
+        37.999055825324
+      ],
+      [
+        -121.57891919973,
+        37.998528830777
+      ],
+      [
+        -121.57604466064,
+        37.994839868948
+      ],
+      [
+        -121.58100076252,
+        37.989494638543
+      ],
+      [
+        -121.58248759309,
+        37.983923553332
+      ],
+      [
+        -121.57237714525,
+        37.979030032538
+      ],
+      [
+        -121.57326924359,
+        37.977674903703
+      ],
+      [
+        -121.58020778622,
+        37.976395059803
+      ],
+      [
+        -121.57445870804,
+        37.972480243169
+      ],
+      [
+        -121.57366573174,
+        37.966457448346
+      ],
+      [
+        -121.5651412365,
+        37.959832374041
+      ],
+      [
+        -121.56553772465,
+        37.957799680789
+      ],
+      [
+        -121.57326924359,
+        37.957347971177
+      ],
+      [
+        -121.5751525623,
+        37.955014138183
+      ],
+      [
+        -121.5735666097,
+        37.953734294283
+      ],
+      [
+        -121.5635552839,
+        37.954712998442
+      ],
+      [
+        -121.56880875189,
+        37.948464348814
+      ],
+      [
+        -121.56742104337,
+        37.943947252697
+      ],
+      [
+        -121.56068074481,
+        37.94756092959
+      ],
+      [
+        -121.55780620572,
+        37.947033935043
+      ],
+      [
+        -121.55820269387,
+        37.945377666467
+      ],
+      [
+        -121.56365440594,
+        37.94342025815
+      ],
+      [
+        -121.56087898888,
+        37.939881866191
+      ],
+      [
+        -121.56147372111,
+        37.935891764621
+      ],
+      [
+        -121.55731059553,
+        37.932278087728
+      ],
+      [
+        -121.55889654813,
+        37.927911561481
+      ],
+      [
+        -121.55641849719,
+        37.923319180429
+      ],
+      [
+        -121.56563684669,
+        37.91819980483
+      ],
+      [
+        -121.57247626729,
+        37.916995245865
+      ],
+      [
+        -121.57822534547,
+        37.920081928212
+      ],
+      [
+        -121.59160682055,
+        37.921512341982
+      ],
+      [
+        -121.59487784779,
+        37.919780788471
+      ],
+      [
+        -121.60687161434,
+        37.918802084312
+      ],
+      [
+        -121.60915142121,
+        37.932579227469
+      ],
+      [
+        -121.62273114036,
+        37.932805082275
+      ],
+      [
+        -121.62292938444,
+        37.919027939118
+      ],
+      [
+        -121.62183904202,
+        37.918952654183
+      ],
+      [
+        -121.6228302624,
+        37.918651514442
+      ],
+      [
+        -121.62302850647,
+        37.903594527385
+      ],
+      [
+        -121.61836977071,
+        37.903745097256
+      ],
+      [
+        -121.61946011312,
+        37.897797587368
+      ],
+      [
+        -121.61817152663,
+        37.896291888663
+      ],
+      [
+        -121.62302850647,
+        37.896442458533
+      ],
+      [
+        -121.62729075409,
+        37.894485050216
+      ],
+      [
+        -121.62738987613,
+        37.892602926834
+      ],
+      [
+        -121.63066090337,
+        37.892527641898
+      ],
+      [
+        -121.63224685597,
+        37.896291888663
+      ],
+      [
+        -121.63214773394,
+        37.889290389681
+      ],
+      [
+        -121.64146520547,
+        37.889139819811
+      ],
+      [
+        -121.64136608344,
+        37.896442458533
+      ],
+      [
+        -121.65028706682,
+        37.896517743468
+      ],
+      [
+        -121.65187301942,
+        37.882138320829
+      ],
+      [
+        -121.65980278243,
+        37.889892669163
+      ],
+      [
+        -121.66000102651,
+        37.885601427852
+      ],
+      [
+        -121.67120181676,
+        37.886580132011
+      ],
+      [
+        -121.67070620657,
+        37.881536041347
+      ],
+      [
+        -121.67814035939,
+        37.8810090468
+      ],
+      [
+        -121.67764474921,
+        37.882891170182
+      ],
+      [
+        -121.68864729538,
+        37.882966455117
+      ],
+      [
+        -121.67794211532,
+        37.871297290149
+      ],
+      [
+        -121.65355809406,
+        37.85691786751
+      ],
+      [
+        -121.6468177955,
+        37.849765798658
+      ],
+      [
+        -121.64691691754,
+        37.839376477589
+      ],
+      [
+        -121.65425194833,
+        37.833353682766
+      ],
+      [
+        -121.65534229074,
+        37.820254104027
+      ],
+      [
+        -121.66099224688,
+        37.816414572327
+      ],
+      [
+        -121.66327205375,
+        37.812424470757
+      ],
+      [
+        -121.66465976228,
+        37.81844726558
+      ],
+      [
+        -121.66158697911,
+        37.82514762482
+      ],
+      [
+        -121.66793078952,
+        37.82898715652
+      ],
+      [
+        -121.66565098265,
+        37.830643425096
+      ],
+      [
+        -121.67467108808,
+        37.831095134708
+      ],
+      [
+        -121.68309646128,
+        37.835612230825
+      ],
+      [
+        -121.68319558331,
+        37.838247203559
+      ],
+      [
+        -121.68864729538,
+        37.838322488495
+      ],
+      [
+        -121.68963851576,
+        37.840656321488
+      ],
+      [
+        -121.68755695297,
+        37.842011450324
+      ],
+      [
+        -121.69231481077,
+        37.842237305129
+      ],
+      [
+        -121.69766740081,
+        37.845850982023
+      ],
+      [
+        -121.69826213303,
+        37.848485954758
+      ],
+      [
+        -121.69439637356,
+        37.850669217881
+      ],
+      [
+        -121.69409900745,
+        37.853304190616
+      ],
+      [
+        -121.69627969228,
+        37.855939163351
+      ],
+      [
+        -121.69637881432,
+        37.861209108821
+      ],
+      [
+        -121.69855949914,
+        37.864145221297
+      ],
+      [
+        -121.6989559873,
+        37.868812887284
+      ],
+      [
+        -121.7237364967,
+        37.86888817222
+      ],
+      [
+        -121.72363737466,
+        37.852701911134
+      ],
+      [
+        -121.77022473235,
+        37.853228905681
+      ],
+      [
+        -121.76992736624,
+        37.843743003835
+      ],
+      [
+        -121.76526863047,
+        37.843743003835
+      ],
+      [
+        -121.76497126435,
+        37.833278397831
+      ],
+      [
+        -121.76497126435,
+        37.814833588686
+      ],
+      [
+        -121.75852833191,
+        37.814758303751
+      ],
+      [
+        -121.75852833191,
+        37.806928670482
+      ],
+      [
+        -121.7537704741,
+        37.806928670482
+      ],
+      [
+        -121.7537704741,
+        37.802787999041
+      ],
+      [
+        -121.75149066724,
+        37.802787999041
+      ],
+      [
+        -121.74990471463,
+        37.774255008569
+      ],
+      [
+        -121.73929865661,
+        37.772147030381
+      ],
+      [
+        -121.67040884046,
+        37.789462565496
+      ],
+      [
+        -121.67040884046,
+        37.783891480285
+      ],
+      [
+        -121.66862464378,
+        37.779600238974
+      ],
+      [
+        -121.67407635585,
+        37.768307498682
+      ],
+      [
+        -121.67764474921,
+        37.764618536853
+      ],
+      [
+        -121.67764474921,
+        37.747754711349
+      ],
+      [
+        -121.67506757623,
+        37.742560050815
+      ],
+      [
+        -121.67169742695,
+        37.739398083533
+      ],
+      [
+        -121.65663087723,
+        37.746926577061
+      ],
+      [
+        -121.64800725996,
+        37.742560050815
+      ],
+      [
+        -121.64196081566,
+        37.743087045362
+      ],
+      [
+        -121.63353544246,
+        37.739849793145
+      ],
+      [
+        -121.62996704911,
+        37.732547154422
+      ],
+      [
+        -121.62312762851,
+        37.729611041946
+      ],
+      [
+        -121.61222420437,
+        37.730815600911
+      ],
+      [
+        -121.605979516,
+        37.728782907658
+      ],
+      [
+        -121.60221287857,
+        37.725922080117
+      ],
+      [
+        -121.60251024469,
+        37.670512367749
+      ],
+      [
+        -121.55691410738,
+        37.67066293762
+      ],
+      [
+        -121.55661674127,
+        37.542753832574
+      ],
+      [
+        -121.55047117493,
+        37.539366010487
+      ],
+      [
+        -121.54571331713,
+        37.532515081376
+      ],
+      [
+        -121.54065809321,
+        37.529804823706
+      ],
+      [
+        -121.53034940129,
+        37.527094566035
+      ],
+      [
+        -121.52886257073,
+        37.528073270194
+      ],
+      [
+        -121.52232051625,
+        37.524685448106
+      ],
+      [
+        -121.50150488835,
+        37.524986587848
+      ],
+      [
+        -121.4982338611,
+        37.522426900048
+      ],
+      [
+        -121.50239698668,
+        37.518436798478
+      ],
+      [
+        -121.49724264073,
+        37.512489288591
+      ],
+      [
+        -121.49535932201,
+        37.508122762344
+      ],
+      [
+        -121.49605317628,
+        37.504960795062
+      ],
+      [
+        -121.49317863719,
+        37.502401107263
+      ],
+      [
+        -121.48207696897,
+        37.501422403104
+      ],
+      [
+        -121.47940067396,
+        37.496453597375
+      ],
+      [
+        -121.46929022612,
+        37.4896779532
+      ],
+      [
+        -121.47117354483,
+        37.482827024089
+      ],
+      [
+        -121.47454369411,
+        37.479363917066
+      ],
+      [
+        -121.47771559932,
+        37.48026733629
+      ],
+      [
+        -121.48336555546,
+        37.475449100431
+      ],
+      [
+        -121.48673570474,
+        37.475674955237
+      ],
+      [
+        -121.4842576538,
+        37.466038483521
+      ],
+      [
+        -121.4750393043,
+        37.462048381951
+      ],
+      [
+        -121.47305686355,
+        37.456778436481
+      ],
+      [
+        -121.46958759223,
+        37.455724447387
+      ],
+      [
+        -121.46889373797,
+        37.453842324005
+      ],
+      [
+        -121.46403675812,
+        37.453842324005
+      ],
+      [
+        -121.46215343941,
+        37.442323728907
+      ],
+      [
+        -121.46235168348,
+        37.437957202661
+      ],
+      [
+        -121.46492885646,
+        37.437053783437
+      ],
+      [
+        -121.47028144649,
+        37.430127569391
+      ],
+      [
+        -121.46889373797,
+        37.425008193792
+      ],
+      [
+        -121.47256125336,
+        37.423351925216
+      ],
+      [
+        -121.46621744295,
+        37.415070582335
+      ],
+      [
+        -121.46354114794,
+        37.415898716623
+      ],
+      [
+        -121.45630523919,
+        37.406713954518
+      ],
+      [
+        -121.45561138493,
+        37.401067584372
+      ],
+      [
+        -121.45828767994,
+        37.398131471896
+      ],
+      [
+        -121.4566026053,
+        37.395571784096
+      ],
+      [
+        -121.45154738138,
+        37.394668364873
+      ],
+      [
+        -121.4481772321,
+        37.391656967462
+      ],
+      [
+        -121.43608434351,
+        37.396098778643
+      ],
+      [
+        -121.4125924206,
+        37.389398419403
+      ],
+      [
+        -121.41060997984,
+        37.386085882251
+      ],
+      [
+        -121.40912314928,
+        37.38066536691
+      ],
+      [
+        -121.41586344784,
+        37.375922415988
+      ],
+      [
+        -121.41933271915,
+        37.364479105825
+      ],
+      [
+        -121.42250462436,
+        37.363123976989
+      ],
+      [
+        -121.42369408881,
+        37.358832735678
+      ],
+      [
+        -121.41943184119,
+        37.351981806568
+      ],
+      [
+        -121.42072042768,
+        37.34475445278
+      ],
+      [
+        -121.40912314928,
+        37.330600884947
+      ],
+      [
+        -121.41110559003,
+        37.327514202601
+      ],
+      [
+        -121.41021349169,
+        37.325029799736
+      ],
+      [
+        -121.41189856633,
+        37.324427520254
+      ],
+      [
+        -121.40942051539,
+        37.321491407778
+      ],
+      [
+        -121.405753,
+        37.311026801774
+      ],
+      [
+        -121.41834149878,
+        37.30327245344
+      ],
+      [
+        -121.42270286843,
+        37.299131781999
+      ],
+      [
+        -121.42349584473,
+        37.2952922503
+      ],
+      [
+        -121.4257756516,
+        37.296346239394
+      ],
+      [
+        -121.43142560774,
+        37.294991110558
+      ],
+      [
+        -121.43677819778,
+        37.291979713147
+      ],
+      [
+        -121.44351849634,
+        37.296647379135
+      ],
+      [
+        -121.44966406267,
+        37.293937121465
+      ],
+      [
+        -121.44867284229,
+        37.290323444571
+      ],
+      [
+        -121.45402543232,
+        37.284074794942
+      ],
+      [
+        -121.45808943587,
+        37.284150079878
+      ],
+      [
+        -121.45908065624,
+        37.282719666107
+      ],
+      [
+        -121.45392631029,
+        37.277148580896
+      ],
+      [
+        -121.45412455436,
+        37.273986613615
+      ],
+      [
+        -121.53371955057,
+        37.273610188938
+      ],
+      [
+        -121.54026160506,
+        37.286258058066
+      ],
+      [
+        -121.54729926973,
+        37.282493811302
+      ],
+      [
+        -121.55364308014,
+        37.28219267156
+      ],
+      [
+        -121.55602200904,
+        37.280461118049
+      ],
+      [
+        -121.56603333484,
+        37.294614685882
+      ],
+      [
+        -121.56742104337,
+        37.292958417306
+      ],
+      [
+        -121.57029558246,
+        37.290925724053
+      ],
+      [
+        -121.57663939287,
+        37.290097589765
+      ],
+      [
+        -121.57981129807,
+        37.286258058066
+      ],
+      [
+        -121.60003219375,
+        37.289645880153
+      ],
+      [
+        -121.61608996384,
+        37.286785052613
+      ],
+      [
+        -121.62005484535,
+        37.290474014442
+      ],
+      [
+        -121.62620041168,
+        37.291226863794
+      ],
+      [
+        -121.62857934058,
+        37.2901728747
+      ],
+      [
+        -121.62818285243,
+        37.288441321189
+      ],
+      [
+        -121.60885405509,
+        37.268340243468
+      ],
+      [
+        -121.61182771622,
+        37.26209159384
+      ],
+      [
+        -121.61390927901,
+        37.260510610199
+      ],
+      [
+        -121.61549523161,
+        37.261263459552
+      ],
+      [
+        -121.61708118422,
+        37.255692374341
+      ],
+      [
+        -121.624416215,
+        37.254939524988
+      ],
+      [
+        -121.62372236074,
+        37.252153982383
+      ],
+      [
+        -121.62957056096,
+        37.253358541347
+      ],
+      [
+        -121.63750032397,
+        37.258553201882
+      ],
+      [
+        -121.64622306328,
+        37.260736465005
+      ],
+      [
+        -121.64741252773,
+        37.263597292546
+      ],
+      [
+        -121.65236862961,
+        37.264199572028
+      ],
+      [
+        -121.65900980613,
+        37.260962319811
+      ],
+      [
+        -121.65712648742,
+        37.25682164837
+      ],
+      [
+        -121.65910892817,
+        37.256068799017
+      ],
+      [
+        -121.66753430137,
+        37.259607190976
+      ],
+      [
+        -121.67011147435,
+        37.259155481364
+      ],
+      [
+        -121.67566230845,
+        37.265027706316
+      ],
+      [
+        -121.67397723381,
+        37.259004911493
+      ],
+      [
+        -121.67774387124,
+        37.255315949664
+      ],
+      [
+        -121.67734738309,
+        37.252304552253
+      ],
+      [
+        -121.66872376582,
+        37.24086124209
+      ],
+      [
+        -121.66882288786,
+        37.226632389322
+      ],
+      [
+        -121.67477021011,
+        37.223244567234
+      ],
+      [
+        -121.6668404471,
+        37.215113794223
+      ],
+      [
+        -121.66713781322,
+        37.213457525647
+      ],
+      [
+        -121.66228083337,
+        37.211198977589
+      ],
+      [
+        -121.66584922673,
+        37.205552607442
+      ],
+      [
+        -121.67140006084,
+        37.201186081196
+      ],
+      [
+        -121.67764474921,
+        37.200132092102
+      ],
+      [
+        -121.67774387124,
+        37.197873544044
+      ],
+      [
+        -121.69409900745,
+        37.198174683785
+      ],
+      [
+        -121.70341647899,
+        37.200132092102
+      ],
+      [
+        -121.71065238774,
+        37.20894042953
+      ],
+      [
+        -121.71471639128,
+        37.20894042953
+      ],
+      [
+        -121.71441902516,
+        37.207886440436
+      ],
+      [
+        -121.72343913059,
+        37.215941928512
+      ],
+      [
+        -121.72839523247,
+        37.215941928512
+      ],
+      [
+        -121.74683193147,
+        37.230321351151
+      ],
+      [
+        -121.75119330112,
+        37.226406534516
+      ],
+      [
+        -121.74653456536,
+        37.222868142558
+      ],
+      [
+        -121.75922218617,
+        37.218953325923
+      ],
+      [
+        -121.75852833191,
+        37.217824051894
+      ],
+      [
+        -121.76100638285,
+        37.216544207994
+      ],
+      [
+        -121.76269145749,
+        37.217824051894
+      ],
+      [
+        -121.76556599658,
+        37.214360944871
+      ],
+      [
+        -121.76804404752,
+        37.216995917605
+      ],
+      [
+        -121.77081946457,
+        37.217372342282
+      ],
+      [
+        -121.7782536174,
+        37.208187580177
+      ],
+      [
+        -121.77974044796,
+        37.205176182766
+      ],
+      [
+        -121.77706415294,
+        37.198626393396
+      ],
+      [
+        -121.77964132592,
+        37.196443130273
+      ],
+      [
+        -121.77448697997,
+        37.193205878056
+      ],
+      [
+        -121.76715194918,
+        37.192076604027
+      ],
+      [
+        -121.7645747762,
+        37.190194480645
+      ],
+      [
+        -121.76308794564,
+        37.191022614933
+      ],
+      [
+        -121.7637817999,
+        37.18898992168
+      ],
+      [
+        -121.75971779636,
+        37.18612909414
+      ],
+      [
+        -121.7598169184,
+        37.183494121405
+      ],
+      [
+        -121.76170023711,
+        37.180783863734
+      ],
+      [
+        -121.76556599658,
+        37.180558008929
+      ],
+      [
+        -121.76388092194,
+        37.177471326582
+      ],
+      [
+        -121.76596248473,
+        37.179654589705
+      ],
+      [
+        -121.76735019326,
+        37.178148891
+      ],
+      [
+        -121.7737931257,
+        37.181386143217
+      ],
+      [
+        -121.77329751551,
+        37.183268266599
+      ],
+      [
+        -121.78182201075,
+        37.182816556987
+      ],
+      [
+        -121.78430006169,
+        37.180633293864
+      ],
+      [
+        -121.78271410909,
+        37.181235573346
+      ],
+      [
+        -121.78241674298,
+        37.179504019835
+      ],
+      [
+        -121.77954220388,
+        37.179052310223
+      ],
+      [
+        -121.78529128207,
+        37.178826455417
+      ],
+      [
+        -121.78519216003,
+        37.175513918265
+      ],
+      [
+        -121.78370532946,
+        37.175438633329
+      ],
+      [
+        -121.78786845505,
+        37.171900241371
+      ],
+      [
+        -121.79649207232,
+        37.172351950983
+      ],
+      [
+        -121.79867275715,
+        37.171072107083
+      ],
+      [
+        -121.79976309956,
+        37.168211279542
+      ],
+      [
+        -121.80352973699,
+        37.165651591743
+      ],
+      [
+        -121.79877187918,
+        37.15706910912
+      ],
+      [
+        -121.79916836733,
+        37.155412840544
+      ],
+      [
+        -121.80184466235,
+        37.156391544703
+      ],
+      [
+        -121.80501656755,
+        37.15307900755
+      ],
+      [
+        -121.80808935072,
+        37.152627297939
+      ],
+      [
+        -121.81284720853,
+        37.153907141838
+      ],
+      [
+        -121.81473052724,
+        37.151799163651
+      ],
+      [
+        -121.81720857818,
+        37.142689686481
+      ],
+      [
+        -121.80422359125,
+        37.135763472435
+      ],
+      [
+        -121.80372798107,
+        37.102562815976
+      ],
+      [
+        -121.80253851661,
+        37.100379552852
+      ],
+      [
+        -121.8330681042,
+        37.100605407658
+      ],
+      [
+        -121.83584352126,
+        37.109790169763
+      ],
+      [
+        -121.83842069423,
+        37.10647763261
+      ],
+      [
+        -121.84813465392,
+        37.105423643516
+      ],
+      [
+        -121.84298030796,
+        37.10120768714
+      ],
+      [
+        -121.84397152834,
+        37.097594010247
+      ],
+      [
+        -121.84625133521,
+        37.096991730765
+      ],
+      [
+        -121.84416977242,
+        37.094733182706
+      ],
+      [
+        -121.83415844662,
+        37.091194790748
+      ],
+      [
+        -121.83039180919,
+        37.088108108401
+      ],
+      [
+        -121.82424624285,
+        37.08780696866
+      ],
+      [
+        -121.82355238859,
+        37.086000130213
+      ],
+      [
+        -121.81819979856,
+        37.083741582155
+      ],
+      [
+        -121.81651472392,
+        37.080353760067
+      ],
+      [
+        -121.81720857818,
+        37.077869357203
+      ],
+      [
+        -121.81264896445,
+        37.075384954338
+      ],
+      [
+        -121.8090805711,
+        37.069286874581
+      ],
+      [
+        -121.81284720853,
+        37.065221488075
+      ],
+      [
+        -121.81681209003,
+        37.064920348334
+      ],
+      [
+        -121.82523746323,
+        37.067931745745
+      ],
+      [
+        -121.83009444307,
+        37.066953041587
+      ],
+      [
+        -121.83009444307,
+        37.06514620314
+      ],
+      [
+        -121.84159259944,
+        37.070717288351
+      ],
+      [
+        -121.84099786721,
+        37.065070918205
+      ],
+      [
+        -121.84416977242,
+        37.068458740292
+      ],
+      [
+        -121.84902675226,
+        37.068232885487
+      ],
+      [
+        -121.84863026411,
+        37.071093713027
+      ],
+      [
+        -121.85576705082,
+        37.072900551474
+      ],
+      [
+        -121.85883983398,
+        37.075761379015
+      ],
+      [
+        -121.8583442238,
+        37.084946141119
+      ],
+      [
+        -121.8899641538,
+        37.093829763483
+      ],
+      [
+        -121.89432552345,
+        37.092474634648
+      ],
+      [
+        -121.90255265258,
+        37.086451839825
+      ],
+      [
+        -121.91732183618,
+        37.081708888902
+      ],
+      [
+        -121.91910603286,
+        37.078622206556
+      ],
+      [
+        -121.92564808734,
+        37.074330965244
+      ],
+      [
+        -121.92465686697,
+        37.073427546021
+      ],
+      [
+        -121.92663930772,
+        37.07056671848
+      ],
+      [
+        -121.92485511104,
+        37.068910449904
+      ],
+      [
+        -121.93774097593,
+        37.069813869128
+      ],
+      [
+        -121.9408137591,
+        37.065823767558
+      ],
+      [
+        -121.94180497948,
+        37.057166
+      ],
+      [
+        -121.94596810506,
+        37.064393353787
+      ],
+      [
+        -121.94834703396,
+        37.065522627816
+      ],
+      [
+        -121.94626547117,
+        37.068534025228
+      ],
+      [
+        -121.94636459321,
+        37.076137803691
+      ],
+      [
+        -121.95350137992,
+        37.076213088627
+      ],
+      [
+        -121.95459172233,
+        37.081708888902
+      ],
+      [
+        -121.96281885145,
+        37.089162097495
+      ],
+      [
+        -121.95855660384,
+        37.09563660193
+      ],
+      [
+        -121.97312754337,
+        37.104369654422
+      ],
+      [
+        -121.97530822819,
+        37.104294369487
+      ],
+      [
+        -121.9768941808,
+        37.109489030022
+      ],
+      [
+        -121.98393184547,
+        37.113479131592
+      ],
+      [
+        -121.98680638456,
+        37.114006126139
+      ],
+      [
+        -121.98650901845,
+        37.111220583533
+      ],
+      [
+        -121.98829321512,
+        37.112048717821
+      ],
+      [
+        -121.99077126606,
+        37.104520224293
+      ],
+      [
+        -121.99196073052,
+        37.105122503775
+      ],
+      [
+        -121.99186160848,
+        37.109263175216
+      ],
+      [
+        -121.99562824591,
+        37.110016024569
+      ],
+      [
+        -121.99632210017,
+        37.111521723274
+      ],
+      [
+        -121.99493439164,
+        37.112876852109
+      ],
+      [
+        -121.99800717481,
+        37.119577211349
+      ],
+      [
+        -121.99671858832,
+        37.113479131592
+      ],
+      [
+        -122.01128952785,
+        37.113554416527
+      ],
+      [
+        -122.01406494491,
+        37.116716383809
+      ],
+      [
+        -122.01376757879,
+        37.118071512644
+      ],
+      [
+        -122.00990181933,
+        37.116641098873
+      ],
+      [
+        -122.01257811434,
+        37.120028920961
+      ],
+      [
+        -122.0124789923,
+        37.127783269295
+      ],
+      [
+        -122.01822807049,
+        37.12770798436
+      ],
+      [
+        -122.01763333826,
+        37.131171091383
+      ],
+      [
+        -122.02804115221,
+        37.135311762824
+      ],
+      [
+        -122.0269508098,
+        37.13711860127
+      ],
+      [
+        -122.02863588444,
+        37.137495025947
+      ],
+      [
+        -122.02823939629,
+        37.138925439717
+      ],
+      [
+        -122.03240252187,
+        37.1382478753
+      ],
+      [
+        -122.03220427779,
+        37.135537617629
+      ],
+      [
+        -122.03636740337,
+        37.13711860127
+      ],
+      [
+        -122.03973755265,
+        37.133354354506
+      ],
+      [
+        -122.04320682397,
+        37.134860053212
+      ],
+      [
+        -122.04489189861,
+        37.133655494247
+      ],
+      [
+        -122.04489189861,
+        37.130719381771
+      ],
+      [
+        -122.04766731566,
+        37.130644096836
+      ],
+      [
+        -122.04875765807,
+        37.127632699425
+      ],
+      [
+        -122.05301990569,
+        37.12514829656
+      ],
+      [
+        -122.05401112607,
+        37.127632699425
+      ],
+      [
+        -122.06144527889,
+        37.124470732143
+      ],
+      [
+        -122.05629093293,
+        37.128009124101
+      ],
+      [
+        -122.06144527889,
+        37.124470732143
+      ],
+      [
+        -122.05837249572,
+        37.128009124101
+      ],
+      [
+        -122.05827337369,
+        37.132074510606
+      ],
+      [
+        -122.08573017811,
+        37.139000724652
+      ],
+      [
+        -122.09752570059,
+        37.117996227709
+      ],
+      [
+        -122.10149058209,
+        37.11829736745
+      ],
+      [
+        -122.1017879482,
+        37.115060115233
+      ],
+      [
+        -122.09663360225,
+        37.113102706915
+      ],
+      [
+        -122.1009949719,
+        37.108962035475
+      ],
+      [
+        -122.11269137234,
+        37.112425142498
+      ],
+      [
+        -122.11427732495,
+        37.111220583533
+      ],
+      [
+        -122.11437644698,
+        37.108585610798
+      ],
+      [
+        -122.10763614842,
+        37.107531621704
+      ],
+      [
+        -122.11368259272,
+        37.104896648969
+      ],
+      [
+        -122.12210796592,
+        37.103917944811
+      ],
+      [
+        -122.11645800977,
+        37.10376737494
+      ],
+      [
+        -122.1194316709,
+        37.101734681687
+      ],
+      [
+        -122.13905783435,
+        37.104369654422
+      ],
+      [
+        -122.14431130235,
+        37.111070013663
+      ],
+      [
+        -122.14391481419,
+        37.113328561721
+      ],
+      [
+        -122.15491736037,
+        37.120857055249
+      ],
+      [
+        -122.1610629267,
+        37.128009124101
+      ],
+      [
+        -122.16086468263,
+        37.129740677613
+      ],
+      [
+        -122.16681200489,
+        37.133279069571
+      ],
+      [
+        -122.16701024896,
+        37.135462332694
+      ],
+      [
+        -122.17117337454,
+        37.136742176594
+      ],
+      [
+        -122.17325493733,
+        37.139828858941
+      ],
+      [
+        -122.17801279514,
+        37.138473730105
+      ],
+      [
+        -122.18931270743,
+        37.142539116611
+      ],
+      [
+        -122.19981964342,
+        37.142840256352
+      ],
+      [
+        -122.19753983655,
+        37.150067610139
+      ],
+      [
+        -122.20527135548,
+        37.150669889621
+      ],
+      [
+        -122.20675818605,
+        37.152476728068
+      ],
+      [
+        -122.20745204031,
+        37.16090864082
+      ],
+      [
+        -122.21181340997,
+        37.160983925755
+      ],
+      [
+        -122.21181340997,
+        37.164823457454
+      ],
+      [
+        -122.20289242658,
+        37.16489874239
+      ],
+      [
+        -122.21548092536,
+        37.168361849413
+      ],
+      [
+        -122.22787118006,
+        37.164447032778
+      ],
+      [
+        -122.230349231,
+        37.160758070949
+      ],
+      [
+        -122.24164914329,
+        37.16105921069
+      ],
+      [
+        -122.24264036367,
+        37.190043910774
+      ],
+      [
+        -122.26890770364,
+        37.189818055968
+      ],
+      [
+        -122.28853386709,
+        37.186731373622
+      ],
+      [
+        -122.31767574615,
+        37.186957228428
+      ],
+      [
+        -122.31182754593,
+        37.147507922339
+      ],
+      [
+        -122.28942596543,
+        37.113479131592
+      ],
+      [
+        -122.29299435878,
+        37.107381051834
+      ],
+      [
+        -122.30597934571,
+        37.116415244068
+      ],
+      [
+        -122.31153017982,
+        37.117920942773
+      ],
+      [
+        -122.31559418336,
+        37.116264674197
+      ],
+      [
+        -122.31846872245,
+        37.11701752355
+      ],
+      [
+        -122.32927302455,
+        37.112274572627
+      ],
+      [
+        -122.33204844161,
+        37.116942238615
+      ],
+      [
+        -122.33690542145,
+        37.117092808485
+      ],
+      [
+        -122.33938347239,
+        37.120932340185
+      ],
+      [
+        -122.33720278756,
+        37.13026767216
+      ],
+      [
+        -122.33829312998,
+        37.136215182047
+      ],
+      [
+        -122.34315010982,
+        37.140431138423
+      ],
+      [
+        -122.34582640484,
+        37.145249374281
+      ],
+      [
+        -122.3517737271,
+        37.145249374281
+      ],
+      [
+        -122.35147636098,
+        37.146529218181
+      ],
+      [
+        -122.35435090007,
+        37.146830357922
+      ],
+      [
+        -122.3557386086,
+        37.14908890598
+      ],
+      [
+        -122.36089295456,
+        37.149691185463
+      ],
+      [
+        -122.35960436807,
+        37.152853152744
+      ],
+      [
+        -122.36128944271,
+        37.160607501079
+      ],
+      [
+        -122.36763325312,
+        37.17287894553
+      ],
+      [
+        -122.37021042609,
+        37.173556509947
+      ],
+      [
+        -122.37923053152,
+        37.181235573346
+      ],
+      [
+        -122.38121297227,
+        37.180783863734
+      ],
+      [
+        -122.38666468434,
+        37.183418836469
+      ],
+      [
+        -122.39171990826,
+        37.183192981663
+      ],
+      [
+        -122.39469356939,
+        37.181386143217
+      ],
+      [
+        -122.39429708124,
+        37.182816556987
+      ],
+      [
+        -122.39707249829,
+        37.185376244787
+      ],
+      [
+        -122.39796459663,
+        37.192227173897
+      ],
+      [
+        -122.40510138334,
+        37.195916135726
+      ],
+      [
+        -122.40520050538,
+        37.206907736278
+      ],
+      [
+        -122.40698470205,
+        37.208187580177
+      ],
+      [
+        -122.40777767835,
+        37.213457525647
+      ],
+      [
+        -122.40916538688,
+        37.213833950324
+      ],
+      [
+        -122.40728206817,
+        37.218652186182
+      ],
+      [
+        -122.41065221744,
+        37.224072701522
+      ],
+      [
+        -122.4089671428,
+        37.225503115292
+      ],
+      [
+        -122.41154431578,
+        37.226481819451
+      ],
+      [
+        -122.41134607171,
+        37.228363942833
+      ],
+      [
+        -122.41590568544,
+        37.23288103895
+      ],
+      [
+        -122.41570744136,
+        37.237021710391
+      ],
+      [
+        -122.41917671268,
+        37.241614091443
+      ]
+    ],
+    [
+      [
+        -122.1663163947,
+        37.923018040688
+      ],
+      [
+        -122.14827618385,
+        37.923846174976
+      ],
+      [
+        -122.13727363767,
+        37.918049234959
+      ],
+      [
+        -122.12577548131,
+        37.9183503747
+      ],
+      [
+        -122.12646933557,
+        37.923018040688
+      ],
+      [
+        -122.11814308441,
+        37.924147314717
+      ],
+      [
+        -122.11794484034,
+        37.929868969799
+      ],
+      [
+        -122.11338522661,
+        37.931525238375
+      ],
+      [
+        -122.11070893159,
+        37.934762490592
+      ],
+      [
+        -122.11427732495,
+        37.934536635786
+      ],
+      [
+        -122.11606152162,
+        37.940032436062
+      ],
+      [
+        -122.11923342683,
+        37.941387564897
+      ],
+      [
+        -122.10961858918,
+        37.945227096596
+      ],
+      [
+        -122.11675537589,
+        37.946506940496
+      ],
+      [
+        -122.117052742,
+        37.949443052972
+      ],
+      [
+        -122.11675537589,
+        37.951475746225
+      ],
+      [
+        -122.12042289128,
+        37.953508439477
+      ],
+      [
+        -122.1254781152,
+        37.953433154542
+      ],
+      [
+        -122.12755967799,
+        37.950798181807
+      ],
+      [
+        -122.1286500204,
+        37.943947252697
+      ],
+      [
+        -122.13162368153,
+        37.947711499461
+      ],
+      [
+        -122.13697627156,
+        37.942366269056
+      ],
+      [
+        -122.14431130235,
+        37.945227096596
+      ],
+      [
+        -122.14956477034,
+        37.943344973214
+      ],
+      [
+        -122.15303404166,
+        37.935214200204
+      ],
+      [
+        -122.14906916015,
+        37.935741194751
+      ],
+      [
+        -122.14817706181,
+        37.932353372663
+      ],
+      [
+        -122.15214194332,
+        37.928589125899
+      ],
+      [
+        -122.16235151319,
+        37.925803583293
+      ],
+      [
+        -122.16572166247,
+        37.93032067941
+      ],
+      [
+        -122.17384966956,
+        37.931374668504
+      ],
+      [
+        -122.18643816834,
+        37.93830088255
+      ],
+      [
+        -122.20190120621,
+        37.938225597615
+      ],
+      [
+        -122.20299154862,
+        37.939505441515
+      ],
+      [
+        -122.20655994197,
+        37.937171608521
+      ],
+      [
+        -122.20467662326,
+        37.939806581256
+      ],
+      [
+        -122.2087406268,
+        37.948012639202
+      ],
+      [
+        -122.2195449289,
+        37.948012639202
+      ],
+      [
+        -122.21944580687,
+        37.957122116371
+      ],
+      [
+        -122.20774940643,
+        37.956896261565
+      ],
+      [
+        -122.20685730809,
+        37.965930453799
+      ],
+      [
+        -122.22142824762,
+        37.984525832814
+      ],
+      [
+        -122.23411586843,
+        37.984450547879
+      ],
+      [
+        -122.24977715038,
+        37.993033030501
+      ],
+      [
+        -122.25047100464,
+        37.991075622184
+      ],
+      [
+        -122.24055880088,
+        37.985956246584
+      ],
+      [
+        -122.23738689568,
+        37.981815575144
+      ],
+      [
+        -122.24006319069,
+        37.977825473574
+      ],
+      [
+        -122.24412719423,
+        37.97549164058
+      ],
+      [
+        -122.24174826533,
+        37.971802678751
+      ],
+      [
+        -122.24769558759,
+        37.972179103428
+      ],
+      [
+        -122.24313597386,
+        37.971125114334
+      ],
+      [
+        -122.24313597386,
+        37.967812577181
+      ],
+      [
+        -122.24680348925,
+        37.964048330417
+      ],
+      [
+        -122.23758513975,
+        37.9582513904
+      ],
+      [
+        -122.24343333997,
+        37.954487143636
+      ],
+      [
+        -122.24670436721,
+        37.955239992989
+      ],
+      [
+        -122.24908329612,
+        37.952303880513
+      ],
+      [
+        -122.25602183875,
+        37.94884077349
+      ],
+      [
+        -122.25364290985,
+        37.945754091143
+      ],
+      [
+        -122.25522886245,
+        37.946055230884
+      ],
+      [
+        -122.25602183875,
+        37.944398962308
+      ],
+      [
+        -122.26187003897,
+        37.944700102049
+      ],
+      [
+        -122.27108838847,
+        37.939806581256
+      ],
+      [
+        -122.25463413022,
+        37.920608922759
+      ],
+      [
+        -122.25423764207,
+        37.917672810283
+      ],
+      [
+        -122.25106573687,
+        37.915113122483
+      ],
+      [
+        -122.25304817762,
+        37.911574730525
+      ],
+      [
+        -122.2495789063,
+        37.911123020913
+      ],
+      [
+        -122.24759646555,
+        37.908864472855
+      ],
+      [
+        -122.24779470963,
+        37.905627220638
+      ],
+      [
+        -122.2448210485,
+        37.904874371285
+      ],
+      [
+        -122.25086749279,
+        37.90095955465
+      ],
+      [
+        -122.25265168947,
+        37.896442458533
+      ],
+      [
+        -122.25294905558,
+        37.893732200863
+      ],
+      [
+        -122.24947978427,
+        37.893054636445
+      ],
+      [
+        -122.24947978427,
+        37.888236400587
+      ],
+      [
+        -122.24194650941,
+        37.881912466023
+      ],
+      [
+        -122.23867548216,
+        37.883342879794
+      ],
+      [
+        -122.22707820376,
+        37.8797292029
+      ],
+      [
+        -122.22172561373,
+        37.87957863303
+      ],
+      [
+        -122.22212210188,
+        37.882063035894
+      ],
+      [
+        -122.22727644784,
+        37.891850077481
+      ],
+      [
+        -122.21429146091,
+        37.901486549197
+      ],
+      [
+        -122.21746336611,
+        37.903820382191
+      ],
+      [
+        -122.22678083765,
+        37.905551935702
+      ],
+      [
+        -122.23252991583,
+        37.909692607143
+      ],
+      [
+        -122.23441323455,
+        37.916995245865
+      ],
+      [
+        -122.23233167176,
+        37.916392966383
+      ],
+      [
+        -122.2295562547,
+        37.911574730525
+      ],
+      [
+        -122.22767293599,
+        37.908412763243
+      ],
+      [
+        -122.22271683411,
+        37.906530639861
+      ],
+      [
+        -122.21577829147,
+        37.907132919343
+      ],
+      [
+        -122.21300287442,
+        37.90750934402
+      ],
+      [
+        -122.21072306755,
+        37.910972451043
+      ],
+      [
+        -122.2163730237,
+        37.91548954716
+      ],
+      [
+        -122.21587741351,
+        37.921662911853
+      ],
+      [
+        -122.21320111849,
+        37.924373169523
+      ],
+      [
+        -122.20893887088,
+        37.921135917306
+      ],
+      [
+        -122.21330024053,
+        37.920985347435
+      ],
+      [
+        -122.21240814219,
+        37.915338977289
+      ],
+      [
+        -122.20596520975,
+        37.911725300395
+      ],
+      [
+        -122.20378452492,
+        37.914811982742
+      ],
+      [
+        -122.20368540288,
+        37.909692607143
+      ],
+      [
+        -122.20011700953,
+        37.917522240412
+      ],
+      [
+        -122.19585476191,
+        37.922114621465
+      ],
+      [
+        -122.17404791363,
+        37.917898665089
+      ],
+      [
+        -122.16453219802,
+        37.921286487176
+      ],
+      [
+        -122.1663163947,
+        37.923018040688
+      ]
+    ],
+    [
+      [
+        -122.23431411251,
+        38.021791875779
+      ],
+      [
+        -122.2295562547,
+        38.021641305908
+      ],
+      [
+        -122.22965537674,
+        38.014715091862
+      ],
+      [
+        -122.22519488505,
+        38.01396224251
+      ],
+      [
+        -122.22569049524,
+        38.011101414969
+      ],
+      [
+        -122.22023878317,
+        38.00884286691
+      ],
+      [
+        -122.21617477962,
+        38.008993436781
+      ],
+      [
+        -122.21597653555,
+        38.007186598334
+      ],
+      [
+        -122.2139940948,
+        38.007111313399
+      ],
+      [
+        -122.20507311141,
+        38.008918151846
+      ],
+      [
+        -122.19040304984,
+        38.015693796021
+      ],
+      [
+        -122.198729301,
+        38.025405552673
+      ],
+      [
+        -122.20606433179,
+        38.028191095278
+      ],
+      [
+        -122.20725379624,
+        38.041591813758
+      ],
+      [
+        -122.20616345382,
+        38.046184194811
+      ],
+      [
+        -122.21240814219,
+        38.045958340005
+      ],
+      [
+        -122.21577829147,
+        38.041817668564
+      ],
+      [
+        -122.21964405094,
+        38.045958340005
+      ],
+      [
+        -122.22113088151,
+        38.045356060523
+      ],
+      [
+        -122.21974317298,
+        38.042344663111
+      ],
+      [
+        -122.22103175947,
+        38.041064819211
+      ],
+      [
+        -122.22737556988,
+        38.04475378104
+      ],
+      [
+        -122.23193518361,
+        38.04347393714
+      ],
+      [
+        -122.23411586843,
+        38.044226786493
+      ],
+      [
+        -122.23431411251,
+        38.021791875779
+      ]
+    ],
+    [
+      [
+        -122.00573869375,
+        37.994087019595
+      ],
+      [
+        -121.9776871571,
+        37.973760087069
+      ],
+      [
+        -121.97550647227,
+        37.97549164058
+      ],
+      [
+        -121.9700547602,
+        37.971727393816
+      ],
+      [
+        -121.96836968556,
+        37.973157807586
+      ],
+      [
+        -121.96390919387,
+        37.972856667845
+      ],
+      [
+        -121.95360050195,
+        37.964876464705
+      ],
+      [
+        -121.94527425079,
+        37.96352133587
+      ],
+      [
+        -121.94041727095,
+        37.967737292246
+      ],
+      [
+        -121.93912868446,
+        37.971576823945
+      ],
+      [
+        -121.93912868446,
+        37.979858166826
+      ],
+      [
+        -121.94220146763,
+        37.981665005273
+      ],
+      [
+        -121.9416067354,
+        37.983396558785
+      ],
+      [
+        -121.93199189775,
+        37.983321273849
+      ],
+      [
+        -121.93109979941,
+        37.999658104806
+      ],
+      [
+        -121.93248750794,
+        38.003497636505
+      ],
+      [
+        -121.93595677926,
+        38.003949346117
+      ],
+      [
+        -121.93298311813,
+        38.010499135487
+      ],
+      [
+        -121.93357785035,
+        38.011929549257
+      ],
+      [
+        -121.94180497948,
+        38.012080119128
+      ],
+      [
+        -121.94091288114,
+        38.006810173658
+      ],
+      [
+        -121.9460672271,
+        38.003347066635
+      ],
+      [
+        -121.94814878989,
+        37.994990438818
+      ],
+      [
+        -121.94993298656,
+        37.997625411553
+      ],
+      [
+        -121.9568715292,
+        37.994990438818
+      ],
+      [
+        -121.96113377681,
+        37.998754685582
+      ],
+      [
+        -121.96153026496,
+        38.00470219547
+      ],
+      [
+        -121.96628812277,
+        38.006057324305
+      ],
+      [
+        -121.96827056352,
+        38.003648206376
+      ],
+      [
+        -121.96718022111,
+        38.008240587428
+      ],
+      [
+        -121.97491174004,
+        38.006207894175
+      ],
+      [
+        -121.97510998412,
+        38.007788877816
+      ],
+      [
+        -121.97957047581,
+        38.008090017558
+      ],
+      [
+        -121.98105730638,
+        38.009520431328
+      ],
+      [
+        -121.97867837747,
+        38.00884286691
+      ],
+      [
+        -121.97857925544,
+        38.011854264322
+      ],
+      [
+        -121.98373360139,
+        38.011778979386
+      ],
+      [
+        -121.98254413694,
+        38.010423850551
+      ],
+      [
+        -121.98938355754,
+        38.014338667186
+      ],
+      [
+        -121.99186160848,
+        38.014188097315
+      ],
+      [
+        -121.99582648998,
+        38.007111313399
+      ],
+      [
+        -121.99790805277,
+        38.008617012105
+      ],
+      [
+        -122.00098083594,
+        37.999356965065
+      ],
+      [
+        -122.00663079208,
+        38.000260384288
+      ],
+      [
+        -122.0061351819,
+        37.997625411553
+      ],
+      [
+        -122.01069479563,
+        37.994087019595
+      ],
+      [
+        -122.0077211345,
+        37.993033030501
+      ],
+      [
+        -122.00573869375,
+        37.994087019595
+      ]
+    ],
+    [
+      [
+        -122.37942877559,
+        37.826803893396
+      ],
+      [
+        -122.3781401891,
+        37.83041757029
+      ],
+      [
+        -122.37328320926,
+        37.832450263543
+      ],
+      [
+        -122.36743500904,
+        37.829438866131
+      ],
+      [
+        -122.36644378866,
+        37.830793994966
+      ],
+      [
+        -122.36545256829,
+        37.829062441455
+      ],
+      [
+        -122.36703852089,
+        37.828610731843
+      ],
+      [
+        -122.36188417493,
+        37.821533947927
+      ],
+      [
+        -122.3633710055,
+        37.821533947927
+      ],
+      [
+        -122.36208241901,
+        37.820781098574
+      ],
+      [
+        -122.36356924957,
+        37.820856383509
+      ],
+      [
+        -122.36237978512,
+        37.820103534156
+      ],
+      [
+        -122.36406485976,
+        37.820103534156
+      ],
+      [
+        -122.36465959199,
+        37.817920271033
+      ],
+      [
+        -122.37110252443,
+        37.816038147651
+      ],
+      [
+        -122.36763325312,
+        37.812424470757
+      ],
+      [
+        -122.35841490362,
+        37.81430659414
+      ],
+      [
+        -122.36089295456,
+        37.812499755693
+      ],
+      [
+        -122.36218154105,
+        37.807003955417
+      ],
+      [
+        -122.36753413108,
+        37.80775680477
+      ],
+      [
+        -122.37268847703,
+        37.810617632311
+      ],
+      [
+        -122.37169725666,
+        37.815134728428
+      ],
+      [
+        -122.37942877559,
+        37.826803893396
+      ]
+    ],
+    [
+      [
+        -122.30141973198,
+        38.105508723814
+      ],
+      [
+        -122.29021894173,
+        38.114618200983
+      ],
+      [
+        -122.28823650098,
+        38.11258550773
+      ],
+      [
+        -122.28813737894,
+        38.116876749041
+      ],
+      [
+        -122.28734440264,
+        38.116500324365
+      ],
+      [
+        -122.28308215502,
+        38.118081308006
+      ],
+      [
+        -122.28149620242,
+        38.118608302553
+      ],
+      [
+        -122.28119883631,
+        38.118457732682
+      ],
+      [
+        -122.27832429721,
+        38.113639496824
+      ],
+      [
+        -122.28119883631,
+        38.118457732682
+      ],
+      [
+        -122.28288391095,
+        38.11800602307
+      ],
+      [
+        -122.27981112778,
+        38.112736077601
+      ],
+      [
+        -122.2680156053,
+        38.100012923538
+      ],
+      [
+        -122.26890770364,
+        38.098356654962
+      ],
+      [
+        -122.26742087308,
+        38.098883649509
+      ],
+      [
+        -122.26593404251,
+        38.097227380932
+      ],
+      [
+        -122.26851121549,
+        38.097679090544
+      ],
+      [
+        -122.2648437001,
+        38.096248676774
+      ],
+      [
+        -122.26692526289,
+        38.096399246644
+      ],
+      [
+        -122.2587972558,
+        38.090225881951
+      ],
+      [
+        -122.26097794063,
+        38.089096607922
+      ],
+      [
+        -122.25810340154,
+        38.089397747663
+      ],
+      [
+        -122.25978847618,
+        38.087967333893
+      ],
+      [
+        -122.25731042524,
+        38.08864489831
+      ],
+      [
+        -122.25899549988,
+        38.08721448454
+      ],
+      [
+        -122.25651744894,
+        38.087892048957
+      ],
+      [
+        -122.25820252358,
+        38.086461635187
+      ],
+      [
+        -122.2556253506,
+        38.087139199605
+      ],
+      [
+        -122.25582359467,
+        38.085482931028
+      ],
+      [
+        -122.25384115392,
+        38.084805366611
+      ],
+      [
+        -122.24710085536,
+        38.075771174377
+      ],
+      [
+        -122.24829031981,
+        38.0766745936
+      ],
+      [
+        -122.25126398094,
+        38.072985631771
+      ],
+      [
+        -122.25126398094,
+        38.069447239813
+      ],
+      [
+        -122.25304817762,
+        38.069070815137
+      ],
+      [
+        -122.25146222502,
+        38.069748379554
+      ],
+      [
+        -122.25294905558,
+        38.072383352289
+      ],
+      [
+        -122.2556253506,
+        38.072759776965
+      ],
+      [
+        -122.25830164561,
+        38.06869439046
+      ],
+      [
+        -122.26979980198,
+        38.067113406819
+      ],
+      [
+        -122.26841209345,
+        38.070802368648
+      ],
+      [
+        -122.27118751051,
+        38.0753947497
+      ],
+      [
+        -122.28000937185,
+        38.080890549976
+      ],
+      [
+        -122.30141973198,
+        38.105508723814
+      ]
+    ],
+    [
+      [
+        -122.69929559101,
+        37.890344378775
+      ],
+      [
+        -122.70306222844,
+        37.889892669163
+      ],
+      [
+        -122.7121814559,
+        37.892377072028
+      ],
+      [
+        -122.71386653054,
+        37.894635620086
+      ],
+      [
+        -122.73210498546,
+        37.898926861397
+      ],
+      [
+        -122.74290928756,
+        37.921286487176
+      ],
+      [
+        -122.74528821647,
+        37.924072029782
+      ],
+      [
+        -122.75688549487,
+        37.930471249281
+      ],
+      [
+        -122.76897838346,
+        37.933482646692
+      ],
+      [
+        -122.77482658368,
+        37.938827877097
+      ],
+      [
+        -122.78017917371,
+        37.939053731903
+      ],
+      [
+        -122.78444142133,
+        37.942667408797
+      ],
+      [
+        -122.78850542487,
+        37.943043833473
+      ],
+      [
+        -122.79118171989,
+        37.954562428571
+      ],
+      [
+        -122.79623694381,
+        37.96480117977
+      ],
+      [
+        -122.80545529331,
+        37.976244489933
+      ],
+      [
+        -122.81080788334,
+        37.980234591503
+      ],
+      [
+        -122.81725081578,
+        37.981815575144
+      ],
+      [
+        -122.82458584657,
+        37.992204896213
+      ],
+      [
+        -122.83201999939,
+        37.994312874401
+      ],
+      [
+        -122.83509278256,
+        37.998754685582
+      ],
+      [
+        -122.84054449462,
+        38.0019919378
+      ],
+      [
+        -122.85967504789,
+        38.01268239861
+      ],
+      [
+        -122.87989594356,
+        38.020512031879
+      ],
+      [
+        -122.90517206316,
+        38.024426848514
+      ],
+      [
+        -122.92469910457,
+        38.02480327319
+      ],
+      [
+        -122.92995257256,
+        38.025857262284
+      ],
+      [
+        -122.93371920999,
+        38.028868659696
+      ],
+      [
+        -122.95869796347,
+        38.023598714226
+      ],
+      [
+        -122.97465661153,
+        38.012531828739
+      ],
+      [
+        -122.9801083236,
+        38.002594217282
+      ],
+      [
+        -122.97307065893,
+        37.998227691035
+      ],
+      [
+        -122.96553738407,
+        37.997625411553
+      ],
+      [
+        -122.96355494332,
+        37.99544214843
+      ],
+      [
+        -122.960085672,
+        37.990473342701
+      ],
+      [
+        -122.96226635683,
+        37.986407956196
+      ],
+      [
+        -122.96424879758,
+        37.985805676714
+      ],
+      [
+        -122.97356626912,
+        37.98603153152
+      ],
+      [
+        -122.97753115062,
+        37.98889235906
+      ],
+      [
+        -123.00469058893,
+        37.989042928931
+      ],
+      [
+        -123.010142301,
+        37.991301476989
+      ],
+      [
+        -123.01658523345,
+        37.990247487895
+      ],
+      [
+        -123.02501060665,
+        37.991301476989
+      ],
+      [
+        -123.028579,
+        37.994614014142
+      ],
+      [
+        -123.02679480332,
+        37.999959244547
+      ],
+      [
+        -123.02203694552,
+        38.002669502217
+      ],
+      [
+        -123.0161887453,
+        38.003572921441
+      ],
+      [
+        -123.00921069625,
+        38.018021404017
+      ],
+      [
+        -122.99462924096,
+        38.048213209999
+      ],
+      [
+        -122.9742860523,
+        38.093301559952
+      ],
+      [
+        -122.96097777034,
+        38.124932237116
+      ],
+      [
+        -122.9608786483,
+        38.128997623622
+      ],
+      [
+        -122.95354361752,
+        38.150679684983
+      ],
+      [
+        -122.95354361752,
+        38.16046672657
+      ],
+      [
+        -122.95869796347,
+        38.168898639322
+      ],
+      [
+        -122.95869796347,
+        38.174469724533
+      ],
+      [
+        -122.96583475018,
+        38.17770697675
+      ],
+      [
+        -122.97039436391,
+        38.177932831556
+      ],
+      [
+        -122.97217856059,
+        38.180718374161
+      ],
+      [
+        -122.97188119448,
+        38.184106196249
+      ],
+      [
+        -122.96940314354,
+        38.18711759366
+      ],
+      [
+        -122.97138558429,
+        38.192538109
+      ],
+      [
+        -122.97059260799,
+        38.198711473694
+      ],
+      [
+        -122.98437057122,
+        38.212187477109
+      ],
+      [
+        -122.9861547679,
+        38.220393535055
+      ],
+      [
+        -122.99150735793,
+        38.224985916107
+      ],
+      [
+        -122.99844590056,
+        38.227169179231
+      ],
+      [
+        -122.99943712094,
+        38.230557001318
+      ],
+      [
+        -122.99785116834,
+        38.233417828859
+      ],
+      [
+        -123.00023009724,
+        38.240042903164
+      ],
+      [
+        -122.99824765649,
+        38.243731864993
+      ],
+      [
+        -122.99567048351,
+        38.245388133569
+      ],
+      [
+        -122.98556003567,
+        38.24169917174
+      ],
+      [
+        -122.98238813047,
+        38.238913629135
+      ],
+      [
+        -122.97802676081,
+        38.240193473035
+      ],
+      [
+        -122.97198031652,
+        38.230481716383
+      ],
+      [
+        -122.93542639084,
+        38.200335948135
+      ],
+      [
+        -122.88118453005,
+        38.126889645434
+      ],
+      [
+        -122.86225222086,
+        38.112359652924
+      ],
+      [
+        -122.83281297569,
+        38.132611300515
+      ],
+      [
+        -122.83479541644,
+        38.135396843121
+      ],
+      [
+        -122.80763597813,
+        38.141193783138
+      ],
+      [
+        -122.80763597813,
+        38.139763369367
+      ],
+      [
+        -122.79633606584,
+        38.132385445709
+      ],
+      [
+        -122.79197469619,
+        38.135396843121
+      ],
+      [
+        -122.78900103506,
+        38.132009021033
+      ],
+      [
+        -122.78533351967,
+        38.132987725192
+      ],
+      [
+        -122.78166600428,
+        38.12982575791
+      ],
+      [
+        -122.77918795333,
+        38.124706382311
+      ],
+      [
+        -122.77641253628,
+        38.123727678152
+      ],
+      [
+        -122.77611517017,
+        38.120490425935
+      ],
+      [
+        -122.77205116663,
+        38.121092705417
+      ],
+      [
+        -122.76620296641,
+        38.126212081016
+      ],
+      [
+        -122.75966091192,
+        38.121092705417
+      ],
+      [
+        -122.74033211459,
+        38.128847053751
+      ],
+      [
+        -122.74023299255,
+        38.130653892198
+      ],
+      [
+        -122.73626811104,
+        38.132009021033
+      ],
+      [
+        -122.73567337882,
+        38.135472128056
+      ],
+      [
+        -122.72496819875,
+        38.14322647639
+      ],
+      [
+        -122.71911999853,
+        38.144958029902
+      ],
+      [
+        -122.71099199145,
+        38.138935235079
+      ],
+      [
+        -122.70960428292,
+        38.135999122603
+      ],
+      [
+        -122.70563940142,
+        38.142548911973
+      ],
+      [
+        -122.7021701301,
+        38.14194663249
+      ],
+      [
+        -122.69661929599,
+        38.142473627037
+      ],
+      [
+        -122.6921588043,
+        38.14578616419
+      ],
+      [
+        -122.68323782091,
+        38.149173986278
+      ],
+      [
+        -122.66688268471,
+        38.148571706795
+      ],
+      [
+        -122.66113360652,
+        38.145485024449
+      ],
+      [
+        -122.65280735536,
+        38.148044712248
+      ],
+      [
+        -122.64160656511,
+        38.146313158737
+      ],
+      [
+        -122.63030665282,
+        38.150604400048
+      ],
+      [
+        -122.62584616113,
+        38.149475126019
+      ],
+      [
+        -122.61712342182,
+        38.150529115113
+      ],
+      [
+        -122.6197005948,
+        38.152862948106
+      ],
+      [
+        -122.61920498461,
+        38.155723775647
+      ],
+      [
+        -122.6236654763,
+        38.159939732023
+      ],
+      [
+        -122.62584616113,
+        38.165586102169
+      ],
+      [
+        -122.62307074408,
+        38.169576203739
+      ],
+      [
+        -122.62624264928,
+        38.171834751798
+      ],
+      [
+        -122.6228725,
+        38.173340450503
+      ],
+      [
+        -122.61870937442,
+        38.168898639322
+      ],
+      [
+        -122.61543834718,
+        38.171232472315
+      ],
+      [
+        -122.60800419436,
+        38.170103198286
+      ],
+      [
+        -122.604435801,
+        38.168070505034
+      ],
+      [
+        -122.59700164818,
+        38.167769365292
+      ],
+      [
+        -122.59511832946,
+        38.169350348933
+      ],
+      [
+        -122.5936314989,
+        38.168748069451
+      ],
+      [
+        -122.58927012925,
+        38.173039310762
+      ],
+      [
+        -122.57350972526,
+        38.16189714034
+      ],
+      [
+        -122.56766152504,
+        38.164532113075
+      ],
+      [
+        -122.57172552859,
+        38.173039310762
+      ],
+      [
+        -122.57053606413,
+        38.174846149209
+      ],
+      [
+        -122.5736088473,
+        38.176502417785
+      ],
+      [
+        -122.5796552916,
+        38.185386040149
+      ],
+      [
+        -122.57816846103,
+        38.183579201702
+      ],
+      [
+        -122.57073430821,
+        38.183503916767
+      ],
+      [
+        -122.57321235915,
+        38.186289459372
+      ],
+      [
+        -122.57073430821,
+        38.18711759366
+      ],
+      [
+        -122.56528259614,
+        38.182751067414
+      ],
+      [
+        -122.56845450134,
+        38.177029412332
+      ],
+      [
+        -122.56448961984,
+        38.175072004015
+      ],
+      [
+        -122.56320103335,
+        38.169350348933
+      ],
+      [
+        -122.55923615184,
+        38.168597499581
+      ],
+      [
+        -122.55428004996,
+        38.170103198286
+      ],
+      [
+        -122.5520002431,
+        38.168973924257
+      ],
+      [
+        -122.55794756536,
+        38.163553408917
+      ],
+      [
+        -122.55794756536,
+        38.160165586829
+      ],
+      [
+        -122.54962131419,
+        38.157304759288
+      ],
+      [
+        -122.54446696824,
+        38.158735173058
+      ],
+      [
+        -122.53485213059,
+        38.149173986278
+      ],
+      [
+        -122.52107416736,
+        38.141494922879
+      ],
+      [
+        -122.51403650269,
+        38.13381585948
+      ],
+      [
+        -122.50689971598,
+        38.116650894235
+      ],
+      [
+        -122.49996117334,
+        38.111531518636
+      ],
+      [
+        -122.48400252529,
+        38.107767271872
+      ],
+      [
+        -122.48261481676,
+        38.09918478925
+      ],
+      [
+        -122.48083062008,
+        38.091957435463
+      ],
+      [
+        -122.47983939971,
+        38.06854382059
+      ],
+      [
+        -122.4874717966,
+        38.04731346884
+      ],
+      [
+        -122.49361736294,
+        38.027287676055
+      ],
+      [
+        -122.49292350867,
+        38.02209301552
+      ],
+      [
+        -122.48539023381,
+        38.017124209791
+      ],
+      [
+        -122.47874905729,
+        38.011477839645
+      ],
+      [
+        -122.45951938199,
+        38.006734888722
+      ],
+      [
+        -122.45783430735,
+        38.002820072088
+      ],
+      [
+        -122.44643527303,
+        37.993183600372
+      ],
+      [
+        -122.44276775763,
+        37.984601117749
+      ],
+      [
+        -122.44782298155,
+        37.979331172279
+      ],
+      [
+        -122.45634747679,
+        37.975943350192
+      ],
+      [
+        -122.46844036538,
+        37.979782881891
+      ],
+      [
+        -122.47072017225,
+        37.977674903703
+      ],
+      [
+        -122.4711166604,
+        37.973609517198
+      ],
+      [
+        -122.4618983109,
+        37.966608018217
+      ],
+      [
+        -122.4618983109,
+        37.963220196129
+      ],
+      [
+        -122.4642772398,
+        37.9608110782
+      ],
+      [
+        -122.47101753836,
+        37.960058228847
+      ],
+      [
+        -122.47874905729,
+        37.965252889382
+      ],
+      [
+        -122.48449813548,
+        37.965554029123
+      ],
+      [
+        -122.48578672196,
+        37.958552530141
+      ],
+      [
+        -122.48400252529,
+        37.952454450384
+      ],
+      [
+        -122.47299997911,
+        37.948389063878
+      ],
+      [
+        -122.47151314855,
+        37.944323677373
+      ],
+      [
+        -122.47230612485,
+        37.941538134767
+      ],
+      [
+        -122.47696486062,
+        37.938225597615
+      ],
+      [
+        -122.48400252529,
+        37.935891764621
+      ],
+      [
+        -122.48816565087,
+        37.932955652145
+      ],
+      [
+        -122.48717443049,
+        37.924975449005
+      ],
+      [
+        -122.48202008453,
+        37.922415761206
+      ],
+      [
+        -122.4711166604,
+        37.920307783018
+      ],
+      [
+        -122.46992719594,
+        37.913080429231
+      ],
+      [
+        -122.46516933814,
+        37.902465253356
+      ],
+      [
+        -122.45723957513,
+        37.898701006592
+      ],
+      [
+        -122.44762473748,
+        37.898324581915
+      ],
+      [
+        -122.4350362387,
+        37.885149718241
+      ],
+      [
+        -122.43473887259,
+        37.876266095877
+      ],
+      [
+        -122.42215037381,
+        37.87445925743
+      ],
+      [
+        -122.41868110249,
+        37.852476056328
+      ],
+      [
+        -122.4242319366,
+        37.850443363075
+      ],
+      [
+        -122.42968364867,
+        37.851196212428
+      ],
+      [
+        -122.43573009296,
+        37.849389373981
+      ],
+      [
+        -122.44385810005,
+        37.852099631651
+      ],
+      [
+        -122.45198610713,
+        37.861585533497
+      ],
+      [
+        -122.45872640569,
+        37.857821286733
+      ],
+      [
+        -122.47161227058,
+        37.863542941814
+      ],
+      [
+        -122.47409032152,
+        37.860230404662
+      ],
+      [
+        -122.47458593171,
+        37.849690513722
+      ],
+      [
+        -122.47141402651,
+        37.842463159935
+      ],
+      [
+        -122.46754826704,
+        37.838171918624
+      ],
+      [
+        -122.46754826704,
+        37.83583808563
+      ],
+      [
+        -122.46804387723,
+        37.833654822507
+      ],
+      [
+        -122.47220700281,
+        37.832073838866
+      ],
+      [
+        -122.47418944356,
+        37.833353682766
+      ],
+      [
+        -122.47805520303,
+        37.832450263543
+      ],
+      [
+        -122.47964115563,
+        37.830492855225
+      ],
+      [
+        -122.47835256914,
+        37.828083737296
+      ],
+      [
+        -122.47914554544,
+        37.825599334432
+      ],
+      [
+        -122.491139312,
+        37.82642746872
+      ],
+      [
+        -122.49480682739,
+        37.822738506891
+      ],
+      [
+        -122.49986205131,
+        37.821835087668
+      ],
+      [
+        -122.49976292927,
+        37.81972710948
+      ],
+      [
+        -122.50144800391,
+        37.821684517797
+      ],
+      [
+        -122.50412429892,
+        37.82100695338
+      ],
+      [
+        -122.50521464134,
+        37.822889076762
+      ],
+      [
+        -122.51106284156,
+        37.824470060403
+      ],
+      [
+        -122.5235522183,
+        37.824695915209
+      ],
+      [
+        -122.52920217444,
+        37.819049545062
+      ],
+      [
+        -122.52741797777,
+        37.814984158557
+      ],
+      [
+        -122.53405915429,
+        37.815360583233
+      ],
+      [
+        -122.53960998839,
+        37.821458662991
+      ],
+      [
+        -122.54020472062,
+        37.826954463267
+      ],
+      [
+        -122.55269409736,
+        37.831471559384
+      ],
+      [
+        -122.55338795162,
+        37.835386376019
+      ],
+      [
+        -122.56389488761,
+        37.843592433965
+      ],
+      [
+        -122.56706679282,
+        37.849464658917
+      ],
+      [
+        -122.57073430821,
+        37.849464658917
+      ],
+      [
+        -122.57806933899,
+        37.854508749581
+      ],
+      [
+        -122.58411578329,
+        37.853379475551
+      ],
+      [
+        -122.5920455463,
+        37.85834828128
+      ],
+      [
+        -122.60136301784,
+        37.869716306508
+      ],
+      [
+        -122.61117609956,
+        37.873480553272
+      ],
+      [
+        -122.61900674053,
+        37.872953558725
+      ],
+      [
+        -122.62475581871,
+        37.876341380813
+      ],
+      [
+        -122.62842333411,
+        37.876491950683
+      ],
+      [
+        -122.64626530088,
+        37.894560335151
+      ],
+      [
+        -122.66718005082,
+        37.901561834132
+      ],
+      [
+        -122.68006591571,
+        37.902314683485
+      ],
+      [
+        -122.69473597728,
+        37.895990748921
+      ],
+      [
+        -122.69929559101,
+        37.890344378775
+      ]
+    ]
+  ],
+  "bbox": [
+    -123.028579,
+    37.057166,
+    -121.405753,
+    38.313521
+  ]
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/layer/maputils.ts b/web/gui2-topo-lib/lib/layer/maputils.ts
new file mode 100644
index 0000000..45c3140
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/maputils.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export interface MapObject {
+    id: string;
+    description: string;
+    filePath: string;
+    scale: number;
+}
diff --git a/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.css b/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.css
new file mode 100644
index 0000000..7897595
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.css
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology View (no devices connected) -- CSS file
+ */
+/* --- "No Devices" Layer --- */
+#topo-noDevsLayer {
+    visibility: hidden;
+}
+
+#topo-noDevsLayer text {
+    font-size: 60pt;
+    font-style: italic;
+    fill: #7e9aa8;
+}
+
+#topo-noDevsLayer .noDevsBird {
+    fill: #db7773;
+}
diff --git a/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.html b/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.html
new file mode 100644
index 0000000..efe044f
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.html
@@ -0,0 +1,23 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<svg:g xmlns:svg="http://www.w3.org/2000/svg" id="topo-noDevsLayer"
+       [attr.transform]="'translate(' + (zoomExtents.tx) + ',' + (450 * zoomExtents.sc) + '),' +
+       'scale(' + zoomExtents.sc + ')'"
+       style="visibility: visible;">
+    <svg:use width="100" height="100" class="noDevsBird" href="#bird"></svg:use>
+    <svg:text x="120" y="80">{{lionFn('no_devices_are_connected')}}</svg:text>
+</svg:g>
+
diff --git a/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.spec.ts
new file mode 100644
index 0000000..218f063
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.spec.ts
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import { NoDeviceConnectedSvgComponent } from './nodeviceconnectedsvg.component';
+import { DebugElement } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import {
+    FnService,
+    IconService,
+    LionService,
+    LogService,
+    UrlFnService,
+    TableFilterPipe,
+    IconComponent,
+    WebSocketService, SvgUtilService, PrefsService
+} from '../../../../gui2-fw-lib/public_api';
+import { of } from 'rxjs';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+class MockWebSocketService {
+    createWebSocket() { }
+    isConnected() { return false; }
+    unbindHandlers() { }
+    bindHandlers() { }
+}
+
+class MockSvgUtilService {
+    translate() {}
+    scale() {}
+}
+
+class MockPrefsService {
+}
+
+
+/**
+ * ONOS GUI -- Topology NoDevicesConnected -- Unit Tests
+ */
+describe('NoDeviceConnectedSvgComponent', () => {
+    let fs: FnService;
+    let ar: MockActivatedRoute;
+    let windowMock: Window;
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: NoDeviceConnectedSvgComponent;
+    let fixture: ComponentFixture<NoDeviceConnectedSvgComponent>;
+
+
+    beforeEach(async(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({'debug': 'panel'});
+
+        windowMock = <any>{
+            innerWidth: 800,
+            innerHeight: 600,
+        };
+        fs = new FnService(ar, logSpy, windowMock);
+
+        const bundleObj = {
+            'core.view.Topo': {
+                test: 'test1'
+            }
+        };
+        const mockLion = (key) => {
+            return bundleObj[key] || '%' + key + '%';
+        };
+
+        TestBed.configureTestingModule({
+            declarations: [ NoDeviceConnectedSvgComponent ],
+            providers: [
+                { provide: FnService, useValue: fs },
+                { provide: LogService, useValue: logSpy },
+                { provide: SvgUtilService, useClass: MockSvgUtilService },
+                { provide: WebSocketService, useClass: MockWebSocketService },
+                { provide: PrefsService, useClass: MockPrefsService },
+                {
+                    provide: LionService, useFactory: (() => {
+                        return {
+                            bundle: ((bundleId) => mockLion),
+                            ubercache: new Array(),
+                            loadCbs: new Map<string, () => void>([])
+                        };
+                    })
+                },
+                { provide: 'Window', useValue: windowMock },
+            ]
+        }).compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(NoDeviceConnectedSvgComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.ts b/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.ts
new file mode 100644
index 0000000..84965fa
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/nodeviceconnectedsvg/nodeviceconnectedsvg.component.ts
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    AfterContentInit,
+    Component,
+    HostListener,
+    Inject,
+    Input,
+    OnInit
+} from '@angular/core';
+import { ViewControllerImpl } from '../viewcontroller';
+import {
+    FnService,
+    LogService,
+    PrefsService,
+    SvgUtilService, LionService, ZoomUtils, TopoZoomPrefs
+} from '../../../../gui2-fw-lib/public_api';
+
+/**
+ * ONOS GUI -- Topology No Connected Devices View.
+ * View that contains the 'No Connected Devices' message
+ *
+ * This component is an SVG snippet that expects to be in an SVG element with a view box of 1000x1000
+ *
+ * It should be added to a template with a tag like <svg:g onos-nodeviceconnected />
+ */
+@Component({
+  selector: '[onos-nodeviceconnected]',
+  templateUrl: './nodeviceconnectedsvg.component.html',
+  styleUrls: ['./nodeviceconnectedsvg.component.css']
+})
+export class NoDeviceConnectedSvgComponent extends ViewControllerImpl implements AfterContentInit, OnInit {
+    @Input() bannerHeight: number = 48;
+    lionFn; // Function
+    zoomExtents: TopoZoomPrefs = <TopoZoomPrefs>{
+        sc: 1.0, tx: 0, ty: 0
+    };
+
+    constructor(
+        protected fs: FnService,
+        protected log: LogService,
+        protected ps: PrefsService,
+        protected sus: SvgUtilService,
+        private lion: LionService,
+        @Inject('Window') public window: any,
+    ) {
+        super(fs, log, ps);
+
+        if (this.lion.ubercache.length === 0) {
+            this.lionFn = this.dummyLion;
+            this.lion.loadCbs.set('topo-nodevices', () => this.doLion());
+        } else {
+            this.doLion();
+        }
+
+        this.log.debug('NoDeviceConnectedSvgComponent constructed');
+    }
+
+    ngOnInit() {
+        this.log.debug('NoDeviceConnectedSvgComponent initialized');
+    }
+
+    ngAfterContentInit(): void {
+        this.zoomExtents = ZoomUtils.zoomToWindowSize(
+            this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
+    }
+
+    @HostListener('window:resize', ['$event'])
+    onResize(event) {
+        this.zoomExtents = ZoomUtils.zoomToWindowSize(
+            this.bannerHeight, event.target.innerWidth, event.target.innerHeight);
+    }
+
+    /**
+     * Read the LION bundle for Details panel and set up the lionFn
+     */
+    doLion() {
+        this.lionFn = this.lion.bundle('core.view.Topo');
+
+    }
+
+    /**
+     * A dummy implementation of the lionFn until the response is received and the LION
+     * bundle is received from the WebSocket
+     */
+    dummyLion(key: string): string {
+        return '%' + key + '%';
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layer/viewcontroller.ts b/web/gui2-topo-lib/lib/layer/viewcontroller.ts
new file mode 100644
index 0000000..e6568ed
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/viewcontroller.ts
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { FnService, LogService, PrefsService } from '../../../gui2-fw-lib/public_api';
+
+export interface ViewControllerPrefs {
+    visible: string;
+}
+
+/*
+ ONOS GUI -- View Controller.
+ A base class for view controllers to extend from
+ */
+export abstract class ViewControllerImpl {
+    id: string;
+    displayName: string = 'View';
+    name: string;
+    prefs: ViewControllerPrefs;
+    visibility: string;
+
+    constructor(
+        protected fs: FnService,
+        protected log: LogService,
+        protected ps: PrefsService
+    ) {
+        this.log.debug('View Controller constructed');
+    }
+
+    initialize() {
+        this.name = this.displayName.toLowerCase().replace(/ /g, '_');
+        this.prefs = {
+            visible: this.name + '_visible',
+        };
+    }
+
+    enabled() {
+        return this.ps.getPrefs('topo2_prefs', null)[this.prefs.visible];
+    }
+
+    isVisible() {
+        return this.visibility;
+    }
+
+    hide() {
+        this.visibility = 'hidden';
+    }
+
+    show() {
+        this.visibility = 'visible';
+    }
+
+    toggle() {
+        if (this.visibility === 'hidden') {
+            this.visibility = 'visible';
+        } else if (this.visibility === 'visible') {
+            this.visibility = 'hidden';
+        }
+    }
+
+    lookupPrefState(key: string): number {
+        // Return 0 if not defined
+        return this.ps.getPrefs('topo2_prefs', null)[key] || 0;
+    }
+
+    updatePrefState(key: string, value: number) {
+        const state = this.ps.getPrefs('topo2_prefs', null);
+        state[key] = value ? 1 : 0;
+        this.ps.setPrefs('topo2_prefs', state);
+    }
+}
diff --git a/web/gui2-topo-lib/lib/layout.service.spec.ts b/web/gui2-topo-lib/lib/layout.service.spec.ts
new file mode 100644
index 0000000..2f8483f
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layout.service.spec.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { TestBed } from '@angular/core/testing';
+
+import { LayoutService } from './layout.service';
+import {ActivatedRoute, Params} from '@angular/router';
+import {of} from 'rxjs';
+import {FnService, LogService} from '../../gui2-fw-lib/public_api';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+describe('LayoutService', () => {
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let ar: ActivatedRoute;
+    let fs: FnService;
+    let mockWindow: Window;
+
+    beforeEach(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['debug', 'warn', 'info']);
+        ar = new MockActivatedRoute({'debug': 'TestService'});
+        mockWindow = <any>{
+            innerWidth: 400,
+            innerHeight: 200,
+            navigator: {
+                userAgent: 'defaultUA'
+            },
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+        fs = new FnService(ar, logSpy, mockWindow);
+
+        TestBed.configureTestingModule({
+            providers: [LayoutService,
+                { provide: FnService, useValue: fs},
+                { provide: LogService, useValue: logSpy },
+                { provide: ActivatedRoute, useValue: ar },
+                { provide: 'Window', useFactory: (() => mockWindow ) }
+            ]
+        });
+        logServiceSpy = TestBed.get(LogService);
+    });
+
+    it('should be created', () => {
+        const service: LayoutService = TestBed.get(LayoutService);
+        expect(service).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/layout.service.ts b/web/gui2-topo-lib/lib/layout.service.ts
new file mode 100644
index 0000000..39844c54
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layout.service.ts
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Injectable } from '@angular/core';
+import {LogService, WebSocketService} from '../../gui2-fw-lib/public_api';
+
+export enum LayoutType {
+    LAYOUT_DEFAULT = 'default',
+    LAYOUT_ACCESS = 'access'
+}
+
+/**
+ * ONOS GUI - Layout service - connects to the Layout UI Extension app
+ */
+@Injectable()
+export class LayoutService {
+
+    constructor(
+        protected log: LogService,
+        protected wss: WebSocketService
+    ) {
+        this.log.debug('LayoutService constructed');
+    }
+
+    /**
+     * tell the server we want a new layout
+     * @param type The type of layout we want
+     */
+    changeLayout(type: LayoutType): void {
+        this.wss.sendEvent('doLayout', {
+            type: type
+        });
+        this.log.debug('Layout changed to', type);
+    }
+}
diff --git a/web/gui2-topo-lib/lib/panel/details/details.component.css b/web/gui2-topo-lib/lib/panel/details/details.component.css
new file mode 100644
index 0000000..05402c8
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/details/details.component.css
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* --- Topo Details Panel --- */
+
+#topo2-p-detail {
+    padding: 16px;
+    opacity: 1;
+    right: 20px;
+    width: 260px;
+    top: 390px;
+}
+
+#topo2-p-detail  div.actionBtns {
+    padding-top: 6px;
+}
+
+html[data-platform='iPad'] {
+    top: 386px;
+}
+
+.actionBtns .actionBtn {
+    display: inline-block;
+}
+.actionBtns .actionBtn svg {
+    width: 28px;
+    height: 28px;
+}
+
+div.actionBtns {
+    padding-top: 6px;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/panel/details/details.component.html b/web/gui2-topo-lib/lib/panel/details/details.component.html
new file mode 100644
index 0000000..5421776
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/details/details.component.html
@@ -0,0 +1,80 @@
+<!--
+~ Copyright 2019-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="topo2-p-detail" class="floatpanel topo2-p"
+     [@detailsPanelState]="on && selectedNodes.length > 0">
+    <!-- Template explanation - Create a HTML header which has an SVG icon along
+    side title text. -->
+    <div class="header">
+        <div class="icon clickable">
+            <onos-icon
+                    [iconSize]="26"
+                    [iconId]="showDetails?.glyphId">
+            </onos-icon>
+        </div>
+        <h2 class="clickable">{{ showDetails?.title }}</h2>
+    </div>
+    <div class="body">
+        <table>
+            <tbody>
+            <!-- Template explanation - Inside a HTML table, create a row per
+            item in the propOrder array returned through the WSS showDetails.
+            If the row name contains only '-' then draw a horiz rule otherwise
+            create a cell for the name and another for the value -->
+                <tr *ngFor="let p of showDetails?.propOrder">
+                    <td *ngIf="showDetails?.propLabels[p] !== '-'"
+                        class="label">{{showDetails?.propLabels[p]}} :</td>
+                    <td *ngIf="showDetails?.propLabels[p] !== '-'"
+                        class="value">{{ showDetails?.propValues[p]}}</td>
+                    <!-- If the label is '-' then insert a horiz line -->
+                    <td *ngIf="showDetails?.propLabels[p] === '-'"
+                        colspan="2"><hr></td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+    <div class="footer">
+        <hr>
+        <div class="actionBtns">
+            <!-- Template explanation - Inside the panel footer, create an SVG icon
+            per entry in the buttons array returned from the WSS showDetails
+            The icons used here are loaded in the ForceSvgComponent
+            -->
+            <div *ngFor="let btn of showDetails?.buttons" class="actionBtn">
+                <onos-icon id="topo2-p-detail-core-{{ btn }}"
+                           (click)="navto(buttonAttribs(btn).path)"
+                        [iconSize]="25"
+                        [iconId]="buttonAttribs(btn).gid"
+                        [toolTip]="lionFnTopo(buttonAttribs(btn).tt)"
+                        classes="button icon selected">
+                </onos-icon>
+            </div>
+            <div *ngIf="showDetails?.buttons?.includes('showDeviceView')" class="actionBtn">
+                <onos-icon id="topo2-p-detail-core-alarms"
+                           (click)="navto('alarmTable')"
+                           [iconSize]="25"
+                           [iconId]="'clock'"
+                           classes="button icon selected">
+                </onos-icon>
+                <onos-icon id="topo2-p-detail-core-pipeconf"
+                           (click)="navto('pipeconf')"
+                           [iconSize]="25"
+                           [iconId]="'pipeconfTable'"
+                           classes="button icon selected">
+                </onos-icon>
+            </div>
+        </div>
+    </div>
+</div>
diff --git a/web/gui2-topo-lib/lib/panel/details/details.component.spec.ts b/web/gui2-topo-lib/lib/panel/details/details.component.spec.ts
new file mode 100644
index 0000000..0648c46
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/details/details.component.spec.ts
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { of } from 'rxjs';
+import { DetailsComponent } from './details.component';
+
+import {
+    FnService, LionService,
+    LogService, IconComponent
+} from '../../../../gui2-fw-lib/public_api';
+import {RouterTestingModule} from '@angular/router/testing';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+/**
+ * ONOS GUI -- Topology View Details Panel-- Unit Tests
+ */
+describe('DetailsComponent', () => {
+    let fs: FnService;
+    let ar: MockActivatedRoute;
+    let windowMock: Window;
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: DetailsComponent;
+    let fixture: ComponentFixture<DetailsComponent>;
+
+    const bundleObj = {
+        'core.view.Flow': {
+            test: 'test1'
+        }
+    };
+    const mockLion = (key) => {
+        return bundleObj[key] || '%' + key + '%';
+    };
+
+    beforeEach(async(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+        windowMock = <any>{
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+        fs = new FnService(ar, logSpy, windowMock);
+
+        TestBed.configureTestingModule({
+            imports: [ BrowserAnimationsModule, RouterTestingModule ],
+            declarations: [ DetailsComponent, IconComponent ],
+            providers: [
+                { provide: FnService, useValue: fs },
+                { provide: LogService, useValue: logSpy },
+                {
+                    provide: LionService, useFactory: (() => {
+                        return {
+                            bundle: ((bundleId) => mockLion),
+                            ubercache: new Array(),
+                            loadCbs: new Map<string, () => void>([])
+                        };
+                    })
+                },
+                { provide: 'Window', useValue: windowMock },
+            ]
+        })
+        .compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(DetailsComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/panel/details/details.component.ts b/web/gui2-topo-lib/lib/panel/details/details.component.ts
new file mode 100644
index 0000000..a0a2837
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/details/details.component.ts
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges} from '@angular/core';
+import {animate, state, style, transition, trigger} from '@angular/animations';
+import {DetailsPanelBaseImpl, FnService, LionService, LogService, WebSocketService} from '../../../../gui2-fw-lib/public_api';
+import {Host, Link, LinkType, NodeType, UiElement} from '../../layer/forcesvg/models';
+import {Params, Router} from '@angular/router';
+
+
+interface ButtonAttrs {
+    gid: string;
+    tt: string;
+    path: string;
+}
+
+const SHOWDEVICEVIEW: ButtonAttrs = {
+    gid: 'deviceTable',
+    tt: 'tt_ctl_show_device',
+    path: 'device',
+};
+const SHOWFLOWVIEW: ButtonAttrs = {
+    gid: 'flowTable',
+    tt: 'title_flows',
+    path: 'flow',
+};
+const SHOWPORTVIEW: ButtonAttrs = {
+    gid: 'portTable',
+    tt: 'tt_ctl_show_port',
+    path: 'port',
+};
+const SHOWGROUPVIEW: ButtonAttrs = {
+    gid: 'groupTable',
+    tt: 'tt_ctl_show_group',
+    path: 'group',
+};
+const SHOWMETERVIEW: ButtonAttrs = {
+    gid: 'meterTable',
+    tt: 'tt_ctl_show_meter',
+    path: 'meter',
+};
+const SHOWPIPECONFVIEW: ButtonAttrs = {
+    gid: 'pipeconfTable',
+    tt: 'tt_ctl_show_pipeconf',
+    path: 'pipeconf',
+};
+const RELATEDINTENTS: ButtonAttrs = {
+    gid: 'm_relatedIntents',
+    tt: 'tr_btn_show_related_traffic',
+    path: 'relatedIntents',
+};
+const CREATEHOSTTOHOSTFLOW: ButtonAttrs = {
+    gid: 'm_endstation',
+    tt: 'tr_btn_create_h2h_flow',
+    path: 'create_h2h_flow',
+};
+const CREATEMULTISOURCEFLOW: ButtonAttrs = {
+    gid: 'm_flows',
+    tt: 'tr_btn_create_msrc_flow',
+    path: 'create_msrc_flow',
+};
+
+
+interface ShowDetails {
+    buttons: string[];
+    glyphId: string;
+    id: string;
+    navPath: string;
+    propLabels: Object;
+    propOrder: string[];
+    propValues: Object;
+    title: string;
+}
+/**
+ * ONOS GUI -- Topology Details Panel.
+ * Displays details of selected device. When no device is selected the panel slides
+ * off to the side and disappears
+ *
+ * This Panel is a child of the Topology component and it gets the 'selectedNodes'
+ * from there as an input component. See TopologyComponent.nodeSelected()
+ * The topology component gets these by listening to events from ForceSvgComponent
+ * which gets them in turn from Device, Host, SubRegion and Link components. This
+ * is so that each component respects the hierarchy
+ */
+@Component({
+    selector: 'onos-details',
+    templateUrl: './details.component.html',
+    styleUrls: [
+        './details.component.css', './details.theme.css',
+        '../../topology.common.css',
+        '../../../../gui2-fw-lib/lib/widget/panel.css',
+        '../../../../gui2-fw-lib/lib/widget/panel-theme.css'
+    ],
+    animations: [
+        trigger('detailsPanelState', [
+            state('true', style({
+                transform: 'translateX(0%)',
+                opacity: '1.0'
+            })),
+            state('false', style({
+                transform: 'translateX(100%)',
+                opacity: '0'
+            })),
+            transition('0 => 1', animate('100ms ease-in')),
+            transition('1 => 0', animate('100ms ease-out'))
+        ])
+    ]
+})
+export class DetailsComponent extends DetailsPanelBaseImpl implements OnInit, OnDestroy, OnChanges {
+    @Input() selectedNodes: UiElement[] = []; // Populated when user selects node or link
+    @Input() on: boolean = false; // Override the parent class attribute
+
+    // deferred localization strings
+    lionFnTopo; // Function
+    lionFnFlow; // Function for flow bundle
+    showDetails: ShowDetails; // Will be populated on callback. Cleared if nothing is selected
+
+    constructor(
+        protected fs: FnService,
+        protected log: LogService,
+        protected router: Router,
+        protected wss: WebSocketService,
+        private lion: LionService
+    ) {
+        super(fs, log, wss, 'topo');
+
+        if (this.lion.ubercache.length === 0) {
+            this.lionFnTopo = this.dummyLion;
+            this.lionFnFlow = this.dummyLion;
+            this.lion.loadCbs.set('detailscore', () => this.doLion());
+        } else {
+            this.doLion();
+        }
+
+        this.log.debug('Topo DetailsComponent constructed');
+    }
+
+    /**
+     * When the component is initializing set up the handler for callbacks of
+     * ShowDetails from the WSS. Set the variable showDetails when ever a callback
+     * is made
+     */
+    ngOnInit(): void {
+        this.wss.bindHandlers(new Map<string, (data) => void>([
+            ['showDetails', (data) => {
+                    this.showDetails = data;
+                    // this.log.debug('showDetails received', data);
+                }
+            ]
+        ]));
+        this.log.debug('Topo DetailsComponent initialized');
+    }
+
+    /**
+     * When the component is being unloaded then unbind the WSS handler.
+     */
+    ngOnDestroy(): void {
+        this.wss.unbindHandlers(['showDetails']);
+        this.log.debug('Topo DetailsComponent destroyed');
+    }
+
+    /**
+     * If changes are detected on the Input param selectedNode, call on WSS sendEvent
+     * and expect ShowDetails to be updated from data sent back from server.
+     *
+     * Note the difference in call to the WSS with requestDetails between a node
+     * and a link - the handling is done in TopologyViewMessageHandler#RequestDetails.process()
+     *
+     * When multiple items are selected fabricate the ShowDetails here, and
+     * present buttons that allow custom actions
+     *
+     * The WSS will call back asynchronously (see fn in ngOnInit())
+     *
+     * @param changes Simple Changes set of updates
+     */
+    ngOnChanges(changes: SimpleChanges): void {
+        if (changes['selectedNodes']) {
+            this.selectedNodes = changes['selectedNodes'].currentValue;
+            let type: any;
+            if (this.selectedNodes.length === 0) {
+                // Selection has been cleared
+                this.showDetails = <ShowDetails>{};
+                return;
+            } else if (this.selectedNodes.length > 1) {
+                // Don't send message to WSS just form dialog here
+                const propOrder: string[] = [];
+                const propValues: Object = {};
+                const propLabels: Object = {};
+                let numHosts: number = 0;
+                for (let i = 0; i < this.selectedNodes.length; i++) {
+                    propOrder.push(i.toString());
+                    propLabels[i.toString()] = i.toString();
+                    propValues[i.toString()] = this.selectedNodes[i].id;
+                    if (this.selectedNodes[i].hasOwnProperty('nodeType') &&
+                        (<Host>this.selectedNodes[i]).nodeType === NodeType.HOST) {
+                        numHosts++;
+                    } else {
+                        numHosts = -128; // Negate the whole thing so other buttons will not be shown
+                    }
+                }
+                const buttons: string[] = [];
+                if (numHosts === 2) {
+                    buttons.push('createHostToHostFlow');
+                } else if (numHosts > 2) {
+                    buttons.push('createMultiSourceFlow');
+                }
+                buttons.push('relatedIntents');
+
+                this.showDetails = <ShowDetails>{
+                    buttons: buttons,
+                    glyphId: undefined,
+                    id: 'multiple',
+                    navPath: undefined,
+                    propLabels: propLabels,
+                    propOrder: propOrder,
+                    propValues: propValues,
+                    title: this.lionFnTopo('title_selected_items')
+                };
+                this.log.debug('Details panel generated from multiple devices', this.showDetails);
+                return;
+            }
+
+            // If only one thing has been selected then request details of that from the server
+            const selectedNode = this.selectedNodes[0];
+            if (selectedNode.hasOwnProperty('nodeType')) { // For Device, Host, SubRegion
+                type = (<Host>selectedNode).nodeType;
+                this.wss.sendEvent('requestDetails', {
+                    id: selectedNode.id,
+                    class: type,
+                });
+            } else if (selectedNode.hasOwnProperty('type')) { // Must be link
+                const link: Link = <Link>selectedNode;
+                if (<LinkType><unknown>LinkType[link.type] === LinkType.UiEdgeLink) { // Number based enum
+                    this.wss.sendEvent('requestDetails', {
+                        key: link.id,
+                        class: 'link',
+                        sourceId: link.epA,
+                        targetId: Link.deviceNameFromEp(link.epB),
+                        targetPort: link.portB,
+                        isEdgeLink: true
+                    });
+                } else {
+                    this.wss.sendEvent('requestDetails', {
+                        key: link.id,
+                        class: 'link',
+                        sourceId: Link.deviceNameFromEp(link.epA),
+                        sourcePort: link.portA,
+                        targetId: Link.deviceNameFromEp(link.epB),
+                        targetPort: link.portB,
+                        isEdgeLink: false
+                    });
+                }
+            } else {
+                this.log.warn('Unexpected type for selected element', selectedNode);
+            }
+        }
+    }
+
+    /**
+     * Table of core button attributes to return per button icon
+     * @param btnName The name of the button
+     * @returns A structure with the button attributes
+     */
+    buttonAttribs(btnName: string): ButtonAttrs {
+        switch (btnName) {
+            case 'showDeviceView':
+                return SHOWDEVICEVIEW;
+            case 'showFlowView':
+                return SHOWFLOWVIEW;
+            case 'showPortView':
+                return SHOWPORTVIEW;
+            case 'showGroupView':
+                return SHOWGROUPVIEW;
+            case 'showMeterView':
+                return SHOWMETERVIEW;
+            case 'showPipeConfView':
+                return SHOWPIPECONFVIEW;
+            case 'relatedIntents':
+                return RELATEDINTENTS;
+            case 'createHostToHostFlow':
+                return CREATEHOSTTOHOSTFLOW;
+            case 'createMultiSourceFlow':
+                return CREATEMULTISOURCEFLOW;
+            default:
+                return <ButtonAttrs>{
+                    gid: btnName,
+                    path: btnName
+                };
+        }
+    }
+
+    /**
+     * Navigate using Angular Routing. Combines the parameters to generate a relative URL
+     * e.g. if params are 'meter', 'device' and 'null:0000000000001' then the
+     * navigation URL will become "http://localhost:4200/#/meter?devId=null:0000000000000002"
+     *
+     * When multiple hosts are selected other actions have to be accommodated
+     *
+     * @param path The path to navigate to
+     * @param navPath The parameter name to use
+     * @param selId the parameter value to use
+     */
+    navto(path: string): void {
+        this.log.debug('navigate to', path, 'for',
+            this.showDetails.navPath, '=', this.showDetails.id);
+
+        const ids: string[] = [];
+        Object.values(this.showDetails.propValues).forEach((v) => ids.push(v));
+        if (path === 'relatedIntents' && this.showDetails.id === 'multiple') {
+            this.wss.sendEvent('topo2RequestRelatedIntents', {
+                'ids': ids,
+                'hover': ''
+            });
+
+        } else if (path === 'create_h2h_flow' && this.showDetails.id === 'multiple') {
+            this.wss.sendEvent('topo2AddHostIntent', {
+                'one': ids[0],
+                'two': ids[1],
+                'ids': ids
+            });
+
+        } else if (path === 'create_msrc_flow' && this.showDetails.id === 'multiple') {
+            // Should only happen when there are 3 or more ids
+            this.wss.sendEvent('topo2AddMultiSourceIntent', {
+                'src': ids.slice(0, ids.length - 1),
+                'dst': ids[ids.length - 1],
+                'ids': ids
+            });
+
+        } else if (this.showDetails.id) {
+            let navPath = this.showDetails.navPath;
+            if (navPath === 'device') {
+                navPath = 'devId';
+            }
+            const queryPar: Params = {};
+            queryPar[navPath] = this.showDetails.id;
+            this.router.navigate([path], { queryParams: queryPar });
+        }
+    }
+
+    /**
+     * Read the LION bundle for Details panel and set up the lionFn
+     */
+    doLion() {
+        this.lionFnTopo = this.lion.bundle('core.view.Topo');
+        this.lionFnFlow = this.lion.bundle('core.view.Flow');
+    }
+
+}
diff --git a/web/gui2-topo-lib/lib/panel/details/details.theme.css b/web/gui2-topo-lib/lib/panel/details/details.theme.css
new file mode 100644
index 0000000..7ad72dd
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/details/details.theme.css
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* --- Topo Details Panel Theme --- */
+
+#topo2-p-detail svg {
+    background: none;
+}
+
+#topo2-p-detail .header svg .glyph {
+    fill: #c0242b;
+}
+
+.dark #topo2-p-detail .header svg .glyph {
+    fill: #91292f;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/panel/instance/instance.component.css b/web/gui2-topo-lib/lib/panel/instance/instance.component.css
new file mode 100644
index 0000000..f335726
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/instance/instance.component.css
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* --- Topo Instance Panel --- */
+
+#topo-p-instance div.onosInst {
+    display: inline-block;
+    width: 170px;
+    height: 85px;
+    cursor: pointer;
+}
+
+#topo-p-instance svg text.instTitle {
+    font-size: 11pt;
+    font-weight: bold;
+    font-variant: small-caps;
+    text-transform: uppercase;
+}
+#topo-p-instance svg text.instLabel {
+    font-size: 10pt;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/panel/instance/instance.component.html b/web/gui2-topo-lib/lib/panel/instance/instance.component.html
new file mode 100644
index 0000000..9f2ee1a
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/instance/instance.component.html
@@ -0,0 +1,40 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="topo-p-instance" class="floatpanel" [ngStyle]="{'left': '20px', 'top':divTopPx+'px', 'width': (onosInstances.length * 170)+'px', 'height': '85px'}" [@instancePanelState]="on">
+    <div *ngFor="let inst of onosInstances | keyvalue ; let i=index"
+         [ngClass]="['onosInst', inst.value.online?'online':'', inst.value.ready? 'ready': '', mastership===inst.value.id?'mastership':'', 'affinity']"
+            (click)="chooseMastership(inst.value.id)">
+        <svg xmlns="http://www.w3.org/2000/svg" width="170" height="85" viewBox="0 0 170 85">
+            <!-- The following blue-glow effect is applied (through CSS) when mastership style is activated on a rectangle -->
+            <filter x="-50%" y="-50%" width="200%" height="200%" id="blue-glow">
+                <feColorMatrix type="matrix" values="0 0 0 0  0 0 0 0 0  0 0 0 0 0  0.7 0 0 0 1  0 "></feColorMatrix>
+                <feGaussianBlur stdDeviation="3" result="coloredBlur"></feGaussianBlur>
+                <feMerge>
+                    <feMergeNode in="coloredBlur"></feMergeNode>
+                    <feMergeNode in="SourceGraphic"></feMergeNode>
+                </feMerge>
+            </filter>
+            <rect x="5" y="5" width="160" height="30" [ngStyle]="{ 'fill': panelColour(i)}"></rect>
+            <text class="instTitle" x="48" y="27">{{ inst.value.id }}</text>
+            <rect x="5" y="35" width="160" height="45"></rect>
+            <text class="instLabel ip" x="48" y="55">{{ inst.value.ip }}</text>
+            <use width="20" height="20" class="glyph badgeIcon bird" xlink:href="#bird" transform="translate(15,10)"></use>
+            <use *ngIf="inst.value.ready" width="16" height="16" class="glyph overlay badgeIcon readyBadge" xlink:href="#checkMark" transform="translate(18,40)"></use>
+            <text class="instLabel ns" x="48" y="73">{{lionFn('devices')}} {{ inst.value.switches }}</text>
+            <use *ngIf="inst.value.uiAttached" width="24" height="24" class="glyph overlay badgeIcon uiBadge" xlink:href="#uiAttached" transform="translate(14,54)"></use>
+        </svg>
+    </div>
+</div>
diff --git a/web/gui2-topo-lib/lib/panel/instance/instance.component.spec.ts b/web/gui2-topo-lib/lib/panel/instance/instance.component.spec.ts
new file mode 100644
index 0000000..6b06753
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/instance/instance.component.spec.ts
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import { of } from 'rxjs';
+import { InstanceComponent } from './instance.component';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+import {
+    FnService,
+    LogService
+} from '../../../../gui2-fw-lib/public_api';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+/**
+ * ONOS GUI -- Topology View Instance Panel-- Unit Tests
+ */
+describe('InstanceComponent', () => {
+    let fs: FnService;
+    let ar: MockActivatedRoute;
+    let windowMock: Window;
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: InstanceComponent;
+    let fixture: ComponentFixture<InstanceComponent>;
+
+    beforeEach(async(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+        windowMock = <any>{
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+        fs = new FnService(ar, logSpy, windowMock);
+
+        TestBed.configureTestingModule({
+            imports: [ BrowserAnimationsModule ],
+            declarations: [ InstanceComponent ],
+            providers: [
+                { provide: FnService, useValue: fs },
+                { provide: LogService, useValue: logSpy },
+                { provide: 'Window', useValue: windowMock },
+            ]
+        })
+        .compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(InstanceComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/panel/instance/instance.component.ts b/web/gui2-topo-lib/lib/panel/instance/instance.component.ts
new file mode 100644
index 0000000..53ecf12
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/instance/instance.component.ts
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    Component,
+    Input,
+    Output,
+    EventEmitter, OnChanges, SimpleChanges
+} from '@angular/core';
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import {
+    LogService,
+    FnService,
+    PanelBaseImpl,
+    IconService,
+    SvgUtilService, LionService
+} from '../../../../gui2-fw-lib/public_api';
+
+/**
+ * A model of instance information that drives each panel
+ */
+export interface Instance {
+    id: string;
+    ip: string;
+    online: boolean;
+    ready: boolean;
+    switches: number;
+    uiAttached: boolean;
+}
+
+/**
+ * ONOS GUI -- Topology Instances Panel.
+ * Displays ONOS instances. The onosInstances Array gets updated by topology.service
+ * whenever a topo2AllInstances update arrives back on the WebSocket
+ *
+ * This emits a mastership event when the user clicks on an instance, to
+ * see the devices that it has mastership of.
+ */
+@Component({
+    selector: 'onos-instance',
+    templateUrl: './instance.component.html',
+    styleUrls: [
+        './instance.component.css', './instance.theme.css',
+        '../../topology.common.css',
+        '../../../../gui2-fw-lib/lib/widget/panel.css',
+        '../../../../gui2-fw-lib/lib/widget/panel-theme.css'
+    ],
+    animations: [
+        trigger('instancePanelState', [
+            state('true', style({
+                transform: 'translateX(0%)',
+                opacity: '1.0'
+            })),
+            state('false', style({
+                transform: 'translateX(-100%)',
+                opacity: '0.0'
+            })),
+            transition('0 => 1', animate('100ms ease-in')),
+            transition('1 => 0', animate('100ms ease-out'))
+        ])
+    ]
+})
+export class InstanceComponent extends PanelBaseImpl implements OnChanges {
+    @Input() onosInstances: Instance[] = [];
+    @Input() divTopPx: number = 100;
+    @Input() on: boolean = false; // Override the parent class attribute
+    @Output() mastershipEvent = new EventEmitter<string>();
+    public mastership: string;
+    lionFn; // Function
+
+    constructor(
+        protected fs: FnService,
+        protected log: LogService,
+        protected is: IconService,
+        protected sus: SvgUtilService,
+        private lion: LionService
+    ) {
+        super(fs, log);
+
+        if (this.lion.ubercache.length === 0) {
+            this.lionFn = this.dummyLion;
+            this.lion.loadCbs.set('topo-inst', () => this.doLion());
+        } else {
+            this.doLion();
+        }
+        this.log.debug('InstanceComponent constructed');
+    }
+
+    ngOnChanges(changes: SimpleChanges): void {
+        if (changes['onosInstances']) {
+            this.onosInstances = <Instance[]>changes['onosInstances'].currentValue;
+        }
+    }
+
+    /**
+     * Get a colour for the banner of the nth panel
+     * @param idx The index of the panel (0-6)
+     */
+    panelColour(idx: number): string {
+        return this.sus.cat7().getColor(idx, false, '');
+    }
+
+    /**
+     * Toggle the display of mastership
+     * If the same instance is clicked a second time then cancel display of mastership
+     * @param instId The instance to display mastership for
+     */
+    chooseMastership(instId: string): void {
+        if (this.mastership === instId) {
+            this.mastership = undefined;
+        } else {
+            this.mastership = instId;
+        }
+        this.mastershipEvent.emit(this.mastership);
+        this.log.debug('Instance', this.mastership, 'chosen on GUI');
+    }
+
+    /**
+     * Read the LION bundle for Details panel and set up the lionFn
+     */
+    doLion() {
+        this.lionFn = this.lion.bundle('core.view.Topo');
+
+    }
+}
diff --git a/web/gui2-topo-lib/lib/panel/instance/instance.theme.css b/web/gui2-topo-lib/lib/panel/instance/instance.theme.css
new file mode 100644
index 0000000..3be7bdd
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/instance/instance.theme.css
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* --- Topo Instance Panel --- */
+
+#topo-p-instance svg rect {
+    stroke-width: 0;
+    fill: #fbfbfb;
+}
+
+/* body of an instance */
+#topo-p-instance .online svg rect {
+    opacity: 1;
+    fill: #fbfbfb;
+}
+
+#topo-p-instance svg .glyph {
+    fill: #fff;
+}
+#topo-p-instance .online svg .glyph {
+    fill: #fff;
+}
+.dark #topo-p-instance .online svg .glyph.overlay {
+    fill: #fff;
+}
+
+/* offline */
+#topo-p-instance svg .badgeIcon {
+    opacity: 0.4;
+    fill: #939598;
+}
+
+/* online */
+#topo-p-instance .online svg .badgeIcon {
+    opacity: 1.0;
+    fill: #939598;
+}
+#topo-p-instance .online svg .badgeIcon.bird {
+    fill: #ffffff;
+}
+
+#topo-p-instance svg .readyBadge {
+    visibility: hidden;
+}
+#topo-p-instance .ready svg .readyBadge {
+    visibility: visible;
+}
+
+#topo-p-instance svg text {
+    text-anchor: start;
+    opacity: 0.5;
+    fill: #3c3a3a;
+}
+
+#topo-p-instance .online svg text {
+    opacity: 1.0;
+    fill: #3c3a3a;
+}
+
+#topo-p-instance .onosInst.mastership {
+    opacity: 0.3;
+}
+#topo-p-instance .onosInst.mastership.affinity {
+    opacity: 1.0;
+}
+#topo-p-instance .onosInst.mastership.affinity svg rect {
+    filter: url(#blue-glow);
+}
+
+.firefox #topo-p-instance .onosInst.mastership.affinity svg rect {
+    filter: url(#blue-glow);
+}
+
+.dark #topo-p-instance {
+    background-color: #2f313c;
+    color: #c2c2b7;
+    border: 1px solid #364144;
+
+}
+
+.dark #topo-p-instance svg rect {
+    stroke-width: 0;
+    fill: #525660;
+}
+
+/* body of an instance */
+.dark #topo-p-instance .online svg rect {
+    opacity: 1;
+    fill: #838992;
+}
+
+.dark #topo-p-instance svg .glyph {
+    fill: #ddd;
+}
+.dark #topo-p-instance .online svg .glyph {
+    fill: #fff;
+}
+.dark #topo-p-instance .online svg .glyph.overlay {
+    fill: #c7c7c7;
+}
+
+/* offline */
+.dark #topo-p-instance svg .badgeIcon {
+    opacity: 0.4;
+    fill: #939598;
+}
+
+/* online */
+.dark #topo-p-instance .online svg .badgeIcon {
+    opacity: 1.0;
+    fill: #939598;
+}
+.dark #topo-p-instance .online svg .badgeIcon.bird {
+    fill: #ffffff;
+}
+
+.dark #topo-p-instance svg text {
+    text-anchor: start;
+    opacity: 0.5;
+    fill: #aaa;
+}
+
+.dark #topo-p-instance .online svg text {
+    opacity: 1.0;
+    fill: #fff;
+}
+
+.dark #topo-p-instance .onosInst.mastership {
+    opacity: 0.3;
+}
+.dark #topo-p-instance .onosInst.mastership.affinity {
+    opacity: 1.0;
+}
+.dark #topo-p-instance .onosInst.mastership.affinity svg rect {
+    filter: url(#blue-glow);
+}
+
+.dark.firefox #topo-p-instance .onosInst.mastership.affinity svg rect {
+    filter: url(#blue-glow);
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.css b/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.css
new file mode 100644
index 0000000..59c0d78
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.css
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ONOS GUI -- Topology Map Selector -- CSS file
+ */
+.dialog h2 {
+    margin: 0;
+    word-wrap: break-word;
+    display: inline-block;
+    width: 210px;
+    vertical-align: middle;
+}
+
+.dialog .dialog-button {
+    display: inline-block;
+    cursor: pointer;
+    height: 20px;
+    padding: 6px 8px 2px 8px;
+    margin: 4px;
+    float: right;
+}
diff --git a/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.html b/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.html
new file mode 100644
index 0000000..579a43b
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.html
@@ -0,0 +1,33 @@
+<!--
+~ Copyright 2019-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="topo-p-dialog"
+     class="floatpanel dialog topo-p"
+     style="opacity: 1; left: 20px; width: 300px;">
+    <div class="header">
+        <h2>{{ lionFn('title_select_map') }}</h2>
+    </div>
+    <div class="map-list">
+        <form [formGroup]="form">
+            <select formControlName="mapid">
+                <option *ngFor="let o of mapSelectorResponse.order" [ngValue]="o">{{ mapSelectorResponse.maps[o]['description'] }}</option>
+            </select>
+        </form>
+    </div>
+    <div class="footer">
+        <div class="dialog-button" (click)="choice(form.value)">{{ lionFn('ok') }}</div>
+        <div class="dialog-button" (click)="choice(undefined)">{{ lionFn('close') }}</div>
+    </div>
+</div>
diff --git a/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.spec.ts b/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.spec.ts
new file mode 100644
index 0000000..3a30421
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.spec.ts
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MapSelectorComponent } from './mapselector.component';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {ActivatedRoute, Params} from '@angular/router';
+import {of} from 'rxjs';
+import {FnService, LogService} from '../../../../gui2-fw-lib/public_api';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+describe('MapSelectorComponent', () => {
+    let fs: FnService;
+    let ar: MockActivatedRoute;
+    let windowMock: Window;
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: MapSelectorComponent;
+    let fixture: ComponentFixture<MapSelectorComponent>;
+
+    beforeEach(async(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+        windowMock = <any>{
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+        fs = new FnService(ar, logSpy, windowMock);
+
+        TestBed.configureTestingModule({
+            imports: [
+                FormsModule,
+                ReactiveFormsModule
+            ],
+            declarations: [ MapSelectorComponent ],
+            providers: [
+                { provide: FnService, useValue: fs },
+                { provide: LogService, useValue: logSpy },
+                { provide: 'Window', useValue: windowMock },
+            ]
+        })
+        .compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(MapSelectorComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
+
+// Expecting WebSocket request and response similar to:
+//
+// {"event":"mapSelectorRequest","payload":{}}
+//
+// {
+//     "event": "mapSelectorResponse",
+//     "payload": {
+//     "order": ["australia", "americas", "n_america", "s_america", "usa", "bayareaGEO",
+//     "europe", "italy", "uk", "japan", "s_korea", "taiwan", "africa", "oceania", "asia"],
+//         "maps": {
+//         "australia": {
+//             "id": "australia",
+//             "description": "Australia",
+//             "filePath": "*australia",
+//             "scale": 1.0
+//         },
diff --git a/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.ts b/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.ts
new file mode 100644
index 0000000..e9cb56a
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/mapselector/mapselector.component.ts
@@ -0,0 +1,90 @@
+
+import {
+    Component, EventEmitter,
+    OnDestroy,
+    OnInit, Output,
+} from '@angular/core';
+import {
+    DetailsPanelBaseImpl,
+    FnService,
+    LionService,
+    LogService,
+    WebSocketService
+} from '../../../../gui2-fw-lib/public_api';
+import {FormControl, FormGroup} from '@angular/forms';
+import { MapObject } from '../../layer/maputils';
+
+interface MapSelection {
+    order: string[];
+    maps: Object[];
+}
+
+@Component({
+    selector: 'onos-mapselector',
+    templateUrl: './mapselector.component.html',
+    styleUrls: ['./mapselector.component.css', './mapselector.theme.css', '../../topology.common.css']
+})
+export class MapSelectorComponent extends DetailsPanelBaseImpl implements OnInit, OnDestroy {
+    @Output() chosenMap = new EventEmitter<MapObject>();
+    lionFn; // Function
+    mapSelectorResponse: MapSelection = <MapSelection>{
+        order: [],
+        maps: []
+    };
+    form = new FormGroup({
+        mapid: new FormControl(this.mapSelectorResponse.order[0]),
+    });
+
+    constructor(
+        protected fs: FnService,
+        protected log: LogService,
+        protected wss: WebSocketService,
+        private lion: LionService
+    ) {
+        super(fs, log, wss, 'topo');
+
+        if (this.lion.ubercache.length === 0) {
+            this.lionFn = this.dummyLion;
+            this.lion.loadCbs.set('topoms', () => this.doLion());
+        } else {
+            this.doLion();
+        }
+
+        this.log.debug('Topo MapSelectorComponent constructed');
+    }
+
+    ngOnInit() {
+        this.wss.bindHandlers(new Map<string, (data) => void>([
+            ['mapSelectorResponse', (data) => {
+                this.mapSelectorResponse = data;
+                this.form.setValue({'mapid': this.mapSelectorResponse.order[0]});
+            }
+            ]
+        ]));
+        this.wss.sendEvent('mapSelectorRequest', {});
+        this.log.debug('Topo MapSelectorComponent initialized');
+    }
+
+    /**
+     * When the component is being unloaded then unbind the WSS handler.
+     */
+    ngOnDestroy(): void {
+        this.wss.unbindHandlers(['mapSelectorResponse']);
+        this.log.debug('Topo MapSelectorComponent destroyed');
+    }
+
+    /**
+     * Read the LION bundle for panel and set up the lionFn
+     */
+    doLion() {
+        this.lionFn = this.lion.bundle('core.view.Topo');
+    }
+
+    choice(mapid: Object): void {
+        if (mapid) {
+            this.chosenMap.emit(<MapObject>this.mapSelectorResponse.maps[mapid['mapid']]);
+        } else {
+            this.chosenMap.emit(<MapObject>{});
+        }
+    }
+}
diff --git a/web/gui2-topo-lib/lib/panel/mapselector/mapselector.theme.css b/web/gui2-topo-lib/lib/panel/mapselector/mapselector.theme.css
new file mode 100644
index 0000000..0ef1538
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/mapselector/mapselector.theme.css
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ONOS GUI -- Topology Map Selector theme -- CSS file
+ */
+
+/*.light */
+.dialog .dialog-button {
+    background-color: #518ecc;
+    color: white;
+}
+
+
+/* ========== DARK Theme ========== */
+
+.dark .dialog .dialog-button {
+    background-color: #345e85;
+    color: #cccccd;
+}
diff --git a/web/gui2-topo-lib/lib/panel/summary/summary.component.css b/web/gui2-topo-lib/lib/panel/summary/summary.component.css
new file mode 100644
index 0000000..b4bd37b
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/summary/summary.component.css
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology Summary Panel -- CSS file
+ */
+#topo2-p-summary {
+    padding: 16px;
+    top: 100px;
+}
+
+#topo2-p-summary  td.label {
+    width: 50%;
+}
+
+#topo2-p div.header div.icon {
+    padding: 10px
+}
+
+#topo2-p-summary div.header h2 {
+    padding: 10px;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/panel/summary/summary.component.html b/web/gui2-topo-lib/lib/panel/summary/summary.component.html
new file mode 100644
index 0000000..d781418
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/summary/summary.component.html
@@ -0,0 +1,20 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="topo2-p-summary" class="floatpanel topo2-p"
+     style="opacity: 1; right: 20px; width: 260px;" [@summaryPanelState]="on">
+    <!-- everything else is filled in dynamically by listProps() and the
+    response showSummary received from the server -->
+</div>
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/panel/summary/summary.component.spec.ts b/web/gui2-topo-lib/lib/panel/summary/summary.component.spec.ts
new file mode 100644
index 0000000..0a07d2e
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/summary/summary.component.spec.ts
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import { of } from 'rxjs';
+import { SummaryComponent } from './summary.component';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+import {
+    FnService,
+    LogService
+} from '../../../../gui2-fw-lib/public_api';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+/**
+ * ONOS GUI -- Topology View Summary Panel -- Unit Tests
+ */
+describe('SummaryComponent', () => {
+    let fs: FnService;
+    let ar: MockActivatedRoute;
+    let windowMock: Window;
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: SummaryComponent;
+    let fixture: ComponentFixture<SummaryComponent>;
+
+    beforeEach(async(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+        windowMock = <any>{
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+        fs = new FnService(ar, logSpy, windowMock);
+
+        TestBed.configureTestingModule({
+            imports: [ BrowserAnimationsModule ],
+            declarations: [ SummaryComponent ],
+            providers: [
+                { provide: FnService, useValue: fs },
+                { provide: LogService, useValue: logSpy },
+                { provide: 'Window', useValue: windowMock },
+            ]
+        })
+        .compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(SummaryComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/panel/summary/summary.component.ts b/web/gui2-topo-lib/lib/panel/summary/summary.component.ts
new file mode 100644
index 0000000..0191cd9
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/summary/summary.component.ts
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    Component,
+    Input,
+    OnDestroy,
+    OnInit,
+    ViewEncapsulation
+} from '@angular/core';
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import * as d3 from 'd3';
+import { TopoPanelBaseImpl } from '../topopanel.base';
+import {
+    LogService,
+    FnService,
+    WebSocketService,
+    GlyphService
+} from '../../../../gui2-fw-lib/public_api';
+
+export interface SummaryResponse {
+    title: string;
+}
+/**
+ * ONOS GUI -- Topology Summary Module.
+ * Defines modeling of ONOS Summary Panel.
+ * Note: This component uses the d3 DOM building technique from the old GUI - this
+ * is not the Angular way of building components and should be avoided generally
+ * See DetailsPanelComponent for a better way of doing this kind of thing
+ */
+@Component({
+    selector: 'onos-summary',
+    templateUrl: './summary.component.html',
+    styleUrls: [
+        './summary.component.css',
+        '../../topology.common.css', '../../topology.theme.css',
+        '../../../../gui2-fw-lib/lib/widget/panel.css',
+        '../../../../gui2-fw-lib/lib/widget/panel-theme.css'
+    ],
+    encapsulation: ViewEncapsulation.None,
+    animations: [
+        trigger('summaryPanelState', [
+            state('true', style({
+                transform: 'translateX(0%)',
+                opacity: '100'
+            })),
+            state('false', style({
+                transform: 'translateX(100%)',
+                opacity: '0'
+            })),
+            transition('0 => 1', animate('100ms ease-in')),
+            transition('1 => 0', animate('100ms ease-out'))
+        ])
+    ]
+})
+export class SummaryComponent extends TopoPanelBaseImpl implements OnInit, OnDestroy {
+    @Input() on: boolean = false; // Override the parent class attribute
+    private handlers: string[] = [];
+    private resp: string = 'showSummary';
+    private summaryData: SummaryResponse;
+
+    constructor(
+        protected fs: FnService,
+        protected log: LogService,
+        protected wss: WebSocketService,
+        protected gs: GlyphService
+    ) {
+        super(fs, log, 'summary');
+        this.summaryData = <SummaryResponse>{};
+        this.log.debug('SummaryComponent constructed');
+    }
+
+
+    ngOnInit() {
+        this.wss.bindHandlers(new Map<string, (data) => void>([
+            [this.resp, (data) => this.handleSummaryData(data)]
+        ]));
+        this.handlers.push(this.resp);
+
+        this.init(d3.select('#topo2-p-summary'));
+
+        this.wss.sendEvent('requestSummary', {});
+    }
+
+    ngOnDestroy() {
+        this.wss.sendEvent('cancelSummary', {});
+        this.wss.unbindHandlers(this.handlers);
+    }
+
+    handleSummaryData(data: SummaryResponse) {
+        this.summaryData = data;
+        this.render();
+    }
+
+    private render() {
+        let endedWithSeparator;
+
+        this.emptyRegions();
+
+        const svg = this.appendToHeader('div')
+                .classed('icon', true)
+                .append('svg');
+        const title = this.appendToHeader('h2');
+        const table = this.appendToBody('table');
+        const tbody = table.append('tbody');
+
+        title.text(this.summaryData.title);
+        this.gs.addGlyph(svg, 'bird', 24, 0, [1, 1]);
+        endedWithSeparator = this.listProps(tbody, this.summaryData);
+        // TODO : review whether we need to use/store end-with-sep state
+    }
+}
diff --git a/web/gui2-topo-lib/lib/panel/toolbar/button.css b/web/gui2-topo-lib/lib/panel/toolbar/button.css
new file mode 100644
index 0000000..1effdbc
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/toolbar/button.css
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Button Service (layout) -- CSS file
+ */
+
+.button,
+.toggleButton,
+.radioSet {
+    display: inline-block;
+    padding: 0 4px;
+}
+.radioButton {
+    display: inline-block;
+    padding: 0 2px;
+}
+
+.button svg.embeddedIcon,
+.toggleButton svg.embeddedIcon,
+.radioButton svg.embeddedIcon {
+    cursor: pointer;
+}
+.button svg.embeddedIcon .icon rect,
+.toggleButton svg.embeddedIcon .icon rect,
+.radioButton svg.embeddedIcon .icon rect{
+    stroke: none;
+}
diff --git a/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.css b/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.css
new file mode 100644
index 0000000..449d436
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.css
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology Toolbar Panel -- CSS file
+ */
+
+
+
+div.tbar-arrow {
+    position: absolute;
+    top: 53%;
+    left: 96%;
+    margin-right: -4%;
+    transform: translate(-50%, -50%);
+    cursor: pointer;
+}
+.safari div.tbar-arrow {
+    top: 46%;
+}
+.firefox div.tbar-arrow {
+    left: 97%;
+    margin-right: -3%;
+}
+
+.toolbar {
+    line-height: 125%;
+}
+.tbar-row {
+    display: inline-block;
+}
+
+.separator {
+    border: 1px solid;
+    margin: 0 4px 0 4px;
+    display: inline-block;
+    height: 23px;
+    width: 0;
+}
+
+#toolbar-topo2-toolbar {
+    padding: 6px;
+}
+
+#toolbar-topo2-toolbar .tbar-row.right {
+    width: 100%;
+}
+
+#toolbar-topo2-toolbar .tbar-row-text {
+    height: 21px;
+    text-align: right;
+    padding: 8px 60px 0 0;
+    font-style: italic;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.html b/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.html
new file mode 100644
index 0000000..a0e2d23
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.html
@@ -0,0 +1,88 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="toolbar-topo2-toolbar" class="floatpanel toolbar" [@toolbarState]="on"
+     style="opacity: 1; left: 0px; width: 286px; top: auto; bottom: 10px;">
+    <div class="tbar-arrow" (click)="on =! on">
+        <onos-icon [iconSize]="10" [iconId]="on?'triangleLeft':'triangleRight'"></onos-icon>
+    </div>
+    <div class="tbar-row ctrl-btns">
+        <div class="toggleButton" id="toolbar-topo2-toolbar-topo2-instance-tog" (click)="buttonClicked('instance-tog')">
+            <onos-icon [iconSize]="25" iconId="m_uiAttached" [toolTip]="lionFn('tbtt_tog_instances')" [classes]="['toggleButton', instancesVisible?'selected':'']"></onos-icon>
+        </div>
+        <div class="toggleButton" id="toolbar-topo2-toolbar-topo2-summary-tog" (click)="buttonClicked('summary-tog')">
+            <onos-icon [iconSize]="25" iconId="m_summary" [toolTip]="lionFn('tbtt_tog_summary')" [classes]="['toggleButton', summaryVisible?'selected':'']"></onos-icon>
+        </div>
+        <div class="toggleButton" id="toolbar-topo2-toolbar-details-tog" (click)="buttonClicked('details-tog')">
+            <onos-icon [iconSize]="25" iconId="m_details" [toolTip]="lionFn('tbtt_tog_use_detail')" [classes]="['toggleButton', detailsVisible?'selected':'']"></onos-icon>
+        </div>
+        <div class="separator"></div>
+        <div class="toggleButton" id="toolbar-topo2-toolbar-hosts-tog" (click)="buttonClicked('hosts-tog')">
+            <onos-icon [iconSize]="25" iconId="m_endstation" [toolTip]="lionFn('tbtt_tog_host')" [classes]="['toggleButton', hostsVisible?'selected':'']"></onos-icon>
+        </div>
+        <div class="toggleButton" id="toolbar-topo2-toolbar-offline-tog" (click)="buttonClicked('offline-tog')">
+            <onos-icon [iconSize]="25" iconId="m_switch" [toolTip]="lionFn('tbtt_tog_offline')" classes="toggleButton selected"></onos-icon>
+        </div>
+        <div class="toggleButton" id="toolbar-topo2-toolbar-topo2-ports-tog" (click)="buttonClicked('ports-tog')">
+            <onos-icon [iconSize]="25" iconId="m_ports" [toolTip]="lionFn('tbtt_tog_porthi')" classes="toggleButton selected" [classes]="['toggleButton', portsVisible?'selected':'']"></onos-icon>
+        </div>
+        <div class="toggleButton" id="toolbar-topo2-toolbar-topo2-bkgrnd-tog" (click)="buttonClicked('bkgrnd-tog')">
+            <onos-icon [iconSize]="25" iconId="m_map" [toolTip]="lionFn('tbtt_tog_map')" classes="toggleButton selected" [classes]="['toggleButton', backgroundVisible?'selected':'']"></onos-icon>
+        </div>
+        <div class="toggleButton" id="toolbar-topo2-toolbar-topo2-bkgrnd-sel" (click)="buttonClicked('bkgrnd-sel')">
+            <onos-icon [iconSize]="25" iconId="m_selectMap" [toolTip]="lionFn('tbtt_sel_map')" classes="button"></onos-icon>
+        </div>
+    </div>
+    <br>
+    <div class="tbar-row">
+        <div class="button" id="toolbar-topo2-toolbar-topo2-cycleLabels-btn" (click)="buttonClicked('cycleLabels-btn')">
+            <onos-icon [iconSize]="25" iconId="m_cycleLabels" [toolTip]="lionFn('tbtt_cyc_dev_labs')" classes="button"></onos-icon>
+        </div>
+        <div class="button" id="toolbar-topo2-toolbar-topo2-resetZoom-btn" (click)="buttonClicked('resetZoom-btn')">
+            <onos-icon [iconSize]="25" iconId="m_resetZoom" [toolTip]="lionFn('tbtt_reset_zoom')" classes="button"></onos-icon>
+        </div>
+        <div class="separator"></div>
+        <div class="button" id="toolbar-topo2-toolbar-topo2-eqMaster-btn" (click)="buttonClicked('eqMaster-btn')">
+            <onos-icon [iconSize]="25" iconId="m_eqMaster" [toolTip]="lionFn('tbtt_eq_master')" classes="button"></onos-icon>
+        </div>
+        <div class="separator"></div>
+        <div class="radioSet" id="toolbar-topo2-traffic">
+            <div class="radioButton selected" id="toolbar-topo2-cancel-traffic" (click)="buttonClicked('cancel-traffic')">
+                <onos-icon [iconSize]="25" iconId="m_unknown" [toolTip]="lionFn('tr_btn_cancel_monitoring')" classes="radioButton selected"></onos-icon>
+            </div>
+            <div class="radioButton" id="toolbar-topo2-all-traffic" (click)="buttonClicked('all-traffic')">
+                <onos-icon [iconSize]="25" iconId="m_allTraffic" [toolTip]="lionFn('tr_btn_show_related_traffic')" classes="radioButton selected"></onos-icon>
+            </div>
+        </div>
+        <div class="separator"></div>
+        <div class="button" id="toolbar-topo2-toolbar-topo2-quickhelp" (click)="buttonClicked('quickhelp-btn')">
+            <onos-icon [iconSize]="25" iconId="query" [toolTip]="lionFn('qh_title')" classes="button"></onos-icon>
+        </div>
+        <div class="button" id="toolbar-topo2-toolbar-topo2-cycleGrid-btn" (click)="buttonClicked('cycleGridDisplay-btn')">
+            <onos-icon [iconSize]="25" iconId="m_cycleGridDisplay" [toolTip]="lionFn('tbtt_cyc_grid_display')" classes="button"></onos-icon>
+        </div>
+    </div>
+    <div class="tbar-row">
+        <div class="button" id="toolbar-topo2-toolbar-topo2-layout-default" (click)="buttonClicked('layout-default-btn')">
+            <onos-icon iconSize="25" iconId="m_fiberSwitch" toolTip="Default (force-based) layout" classes="button"></onos-icon>
+        </div>
+        <div class="button" id="toolbar-topo2-toolbar-topo2-layout-access" (click)="buttonClicked('layout-access-btn')">
+            <onos-icon iconSize="25" iconId="m_disjointPaths" toolTip="Access layout - separate service leafs" classes="button"></onos-icon>
+        </div>
+        <div class="button" id="toolbar-topo2-toolbar-topo2-alarms-tog" (click)="buttonClicked('alarms-tog')">
+            <onos-icon iconSize="25" iconId="clock" toolTip="Toggle Alarms display" classes="button" [classes]="['toggleButton', alarmsVisible?'selected':'']"></onos-icon>
+        </div>
+    </div>
+</div>
diff --git a/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.spec.ts b/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.spec.ts
new file mode 100644
index 0000000..b1dff0b
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.spec.ts
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import { of } from 'rxjs';
+import { ToolbarComponent } from './toolbar.component';
+
+import {
+    FnService, LionService,
+    LogService, IconComponent
+} from '../../../../gui2-fw-lib/public_api';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+/**
+ * ONOS GUI -- Topology View Topology Panel-- Unit Tests
+ */
+describe('ToolbarComponent', () => {
+    let fs: FnService;
+    let ar: MockActivatedRoute;
+    let windowMock: Window;
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: ToolbarComponent;
+    let fixture: ComponentFixture<ToolbarComponent>;
+
+    const bundleObj = {
+        'core.view.Topo': {
+            test: 'test1'
+        }
+    };
+    const mockLion = (key) => {
+        return bundleObj[key] || '%' + key + '%';
+    };
+
+    beforeEach(async(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+        windowMock = <any>{
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+        fs = new FnService(ar, logSpy, windowMock);
+        TestBed.configureTestingModule({
+            imports: [ BrowserAnimationsModule ],
+            declarations: [ ToolbarComponent, IconComponent ],
+            providers: [
+                { provide: FnService, useValue: fs },
+                { provide: LogService, useValue: logSpy },
+                {
+                    provide: LionService, useFactory: (() => {
+                        return {
+                            bundle: ((bundleId) => mockLion),
+                            ubercache: new Array(),
+                            loadCbs: new Map<string, () => void>([])
+                        };
+                    })
+                },
+                { provide: 'Window', useValue: windowMock },
+            ]
+        })
+        .compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(ToolbarComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.ts b/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.ts
new file mode 100644
index 0000000..725cbb1
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/toolbar/toolbar.component.ts
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {
+    LogService,
+    FnService,
+    PanelBaseImpl, LionService
+} from '../../../../gui2-fw-lib/public_api';
+
+import {animate, state, style, transition, trigger} from '@angular/animations';
+
+export const INSTANCE_TOGGLE = 'instance-tog';
+export const SUMMARY_TOGGLE = 'summary-tog';
+export const DETAILS_TOGGLE = 'details-tog';
+export const HOSTS_TOGGLE = 'hosts-tog';
+export const OFFLINE_TOGGLE = 'offline-tog';
+export const PORTS_TOGGLE = 'ports-tog';
+export const BKGRND_TOGGLE = 'bkgrnd-tog';
+export const BKGRND_SELECT = 'bkgrnd-sel';
+export const CYCLELABELS_BTN = 'cycleLabels-btn';
+export const CYCLEHOSTLABEL_BTN = 'cycleHostLabel-btn';
+export const CYCLEGRIDDISPLAY_BTN = 'cycleGridDisplay-btn';
+export const RESETZOOM_BTN = 'resetZoom-btn';
+export const EQMASTER_BTN = 'eqMaster-btn';
+export const CANCEL_TRAFFIC = 'cancel-traffic';
+export const ALL_TRAFFIC = 'all-traffic';
+export const QUICKHELP_BTN = 'quickhelp-btn';
+export const LAYOUT_DEFAULT_BTN = 'layout-default-btn';
+export const LAYOUT_ACCESS_BTN = 'layout-access-btn';
+export const ALARMS_TOGGLE = 'alarms-tog';
+
+/*
+ ONOS GUI -- Topology Toolbar Module.
+ Defines modeling of ONOS toolbar.
+ */
+@Component({
+    selector: 'onos-toolbar',
+    templateUrl: './toolbar.component.html',
+    styleUrls: [
+        './toolbar.component.css', './toolbar.theme.css',
+        '../../topology.common.css',
+        '../../../../gui2-fw-lib/lib/widget/panel.css',
+        '../../../../gui2-fw-lib/lib/widget/panel-theme.css',
+        './button.css'
+    ],
+    animations: [
+        trigger('toolbarState', [
+            state('true', style({
+                transform: 'translateX(0%)',
+                // opacity: '1.0'
+            })),
+            state('false', style({
+                transform: 'translateX(-93%)',
+                // opacity: '0.0'
+            })),
+            transition('0 => 1', animate('500ms ease-in')),
+            transition('1 => 0', animate('500ms ease-out'))
+        ])
+    ]
+})
+export class ToolbarComponent extends PanelBaseImpl {
+    @Input() on: boolean = false; // Override the parent class attribute
+    // deferred localization strings
+    lionFn; // Function
+    // Used to drive the display of the hosts icon - there is also another such variable on the forcesvg
+    @Input() hostsVisible: boolean = false;
+    @Input() instancesVisible: boolean = true;
+    @Input() summaryVisible: boolean = true;
+    @Input() detailsVisible: boolean = true;
+    @Input() backgroundVisible: boolean = false;
+    @Input() portsVisible: boolean = true;
+    @Input() alarmsVisible: boolean = true;
+
+    @Output() buttonEvent = new EventEmitter<string>();
+
+    constructor(
+        protected fs: FnService,
+        protected log: LogService,
+        private lion: LionService
+    ) {
+        super(fs, log);
+
+        if (this.lion.ubercache.length === 0) {
+            this.lionFn = this.dummyLion;
+            this.lion.loadCbs.set('topo-toolbar', () => this.doLion());
+        } else {
+            this.doLion();
+        }
+
+        this.log.debug('ToolbarComponent constructed');
+    }
+
+    /**
+     * Read the LION bundle for Toolbar and set up the lionFn
+     */
+    doLion() {
+        this.lionFn = this.lion.bundle('core.view.Topo');
+    }
+
+    /**
+     * As buttons are clicked on the toolbar, emit events up to the parent
+     *
+     * The toggling of the input variables here is in addition to the control
+     * of these input variables from the parent. This is so that this component
+     * may be reused and is not dependent on a particular parent implementation
+     * to work
+     * @param name The name of button clicked.
+     */
+    buttonClicked(name: string): void {
+        switch (name) {
+            case HOSTS_TOGGLE:
+                this.hostsVisible = !this.hostsVisible;
+                break;
+            case INSTANCE_TOGGLE:
+                this.instancesVisible = !this.instancesVisible;
+                break;
+            case SUMMARY_TOGGLE:
+                this.summaryVisible = !this.summaryVisible;
+                break;
+            case DETAILS_TOGGLE:
+                this.detailsVisible = !this.detailsVisible;
+                break;
+            case BKGRND_TOGGLE:
+                this.backgroundVisible = !this.backgroundVisible;
+                break;
+            case ALARMS_TOGGLE:
+                this.alarmsVisible = !this.alarmsVisible;
+                break;
+            default:
+        }
+        // Send a message up to let TopologyComponent know of the event
+        this.buttonEvent.emit(name);
+    }
+}
diff --git a/web/gui2-topo-lib/lib/panel/toolbar/toolbar.theme.css b/web/gui2-topo-lib/lib/panel/toolbar/toolbar.theme.css
new file mode 100644
index 0000000..7933ee6
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/toolbar/toolbar.theme.css
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * ONOS GUI -- Topology Toolbar Panel -- Theme CSS file
+ */
+.tbar-arrow svg.embeddedIcon .icon rect {
+    stroke: none;
+}
+
+.tbar-arrow svg.embeddedIcon .icon .glyph {
+    fill: #838383;
+}
+
+.tbar-arrow svg.embeddedIcon .icon rect {
+    fill: none;
+}
+
+.separator {
+    border-color: #ddd;
+}
+
+/* ========== DARK Theme ========== */
+
+.dark .tbar-arrow svg.embeddedIcon .icon .glyph {
+    fill: #B2B2B2;
+}
+
+.dark .tbar-arrow svg.embeddedIcon .icon rect {
+    fill: none;
+}
+
+.dark .separator {
+    border-color: #454545;
+}
+
+.dark #toolbar-topo2-toolbar .tbar-row.right {
+    color: #666;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/panel/topopanel.base.ts b/web/gui2-topo-lib/lib/panel/topopanel.base.ts
new file mode 100644
index 0000000..bea8f58
--- /dev/null
+++ b/web/gui2-topo-lib/lib/panel/topopanel.base.ts
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    FnService,
+    LogService,
+    PanelBaseImpl
+} from '../../../gui2-fw-lib/public_api';
+
+/**
+ * Base model of panel view - implemented by Topology Panel components
+ */
+export abstract class TopoPanelBaseImpl extends PanelBaseImpl {
+
+    protected header: any;
+    protected body: any;
+    protected footer: any;
+
+    protected constructor(
+        protected fs: FnService,
+        protected log: LogService,
+        protected id: string
+    ) {
+        super(fs, log);
+    }
+
+    protected init(el: any) {
+        this.header = el.append('div').classed('header', true);
+        this.body = el.append('div').classed('body', true);
+        this.footer = el.append('div').classed('footer', true);
+    }
+
+    /**
+     * Decode lists of props sent back through Web Socket
+     *
+     * Means that panels do not have to know property names in advance
+     * Driven by PropertyPanel on Server side
+     */
+    listProps(el, data) {
+        let sepLast: boolean = false;
+
+        // note: track whether we end with a separator or not...
+        data.propOrder.forEach((p) => {
+            if (p === '-') {
+                this.addSep(el);
+                sepLast = true;
+            } else {
+                this.addProp(el, data.propLabels[p], data.propValues[p]);
+                sepLast = false;
+            }
+        });
+        return sepLast;
+    }
+
+    addProp(el, label, value) {
+        const tr = el.append('tr');
+        let lab;
+
+        if (typeof label === 'string') {
+            lab = label.replace(/_/g, ' ');
+        } else {
+            lab = label;
+        }
+
+        function addCell(cls, txt) {
+            tr.append('td').attr('class', cls).text(txt);
+        }
+
+        addCell('label', lab + ' :');
+        addCell('value', value);
+    }
+
+    addSep(el) {
+        el.append('tr').append('td').attr('colspan', 2).append('hr');
+    }
+
+    appendToHeader(x) {
+        return this.header.append(x);
+    }
+
+    appendToBody(x) {
+        return this.body.append(x);
+    }
+
+    appendToFooter(x) {
+        return this.footer.append(x);
+    }
+
+    emptyRegions() {
+        this.header.selectAll('*').remove();
+        this.body.selectAll('*').remove();
+        this.footer.selectAll('*').remove();
+    }
+
+}
diff --git a/web/gui2-topo-lib/lib/topology-routing.module.ts b/web/gui2-topo-lib/lib/topology-routing.module.ts
new file mode 100644
index 0000000..66e17b6
--- /dev/null
+++ b/web/gui2-topo-lib/lib/topology-routing.module.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+import { TopologyComponent } from './topology/topology.component';
+
+const topologyRoutes: Routes = [
+    {
+        path: '',
+        component: TopologyComponent
+    },
+];
+
+/**
+ * ONOS GUI -- Topology Tabular View Feature Routing Module - allows it to be lazy loaded
+ *
+ * See https://angular.io/guide/lazy-loading-ngmodules
+ */
+@NgModule({
+    imports: [RouterModule.forChild(topologyRoutes)],
+    exports: [RouterModule]
+})
+export class TopologyRoutingModule { }
diff --git a/web/gui2-topo-lib/lib/topology.common.css b/web/gui2-topo-lib/lib/topology.common.css
new file mode 100644
index 0000000..1ad9fbe
--- /dev/null
+++ b/web/gui2-topo-lib/lib/topology.common.css
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ONOS GUI -- Topology Common styles -- CSS file
+ */
+.topo2-p div.header {
+    margin-bottom: 10px;
+}
+
+.topo2-p div.header div.icon {
+    vertical-align: middle;
+    display: inline-block;
+}
+.topo2-p div.body {
+    overflow-y: scroll;
+}
+
+.topo2-p div.body::-webkit-scrollbar {
+    display: none;
+}
+
+.topo2-p svg {
+    display: inline-block;
+    width: 26px;
+    height: 26px;
+}
+
+
+.topo2-p h2 {
+    padding: 0 0 0 10px;
+    margin: 0;
+    font-weight: lighter;
+    word-wrap: break-word;
+    display: inline-block;
+    vertical-align: middle;
+}
+
+.topo2-p h3 {
+    padding: 0 4px;
+    margin: 0;
+    word-wrap: break-word;
+    top: 20px;
+    left: 50px;
+}
+
+.topo2-p p,
+.topo2-p table {
+    padding: 0;
+    margin: 0;
+    width: 100%;
+}
+
+.topo2-p td {
+    word-wrap: break-word;
+}
+.topo2-p td.label {
+    font-weight: bold;
+    padding: 0 10px 0 0;
+}
+.topo2-p td.value {
+    padding: 0;
+}
+
+#topo2-p-summary  td.label {
+    width: 50%;
+}
+
+.topo2-p hr {
+    height: 1px;
+    border: 0;
+    margin: 4px -3px;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/topology.service.spec.ts b/web/gui2-topo-lib/lib/topology.service.spec.ts
new file mode 100644
index 0000000..a7c8463
--- /dev/null
+++ b/web/gui2-topo-lib/lib/topology.service.spec.ts
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { TestBed, inject } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import {of} from 'rxjs';
+
+import { TopologyService } from './topology.service';
+import {
+    LogService,
+    FnService
+} from '../../gui2-fw-lib/public_api';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+/**
+ * ONOS GUI -- Topology Service - Unit Tests
+ */
+describe('TopologyService', () => {
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let ar: ActivatedRoute;
+    let fs: FnService;
+    let mockWindow: Window;
+
+    beforeEach(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['debug', 'warn', 'info']);
+        ar = new MockActivatedRoute({'debug': 'TestService'});
+        mockWindow = <any>{
+            innerWidth: 400,
+            innerHeight: 200,
+            navigator: {
+                userAgent: 'defaultUA'
+            },
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+        fs = new FnService(ar, logSpy, mockWindow);
+
+        TestBed.configureTestingModule({
+            providers: [TopologyService,
+                { provide: FnService, useValue: fs},
+                { provide: LogService, useValue: logSpy },
+                { provide: ActivatedRoute, useValue: ar },
+                { provide: 'Window', useFactory: (() => mockWindow ) }
+            ]
+        });
+        logServiceSpy = TestBed.get(LogService);
+    });
+
+    it('should be created', inject([TopologyService], (service: TopologyService) => {
+        expect(service).toBeTruthy();
+    }));
+});
diff --git a/web/gui2-topo-lib/lib/topology.service.ts b/web/gui2-topo-lib/lib/topology.service.ts
new file mode 100644
index 0000000..693a29a
--- /dev/null
+++ b/web/gui2-topo-lib/lib/topology.service.ts
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Injectable, SimpleChange} from '@angular/core';
+import {
+    LogService, WebSocketService,
+} from '../../gui2-fw-lib/public_api';
+import {Instance, InstanceComponent} from './panel/instance/instance.component';
+import { BackgroundSvgComponent } from './layer/backgroundsvg/backgroundsvg.component';
+import { ForceSvgComponent } from './layer/forcesvg/forcesvg.component';
+import {
+    ModelEventMemo,
+    ModelEventType,
+    Region
+} from './layer/forcesvg/models';
+
+/**
+ * Model of the Intent to be displayed
+ */
+export interface Intent {
+    appId: string;
+    appName: string;
+    key: string;
+    type: string;
+}
+
+export interface RelatedIntent {
+    ids: string[];
+    hover: string;
+}
+
+/**
+ * ONOS GUI -- Topology Service Module.
+ */
+@Injectable()
+export class TopologyService {
+
+    private handlers: string[] = [];
+    private openListener: any;
+    public instancesIndex: Map<string, number>;
+
+    constructor(
+        protected log: LogService,
+        protected wss: WebSocketService
+    ) {
+        this.instancesIndex = new Map();
+        this.log.debug('TopologyService constructed');
+    }
+
+    /**
+     * bind our event handlers to the web socket service, so that our
+     * callbacks get invoked for incoming events
+     */
+    init(instance: InstanceComponent, background: BackgroundSvgComponent, force: ForceSvgComponent) {
+        this.wss.bindHandlers(new Map<string, (data) => void>([
+            ['topo2AllInstances', (data) => {
+                    this.log.debug('Instances updated through WSS as topo2AllInstances', data);
+                    instance.ngOnChanges(
+                        {'onosInstances': new SimpleChange({}, data.members, true)});
+
+                    // Also generate an index locally of the instances
+                    // needed so that devices can be coloured by instance
+                    this.instancesIndex.clear();
+                    (<Instance[]>data.members).forEach((inst, idx) => this.instancesIndex.set(inst.id, idx));
+                    this.log.debug('Created local index of instances', this.instancesIndex);
+                }
+            ],
+            ['topo2CurrentLayout', (data) => {
+                    this.log.debug('Background Data updated from WSS as topo2CurrentLayout', data);
+                    if (background) {
+                        background.layoutData = data;
+                    }
+                }
+            ],
+            ['topo2CurrentRegion', (data) => {
+                    force.regionData = data;
+                    force.ngOnChanges({
+                        'regionData' : new SimpleChange(<Region>{}, data, true)
+                    });
+                    this.log.debug('Region Data replaced from WSS as topo2CurrentRegion', force.regionData);
+                }
+            ],
+            ['topo2PeerRegions', (data) => { this.log.warn('Add fn for topo2PeerRegions callback', data); } ],
+            ['topo2UiModelEvent', (event) => {
+                    // this.log.debug('Handling', event);
+                    force.handleModelEvent(
+                        <ModelEventType><unknown>(ModelEventType[event.type]), // Number based enum
+                        <ModelEventMemo>(event.memo), // String based enum
+                        event.subject, event.data);
+                }
+            ],
+            ['showHighlights', (event) => {
+                this.log.debug('Handling showHighlights', event);
+                force.handleHighlights(event.devices, event.hosts, event.links, 5000);
+            }]
+            // topo2Highlights is handled by TrafficService
+        ]));
+        this.handlers.push('topo2AllInstances');
+        this.handlers.push('topo2CurrentLayout');
+        this.handlers.push('topo2CurrentRegion');
+        this.handlers.push('topo2PeerRegions');
+        this.handlers.push('topo2UiModelEvent');
+        this.handlers.push('showHighlights');
+        // this.handlers.push('topo2Highlights');
+
+        // in case we fail over to a new server,
+        // listen for wsock-open events
+        this.openListener = this.wss.addOpenListener(() => this.wsOpen);
+
+        // tell the server we are ready to receive topology events
+        this.wss.sendEvent('topo2Start', {});
+        this.log.debug('TopologyService initialized');
+    }
+
+    /**
+     * tell the server we no longer wish to receive topology events
+     */
+    destroy() {
+        this.wss.sendEvent('topo2Stop', {});
+        this.wss.unbindHandlers(this.handlers);
+        this.wss.removeOpenListener(this.openListener);
+        this.openListener = null;
+        this.log.debug('TopologyService destroyed');
+    }
+
+
+    wsOpen(host: string, url: string) {
+        this.log.debug('topo2Event: WSopen - cluster node:', host, 'URL:', url);
+        // tell the server we are ready to receive topo events
+        this.wss.sendEvent('topo2Start', {});
+    }
+
+    /*
+     * Result will be handled by showHighlights handler (set up in topology service)
+     * which will call handleHighlights() in Force Component
+     */
+    setSelectedIntent(selectedIntent: Intent): void {
+        this.log.debug('Selected intent changed to', selectedIntent);
+        this.wss.sendEvent('selectIntent', selectedIntent);
+    }
+
+    selectRelatedIntent(ids: string[]): void {
+        this.log.debug('Select next intent');
+        this.wss.sendEvent('requestNextRelatedIntent', <RelatedIntent>{
+            ids: ids,
+            hover: undefined,
+        });
+    }
+
+    /*
+     * Tell the backend to stop sending highlights - any present will fade after 5 seconds
+     * There is also a cancel traffic for Topo 2 in Traffic Service
+     */
+    cancelHighlights(): void {
+        this.wss.sendEvent('cancelTraffic', {});
+        this.log.debug('Highlights canceled');
+    }
+}
diff --git a/web/gui2-topo-lib/lib/topology.theme.css b/web/gui2-topo-lib/lib/topology.theme.css
new file mode 100644
index 0000000..caa6199
--- /dev/null
+++ b/web/gui2-topo-lib/lib/topology.theme.css
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ONOS GUI -- Topology Common styles -- CSS file
+ */
+
+.topo2-p h2 {
+    display: inline-block;
+    padding: 6px;
+}
+
+.topo2-p svg {
+    background: #c0242b;
+    width: 28px;
+    height: 28px;
+}
+
+.topo2-p svg .glyph {
+    fill: #ffffff;
+}
+
+.topo2-p hr {
+    background-color: #cccccc;
+}
+
+#topo2-p-detail svg {
+    background: none;
+}
+
+#topo2-p-detail .header svg .glyph {
+    fill: #c0242b;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/topology/topology.component.css b/web/gui2-topo-lib/lib/topology/topology.component.css
new file mode 100644
index 0000000..f1cde38
--- /dev/null
+++ b/web/gui2-topo-lib/lib/topology/topology.component.css
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology View (layout) -- CSS file
+ */
+/* --- Base SVG Layer --- */
+#ov-topo2 svg {
+    /* prevents the little cut/copy/paste square that would appear on iPad */
+    -webkit-user-select: none;
+    background-color: #f4f4f4;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/lib/topology/topology.component.html b/web/gui2-topo-lib/lib/topology/topology.component.html
new file mode 100644
index 0000000..019c0d6
--- /dev/null
+++ b/web/gui2-topo-lib/lib/topology/topology.component.html
@@ -0,0 +1,128 @@
+<!--
+~ Copyright 2019-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~     http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<!-- Template explaination - Add in the flash message component - and link it to
+the local variable - this is used to display messages when keyboard shortcuts are pressed
+-->
+<onos-flash id="topoMsgFlash" message="{{ flashMsg }}" (closed)="flashMsg = ''"></onos-flash>
+
+<onos-quickhelp id="topoQuickHelp"></onos-quickhelp>
+<!-- Template explanation - Add in the Panel components for the Topology view
+    These are referenced inside the typescript by @ViewChild and their label
+-->
+<onos-instance #instance [divTopPx]="80"
+               (mastershipEvent)="force.changeInstSelection($event)"
+               [on]="prefsState.insts">
+</onos-instance>
+<onos-summary #summary [on]="prefsState.summary"></onos-summary>
+<onos-toolbar #toolbar
+              (buttonEvent)="toolbarButtonClicked($event)"
+              [on]="prefsState.toolbar"
+              [backgroundVisible]="prefsState.bg"
+              [detailsVisible]="prefsState.detail"
+              [hostsVisible]="prefsState.hosts"
+              [instancesVisible]="prefsState.insts"
+              [portsVisible]="prefsState.porthl"
+              [summaryVisible]="prefsState.summary">
+</onos-toolbar>
+<onos-details #details [on]="prefsState.detail"></onos-details>
+<onos-mapselector *ngIf="mapSelShown" (chosenMap)="changeMap($event)"></onos-mapselector>
+
+<div id="ov-topo2">
+    <!-- Template explanation -
+    Line 0) This is the root of the whole SVG canvas of the Topology View - all
+        components beneath it are SVG components only (no HTML)
+    Line 1) if the background is clicked then send the event to the function
+        where it is filtered to make sure it really applied to the background
+        or the map and then used to clear any selections made
+    -->
+    <svg:svg #svgZoom xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" id="topo2"
+        preserveAspectRatio="xMaxYMax meet" (click)="backgroundClicked($event)"
+        transform="'translate(0,0), scale(1.0)'">
+        <svg:desc>The main SVG canvas of the Topology View</svg:desc>
+        <!-- Template explanation -
+        line 0) the No Devices Connected banner is shown if the force component
+            (from later) does not contain any devices
+        -->
+        <svg:g *ngIf="force.regionData?.devices[0].length +
+                        force.regionData?.devices[1].length +
+                        force.regionData?.devices[2].length=== 0"
+               onos-nodeviceconnected />
+        <!-- Template explanation -
+        line 0) Create an SVG Grouping and apply the onosZoomableOf directive to it,
+            passing in the whole SVG canvas (#svgZoom)
+        -->
+        <svg:g id="topo-zoomlayer" onosZoomableOf [zoomableOf]="svgZoom" #onosZoom>
+            <svg:desc>A logical layer that allows the main SVG canvas to be zoomed and panned</svg:desc>
+            <!-- Template explanation
+            Line 0) Create an instance of the onos-gridsvg component with the name gridFull
+                    All Inputs to that component will use default values
+                    This is the grey grid of 1000x1000 with origin at top left
+                    Only show it if the grid prefs value is 1 or 3
+            -->
+            <svg:g #gridFull *ngIf="prefsState.grid == 1 || prefsState.grid == 3" onos-gridsvg>
+            </svg:g>
+            <!-- Template explanation
+            Line 0) Create another instance of the onos-gridsvg component with the name geoGrid
+                    This is the blue geo grid of -180 to +180 longitude and -75
+                    to +75 latitude with with origin in the centre
+                    Only show it if the grid prefs value is 2 or 3
+            Line 1) Set the Inputs for the longitude extents
+            Line 2) Set the Inputs for the latitude extents and set the minor line
+                    spacing at 15 degrees long and lat
+            Line 3) Invert the vertical axis - positive in the y direction is up
+                    Set the vertical extents of this grid to 1000 units high in SVG terms
+                    Set the aspect ratio to 5/6 - this is because it is an eqi-
+                    rectangular projection and looks stretched out horizontally
+                    otherwise. This balances the distortion of equi-rect between
+                    equator and the poles
+            Line 4) Set the color of the grid to light blue
+            -->
+            <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>
+            <!-- Template explanation -
+            line 0) Add in the Background Svg Component (if showBackground is true - toggled
+                by toolbar and by keyboard shortcut 'B'
+            -->
+            <svg:g *ngIf="prefsState.bg"
+                   onos-backgroundsvg [map]="mapIdState" (zoomlevel)="mapExtentsZoom($event)">
+                <svg:desc>The Background SVG component - contains maps</svg:desc>
+            </svg:g>
+            <!-- Template explanation -
+            line 0) Add in the layer of the Force Svg Component.
+                This is node and line graph
+                whose contents are supplied through the Topology Service, and whose positions
+                are driven by the d3.force engine
+            line 6) If any item is selected on it, pass to the details view and deselect all others.
+            -->
+            <svg:g #force onos-forcesvg
+                   [deviceLabelToggle]="prefsState.dlbls"
+                   [hostLabelToggle]="prefsState.hlbls"
+                   [showHosts]="prefsState.hosts"
+                   [showAlarms]="prefsState.alarms"
+                   [highlightPorts]="prefsState.porthl"
+                   [scale]="window.innerHeight / (window.innerWidth * zoomDirective?.zoomCached.sc)"
+                   (selectedNodeEvent)="nodeSelected($event)">
+                <svg:desc>The Force SVG component - contains all the devices, hosts and links</svg:desc>
+            </svg:g>
+        </svg:g>
+    </svg:svg>
+</div>
+
+<div id="breadcrumbs"></div>
diff --git a/web/gui2-topo-lib/lib/topology/topology.component.spec.ts b/web/gui2-topo-lib/lib/topology/topology.component.spec.ts
new file mode 100644
index 0000000..0ef9c5b
--- /dev/null
+++ b/web/gui2-topo-lib/lib/topology/topology.component.spec.ts
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import { of } from 'rxjs';
+import { HttpClient } from '@angular/common/http';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import * as d3 from 'd3';
+import { TopologyComponent } from './topology.component';
+import {
+    Instance,
+    InstanceComponent
+} from '../panel/instance/instance.component';
+import { SummaryComponent } from '../panel/summary/summary.component';
+import { ToolbarComponent } from '../panel/toolbar/toolbar.component';
+import { DetailsComponent } from '../panel/details/details.component';
+import {Intent, TopologyService} from '../topology.service';
+
+import {
+    FlashComponent,
+    QuickhelpComponent,
+    FnService,
+    LogService,
+    IconService, IconComponent, PrefsService, KeysService, LionService, ZoomableDirective
+} from '../../../gui2-fw-lib/public_api';
+import {RouterTestingModule} from '@angular/router/testing';
+import {TrafficService} from '../traffic.service';
+import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component';
+import {DraggableDirective} from '../layer/forcesvg/draggable/draggable.directive';
+import {MapSelectorComponent} from '../panel/mapselector/mapselector.component';
+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';
+import {LinkSvgComponent} from '../layer/forcesvg/visuals/linksvg/linksvg.component';
+import {DeviceNodeSvgComponent} from '../layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component';
+import {SubRegionNodeSvgComponent} from '../layer/forcesvg/visuals/subregionnodesvg/subregionnodesvg.component';
+import {HostNodeSvgComponent} from '../layer/forcesvg/visuals/hostnodesvg/hostnodesvg.component';
+import {LayoutService} from '../layout.service';
+import {BadgeSvgComponent} from '../layer/forcesvg/visuals/badgesvg/badgesvg.component';
+
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+class MockHttpClient {}
+
+class MockTopologyService {
+    init(instance: InstanceComponent) {
+        instance.onosInstances = [
+            <Instance>{
+                'id': 'inst1',
+                'ip': '127.0.0.1',
+                'reachable': true,
+                'online': true,
+                'ready': true,
+                'switches': 4,
+                'uiAttached': true
+            },
+            <Instance>{
+                'id': 'inst1',
+                'ip': '127.0.0.2',
+                'reachable': true,
+                'online': true,
+                'ready': true,
+                'switches': 3,
+                'uiAttached': false
+            }
+        ];
+    }
+    destroy() {}
+    setSelectedIntent(selectedIntent: Intent): void {}
+    selectRelatedIntent(ids: string[]): void {}
+    cancelHighlights(): void {}
+}
+
+class MockIconService {
+    loadIconDef() { }
+}
+
+class MockKeysService {
+    quickHelpShown: boolean = true;
+
+    keyHandler: {
+        viewKeys: any[],
+        globalKeys: any[]
+    };
+
+    mockViewKeys: Object[];
+    constructor() {
+        this.mockViewKeys = [];
+        this.keyHandler = {
+            viewKeys: this.mockViewKeys,
+            globalKeys: this.mockViewKeys
+        };
+    }
+
+    keyBindings(x) {
+        return {};
+    }
+
+    gestureNotes() {
+        return {};
+    }
+}
+
+class MockTrafficService {
+    init(force: ForceSvgComponent) {}
+    destroy() {}
+}
+
+class MockLayoutService {}
+
+class MockPrefsService {
+    listeners: ((data) => void)[] = [];
+
+    getPrefs() {
+        return { 'topo2_prefs': ''};
+    }
+
+    addListener(listener: (data) => void): void {
+        this.listeners.push(listener);
+    }
+
+    removeListener(listener: (data) => void) {
+        this.listeners = this.listeners.filter((obj) => obj !== listener);
+    }
+
+    setPrefs(name: string, obj: Object) {
+
+    }
+
+}
+
+/**
+ * ONOS GUI -- Topology View -- Unit Tests
+ */
+// Skipping temporarily
+describe('TopologyComponent', () => {
+    let fs: FnService;
+    let ar: MockActivatedRoute;
+    let windowMock: Window;
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: TopologyComponent;
+    let fixture: ComponentFixture<TopologyComponent>;
+
+    const bundleObj = {
+        'core.fw.QuickHelp': {
+            test: 'test1',
+            tt_help: 'Help!'
+        }
+    };
+    const mockLion = (key) =>  {
+        return bundleObj[key] || '%' + key + '%';
+    };
+
+    beforeEach(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+        windowMock = <any>{
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+        fs = new FnService(ar, logSpy, windowMock);
+
+        TestBed.configureTestingModule({
+            imports: [
+                BrowserAnimationsModule,
+                RouterTestingModule,
+                FormsModule,
+                ReactiveFormsModule
+            ],
+            declarations: [
+                TopologyComponent,
+                InstanceComponent,
+                SummaryComponent,
+                ToolbarComponent,
+                DetailsComponent,
+                FlashComponent,
+                IconComponent,
+                QuickhelpComponent,
+                ForceSvgComponent,
+                LinkSvgComponent,
+                DeviceNodeSvgComponent,
+                HostNodeSvgComponent,
+                DraggableDirective,
+                ZoomableDirective,
+                SubRegionNodeSvgComponent,
+                MapSelectorComponent,
+                BackgroundSvgComponent,
+                MapSvgComponent,
+                GridsvgComponent,
+                BadgeSvgComponent
+            ],
+            providers: [
+                { provide: FnService, useValue: fs },
+                { provide: LogService, useValue: logSpy },
+                { provide: 'Window', useValue: windowMock },
+                { provide: HttpClient, useClass: MockHttpClient },
+                { provide: TopologyService, useClass: MockTopologyService },
+                { provide: TrafficService, useClass: MockTrafficService },
+                { provide: LayoutService, useClass: MockLayoutService },
+                { provide: IconService, useClass: MockIconService },
+                { provide: PrefsService, useClass: MockPrefsService },
+                { provide: KeysService, useClass: MockKeysService },
+                { provide: LionService, useFactory: (() => {
+                        return {
+                            bundle: ((bundleId) => mockLion),
+                            ubercache: new Array(),
+                            loadCbs: new Map<string, () => void>([])
+                        };
+                    })
+                },
+            ]
+        }).compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+    });
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(TopologyComponent);
+        component = fixture.componentInstance;
+
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/topology/topology.component.ts b/web/gui2-topo-lib/lib/topology/topology.component.ts
new file mode 100644
index 0000000..601cfb1
--- /dev/null
+++ b/web/gui2-topo-lib/lib/topology/topology.component.ts
@@ -0,0 +1,805 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+    AfterViewInit,
+    Component, HostListener, Inject, Input,
+    OnDestroy,
+    OnInit, SimpleChange,
+    ViewChild
+} from '@angular/core';
+import * as d3 from 'd3';
+import {
+    FnService,
+    IconService,
+    KeysService,
+    KeysToken,
+    LionService,
+    LogService,
+    PrefsService,
+    SvgUtilService,
+    TopoZoomPrefs,
+    WebSocketService,
+    ZoomUtils
+} from '../../../gui2-fw-lib/public_api';
+import {InstanceComponent} from '../panel/instance/instance.component';
+import {DetailsComponent} from '../panel/details/details.component';
+import {BackgroundSvgComponent} from '../layer/backgroundsvg/backgroundsvg.component';
+import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component';
+import {Intent, TopologyService} from '../topology.service';
+import {
+    GridDisplayToggle,
+    HostLabelToggle,
+    LabelToggle,
+    UiElement
+} from '../layer/forcesvg/models';
+import {
+    ALL_TRAFFIC,
+    BKGRND_SELECT,
+    BKGRND_TOGGLE,
+    CANCEL_TRAFFIC,
+    CYCLEGRIDDISPLAY_BTN,
+    CYCLEHOSTLABEL_BTN,
+    CYCLELABELS_BTN,
+    DETAILS_TOGGLE,
+    EQMASTER_BTN,
+    HOSTS_TOGGLE,
+    INSTANCE_TOGGLE,
+    LAYOUT_ACCESS_BTN,
+    LAYOUT_DEFAULT_BTN,
+    OFFLINE_TOGGLE,
+    PORTS_TOGGLE,
+    QUICKHELP_BTN,
+    RESETZOOM_BTN,
+    SUMMARY_TOGGLE,
+    ALARMS_TOGGLE
+} from '../panel/toolbar/toolbar.component';
+import {TrafficService, TrafficType} from '../traffic.service';
+import {ZoomableDirective} from '../../../gui2-fw-lib/public_api';
+import {MapObject} from '../layer/maputils';
+import {LayoutService, LayoutType} from '../layout.service';
+import {SelectedEvent} from '../layer/forcesvg/visuals/nodevisual';
+import {ActivatedRoute} from '@angular/router';
+
+const TOPO2_PREFS = 'topo2_prefs';
+const TOPO_MAPID_PREFS = 'topo_mapid';
+
+const PREF_BG = 'bg';
+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';
+const PREF_PORTHL = 'porthl';
+const PREF_SUMMARY = 'summary';
+const PREF_TOOLBAR = 'toolbar';
+const PREF_PINNED = 'pinned';
+const PREF_TRAFFIC = 'traffic';
+const PREF_ALARMS = 'alarms';
+
+const BACKGROUND_ELEMENTS = [
+    'svg topo2',
+    'path bgmap'
+];
+
+/**
+ * Model of the topo2_prefs object - this is a subset of the overall Prefs returned
+ * by the server
+ */
+export interface Topo2Prefs {
+    bg: number;
+    detail: number;
+    dlbls: number;
+    hlbls: number;
+    hosts: number;
+    insts: number;
+    offdev: number;
+    porthl: number;
+    spr: number;
+    ovid: string;
+    summary: number;
+    toolbar: number;
+    grid: number;
+    pinned: number;
+    traffic: number;
+    alarms: number;
+}
+
+/**
+ * ONOS GUI Topology View
+ *
+ * This Topology View component is the top level component in a hierarchy that
+ * comprises the whole Topology View
+ *
+ * There are three main parts (panels, graphical and breadcrumbs)
+ * The panel hierarchy
+ * |-- Instances Panel (shows ONOS instances)
+ * |-- Summary Panel (summary of ONOS)
+ * |-- Toolbar Panel (the toolbar)
+ * |-- Details Panel (when a node is selected in the Force graphical view (see below))
+ *
+ * The graphical hierarchy contains
+ * Topology (this)
+ *  |-- No Devices Connected (only of there are no nodes to show)
+ *  |-- Zoom Layer (everything beneath this can be zoomed and panned)
+ *      |-- Background (container for any backgrounds - can be toggled on and off)
+ *          |-- Map
+ *      |-- Forces (all of the nodes and links laid out by a d3.force simulation)
+ *
+ * The breadcrumbs
+ * |-- Breadcrumb (in region view a way of navigating back up through regions)
+ */
+@Component({
+  selector: 'onos-topology',
+  templateUrl: './topology.component.html',
+  styleUrls: ['./topology.component.css']
+})
+export class TopologyComponent implements OnInit, OnDestroy, AfterViewInit {
+    @Input() bannerHeight: number = 48;
+    // These are references to the components inserted in the template
+    @ViewChild(InstanceComponent, {static: true}) instance: InstanceComponent;
+    @ViewChild(DetailsComponent, {static: true}) details: DetailsComponent;
+    @ViewChild(BackgroundSvgComponent, {static: true}) background: BackgroundSvgComponent;
+    @ViewChild(ForceSvgComponent, {static: true}) force: ForceSvgComponent;
+    @ViewChild(ZoomableDirective, {static: true}) zoomDirective: ZoomableDirective;
+
+    flashMsg: string = '';
+    // These are used as defaults if nothing is set on the server
+    prefsState: Topo2Prefs = <Topo2Prefs>{
+        bg: 0,
+        detail: 1,
+        dlbls: 0,
+        hlbls: 2,
+        hosts: 0,
+        insts: 1,
+        offdev: 1,
+        ovid: 'traffic', // default to traffic overlay
+        porthl: 1,
+        spr: 0,
+        summary: 1,
+        toolbar: 0,
+        grid: 0,
+        pinned: 0,
+        traffic: 2, // default to PORTSTATSPKTSEC, as it will iterate over to 0 on init
+        alarms: 1,
+    };
+
+    mapIdState: MapObject = <MapObject>{
+        id: undefined,
+        scale: 1.0
+    };
+    mapSelShown: boolean = false;
+    lionFn; // Function
+
+    gridShown: boolean = true;
+    geoGridShown: boolean = true;
+
+    constructor(
+        protected log: LogService,
+        protected fs: FnService,
+        protected ks: KeysService,
+        protected sus: SvgUtilService,
+        protected ps: PrefsService,
+        protected wss: WebSocketService,
+        protected ts: TopologyService,
+        protected trs: TrafficService,
+        protected is: IconService,
+        private lion: LionService,
+        private layout: LayoutService,
+        protected ar: ActivatedRoute,
+        @Inject('Window') public window: any,
+    ) {
+        if (this.lion.ubercache.length === 0) {
+            this.lionFn = this.dummyLion;
+            this.lion.loadCbs.set('topo-toolbar', () => this.doLion());
+        } else {
+            this.doLion();
+        }
+
+        this.log.warn('Constructor', this.zoomDirective);
+
+        this.is.loadIconDef('active');
+        this.is.loadIconDef('bgpSpeaker');
+        this.is.loadIconDef('bird');
+        this.is.loadIconDef('deviceTable');
+        this.is.loadIconDef('fiber_switch');
+        this.is.loadIconDef('flowTable');
+        this.is.loadIconDef('groupTable');
+        this.is.loadIconDef('m_allTraffic');
+        this.is.loadIconDef('m_cycleLabels');
+        this.is.loadIconDef('m_cycleGridDisplay');
+        this.is.loadIconDef('m_disjointPaths');
+        this.is.loadIconDef('m_details');
+        this.is.loadIconDef('m_endstation');
+        this.is.loadIconDef('m_eqMaster');
+        this.is.loadIconDef('m_fiberSwitch');
+        this.is.loadIconDef('m_firewall');
+        this.is.loadIconDef('m_map');
+        this.is.loadIconDef('m_microwave');
+        this.is.loadIconDef('m_ols');
+        this.is.loadIconDef('m_otn');
+        this.is.loadIconDef('m_ports');
+        this.is.loadIconDef('m_resetZoom');
+        this.is.loadIconDef('m_roadm');
+        this.is.loadIconDef('m_roadm_otn');
+        this.is.loadIconDef('m_router');
+        this.is.loadIconDef('m_selectMap');
+        this.is.loadIconDef('m_summary');
+        this.is.loadIconDef('m_switch');
+        this.is.loadIconDef('m_terminal_device');
+        this.is.loadIconDef('m_uiAttached');
+        this.is.loadIconDef('m_unknown');
+        this.is.loadIconDef('meterTable');
+        this.is.loadIconDef('microwave');
+        this.is.loadIconDef('otn');
+        this.is.loadIconDef('portTable');
+        this.is.loadIconDef('roadm_otn');
+        this.is.loadIconDef('triangleUp');
+        this.is.loadIconDef('uiAttached');
+    }
+
+    /**
+     * Static functions must come before member variables
+     * @param index Corresponds to LabelToggle.Enum index
+     */
+    private static deviceLabelFlashMessage(index: number): string {
+        switch (index) {
+            case 0: return 'fl_device_labels_hide';
+            case 1: return 'fl_device_labels_show_friendly';
+            case 2: return 'fl_device_labels_show_id';
+        }
+    }
+
+    private static hostLabelFlashMessage(index: number): string {
+        switch (index) {
+            case 0: return 'fl_host_labels_hide';
+            case 1: return 'fl_host_labels_show_friendly';
+            case 2: return 'fl_host_labels_show_ip';
+            case 3: return 'fl_host_labels_show_mac';
+        }
+    }
+
+    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';
+        }
+    }
+
+    private static trafficTypeFlashMessage(index: number): string {
+        switch (index) {
+            case 0: return 'tr_fl_fstats_bytes';
+            case 1: return 'tr_fl_pstats_bits';
+            case 2: return 'tr_fl_pstats_pkts';
+        }
+    }
+
+    /**
+     * Pass the list of Key Commands to the KeyService, and initialize the Topology
+     * Service - which communicates with through the WebSocket to the ONOS server
+     * to get the nodes and links.
+     */
+    ngOnInit() {
+        this.bindCommands();
+
+        // The components from the template are handed over to TopologyService here
+        // so that WebSocket responses can be passed back in to them
+        // The handling of the WebSocket call is delegated out to the Topology
+        // Service just to compartmentalize things a bit
+        this.ts.init(this.instance, this.background, this.force);
+
+        // For the 2.1 release to not listen to updates of prefs as they are
+        // only the echo of what we have sent down and the event mechanism
+        // does not discern between users. Can get confused if multiple windows open
+        // this.ps.addListener((data) => this.prefsUpdateHandler(data));
+
+        this.prefsState = this.ps.getPrefs(TOPO2_PREFS, this.prefsState);
+        this.mapIdState = this.ps.getPrefs(TOPO_MAPID_PREFS, this.mapIdState);
+        this.trs.init(this.force);
+
+        // Scale the window initially - then after resize
+        const zoomMapExtents = ZoomUtils.zoomToWindowSize(
+            this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
+        this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
+
+        // TODO find out why the following is never printed
+        this.log.debug('TopologyComponent initialized,',
+            this.bannerHeight, this.window.innerWidth, this.window.innerHeight,
+            zoomMapExtents);
+    }
+
+    ngAfterViewInit(): void {
+        this.ar.queryParams.subscribe(params => {
+            const intentId = params['intentId'];
+            const intentType = params['intentType'];
+            const appId = params['appId'];
+            const appName = params['appName'];
+
+            if (intentId && intentType && appId) {
+                const selectedIntent = <Intent>{
+                    key: intentId,
+                    type: intentType,
+                    appId: appId,
+                    appName: appName,
+                };
+                this.ts.setSelectedIntent(selectedIntent);
+
+                this.log.warn('TopologyComponent init with Intent: ', selectedIntent, params);
+            }
+        });
+    }
+
+    /**
+     * Callback function that's called whenever new Prefs are received from WebSocket
+     *
+     * Note: At present the backend server does not filter updated by logged in user,
+     * so you might get updates pertaining to a different user
+     */
+    prefsUpdateHandler(data: any): void {
+        // Extract the TOPO2 prefs from it
+        if (data[TOPO2_PREFS]) {
+            this.prefsState = data[TOPO2_PREFS];
+        }
+        this.log.debug('Updated topo2 prefs', this.prefsState, this.mapIdState);
+    }
+
+    /**
+     * When this component is being stopped, disconnect the TopologyService from
+     * the WebSocket
+     */
+    ngOnDestroy() {
+        this.ts.destroy();
+        this.ps.removeListener((data) => this.prefsUpdateHandler(data));
+        this.trs.destroy();
+        this.log.debug('Topology component destroyed');
+    }
+
+    @HostListener('window:resize', ['$event'])
+    onResize(event) {
+        const zoomMapExtents = ZoomUtils.zoomToWindowSize(
+                this.bannerHeight, event.target.innerWidth, event.target.innerHeight);
+        this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
+        this.log.debug('Topology window resize',
+            event.target.innerWidth, event.target.innerHeight, this.bannerHeight, zoomMapExtents);
+    }
+
+    /**
+     * When ever a toolbar button is clicked, an event is sent up from toolbar
+     * component which is caught and passed on to here.
+     * @param name The name of the button that was clicked
+     */
+    toolbarButtonClicked(name: string) {
+        switch (name) {
+            case INSTANCE_TOGGLE:
+                this.toggleInstancePanel();
+                break;
+            case SUMMARY_TOGGLE:
+                this.toggleSummary();
+                break;
+            case DETAILS_TOGGLE:
+                this.toggleDetails();
+                break;
+            case HOSTS_TOGGLE:
+                this.toggleHosts();
+                break;
+            case OFFLINE_TOGGLE:
+                this.toggleOfflineDevices();
+                break;
+            case PORTS_TOGGLE:
+                this.togglePorts();
+                break;
+            case BKGRND_TOGGLE:
+                this.toggleBackground();
+                break;
+            case BKGRND_SELECT:
+                this.mapSelShown = !this.mapSelShown;
+                break;
+            case CYCLELABELS_BTN:
+                this.cycleDeviceLabels();
+                break;
+            case CYCLEHOSTLABEL_BTN:
+                this.cycleHostLabels();
+                break;
+            case CYCLEGRIDDISPLAY_BTN:
+                this.cycleGridDisplay();
+                break;
+            case RESETZOOM_BTN:
+                this.resetZoom();
+                break;
+            case EQMASTER_BTN:
+                this.equalizeMasters();
+                break;
+            case CANCEL_TRAFFIC:
+                this.cancelTraffic();
+                break;
+            case ALL_TRAFFIC:
+                this.cycleTrafficTypeDisplay();
+                break;
+            case QUICKHELP_BTN:
+                this.ks.quickHelpShown = true;
+                break;
+            case LAYOUT_DEFAULT_BTN:
+                this.layout.changeLayout(LayoutType.LAYOUT_DEFAULT);
+                break;
+            case LAYOUT_ACCESS_BTN:
+                this.layout.changeLayout(LayoutType.LAYOUT_ACCESS);
+                break;
+            case ALARMS_TOGGLE:
+                this.toggleAlarms();
+                break;
+            default:
+                this.log.warn('Unhandled Toolbar action', name);
+        }
+    }
+
+    /**
+     * The list of key strokes that will be active in the Topology View.
+     *
+     * This action map is passed to the KeyService through the bindCommands()
+     * when this component is being initialized
+     *
+     * TODO - Replace this doggy doo doo (copied over from GUI-1)
+     * with something more structured
+     */
+    actionMap() {
+        return {
+            A: [() => {this.cycleTrafficTypeDisplay(); }, this.lionFn('tr_btn_monitor_all')],
+            B: [(token) => {this.toggleBackground(token); }, this.lionFn('tbtt_tog_map')],
+            D: [(token) => {this.toggleDetails(token); }, this.lionFn('tbtt_tog_use_detail')],
+            E: [() => {this.equalizeMasters(); }, this.lionFn('tbtt_eq_master')],
+            H: [() => {this.toggleHosts(); }, this.lionFn('tbtt_tog_host')],
+            I: [(token) => {this.toggleInstancePanel(token); }, this.lionFn('tbtt_tog_instances')],
+            G: [() => {this.mapSelShown = !this.mapSelShown; }, this.lionFn('tbtt_sel_map')],
+            L: [() => {this.cycleDeviceLabels(); }, this.lionFn('tbtt_cyc_dev_labs')],
+            M: [() => {this.toggleOfflineDevices(); }, this.lionFn('tbtt_tog_offline')],
+            O: [() => {this.toggleSummary(); }, this.lionFn('tbtt_tog_summary')],
+            P: [(token) => {this.togglePorts(token); }, this.lionFn('tbtt_tog_porthi')],
+            Q: [() => {this.cycleGridDisplay(); }, this.lionFn('tbtt_cyc_grid_display')],
+            R: [() => {this.resetZoom(); }, this.lionFn('tbtt_reset_zoom')],
+            U: [() => {this.unpinOrFreezeNodes(); }, this.lionFn('tbtt_unpin_node')],
+            X: [() => {this.resetNodeLocation(); }, this.lionFn('tbtt_reset_loc')],
+            dot: [() => {this.toggleToolbar(); }, this.lionFn('tbtt_tog_toolbar')],
+            0: [() => {this.cancelTraffic(); }, this.lionFn('tr_btn_cancel_monitoring')],
+            'shift-L': [() => {this.cycleHostLabels(); }, this.lionFn('tbtt_cyc_host_labs')],
+
+            // -- instance color palette debug
+            9: () => {
+                this.sus.cat7().testCard(d3.select('svg#topo2'));
+            },
+
+            esc: [() => {this.handleEscape(); }, this.lionFn('qh_hint_esc')],
+
+            // TODO update after adding in Background Service
+            // topology overlay selections
+            // F1: function () { t2tbs.fnKey(0); },
+            // F2: function () { t2tbs.fnKey(1); },
+            // F3: function () { t2tbs.fnKey(2); },
+            // F4: function () { t2tbs.fnKey(3); },
+            // F5: function () { t2tbs.fnKey(4); },
+            //
+            // _keyListener: t2tbs.keyListener.bind(t2tbs),
+
+            _helpFormat: [
+                ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B'],
+                ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', 'E', 'dot'],
+                [], // this column reserved for overlay actions
+            ],
+        };
+    }
+
+
+    bindCommands(additional?: any) {
+
+        const am = this.actionMap();
+        this.ks.keyBindings(am);
+
+        this.ks.gestureNotes([
+            ['click', 'Select the item and show details'],
+            ['shift-click', 'Toggle selection state'],
+            ['drag', 'Reposition (and pin) device / host'],
+            ['cmd-scroll', 'Zoom in / out'],
+            ['cmd-drag', 'Pan'],
+        ]);
+    }
+
+    handleEscape() {
+
+        if (false) {
+            // TODO: Cancel show mastership
+            // TODO: Cancel Active overlay
+            // TODO: Reinstate with components
+        } else {
+            this.nodeSelected(undefined);
+            this.log.debug('Handling escape');
+            // } else if (t2rs.deselectAllNodes()) {
+            //     // else if we have node selections, deselect them all
+            //     // (work already done)
+            // } else if (t2rs.deselectLink()) {
+            //     // else if we have a link selection, deselect it
+            //     // (work already done)
+            // } else if (t2is.isVisible()) {
+            //     // If the instance panel is visible, close it
+            //     t2is.toggle();
+            // } else if (t2sp.isVisible()) {
+            //     // If the summary panel is visible, close it
+            //     t2sp.toggle();
+        }
+    }
+
+    /**
+     * Updates the cache of preferences locally and onwards to the PrefsService
+     * @param what The attribute of the local topo2-prefs cache to update
+     * @param b the value to update it with
+     */
+    updatePrefsState(what: string, b: number) {
+        this.prefsState[what] = b;
+        this.ps.setPrefs(TOPO2_PREFS, this.prefsState);
+    }
+
+    /**
+     * When the button is clicked on the toolbar or the L key is pressed
+     * 1) cycle through options
+     * 2) flash up a message
+     * 3a) Update the local prefs cache
+     * 3b) And passes on to the global prefs service which sends back to the server
+     * 3c) It also has a knock on effect of passing it on to ForceSvgComponent
+     *      because prefsState.dlbls is given as an input to it
+     * 3d) This will in turn pass it down to the DeviceSvgComponent which
+     *       displays the label
+     */
+    protected cycleDeviceLabels() {
+        const old: LabelToggle.Enum = this.prefsState.dlbls;
+        const next = LabelToggle.next(old);
+        this.flashMsg = this.lionFn(TopologyComponent.deviceLabelFlashMessage(next));
+        this.updatePrefsState(PREF_DLBLS, next);
+        this.log.debug('Cycling device labels', old, next);
+    }
+
+    protected cycleHostLabels() {
+        const old: HostLabelToggle.Enum = this.prefsState.hlbls;
+        const next = HostLabelToggle.next(old);
+        this.flashMsg = this.lionFn(TopologyComponent.hostLabelFlashMessage(next));
+        this.updatePrefsState(PREF_HLBLS, next);
+        this.log.debug('Cycling host labels', old, next);
+    }
+
+    protected cycleGridDisplay() {
+        const old: GridDisplayToggle.Enum = 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);
+    }
+
+    protected cycleTrafficTypeDisplay() {
+        const old: TrafficType.Enum = this.prefsState.traffic; // by number
+        const next = TrafficType.next(old);
+        this.flashMsg = this.lionFn(TopologyComponent.trafficTypeFlashMessage(next));
+        this.updatePrefsState(PREF_TRAFFIC, next);
+        this.trs.requestTraffic(next);
+        this.log.debug('Cycling traffic 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)
+     * 2) Flash up a message on screen
+     * 3b) And passes on to the global prefs service which sends back to the server
+     * 3c) It also has a knock on effect of passing it on to ToolbarComponent
+     *      because prefsState.bg is given as an input to it
+     * @param token not currently used
+     */
+    protected toggleBackground(token?: KeysToken) {
+        const bg: boolean = !Boolean(this.prefsState.bg);
+        this.flashMsg = this.lionFn(bg ? 'show' : 'hide') +
+            ' ' + this.lionFn('fl_background_map');
+        this.updatePrefsState(PREF_BG, bg ? 1 : 0);
+        this.log.debug('Toggling background', token, bg ? 'shown' : 'hidden');
+    }
+
+    protected toggleDetails(token?: KeysToken) {
+        const on: boolean = !Boolean(this.prefsState.detail);
+        this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
+            ' ' + this.lionFn('fl_panel_details');
+        this.updatePrefsState(PREF_DETAIL, on ? 1 : 0);
+        this.log.debug('Toggling details', token);
+    }
+
+    protected toggleInstancePanel(token?: KeysToken) {
+        const on: boolean = !Boolean(this.prefsState.insts);
+        this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
+            ' ' + this.lionFn('fl_panel_instances');
+        this.updatePrefsState(PREF_INSTS, on ? 1 : 0);
+        this.log.debug('Toggling instances', token, on);
+    }
+
+    protected toggleSummary() {
+        const on: boolean = !Boolean(this.prefsState.summary);
+        this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
+            ' ' + this.lionFn('fl_panel_summary');
+        this.updatePrefsState(PREF_SUMMARY, on ? 1 : 0);
+    }
+
+    protected togglePorts(token?: KeysToken) {
+        const current: boolean = !Boolean(this.prefsState.porthl);
+        this.flashMsg = this.lionFn(current ? 'enable' : 'disable') +
+            ' ' + this.lionFn('fl_port_highlighting');
+        this.updatePrefsState(PREF_PORTHL, current ? 1 : 0);
+        this.log.debug(current ? 'Enable' : 'Disable', 'port highlighting');
+    }
+
+    protected toggleToolbar() {
+        const on: boolean = !Boolean(this.prefsState.toolbar);
+        this.updatePrefsState(PREF_TOOLBAR, on ? 1 : 0);
+        this.log.debug('toggling toolbar', on ? 'shown' : 'hidden');
+    }
+
+    protected toggleHosts() {
+        const current: boolean = !Boolean(this.prefsState.hosts);
+        this.flashMsg = this.lionFn('hosts') + ' ' +
+                        this.lionFn(current ? 'visible' : 'hidden');
+        this.updatePrefsState(PREF_HOSTS, current ? 1 : 0);
+        this.log.debug('toggling hosts: ', this.prefsState.hosts ? 'Show' : 'Hide');
+    }
+
+    protected toggleOfflineDevices() {
+        const on: boolean = !Boolean(this.prefsState.offdev);
+        this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
+            ' ' + this.lionFn('fl_offline_devices');
+        this.updatePrefsState(PREF_OFFDEV, on ? 1 : 0);
+        this.log.debug('toggling offline devices', this.prefsState.offdev);
+    }
+
+
+    protected toggleAlarms() {
+        const on: boolean = !Boolean(this.prefsState.alarms);
+        this.flashMsg = this.lionFn(on ? 'show' : 'hide') + ' Alarms';
+        this.updatePrefsState(PREF_ALARMS, on ? 1 : 0);
+        this.log.debug('Alarms toggled', on);
+    }
+
+    protected resetZoom() {
+        const zoomMapExtents = ZoomUtils.zoomToWindowSize(
+            this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
+        this.zoomDirective.changeZoomLevel(zoomMapExtents, false);
+        this.flashMsg = this.lionFn('fl_pan_zoom_reset');
+    }
+
+    protected equalizeMasters() {
+        this.wss.sendEvent('equalizeMasters', {});
+        this.flashMsg = this.lionFn('fl_eq_masters');
+        this.log.debug('equalizing masters');
+    }
+
+    /**
+     * If any nodes with fixed positions had been dragged out of place
+     * then put back where they belong
+     * If there are some devices selected reset only these
+     */
+    protected resetNodeLocation() {
+        const numNodes = this.force.resetNodeLocations();
+        this.flashMsg = this.lionFn('fl_reset_node_locations') +
+            '(' + String(numNodes) + ')';
+        this.log.debug('resetting ', numNodes, 'node(s) location');
+    }
+
+    /**
+     * Toggle floating nodes between pinned and frozen
+     * If there are floating nodes selected toggle only these
+     */
+    protected unpinOrFreezeNodes() {
+        const pinned: boolean = !Boolean(this.prefsState.pinned);
+        const numNodes = this.force.unpinOrFreezeNodes(pinned);
+        this.flashMsg = this.lionFn(pinned ?
+            'fl_pinned_floating_nodes' : 'fl_unpinned_floating_nodes') +
+            '(' + String(numNodes) + ')';
+        this.updatePrefsState(PREF_PINNED, pinned ? 1 : 0);
+        this.log.debug('Toggling pinning for floating ', numNodes, 'nodes', pinned);
+    }
+
+    /**
+     * Check to see if this is needed anymore
+     * @param what - a key stroke
+     */
+    protected notValid(what) {
+        this.log.warn('topo.js getActionEntry(): Not a valid ' + what);
+    }
+
+    /**
+     * Check to see if this is needed anymore
+     * @param key - a key stroke
+     */
+    getActionEntry(key) {
+        let entry;
+
+        if (!key) {
+            this.notValid('key');
+            return null;
+        }
+
+        entry = this.actionMap()[key];
+
+        if (!entry) {
+            this.notValid('actionMap (' + key + ') entry');
+            return null;
+        }
+        return this.fs.isA(entry) || [entry, ''];
+    }
+
+    /**
+     * An event handler that updates the details panel as items are
+     * selected in the forcesvg layer
+     *
+     * @param nodesOrLink the item(s) to display details of
+     */
+    nodeSelected(nodesOrLink: UiElement[]) {
+        this.details.ngOnChanges({'selectedNodes':
+            new SimpleChange(undefined, nodesOrLink, true)});
+    }
+
+    /**
+     * Cancel traffic monitoring
+     */
+    cancelTraffic() {
+        this.flashMsg = this.lionFn('fl_monitoring_canceled');
+        this.trs.cancelTraffic();
+    }
+
+    changeMap(map: MapObject) {
+        this.mapSelShown = false; // Hide the MapSelector component
+        this.mapIdState = map;
+        this.ps.setPrefs(TOPO_MAPID_PREFS, this.mapIdState);
+        this.log.debug('Map has been changed to ', map);
+    }
+
+    mapExtentsZoom(zoomMapExtents: TopoZoomPrefs) {
+        // this.zoomDirective.updateZoomState(zoomPrefs.tx, zoomPrefs.ty, zoomPrefs.sc);
+        this.zoomDirective.changeZoomLevel(zoomMapExtents);
+        this.log.debug('Map zoom prefs updated', zoomMapExtents);
+    }
+
+    backgroundClicked(event: MouseEvent) {
+        const elemTagName = event.target['tagName'] + ' ' + event.target['id'];
+        if (BACKGROUND_ELEMENTS.includes(elemTagName)) {
+            this.force.updateSelected(
+                <SelectedEvent>{
+                    uiElement: undefined,
+                    deselecting: true
+                }
+            );
+        }
+        this.ts.cancelHighlights();
+        this.force.cancelAllLinkHighlightsNow();
+    }
+
+    /**
+     * Read the LION bundle for Toolbar and set up the lionFn
+     */
+    doLion() {
+        this.lionFn = this.lion.bundle('core.view.Topo');
+    }
+
+    /**
+     * A dummy implementation of the lionFn until the response is received and the LION
+     * bundle is received from the WebSocket
+     */
+    dummyLion(key: string): string {
+        return '%' + key + '%';
+    }
+}
diff --git a/web/gui2-topo-lib/lib/traffic.service.spec.ts b/web/gui2-topo-lib/lib/traffic.service.spec.ts
new file mode 100644
index 0000000..49ff94a
--- /dev/null
+++ b/web/gui2-topo-lib/lib/traffic.service.spec.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { TestBed } from '@angular/core/testing';
+
+import { TrafficService } from './traffic.service';
+import {FnService, LogService} from '../../gui2-fw-lib/public_api';
+import {ActivatedRoute, Params} from '@angular/router';
+import {of} from 'rxjs';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+describe('TrafficService', () => {
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let ar: ActivatedRoute;
+    let fs: FnService;
+    let mockWindow: Window;
+
+    beforeEach(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['debug', 'warn', 'info']);
+        ar = new MockActivatedRoute({'debug': 'TestService'});
+        mockWindow = <any>{
+            innerWidth: 400,
+            innerHeight: 200,
+            navigator: {
+                userAgent: 'defaultUA'
+            },
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+        fs = new FnService(ar, logSpy, mockWindow);
+
+        TestBed.configureTestingModule({
+            providers: [TrafficService,
+                { provide: FnService, useValue: fs},
+                { provide: LogService, useValue: logSpy },
+                { provide: ActivatedRoute, useValue: ar },
+                { provide: 'Window', useFactory: (() => mockWindow ) }
+            ]
+        });
+        logServiceSpy = TestBed.get(LogService);
+    });
+
+    it('should be created', () => {
+        const service: TrafficService = TestBed.get(TrafficService);
+        expect(service).toBeTruthy();
+    });
+});
diff --git a/web/gui2-topo-lib/lib/traffic.service.ts b/web/gui2-topo-lib/lib/traffic.service.ts
new file mode 100644
index 0000000..cd23209
--- /dev/null
+++ b/web/gui2-topo-lib/lib/traffic.service.ts
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Injectable } from '@angular/core';
+import {LogService, WebSocketService} from '../../gui2-fw-lib/public_api';
+import {ForceSvgComponent} from './layer/forcesvg/forcesvg.component';
+
+export namespace TrafficType {
+    /**
+     * Toggle state for how the traffic should be displayed
+     */
+    export enum Enum { // Do not add an alias - they need to be number indexed
+        FLOWSTATSBYTES, // 0 flowStatsBytes
+        PORTSTATSBITSEC, // 1 portStatsBitSec
+        PORTSTATSPKTSEC // 2 portStatsPktSec
+    }
+
+    /**
+     * Add the method 'next()' to the TrafficType enum above
+     */
+    export function next(current: Enum) {
+        if (current === Enum.FLOWSTATSBYTES) {
+            return Enum.PORTSTATSBITSEC;
+        } else if (current === Enum.PORTSTATSBITSEC) {
+            return Enum.PORTSTATSPKTSEC;
+        } else if (current === Enum.PORTSTATSPKTSEC) {
+            return Enum.FLOWSTATSBYTES;
+        } else { // e.g. undefined
+            return Enum.PORTSTATSBITSEC;
+        }
+    }
+
+    export function literal(type: Enum) {
+        if (type === Enum.FLOWSTATSBYTES) {
+            return 'flowStatsBytes';
+        } else if (type === Enum.PORTSTATSBITSEC) {
+            return 'portStatsBitSec';
+        } else if (type === Enum.PORTSTATSPKTSEC) {
+            return 'portStatsPktSec';
+        }
+    }
+}
+
+/**
+ * ONOS GUI -- Traffic Service Module.
+ */
+@Injectable()
+export class TrafficService {
+    private handlers: string[] = [];
+    private openListener: any;
+    private trafficType: TrafficType.Enum;
+
+    constructor(
+        protected log: LogService,
+        protected wss: WebSocketService
+    ) {
+        this.log.debug('TrafficService constructed');
+    }
+
+    init(force: ForceSvgComponent) {
+        this.wss.bindHandlers(new Map<string, (data) => void>([
+            ['topo2Highlights', (data) => {
+                  force.handleHighlights(data.devices, data.hosts, data.links, 5000);
+                }
+            ]
+        ]));
+
+        this.handlers.push('topo2Highlights');
+
+        // in case we fail over to a new server,
+        // listen for wsock-open events
+        this.openListener = this.wss.addOpenListener(() => this.wsOpen);
+    }
+
+    destroy() {
+        this.wss.unbindHandlers(this.handlers);
+        this.handlers.pop();
+    }
+
+    wsOpen(host: string, url: string) {
+        this.log.debug('topo2RequestAllTraffic: WSopen - cluster node:', host, 'URL:', url);
+        // tell the server we are ready to receive topo events
+        this.wss.sendEvent('topo2RequestAllTraffic', {
+            trafficType: TrafficType.literal(this.trafficType)
+        });
+    }
+
+    requestTraffic(trafficType: TrafficType.Enum) {
+        // tell the server we are ready to receive topology events
+        this.wss.sendEvent('topo2RequestAllTraffic', {
+            trafficType: TrafficType.literal(trafficType)
+        });
+        this.log.debug('Topo2Traffic: Show', trafficType);
+    }
+
+    cancelTraffic() {
+        this.wss.sendEvent('topo2CancelTraffic', {});
+        this.log.debug('Traffic monitoring canceled');
+    }
+}