Working on Bay-Area-Region Demo.
- added latitude / longitude parameters to regions, so the icons can be "placed" on a GEO-map

Change-Id: I5cf939f22597d4658ab603459b2b3059ba0db0c9
diff --git a/cli/src/main/java/org/onosproject/cli/net/RegionAddCommand.java b/cli/src/main/java/org/onosproject/cli/net/RegionAddCommand.java
index f16c695..eff8fb5 100644
--- a/cli/src/main/java/org/onosproject/cli/net/RegionAddCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/RegionAddCommand.java
@@ -23,6 +23,8 @@
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.cluster.NodeId;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicRegionConfig;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionAdminService;
 import org.onosproject.net.region.RegionId;
@@ -37,7 +39,10 @@
         description = "Adds a new region.")
 public class RegionAddCommand extends AbstractShellCommand {
 
-    private static final BiMap<String, Region.Type> REGION_TYPE_MAP = HashBiMap.create();
+    private static final String SLASH = "/";
+
+    private static final BiMap<String, Region.Type> REGION_TYPE_MAP
+            = HashBiMap.create();
 
     static {
         for (Region.Type t : Region.Type.values()) {
@@ -54,11 +59,19 @@
     String name = null;
 
     @Argument(index = 2, name = "type", description = "Region Type (CONTINENT|" +
-            "COUNTRY|METRO|CAMPUS|BUILDING|FLOOR|ROOM|RACK|LOGICAL_GROUP)",
+            "COUNTRY|METRO|CAMPUS|BUILDING|DATA_CENTER|FLOOR|ROOM|RACK|LOGICAL_GROUP)",
             required = true, multiValued = false)
     String type = null;
 
-    @Argument(index = 3, name = "masters", description = "Region Master, a set " +
+    @Argument(index = 3, name = "latitude", description = "Geo latitude",
+            required = true, multiValued = false)
+    Double latitude = null;
+
+    @Argument(index = 4, name = "longitude", description = "Geo longitude",
+            required = true, multiValued = false)
+    Double longitude = null;
+
+    @Argument(index = 5, name = "masters", description = "Region Master, a set " +
             "of nodeIds should be split with '/' delimiter (e.g., 1 2 3 / 4 5 6)",
             required = true, multiValued = true)
     List<String> masterArgs = null;
@@ -71,7 +84,7 @@
         List<Set<NodeId>> masters = Lists.newArrayList();
         Set<NodeId> nodeIds = Sets.newHashSet();
         for (String masterArg : masterArgs) {
-            if (masterArg.equals("/")) {
+            if (masterArg.equals(SLASH)) {
                 masters.add(nodeIds);
                 nodeIds = Sets.newHashSet();
             } else {
@@ -80,6 +93,13 @@
         }
         masters.add(nodeIds);
 
+        NetworkConfigService cfgService = get(NetworkConfigService.class);
+        BasicRegionConfig cfg = cfgService.addConfig(regionId, BasicRegionConfig.class);
+        cfg.name(name)
+                .latitude(latitude)
+                .longitude(longitude)
+                .apply();
+
         service.createRegion(regionId, name, REGION_TYPE_MAP.get(type), masters);
         print("Region successfully added.");
     }
diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/BasicElementConfig.java b/core/api/src/main/java/org/onosproject/net/config/basics/BasicElementConfig.java
index 7c2a5b8..295995f 100644
--- a/core/api/src/main/java/org/onosproject/net/config/basics/BasicElementConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/config/basics/BasicElementConfig.java
@@ -22,20 +22,46 @@
  */
 public abstract class BasicElementConfig<S> extends AllowedEntityConfig<S> {
 
-    protected static final String NAME = "name";
-    protected static final String UI_TYPE = "uiType";
+    /**
+     * Key for friendly name.
+     */
+    public static final String NAME = "name";
 
-    protected static final String LATITUDE = "latitude";
-    protected static final String LONGITUDE = "longitude";
+    /**
+     * Key for UI type (glyph identifier).
+     */
+    public static final String UI_TYPE = "uiType";
 
+    /**
+     * Key for latitude.
+     */
+    public static final String LATITUDE = "latitude";
+
+    /**
+     * Key for longitude.
+     */
+    public static final String LONGITUDE = "longitude";
+
+    /**
+     * Key for rack address.
+     */
     protected static final String RACK_ADDRESS = "rackAddress";
+
+    /**
+     * Key for owner.
+     */
     protected static final String OWNER = "owner";
 
+    /**
+     * Threshold for detecting double value is zero.
+     */
     protected static final double ZERO_THRESHOLD = Double.MIN_VALUE * 2.0;
+
     private static final double DEFAULT_COORD = 0.0;
 
     /**
-     * Returns friendly label for the element.
+     * Returns friendly label for the element. If not set, returns the
+     * element identifier.
      *
      * @return friendly label or element identifier itself if not set
      */
@@ -55,7 +81,7 @@
 
     /**
      * Returns the UI type (glyph image to be used) for the element in
-     * the Topology View.
+     * the Topology View. If not set, null is returned.
      *
      * @return the UI type
      */
@@ -65,7 +91,8 @@
 
     /**
      * Sets the UI type (glyph image to be used) for the element in
-     * the Topology View.
+     * the Topology View. Setting this to null will indicate that the
+     * default glyph image should be used for the element type.
      *
      * @param uiType the UI type; null for default
      * @return self
diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/BasicRegionConfig.java b/core/api/src/main/java/org/onosproject/net/config/basics/BasicRegionConfig.java
index 2d15fa5..e27157e 100644
--- a/core/api/src/main/java/org/onosproject/net/config/basics/BasicRegionConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/config/basics/BasicRegionConfig.java
@@ -18,7 +18,6 @@
 
 import com.google.common.base.MoreObjects;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.config.Config;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionId;
 
@@ -28,46 +27,30 @@
 /**
  * Basic configuration for network regions.
  */
-public final class BasicRegionConfig extends Config<RegionId> {
+public final class BasicRegionConfig extends BasicElementConfig<RegionId> {
 
-    private static final String NAME = "name";
     private static final String TYPE = "type";
     private static final String DEVICES = "devices";
 
     @Override
     public boolean isValid() {
-        return hasOnlyFields(NAME, TYPE, DEVICES);
+        return hasOnlyFields(ALLOWED, NAME, LATITUDE, LONGITUDE, UI_TYPE,
+                RACK_ADDRESS, OWNER, TYPE, DEVICES);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
-                .add("name", name())
-                .add("type", type())
-                .add("devices", devices())
+                .add(NAME, name())
+                .add(TYPE, type())
+                .add(UI_TYPE, uiType())
+                .add(LATITUDE, latitude())
+                .add(LONGITUDE, longitude())
+                .add(DEVICES, devices())
                 .toString();
     }
 
     /**
-     * Returns the region name.
-     *
-     * @return the region name
-     */
-    public String name() {
-        return get(NAME, null);
-    }
-
-    /**
-     * Sets the name of this region.
-     *
-     * @param name name of region, or null to unset
-     * @return the config of the region
-     */
-    public BasicRegionConfig name(String name) {
-        return (BasicRegionConfig) setOrClear(NAME, name);
-    }
-
-    /**
      * Returns the region type.
      *
      * @return the region type
diff --git a/core/api/src/main/java/org/onosproject/net/region/DefaultRegion.java b/core/api/src/main/java/org/onosproject/net/region/DefaultRegion.java
index 1926a3d..d229e92 100644
--- a/core/api/src/main/java/org/onosproject/net/region/DefaultRegion.java
+++ b/core/api/src/main/java/org/onosproject/net/region/DefaultRegion.java
@@ -19,6 +19,8 @@
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
 import org.onosproject.cluster.NodeId;
+import org.onosproject.net.AbstractAnnotated;
+import org.onosproject.net.Annotations;
 
 import java.util.List;
 import java.util.Objects;
@@ -27,7 +29,7 @@
 /**
  * Default implementation of a region.
  */
-public final class DefaultRegion implements Region {
+public final class DefaultRegion extends AbstractAnnotated implements Region {
 
     private final RegionId id;
     private final String name;
@@ -40,9 +42,12 @@
      * @param id      region identifier
      * @param name    friendly name
      * @param type    region type
+     * @param annots  annotations
      * @param masters list of sets of cluster node identifiers; in order of mastership
      */
-    public DefaultRegion(RegionId id, String name, Type type, List<Set<NodeId>> masters) {
+    public DefaultRegion(RegionId id, String name, Type type,
+                         Annotations annots, List<Set<NodeId>> masters) {
+        super(annots);
         this.id = id;
         this.name = name;
         this.type = type;
diff --git a/core/api/src/main/java/org/onosproject/net/region/Region.java b/core/api/src/main/java/org/onosproject/net/region/Region.java
index afe1805..ed8fff18 100644
--- a/core/api/src/main/java/org/onosproject/net/region/Region.java
+++ b/core/api/src/main/java/org/onosproject/net/region/Region.java
@@ -17,6 +17,7 @@
 package org.onosproject.net.region;
 
 import org.onosproject.cluster.NodeId;
+import org.onosproject.net.Annotated;
 
 import java.util.List;
 import java.util.Set;
@@ -26,7 +27,7 @@
  * logical region. Optionally, devices in the region can share the same
  * cluster nodes mastership affinities.
  */
-public interface Region {
+public interface Region extends Annotated {
 
     /**
      * Coarse representation of the type of the region.
@@ -58,6 +59,11 @@
         BUILDING,
 
         /**
+         * Region represents a data center.
+         */
+        DATA_CENTER,
+
+        /**
          * Region represents a building floor.
          */
         FLOOR,
diff --git a/core/api/src/main/java/org/onosproject/net/region/RegionStore.java b/core/api/src/main/java/org/onosproject/net/region/RegionStore.java
index 49cc378..b8527c7 100644
--- a/core/api/src/main/java/org/onosproject/net/region/RegionStore.java
+++ b/core/api/src/main/java/org/onosproject/net/region/RegionStore.java
@@ -16,6 +16,7 @@
 package org.onosproject.net.region;
 
 import org.onosproject.cluster.NodeId;
+import org.onosproject.net.Annotations;
 import org.onosproject.net.DeviceId;
 import org.onosproject.store.Store;
 
@@ -67,12 +68,13 @@
      * @param regionId      region identifier
      * @param name          friendly name
      * @param type          region type
+     * @param annots        annotations
      * @param masterNodeIds list of master nodes; null implies empty list
      * @return new region descriptor
      * @throws IllegalArgumentException if item already exists
      */
     Region createRegion(RegionId regionId, String name, Region.Type type,
-                        List<Set<NodeId>> masterNodeIds);
+                        Annotations annots, List<Set<NodeId>> masterNodeIds);
 
     /**
      * Updates the specified new region using the supplied data.
@@ -80,12 +82,13 @@
      * @param regionId      region identifier
      * @param name          friendly name
      * @param type          region type
+     * @param annots        annotations
      * @param masterNodeIds list of master nodes; null implies empty list
      * @return new region descriptor
      * @throws IllegalArgumentException if item already exists
      */
     Region updateRegion(RegionId regionId, String name, Region.Type type,
-                        List<Set<NodeId>> masterNodeIds);
+                        Annotations annots, List<Set<NodeId>> masterNodeIds);
 
     /**
      * Removes the specified region using the new set of data.
diff --git a/core/api/src/test/java/org/onosproject/net/config/basics/BasicRegionConfigTest.java b/core/api/src/test/java/org/onosproject/net/config/basics/BasicRegionConfigTest.java
index 51c5676..358c4e1 100644
--- a/core/api/src/test/java/org/onosproject/net/config/basics/BasicRegionConfigTest.java
+++ b/core/api/src/test/java/org/onosproject/net/config/basics/BasicRegionConfigTest.java
@@ -115,7 +115,7 @@
     @Test
     public void region3Config() {
         loadRegion(R3);
-        checkRegion(null, null, R3_DEVS);
+        checkRegion(R3, null, R3_DEVS);
     }
 
     @Test
@@ -128,8 +128,10 @@
     @Test
     public void clearName() {
         loadRegion(R1);
+        checkRegion(EUROPE, Region.Type.CONTINENT, R1_DEVS);
         cfg.name(null);
-        checkRegion(null, Region.Type.CONTINENT, R1_DEVS);
+        // if the friendly name is cleared, name() returns the identifier
+        checkRegion(R1, Region.Type.CONTINENT, R1_DEVS);
     }
 
     @Test
@@ -150,14 +152,14 @@
     public void modifyDevices() {
         loadRegion(R3);
         cfg.devices(ALT_DEVICES);
-        checkRegion(null, null, ALT_DEVICES);
+        checkRegion(R3, null, ALT_DEVICES);
     }
 
     @Test
     public void clearDevices() {
         loadRegion(R3);
         cfg.devices(null);
-        checkRegion(null, null, null);
+        checkRegion(R3, null, null);
     }
 
 
diff --git a/core/api/src/test/java/org/onosproject/net/region/DefaultRegionTest.java b/core/api/src/test/java/org/onosproject/net/region/DefaultRegionTest.java
index 392c565..9e85f3f 100644
--- a/core/api/src/test/java/org/onosproject/net/region/DefaultRegionTest.java
+++ b/core/api/src/test/java/org/onosproject/net/region/DefaultRegionTest.java
@@ -21,6 +21,8 @@
 import com.google.common.testing.EqualsTester;
 import org.junit.Test;
 import org.onosproject.cluster.NodeId;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
 
 import java.util.Set;
 
@@ -34,13 +36,16 @@
 public class DefaultRegionTest {
 
     private static final RegionId ID1 = RegionId.regionId("r1");
+    private static final Annotations NO_ANNOTS = DefaultAnnotations.EMPTY;
 
     @Test
     public void basics() {
-        ImmutableList<Set<NodeId>> masters = ImmutableList
-                .of(ImmutableSet.of(nodeId("n1"), nodeId("n2")),
-                    ImmutableSet.of(nodeId("n3"), nodeId("n4")));
-        Region r = new DefaultRegion(ID1, "R1", METRO, masters);
+        ImmutableList<Set<NodeId>> masters =
+                ImmutableList.of(
+                        ImmutableSet.of(nodeId("n1"), nodeId("n2")),
+                        ImmutableSet.of(nodeId("n3"), nodeId("n4"))
+                );
+        Region r = new DefaultRegion(ID1, "R1", METRO, NO_ANNOTS, masters);
         assertEquals("incorrect id", ID1, r.id());
         assertEquals("incorrect name", "R1", r.name());
         assertEquals("incorrect type", METRO, r.type());
@@ -49,9 +54,9 @@
 
     @Test
     public void equality() {
-        Region a = new DefaultRegion(ID1, "R1", METRO, null);
-        Region b = new DefaultRegion(ID1, "R1", METRO, null);
-        Region c = new DefaultRegion(ID1, "R2", METRO, null);
+        Region a = new DefaultRegion(ID1, "R1", METRO, NO_ANNOTS, null);
+        Region b = new DefaultRegion(ID1, "R1", METRO, NO_ANNOTS, null);
+        Region c = new DefaultRegion(ID1, "R2", METRO, NO_ANNOTS, null);
 
         new EqualsTester().addEqualityGroup(a, b).addEqualityGroup(c).testEquals();
     }
diff --git a/core/api/src/test/java/org/onosproject/ui/model/topo/UiTopoLayoutTest.java b/core/api/src/test/java/org/onosproject/ui/model/topo/UiTopoLayoutTest.java
index b8db969..1c840fa 100644
--- a/core/api/src/test/java/org/onosproject/ui/model/topo/UiTopoLayoutTest.java
+++ b/core/api/src/test/java/org/onosproject/ui/model/topo/UiTopoLayoutTest.java
@@ -17,6 +17,8 @@
 package org.onosproject.ui.model.topo;
 
 import org.junit.Test;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.region.DefaultRegion;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionId;
@@ -44,10 +46,11 @@
 
     private static final double DELTA = Double.MIN_VALUE * 2.0;
 
+    private static final Annotations NO_ANNOTS = DefaultAnnotations.EMPTY;
     private static final UiTopoLayoutId OTHER_ID = layoutId("other-id");
     private static final RegionId REGION_ID = regionId("some-region");
     private static final Region REGION =
-            new DefaultRegion(REGION_ID, "Region-1", CAMPUS, null);
+            new DefaultRegion(REGION_ID, "Region-1", CAMPUS, NO_ANNOTS, null);
     private static final String GEOMAP = "geo1";
     private static final String SPRITE = "spr1";
 
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/RegionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/RegionCodec.java
index 5f003e0..f3aa128 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/RegionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/RegionCodec.java
@@ -23,7 +23,7 @@
 import com.google.common.collect.Sets;
 import org.onosproject.cluster.NodeId;
 import org.onosproject.codec.CodecContext;
-import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.Annotations;
 import org.onosproject.net.region.DefaultRegion;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionId;
@@ -39,7 +39,7 @@
 /**
  * Codec for the Region class.
  */
-public class RegionCodec extends JsonCodec<Region> {
+public class RegionCodec extends AnnotatedCodec<Region> {
 
     // JSON field names
     private static final String REGION_ID = "id";
@@ -77,7 +77,7 @@
             masters.add(setsJson);
         });
         result.set(MASTERS, masters);
-        return result;
+        return annotate(result, region, context);
     }
 
     @Override
@@ -108,8 +108,9 @@
         RegionId regionId = RegionId.regionId(extractMember(REGION_ID, json));
         String name = extractMember(NAME, json);
         Region.Type type = REGION_TYPE_MAP.get(extractMember(TYPE, json));
+        Annotations annots = extractAnnotations(json, context);
 
-        return new DefaultRegion(regionId, name, type, masters);
+        return new DefaultRegion(regionId, name, type, annots, masters);
     }
 
     private String extractMember(String key, ObjectNode json) {
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/RegionCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/RegionCodecTest.java
index 05d95e1..a9e7a2f 100644
--- a/core/common/src/test/java/org/onosproject/codec/impl/RegionCodecTest.java
+++ b/core/common/src/test/java/org/onosproject/codec/impl/RegionCodecTest.java
@@ -25,6 +25,8 @@
 import org.onosproject.cluster.NodeId;
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.core.CoreService;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.region.DefaultRegion;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionId;
@@ -75,8 +77,9 @@
         RegionId regionId = RegionId.regionId("1");
         String name = "foo";
         Region.Type type = Region.Type.ROOM;
+        Annotations noAnnots = DefaultAnnotations.EMPTY;
 
-        Region region = new DefaultRegion(regionId, name, type, masters);
+        Region region = new DefaultRegion(regionId, name, type, noAnnots, masters);
 
         ObjectNode regionJson = regionCodec.encode(region, context);
         assertThat(regionJson, matchesRegion(region));
diff --git a/core/net/src/main/java/org/onosproject/net/region/impl/RegionManager.java b/core/net/src/main/java/org/onosproject/net/region/impl/RegionManager.java
index c7b8342..bf82842 100644
--- a/core/net/src/main/java/org/onosproject/net/region/impl/RegionManager.java
+++ b/core/net/src/main/java/org/onosproject/net/region/impl/RegionManager.java
@@ -25,8 +25,13 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onosproject.cluster.NodeId;
 import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.HostId;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicElementConfig;
+import org.onosproject.net.config.basics.BasicRegionConfig;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionAdminService;
 import org.onosproject.net.region.RegionEvent;
@@ -44,9 +49,9 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.ImmutableList.of;
-import static org.slf4j.LoggerFactory.getLogger;
 import static org.onosproject.security.AppGuard.checkPermission;
 import static org.onosproject.security.AppPermission.Type.REGION_READ;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Provides implementation of the region service APIs.
@@ -70,6 +75,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected RegionStore store;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService networkConfigService;
+
     @Activate
     public void activate() {
         store.setDelegate(delegate);
@@ -84,13 +92,41 @@
         log.info("Stopped");
     }
 
+    private String dstr(double d) {
+        return Double.toString(d);
+    }
+
+    private Annotations genAnnots(RegionId id) {
+        BasicRegionConfig cfg =
+                networkConfigService.getConfig(id, BasicRegionConfig.class);
+
+        if (cfg == null) {
+            return DefaultAnnotations.builder().build();
+        }
+
+        DefaultAnnotations.Builder builder = DefaultAnnotations.builder()
+                .set(BasicElementConfig.NAME, cfg.name())
+                .set(BasicElementConfig.LATITUDE, dstr(cfg.latitude()))
+                .set(BasicElementConfig.LONGITUDE, dstr(cfg.longitude()));
+
+        // only set the UI_TYPE annotation if it is not null in the config
+        String uiType = cfg.uiType();
+        if (uiType != null) {
+            builder.set(BasicElementConfig.UI_TYPE, uiType);
+        }
+
+        return builder.build();
+    }
+
     @Override
     public Region createRegion(RegionId regionId, String name, Region.Type type,
                                List<Set<NodeId>> masterNodeIds) {
         checkNotNull(regionId, REGION_ID_NULL);
         checkNotNull(name, NAME_NULL);
         checkNotNull(name, REGION_TYPE_NULL);
-        return store.createRegion(regionId, name, type, masterNodeIds == null ? of() : masterNodeIds);
+
+        return store.createRegion(regionId, name, type, genAnnots(regionId),
+                masterNodeIds == null ? of() : masterNodeIds);
     }
 
     @Override
@@ -99,7 +135,9 @@
         checkNotNull(regionId, REGION_ID_NULL);
         checkNotNull(name, NAME_NULL);
         checkNotNull(name, REGION_TYPE_NULL);
-        return store.updateRegion(regionId, name, type, masterNodeIds == null ? of() : masterNodeIds);
+
+        return store.updateRegion(regionId, name, type, genAnnots(regionId),
+                masterNodeIds == null ? of() : masterNodeIds);
     }
 
     @Override
diff --git a/core/net/src/test/java/org/onosproject/cluster/impl/MastershipManagerTest.java b/core/net/src/test/java/org/onosproject/cluster/impl/MastershipManagerTest.java
index ac2e558..81533fb 100644
--- a/core/net/src/test/java/org/onosproject/cluster/impl/MastershipManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/cluster/impl/MastershipManagerTest.java
@@ -37,6 +37,7 @@
 import org.onosproject.mastership.MastershipStore;
 import org.onosproject.mastership.MastershipTermService;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.NetworkConfigServiceAdapter;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionId;
 import org.onosproject.net.region.RegionStore;
@@ -374,6 +375,7 @@
     private class TestRegionManager extends RegionManager {
         TestRegionManager() {
             eventDispatcher = new TestEventDispatcher();
+            networkConfigService = new NetworkConfigServiceAdapter();
         }
     }
 }
diff --git a/core/net/src/test/java/org/onosproject/net/region/impl/RegionManagerTest.java b/core/net/src/test/java/org/onosproject/net/region/impl/RegionManagerTest.java
index fd15104..1e29365 100644
--- a/core/net/src/test/java/org/onosproject/net/region/impl/RegionManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/region/impl/RegionManagerTest.java
@@ -27,6 +27,7 @@
 import org.onosproject.common.event.impl.TestEventDispatcher;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.NetTestTools;
+import org.onosproject.net.config.NetworkConfigServiceAdapter;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionAdminService;
 import org.onosproject.net.region.RegionEvent;
@@ -175,6 +176,7 @@
     private class TestManager extends RegionManager {
         TestManager() {
             eventDispatcher = new TestEventDispatcher();
+            networkConfigService = new NetworkConfigServiceAdapter();
         }
     }
 
diff --git a/core/store/dist/src/main/java/org/onosproject/store/region/impl/DistributedRegionStore.java b/core/store/dist/src/main/java/org/onosproject/store/region/impl/DistributedRegionStore.java
index 6d70376..e00af16 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/region/impl/DistributedRegionStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/region/impl/DistributedRegionStore.java
@@ -26,6 +26,7 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.util.Identifier;
 import org.onosproject.cluster.NodeId;
+import org.onosproject.net.Annotations;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.region.DefaultRegion;
 import org.onosproject.net.region.Region;
@@ -90,7 +91,7 @@
     protected void activate() {
         Serializer serializer =
                 Serializer.using(Arrays.asList(KryoNamespaces.API),
-                                 Identifier.class);
+                        Identifier.class);
 
         regionsRepo = storageService.<RegionId, Region>consistentMapBuilder()
                 .withSerializer(serializer)
@@ -141,19 +142,19 @@
 
     @Override
     public Region createRegion(RegionId regionId, String name, Region.Type type,
-                               List<Set<NodeId>> masterNodeIds) {
+                               Annotations annots, List<Set<NodeId>> masterNodeIds) {
         return regionsRepo.compute(regionId, (id, region) -> {
             checkArgument(region == null, DUPLICATE_REGION);
-            return new DefaultRegion(regionId, name, type, masterNodeIds);
+            return new DefaultRegion(regionId, name, type, annots, masterNodeIds);
         }).value();
     }
 
     @Override
     public Region updateRegion(RegionId regionId, String name, Region.Type type,
-                               List<Set<NodeId>> masterNodeIds) {
+                               Annotations annots, List<Set<NodeId>> masterNodeIds) {
         return regionsRepo.compute(regionId, (id, region) -> {
             nullIsNotFound(region, NO_REGION);
-            return new DefaultRegion(regionId, name, type, masterNodeIds);
+            return new DefaultRegion(regionId, name, type, annots, masterNodeIds);
         }).value();
     }
 
@@ -165,8 +166,9 @@
 
     @Override
     public void addDevices(RegionId regionId, Collection<DeviceId> deviceIds) {
-        // Devices can only be a member in one region.  Remove the device if it belongs to
-        // a different region than the region for which we are attempting to add it.
+        // Devices can only be a member in one region.  Remove the device if it
+        // belongs to a different region than the region for which we are
+        // attempting to add it.
         for (DeviceId deviceId : deviceIds) {
             Region region = getRegionForDevice(deviceId);
             if ((region != null) && (!regionId.id().equals(region.id().id()))) {
@@ -200,7 +202,7 @@
             } else {
                 return ImmutableSet.<DeviceId>builder()
                         .addAll(Sets.difference(existingDevices,
-                                                ImmutableSet.copyOf(deviceIds)))
+                                ImmutableSet.copyOf(deviceIds)))
                         .build();
             }
         });
@@ -211,7 +213,8 @@
     /**
      * Listener class to map listener events to the region inventory events.
      */
-    private class InternalRegionListener implements MapEventListener<RegionId, Region> {
+    private class InternalRegionListener
+            implements MapEventListener<RegionId, Region> {
         @Override
         public void event(MapEvent<RegionId, Region> event) {
             Region region = null;
@@ -239,13 +242,14 @@
     /**
      * Listener class to map listener events to the region membership events.
      */
-    private class InternalMembershipListener implements MapEventListener<RegionId, Set<DeviceId>> {
+    private class InternalMembershipListener
+            implements MapEventListener<RegionId, Set<DeviceId>> {
         @Override
         public void event(MapEvent<RegionId, Set<DeviceId>> event) {
             if (event.type() != MapEvent.Type.REMOVE) {
                 notifyDelegate(new RegionEvent(REGION_MEMBERSHIP_CHANGED,
-                                               regionsById.get(event.key()),
-                                               event.newValue().value()));
+                        regionsById.get(event.key()),
+                        event.newValue().value()));
             }
         }
     }
diff --git a/core/store/dist/src/test/java/org/onosproject/store/region/impl/DistributedRegionStoreTest.java b/core/store/dist/src/test/java/org/onosproject/store/region/impl/DistributedRegionStoreTest.java
index 16520f3..7ec68e0 100644
--- a/core/store/dist/src/test/java/org/onosproject/store/region/impl/DistributedRegionStoreTest.java
+++ b/core/store/dist/src/test/java/org/onosproject/store/region/impl/DistributedRegionStoreTest.java
@@ -23,6 +23,8 @@
 import org.junit.Test;
 import org.onlab.util.ItemNotFoundException;
 import org.onosproject.cluster.NodeId;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionEvent;
@@ -50,6 +52,8 @@
 
     private static final NodeId NID1 = NodeId.nodeId("n1");
 
+    private static final Annotations NO_ANNOTS = DefaultAnnotations.EMPTY;
+
     private static final List<Set<NodeId>> MASTERS = ImmutableList.of(ImmutableSet.of(NID1));
 
     private TestStore store;
@@ -76,16 +80,16 @@
 
     @Test
     public void basics() {
-        Region r1 = store.createRegion(RID1, "R1", METRO, MASTERS);
+        Region r1 = store.createRegion(RID1, "R1", METRO, NO_ANNOTS, MASTERS);
         assertEquals("incorrect id", RID1, r1.id());
         assertEquals("incorrect event", REGION_ADDED, event.type());
 
-        Region r2 = store.createRegion(RID2, "R2", CAMPUS, MASTERS);
+        Region r2 = store.createRegion(RID2, "R2", CAMPUS, NO_ANNOTS, MASTERS);
         assertEquals("incorrect id", RID2, r2.id());
         assertEquals("incorrect type", CAMPUS, r2.type());
         assertEquals("incorrect event", REGION_ADDED, event.type());
 
-        r2 = store.updateRegion(RID2, "R2", COUNTRY, MASTERS);
+        r2 = store.updateRegion(RID2, "R2", COUNTRY, NO_ANNOTS, MASTERS);
         assertEquals("incorrect type", COUNTRY, r2.type());
         assertEquals("incorrect event", REGION_UPDATED, event.type());
 
@@ -106,18 +110,18 @@
 
     @Test(expected = IllegalArgumentException.class)
     public void duplicateCreate() {
-        store.createRegion(RID1, "R1", METRO, MASTERS);
-        store.createRegion(RID1, "R2", CAMPUS, MASTERS);
+        store.createRegion(RID1, "R1", METRO, NO_ANNOTS, MASTERS);
+        store.createRegion(RID1, "R2", CAMPUS, NO_ANNOTS, MASTERS);
     }
 
     @Test(expected = ItemNotFoundException.class)
     public void missingUpdate() {
-        store.updateRegion(RID1, "R1", METRO, MASTERS);
+        store.updateRegion(RID1, "R1", METRO, NO_ANNOTS, MASTERS);
     }
 
     @Test
     public void membership() {
-        Region r = store.createRegion(RID1, "R1", METRO, MASTERS);
+        Region r = store.createRegion(RID1, "R1", METRO, NO_ANNOTS, MASTERS);
         assertTrue("no devices expected", store.getRegionDevices(RID1).isEmpty());
         assertNull("no region expected", store.getRegionForDevice(DID1));
 
@@ -139,7 +143,7 @@
 
         // Test adding DID3 to RID2 but it is already in RID1.
         // DID3 will be removed from RID1 and added to RID2.
-        Region r2 = store.createRegion(RID2, "R2", CAMPUS, MASTERS);
+        Region r2 = store.createRegion(RID2, "R2", CAMPUS, NO_ANNOTS, MASTERS);
         store.addDevices(RID2, ImmutableSet.of(DID3));
         deviceIds = store.getRegionDevices(RID1);
         assertEquals("incorrect device count", 2, deviceIds.size());
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullDevice.java b/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullDevice.java
index 959a7c5..3b81d4d 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullDevice.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullDevice.java
@@ -67,12 +67,10 @@
         CustomTopologySimulator sim = (CustomTopologySimulator) simulator;
         DeviceId deviceId = sim.nextDeviceId();
         BasicDeviceConfig cfg = cfgService.addConfig(deviceId, BasicDeviceConfig.class);
-        cfg.name(name);
-        if (latitude != 0 || longitude != 0) {
-            cfg.latitude(latitude);
-            cfg.longitude(longitude);
-        }
-        cfg.apply();
+        cfg.name(name)
+                .latitude(latitude)
+                .longitude(longitude)
+                .apply();
 
         sim.createDevice(deviceId, name, Device.Type.valueOf(type.toUpperCase()), portCount);
     }
diff --git a/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullHost.java b/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullHost.java
index 93ef2a6..26bca8c 100644
--- a/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullHost.java
+++ b/providers/null/src/main/java/org/onosproject/provider/nil/cli/CreateNullHost.java
@@ -73,9 +73,9 @@
         HostId id = sim.nextHostId();
         HostLocation location = findAvailablePort(deviceId);
         BasicHostConfig cfg = cfgService.addConfig(id, BasicHostConfig.class);
-        cfg.latitude(latitude);
-        cfg.longitude(longitude);
-        cfg.apply();
+        cfg.latitude(latitude)
+                .longitude(longitude)
+                .apply();
 
         sim.createHost(id, location, IpAddress.valueOf(hostIp));
     }
diff --git a/tools/test/topos/regions-bayarea.sh b/tools/test/topos/regions-bayarea.sh
new file mode 100755
index 0000000..6ecdcde
--- /dev/null
+++ b/tools/test/topos/regions-bayarea.sh
@@ -0,0 +1,288 @@
+#!/bin/bash
+#
+# A test topology of four data centers (spine-leaf fabrics), configured as
+# four sub-regions, and positioned in the Bay Area (geographically speaking).
+#
+# Script Configuration:
+#
+# host     : the controller instance against which this script is run
+# nports   : the number of ports to configure on each switch
+# sleepfor : the number of seconds to wait for sim to restart
+
+host=${1:-127.0.0.1}
+nports=16
+sleepfor=5
+
+### start up null provider
+
+onos ${host} null-simulation stop custom
+onos ${host} wipe-out please
+onos ${host} null-simulation start custom
+
+
+## unfortunately, it takes a time for the sim to start up
+#  this is not ideal...
+
+echo
+echo "Sleeping while sim starts up... (${sleepfor} seconds)..."
+echo
+sleep ${sleepfor}
+
+
+### Add devices and links
+#
+# null-create-device <type> <name> <#ports> <latitude> <longitude>
+# null-create-link <type> <src> <dst>
+
+onos ${host} <<-EOF
+
+# root region
+
+# CO 1
+null-create-device switch SPINE-A-1 ${nports} 37.783197 -122.503738
+null-create-device switch SPINE-A-2 ${nports} 37.783972 -122.463738
+null-create-device switch SPINE-A-3 ${nports} 37.783972 -122.423738
+null-create-device switch SPINE-A-4 ${nports} 37.783972 -122.383738
+
+null-create-device switch LEAF-A-1 ${nports} 37.733972 -122.503738
+null-create-device switch LEAF-A-2 ${nports} 37.733972 -122.463738
+null-create-device switch LEAF-A-3 ${nports} 37.733972 -122.423738
+null-create-device switch LEAF-A-4 ${nports} 37.733972 -122.383738
+
+# Links in CO 1
+null-create-link direct LEAF-A-1 SPINE-A-1
+null-create-link direct LEAF-A-1 SPINE-A-2
+null-create-link direct LEAF-A-1 SPINE-A-3
+null-create-link direct LEAF-A-1 SPINE-A-4
+
+null-create-link direct LEAF-A-2 SPINE-A-1
+null-create-link direct LEAF-A-2 SPINE-A-2
+null-create-link direct LEAF-A-2 SPINE-A-3
+null-create-link direct LEAF-A-2 SPINE-A-4
+
+null-create-link direct LEAF-A-3 SPINE-A-1
+null-create-link direct LEAF-A-3 SPINE-A-2
+null-create-link direct LEAF-A-3 SPINE-A-3
+null-create-link direct LEAF-A-3 SPINE-A-4
+
+null-create-link direct LEAF-A-4 SPINE-A-1
+null-create-link direct LEAF-A-4 SPINE-A-2
+null-create-link direct LEAF-A-4 SPINE-A-3
+null-create-link direct LEAF-A-4 SPINE-A-4
+
+# CO 2
+null-create-device switch SPINE-B-1 ${nports} 37.458236 -122.252604
+null-create-device switch SPINE-B-2 ${nports} 37.458236 -122.212604
+null-create-device switch SPINE-B-3 ${nports} 37.458236 -122.172604
+null-create-device switch SPINE-B-4 ${nports} 37.458236 -122.132604
+
+null-create-device switch LEAF-B-1 ${nports} 37.418236 -122.252604
+null-create-device switch LEAF-B-2 ${nports} 37.418236 -122.212604
+null-create-device switch LEAF-B-3 ${nports} 37.418236 -122.172604
+null-create-device switch LEAF-B-4 ${nports} 37.418236 -122.132604
+
+# Links in CO 2
+null-create-link direct LEAF-B-1 SPINE-B-1
+null-create-link direct LEAF-B-1 SPINE-B-2
+null-create-link direct LEAF-B-1 SPINE-B-3
+null-create-link direct LEAF-B-1 SPINE-B-4
+
+null-create-link direct LEAF-B-2 SPINE-B-1
+null-create-link direct LEAF-B-2 SPINE-B-2
+null-create-link direct LEAF-B-2 SPINE-B-3
+null-create-link direct LEAF-B-2 SPINE-B-4
+
+null-create-link direct LEAF-B-3 SPINE-B-1
+null-create-link direct LEAF-B-3 SPINE-B-2
+null-create-link direct LEAF-B-3 SPINE-B-3
+null-create-link direct LEAF-B-3 SPINE-B-4
+
+null-create-link direct LEAF-B-4 SPINE-B-1
+null-create-link direct LEAF-B-4 SPINE-B-2
+null-create-link direct LEAF-B-4 SPINE-B-3
+null-create-link direct LEAF-B-4 SPINE-B-4
+
+# CO 3
+null-create-device switch SPINE-C-1 ${nports} 37.338208 -121.926329
+null-create-device switch SPINE-C-2 ${nports} 37.338208 -121.886329
+null-create-device switch SPINE-C-3 ${nports} 37.338208 -121.846329
+null-create-device switch SPINE-C-4 ${nports} 37.338208 -121.806329
+
+null-create-device switch LEAF-C-1 ${nports} 37.298208 -121.926329
+null-create-device switch LEAF-C-2 ${nports} 37.298208 -121.886329
+null-create-device switch LEAF-C-3 ${nports} 37.298208 -121.846329
+null-create-device switch LEAF-C-4 ${nports} 37.298208 -121.806329
+
+# Links in CO 3
+null-create-link direct LEAF-C-1 SPINE-C-1
+null-create-link direct LEAF-C-1 SPINE-C-2
+null-create-link direct LEAF-C-1 SPINE-C-3
+null-create-link direct LEAF-C-1 SPINE-C-4
+
+null-create-link direct LEAF-C-2 SPINE-C-1
+null-create-link direct LEAF-C-2 SPINE-C-2
+null-create-link direct LEAF-C-2 SPINE-C-3
+null-create-link direct LEAF-C-2 SPINE-C-4
+
+null-create-link direct LEAF-C-3 SPINE-C-1
+null-create-link direct LEAF-C-3 SPINE-C-2
+null-create-link direct LEAF-C-3 SPINE-C-3
+null-create-link direct LEAF-C-3 SPINE-C-4
+
+null-create-link direct LEAF-C-4 SPINE-C-1
+null-create-link direct LEAF-C-4 SPINE-C-2
+null-create-link direct LEAF-C-4 SPINE-C-3
+null-create-link direct LEAF-C-4 SPINE-C-4
+
+# CO 4
+null-create-device switch SPINE-D-1 ${nports} 37.556826 -122.051926
+null-create-device switch SPINE-D-2 ${nports} 37.556826 -122.011926
+null-create-device switch SPINE-D-3 ${nports} 37.556826 -121.971926
+null-create-device switch SPINE-D-4 ${nports} 37.556826 -121.931926
+
+null-create-device switch LEAF-D-1 ${nports} 37.516826 -122.051926
+null-create-device switch LEAF-D-2 ${nports} 37.516826 -122.011926
+null-create-device switch LEAF-D-3 ${nports} 37.516826 -121.971926
+null-create-device switch LEAF-D-4 ${nports} 37.516826 -121.931926
+
+# Links in CO 4
+null-create-link direct LEAF-D-1 SPINE-D-1
+null-create-link direct LEAF-D-1 SPINE-D-2
+null-create-link direct LEAF-D-1 SPINE-D-3
+null-create-link direct LEAF-D-1 SPINE-D-4
+
+null-create-link direct LEAF-D-2 SPINE-D-1
+null-create-link direct LEAF-D-2 SPINE-D-2
+null-create-link direct LEAF-D-2 SPINE-D-3
+null-create-link direct LEAF-D-2 SPINE-D-4
+
+null-create-link direct LEAF-D-3 SPINE-D-1
+null-create-link direct LEAF-D-3 SPINE-D-2
+null-create-link direct LEAF-D-3 SPINE-D-3
+null-create-link direct LEAF-D-3 SPINE-D-4
+
+null-create-link direct LEAF-D-4 SPINE-D-1
+null-create-link direct LEAF-D-4 SPINE-D-2
+null-create-link direct LEAF-D-4 SPINE-D-3
+null-create-link direct LEAF-D-4 SPINE-D-4
+
+# Inter-CO Links
+null-create-link direct LEAF-A-4 LEAF-B-1
+null-create-link direct LEAF-A-4 LEAF-C-1
+null-create-link direct LEAF-A-4 LEAF-D-4
+null-create-link direct LEAF-B-1 LEAF-C-1
+null-create-link direct LEAF-B-1 LEAF-D-4
+null-create-link direct LEAF-C-1 LEAF-D-4
+
+EOF
+
+### Add a host per device
+#
+# null-create-host <device-id> <host-ip> <latitude> <longitude>
+
+onos ${host} <<-EOF
+
+null-create-host LEAF-A-1 192.168.1.1 37.703972 -122.503738
+null-create-host LEAF-A-2 192.168.2.1 37.703972 -122.463738
+null-create-host LEAF-A-3 192.168.3.1 37.703972 -122.423738
+null-create-host LEAF-A-4 192.168.4.1 37.703972 -122.383738
+
+null-create-host LEAF-B-1 192.168.51.1 37.388236 -122.252604
+null-create-host LEAF-B-2 192.168.52.1 37.388236 -122.212604
+null-create-host LEAF-B-3 192.168.53.1 37.388236 -122.172604
+null-create-host LEAF-B-4 192.168.54.1 37.388236 -122.132604
+
+null-create-host LEAF-C-1 192.168.101.1 37.268208 -121.926329
+null-create-host LEAF-C-2 192.168.102.1 37.268208 -121.886329
+null-create-host LEAF-C-3 192.168.103.1 37.268208 -121.846329
+null-create-host LEAF-C-4 192.168.104.1 37.268208 -121.806329
+
+null-create-host LEAF-D-1 192.168.151.1 37.476826 -122.051926
+null-create-host LEAF-D-2 192.168.152.1 37.476826 -122.011926
+null-create-host LEAF-D-3 192.168.153.1 37.476826 -121.971926
+null-create-host LEAF-D-4 192.168.154.1 37.476826 -121.931926
+
+EOF
+
+### Add regions and associate devices with them
+#
+# region-add <region-id> <region-name> <region-type> <region-master>
+# region-add-devices <region-id> <device-id>...
+
+onos ${host} <<-EOF
+
+region-add c01 SanFrancisco DATA_CENTER 37.75394143914288 -122.45945851660800 ${host}
+region-add c02 PaloAlto     DATA_CENTER 37.45466637790734 -122.21838933304870 ${host}
+region-add c03 SanJose      DATA_CENTER 37.34425619809433 -121.94768095808017 ${host}
+region-add c04 Fremont      DATA_CENTER 37.54328280574901 -122.01205548699211 ${host}
+
+region-add-devices c01 \
+    null:0000000000000001 \
+    null:0000000000000002 \
+    null:0000000000000003 \
+    null:0000000000000004 \
+    null:0000000000000005 \
+    null:0000000000000006 \
+    null:0000000000000007 \
+    null:0000000000000008 \
+
+region-add-devices c02 \
+    null:0000000000000009 \
+    null:000000000000000a \
+    null:000000000000000b \
+    null:000000000000000c \
+    null:000000000000000d \
+    null:000000000000000e \
+    null:000000000000000f \
+    null:0000000000000010 \
+
+region-add-devices c03 \
+    null:0000000000000011 \
+    null:0000000000000012 \
+    null:0000000000000013 \
+    null:0000000000000014 \
+    null:0000000000000015 \
+    null:0000000000000016 \
+    null:0000000000000017 \
+    null:0000000000000018 \
+
+region-add-devices c04 \
+    null:0000000000000019 \
+    null:000000000000001a \
+    null:000000000000001b \
+    null:000000000000001c \
+    null:000000000000001d \
+    null:000000000000001e \
+    null:000000000000001f \
+    null:0000000000000020 \
+
+regions
+
+EOF
+
+### Add layouts, associating backing regions, and optional parent.
+#
+# layout-add <layout-id> <region-id(opt)> <parent-layout-id(opt)>
+
+onos ${host} <<-EOF
+
+layout-add lC01 c01
+layout-add lC02 c02
+layout-add lC03 c03
+layout-add lC04 c04
+
+layouts
+
+EOF
+
+
+### Set up debug log messages for classes we care about
+
+onos ${host} <<-EOF
+
+log:set DEBUG org.onosproject.ui.impl.topo.Topo2ViewMessageHandler
+log:set DEBUG org.onosproject.ui.impl.topo.Topo2Jsonifier
+log:set DEBUG org.onosproject.ui.impl.UiWebSocket
+log:list
+
+EOF
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/RegionsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/RegionsResourceTest.java
index a00edc8..f387140 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/RegionsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/RegionsResourceTest.java
@@ -32,6 +32,8 @@
 import org.onosproject.cluster.NodeId;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.region.Region;
 import org.onosproject.net.region.RegionAdminService;
@@ -121,6 +123,11 @@
         public List<Set<NodeId>> masters() {
             return this.masters;
         }
+
+        @Override
+        public Annotations annotations() {
+            return DefaultAnnotations.EMPTY;
+        }
     }
 
     /**
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 9e02334..323ccee 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
@@ -36,6 +36,7 @@
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.link.LinkService;
+import org.onosproject.net.region.Region;
 import org.onosproject.net.statistic.StatisticService;
 import org.onosproject.net.topology.TopologyService;
 import org.onosproject.ui.JsonUtils;
@@ -292,8 +293,8 @@
         return node;
     }
 
-    private void addProps(ObjectNode node, Device dev) {
-        Annotations annot = dev.annotations();
+    private void addProps(ObjectNode node, Annotated a) {
+        Annotations annot = a.annotations();
         ObjectNode props = objectNode();
         if (annot != null) {
             annot.keys().forEach(k -> props.put(k, annot.value(k)));
@@ -404,7 +405,10 @@
                 .put("nodeType", REGION)
                 .put("nDevs", region.deviceCount())
                 .put("nHosts", region.hostCount());
-        // TODO: complete closed-region details
+
+        Region r = region.backingRegion();
+        addGeoLocation(node, r);
+        addProps(node, r);
 
         addMetaUi(node, region.idAsString());
         return node;
diff --git a/web/gui/src/main/webapp/app/fw/svg/zoom.js b/web/gui/src/main/webapp/app/fw/svg/zoom.js
index cebe8c7..0effdc2 100644
--- a/web/gui/src/main/webapp/app/fw/svg/zoom.js
+++ b/web/gui/src/main/webapp/app/fw/svg/zoom.js
@@ -23,7 +23,7 @@
     // configuration
     var defaultSettings = {
         zoomMin: 0.05,
-        zoomMax: 10,
+        zoomMax: 50,
         zoomEnabled: function (ev) { return true; },
         zoomCallback: function () {}
     };
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
index 24df147..73b76a5 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
@@ -54,26 +54,35 @@
             link.createLink();
         });
 
+
+        // TODO: replace with an algorithm that computes appropriate transition
+        //        based on the location of the "region node" on the parent map
+
         // TEMP Map Zoom
         var regionPanZooms = {
             "(root)": {
-                scale: 0.8,
-                translate: [-384.5881010374517, -512.2527728775849]
+                scale: 4.21,
+                translate: [-2066.3049871603093, -2130.190726668792]
             },
-            rBrg: {
-                scale: 2.75,
-                translate: [-2929.288248714413, -3498.849169115524]
+            c01: {
+                scale: 19.8855,
+                translate: [-10375.91165337411, -10862.217941271818]
             },
-            rLon: {
-                scale: 2.75,
-                translate: [-2873.682762707102, -3320.483337006704]
+            c02: {
+                scale: 24.25,
+                translate: [-14169.70851936781, -15649.174761455488]
             },
-            rTha: {
-                scale: 7.5,
-                translate: [-8751.376289753565, -9950.962850877779]
+            c03: {
+                scale: 22.72,
+                translate: [-14950.92246589002, -15390.955326616648]
+            },
+            c04: {
+                scale: 26.24,
+                translate: [-16664.006814209282, -16217.021478816077]
             }
         };
 
+
         setTimeout(function () {
             var reigionPZ = regionPanZooms[region.get('id')];
             t2zs.panAndZoom(reigionPZ.translate, reigionPZ.scale);
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/UiTopoLayoutManagerTest.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/UiTopoLayoutManagerTest.java
index 67fbf69..80578b0 100644
--- a/web/gui/src/test/java/org/onosproject/ui/impl/topo/UiTopoLayoutManagerTest.java
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/UiTopoLayoutManagerTest.java
@@ -19,6 +19,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.config.NetworkConfigRegistryAdapter;
 import org.onosproject.net.region.DefaultRegion;
 import org.onosproject.net.region.Region;
@@ -41,7 +42,8 @@
     }
 
     private static Region region(String id, String name, Region.Type type) {
-        return new DefaultRegion(regionId(id), name, type, null);
+        return new DefaultRegion(regionId(id), name, type,
+                DefaultAnnotations.EMPTY, null);
     }
 
     private static UiTopoLayout layout(String id, Region region, String parentId) {
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java
index 559a6be..71bc7d2 100644
--- a/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/model/AbstractTopoModelTest.java
@@ -30,6 +30,7 @@
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.mastership.MastershipServiceAdapter;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DefaultDevice;
 import org.onosproject.net.DefaultHost;
 import org.onosproject.net.DefaultLink;
@@ -277,7 +278,7 @@
     protected static Region region(String id, Region.Type type,
                                    List<Set<NodeId>> masters) {
         return new DefaultRegion(RegionId.regionId(id), "Region-" + id,
-                type, masters);
+                type, DefaultAnnotations.EMPTY, masters);
     }
 
     /**