More plumbing of grid coordinates vs. geo coordinates.
- Added background reference parameter to layout command
- send correct location data to client for devices, hosts

Change-Id: Ic00bda76f4e4bc8d3e23e07a08f3bc5367ec85a9
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyResource.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyResource.java
index fa2613b..2f813d0 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyResource.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyResource.java
@@ -40,10 +40,23 @@
 @Path("topology")
 public class TopologyResource extends BaseResource {
 
+    private static final String ID = "id";
+    private static final String URI = "uri";
+
+    // length of a MAC defined as a string ... "xx:xx:xx:xx:xx:xx"
+    private static final int MAC_LEN = 17;
+    private static final char SLASH_CHAR = '/';
+
     private static final Logger log = getLogger(TopologyResource.class);
 
     private final ObjectMapper mapper = new ObjectMapper();
 
+    /**
+     * Returns the location data associated with devices and hosts, that is
+     * currently cached in the Meta-UI store.
+     *
+     * @return cached location data for devices and hosts
+     */
     @Path("geoloc")
     @GET
     @Produces("application/json")
@@ -55,10 +68,10 @@
         Map<String, ObjectNode> metaUi = TopologyViewMessageHandler.getMetaUi();
         for (String id : metaUi.keySet()) {
             ObjectNode memento = metaUi.get(id);
-            if (id.length() > 17 && id.charAt(17) == '/') {
-                addGeoData(hosts, "id", id, memento);
+            if (isHostId(id)) {
+                addGeoData(hosts, ID, id, memento);
             } else {
-                addGeoData(devices, "uri", id, memento);
+                addGeoData(devices, URI, id, memento);
             }
         }
 
@@ -67,11 +80,18 @@
         return Response.ok(rootNode.toString()).build();
     }
 
+    private boolean isHostId(String id) {
+        return id.length() > MAC_LEN && id.charAt(MAC_LEN) == SLASH_CHAR;
+    }
+
     private void addGeoData(ArrayNode array, String idField, String id,
                             ObjectNode memento) {
         ObjectNode node = mapper.createObjectNode().put(idField, id);
         ObjectNode annot = mapper.createObjectNode();
         node.set("annotations", annot);
+
+        // TODO: add handling of gridY/gridX if locType is "grid" (not "geo")
+
         try {
             annot.put("latitude", memento.get("lat").asDouble())
                     .put("longitude", memento.get("lng").asDouble());
@@ -81,14 +101,22 @@
         }
     }
 
+    /**
+     * Stores sprite data for retrieval by the UI Topology View.
+     *
+     * @param stream input data stream (typically from an uploaded file).
+     * @return REST response
+     * @throws IOException if there is an issue reading from the stream
+     * @deprecated since Junco (1.9), in favor of client-side defined sprite layers
+     */
     @Path("sprites")
     @POST
     @Consumes("application/json")
+    @Deprecated
     public Response setSprites(InputStream stream) throws IOException {
         JsonNode root = mapper.readTree(stream);
         String name = root.path("defn_name").asText("sprites");
         get(SpriteService.class).put(name, root);
         return Response.ok().build();
     }
-
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
index 0ad0585..bd2574b 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
@@ -365,7 +365,7 @@
                 double lng = Double.parseDouble(slng);
                 double lat = Double.parseDouble(slat);
                 ObjectNode loc = objectNode()
-                        .put("type", "lnglat")
+                        .put("type", "geo")
                         .put("lng", lng)
                         .put("lat", lat);
                 payload.set("location", loc);
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
index 8db72aa..fe59dce 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
@@ -63,6 +63,8 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.AnnotationKeys.GRID_X;
+import static org.onosproject.net.AnnotationKeys.GRID_Y;
 import static org.onosproject.net.AnnotationKeys.LATITUDE;
 import static org.onosproject.net.AnnotationKeys.LONGITUDE;
 import static org.onosproject.ui.model.topo.UiNode.LAYER_DEFAULT;
@@ -196,9 +198,20 @@
                 .put("region", nullIsEmpty(layout.regionId()))
                 .put("regionName", UiRegion.safeName(layout.region()));
         addCrumbs(result, crumbs);
+        addBgRef(result, layout);
         return result;
     }
 
+    private void addBgRef(ObjectNode result, UiTopoLayout layout) {
+        String map = layout.geomap();
+        String spr = layout.sprites();
+        if (map != null) {
+            result.put("bgType", "geo").put("bgId", map);
+        } else if (spr != null) {
+            result.put("bgType", "grid").put("bgId", spr);
+        }
+    }
+
     private void addCrumbs(ObjectNode result, List<UiTopoLayout> crumbs) {
         ArrayNode trail = arrayNode();
         crumbs.forEach(c -> {
@@ -342,7 +355,7 @@
         Device d = device.backingDevice();
 
         addProps(node, d);
-        addGeoLocation(node, d);
+        addGeoGridLocation(node, d);
         addMetaUi(node, device.idAsString());
 
         return node;
@@ -364,25 +377,31 @@
         }
     }
 
-    // FIXME: need to handle geo vs. grid location...
-    private void addGeoLocation(ObjectNode node, Annotated a) {
+    private void addGeoGridLocation(ObjectNode node, Annotated a) {
         List<String> lngLat = getAnnotValues(a, LONGITUDE, LATITUDE);
-        if (lngLat != null) {
-            try {
-                double lng = Double.parseDouble(lngLat.get(0));
-                double lat = Double.parseDouble(lngLat.get(1));
-                ObjectNode loc = objectNode()
-                        .put("type", "lnglat")
-                        .put("lng", lng)
-                        .put("lat", lat);
-                node.set("location", loc);
+        List<String> gridYX = getAnnotValues(a, GRID_Y, GRID_X);
 
-            } catch (NumberFormatException e) {
-                log.warn("Invalid geo data: longitude={}, latitude={}",
-                        lngLat.get(0), lngLat.get(1));
-            }
-        } else {
-            log.debug("No geo lng/lat for {}", a);
+        if (lngLat != null) {
+            attachLocation(node, "geo", "lng", "lat", lngLat);
+        } else if (gridYX != null) {
+            attachLocation(node, "grid", "gridY", "gridX", gridYX);
+        }
+    }
+
+    private void attachLocation(ObjectNode node, String locType,
+                                String keyA, String keyB, List<String> values) {
+        try {
+            double valA = Double.parseDouble(values.get(0));
+            double valB = Double.parseDouble(values.get(1));
+            ObjectNode loc = objectNode()
+                    .put("type", locType)
+                    .put(keyA, valA)
+                    .put(keyB, valB);
+            node.set("location", loc);
+
+        } catch (NumberFormatException e) {
+            log.warn("Invalid {} data: long/Y={}, lat/X={}",
+                    locType, values.get(0), values.get(1));
         }
     }
 
@@ -429,7 +448,8 @@
         Host h = host.backingHost();
 
         addIps(node, h);
-        addGeoLocation(node, h);
+        addProps(node, h);
+        addGeoGridLocation(node, h);
         addMetaUi(node, host.idAsString());
 
         return node;
@@ -466,7 +486,7 @@
                 .put("nHosts", region.hostCount());
 
         Region r = region.backingRegion();
-        addGeoLocation(node, r);
+        addGeoGridLocation(node, r);
         addProps(node, r);
 
         addMetaUi(node, region.idAsString());
diff --git a/web/gui/src/main/webapp/app/view/topo/topoModel.js b/web/gui/src/main/webapp/app/view/topo/topoModel.js
index 0b4fd2c..887635d 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoModel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoModel.js
@@ -115,7 +115,7 @@
         var loc = node.location,
             coord;
 
-        if (loc && loc.type === 'lnglat') {
+        if (loc && loc.type === 'geo') {
             coord = coordFromLngLat(loc);
             node.fixed = true;
             node.px = node.x = coord[0];
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js b/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js
index 6dedcd9..694af14 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js
@@ -93,7 +93,7 @@
         var loc = el.get('location'),
             coord;
 
-        if (loc && loc.type === 'lnglat') {
+        if (loc && loc.type === 'geo') {
 
             if (loc.lat === 0 && loc.lng === 0) {
                 return false;
@@ -106,6 +106,9 @@
 
             return true;
         }
+
+        // TODO: handle case where loc.type === 'grid'
+        //  implying loc.gridX and loc.gridY hold values
     }
 
     function coordFromLngLat(loc) {
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
index 0d1f8a5..e9f63f6 100644
--- a/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
@@ -232,7 +232,7 @@
     it('should position a node by translating lng/lat', function () {
         var node = {
             location: {
-                type: 'lnglat',
+                type: 'geo',
                 lng: 2008,
                 lat: 3009
             }
@@ -319,7 +319,7 @@
             type: 'yowser',
             online: true,
             location: {
-                type: 'lnglat',
+                type: 'geo',
                 lng: 2048,
                 lat: 3096
             }