GUI -- Split MapService into GeoDataService and MapService. WIP.
Change-Id: Ibfe5b35ecdfaaf39b9d48abd29d0a44327dec130
diff --git a/web/gui/src/main/webapp/app/fw/svg/geodata.js b/web/gui/src/main/webapp/app/fw/svg/geodata.js
new file mode 100644
index 0000000..244507a
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/svg/geodata.js
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 -- SVG -- GeoData Service
+
+ @author Simon Hunt
+ */
+
+/*
+ The GeoData Service caches GeoJSON map data, and provides supporting
+ projections for mapping into SVG layers.
+
+ A GeoMap object can be fetched by ID. IDs that start with an asterisk
+ identify maps bundled with the GUI. IDs that do not start with an
+ asterisk are assumed to be URLs to externally provided data (exact
+ format to be decided).
+
+ e.g. var geomap = GeoDataService.fetchGeoMap('*continental-us');
+
+ Note that, since the GeoMap instance is cached / shared, it should
+ contain no state.
+ */
+
+(function () {
+ 'use strict';
+
+ // injected references
+ var $log, $http, fs;
+
+ // internal state
+ var cache = d3.map(),
+ bundledUrlPrefix = '../data/map/';
+
+ function getUrl(id) {
+ if (id[0] === '*') {
+ return bundledUrlPrefix + id.slice(1) + '.json';
+ }
+ return id + '.json';
+ }
+
+ angular.module('onosSvg')
+ .factory('GeoDataService', ['$log', '$http', 'FnService',
+ function (_$log_, _$http_, _fs_) {
+ $log = _$log_;
+ $http = _$http_;
+ fs = _fs_;
+
+ // start afresh...
+ function clearCache() {
+ cache = d3.map();
+ }
+
+ // returns a promise decorated with:
+ // .meta -- id, url, and whether the data was cached
+ // .mapdata -- geojson data (on response from server)
+
+ function fetchGeoMap(id) {
+ if (!fs.isS(id)) {
+ return null;
+ }
+ var url = getUrl(id),
+ promise = cache.get(id);
+
+ if (!promise) {
+ // need to fetch the data, build the object,
+ // cache it, and return it.
+ promise = $http.get(url);
+
+ promise.meta = {
+ id: id,
+ url: url,
+ wasCached: false
+ };
+
+ promise.then(function (response) {
+ // success
+ promise.mapdata = response.data;
+ }, function (response) {
+ // error
+ $log.warn('Failed to retrieve map data: ' + url,
+ response.status, response.data);
+ });
+
+ cache.set(id, promise);
+
+ } else {
+ promise.meta.wasCached = true;
+ }
+
+ return promise;
+ }
+
+ // TODO: clean up implementation of projection...
+ function setProjForView(path, topoData) {
+ var dim = 1000;
+
+ // start with unit scale, no translation..
+ geoMapProj.scale(1).translate([0, 0]);
+
+ // figure out dimensions of map data..
+ var b = path.bounds(topoData),
+ x1 = b[0][0],
+ y1 = b[0][1],
+ x2 = b[1][0],
+ y2 = b[1][1],
+ dx = x2 - x1,
+ dy = y2 - y1,
+ x = (x1 + x2) / 2,
+ y = (y1 + y2) / 2;
+
+ // size map to 95% of minimum dimension to fill space..
+ var s = .95 / Math.min(dx / dim, dy / dim);
+ var t = [dim / 2 - s * x, dim / 2 - s * y];
+
+ // set new scale, translation on the projection..
+ geoMapProj.scale(s).translate(t);
+ }
+
+
+ return {
+ clearCache: clearCache,
+ fetchGeoMap: fetchGeoMap
+ };
+ }]);
+}());
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/app/fw/svg/map.js b/web/gui/src/main/webapp/app/fw/svg/map.js
index 7a6b981..d57e65d 100644
--- a/web/gui/src/main/webapp/app/fw/svg/map.js
+++ b/web/gui/src/main/webapp/app/fw/svg/map.js
@@ -21,22 +21,14 @@
*/
/*
- The Map Service caches GeoJSON maps, which can be loaded into the map
- layer of the Topology View.
+ The Map Service provides a simple API for loading geographical maps into
+ an SVG layer. For example, as a background to the Topology View.
- A GeoMap object can be fetched by ID. IDs that start with an asterisk
- identify maps bundled with the GUI. IDs that do not start with an
- asterisk are assumed to be URLs to externally provided data (exact
- format to be decided).
+ e.g. var ok = MapService.loadMapInto(svgLayer, '*continental-us');
- e.g. var geomap = MapService.fetchGeoMap('*continental-us');
-
- The GeoMap object encapsulates topology data (features), and the
- D3 projection object.
-
- Note that, since the GeoMap instance is cached / shared, it should
- contain no state.
- */
+ The Map Service makes use of the GeoDataService to load the required data
+ from the server.
+*/
(function () {
'use strict';
diff --git a/web/gui/src/main/webapp/app/index.html b/web/gui/src/main/webapp/app/index.html
index d261b3f..5fa0364 100644
--- a/web/gui/src/main/webapp/app/index.html
+++ b/web/gui/src/main/webapp/app/index.html
@@ -44,6 +44,7 @@
<script src="fw/svg/svg.js"></script>
<script src="fw/svg/glyph.js"></script>
<script src="fw/svg/icon.js"></script>
+ <script src="fw/svg/geodata.js"></script>
<script src="fw/svg/map.js"></script>
<script src="fw/svg/zoom.js"></script>
diff --git a/web/gui/src/main/webapp/tests/app/fw/svg/geodata-spec.js b/web/gui/src/main/webapp/tests/app/fw/svg/geodata-spec.js
new file mode 100644
index 0000000..c88a1b0
--- /dev/null
+++ b/web/gui/src/main/webapp/tests/app/fw/svg/geodata-spec.js
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 -- SVG -- GeoData Service - Unit Tests
+
+ @author Simon Hunt
+ */
+describe('factory: fw/svg/geodata.js', function() {
+ var $log, $httpBackend, fs, gds, promise;
+
+ beforeEach(module('onosUtil', 'onosSvg'));
+
+ beforeEach(inject(function (_$log_, _$httpBackend_, FnService, GeoDataService) {
+ $log = _$log_;
+ $httpBackend = _$httpBackend_;
+ fs = FnService;
+ gds = GeoDataService;
+ gds.clearCache();
+ }));
+
+
+ it('should define GeoDataService', function () {
+ expect(gds).toBeDefined();
+ });
+
+ it('should define api functions', function () {
+ expect(fs.areFunctions(gds, [
+ 'clearCache', 'fetchGeoMap'
+ ])).toBeTruthy();
+ });
+
+ it('should return null when no parameters given', function () {
+ promise = gds.fetchGeoMap();
+ expect(promise).toBeNull();
+ });
+
+ it('should augment the id of a bundled map', function () {
+ var id = '*foo';
+ promise = gds.fetchGeoMap(id);
+ expect(promise.meta).toBeDefined();
+ expect(promise.meta.id).toBe(id);
+ expect(promise.meta.url).toBe('../data/map/foo.json');
+ });
+
+ it('should treat an external id as the url itself', function () {
+ var id = 'some/path/to/foo';
+ promise = gds.fetchGeoMap(id);
+ expect(promise.meta).toBeDefined();
+ expect(promise.meta.id).toBe(id);
+ expect(promise.meta.url).toBe(id + '.json');
+ });
+
+ it('should cache the returned objects', function () {
+ var id = 'foo';
+ promise = gds.fetchGeoMap(id);
+ expect(promise).toBeDefined();
+ expect(promise.meta.wasCached).toBeFalsy();
+ expect(promise.tagged).toBeUndefined();
+
+ promise.tagged = 'I woz here';
+
+ promise = gds.fetchGeoMap(id);
+ expect(promise).toBeDefined();
+ expect(promise.meta.wasCached).toBeTruthy();
+ expect(promise.tagged).toEqual('I woz here');
+ });
+
+ it('should clear the cache when asked', function () {
+ var id = 'foo';
+ promise = gds.fetchGeoMap(id);
+ expect(promise.meta.wasCached).toBeFalsy();
+
+ promise = gds.fetchGeoMap(id);
+ expect(promise.meta.wasCached).toBeTruthy();
+
+ gds.clearCache();
+ promise = gds.fetchGeoMap(id);
+ expect(promise.meta.wasCached).toBeFalsy();
+ });
+
+
+ it('should log a warning if data fails to load', function () {
+ var id = 'foo';
+ $httpBackend.expectGET('foo.json').respond(404, 'Not found');
+ spyOn($log, 'warn');
+
+ promise = gds.fetchGeoMap(id);
+ $httpBackend.flush();
+ expect(promise.mapdata).toBeUndefined();
+ expect($log.warn)
+ .toHaveBeenCalledWith('Failed to retrieve map data: foo.json',
+ 404, 'Not found');
+ });
+
+});
diff --git a/web/gui/src/main/webapp/tests/app/fw/svg/map-spec.js b/web/gui/src/main/webapp/tests/app/fw/svg/map-spec.js
index 23746f4..7ac7857 100644
--- a/web/gui/src/main/webapp/tests/app/fw/svg/map-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/svg/map-spec.js
@@ -82,76 +82,8 @@
// TODO: figure out how to test this function as a black box test.
expect(obj).toBeTruthy();
- debugger;
// todo: assert that paths are added to map layer element
});
-/*
-
-
-
- it('should return null when no parameters given', function () {
- promise = ms.fetchGeoMap();
- expect(promise).toBeNull();
- });
-
- it('should augment the id of a bundled map', function () {
- var id = '*foo';
- promise = ms.fetchGeoMap(id);
- expect(promise.meta).toBeDefined();
- expect(promise.meta.id).toBe(id);
- expect(promise.meta.url).toBe('../data/map/foo.json');
- });
-
- it('should treat an external id as the url itself', function () {
- var id = 'some/path/to/foo';
- promise = ms.fetchGeoMap(id);
- expect(promise.meta).toBeDefined();
- expect(promise.meta.id).toBe(id);
- expect(promise.meta.url).toBe(id + '.json');
- });
-
- it('should cache the returned objects', function () {
- var id = 'foo';
- promise = ms.fetchGeoMap(id);
- expect(promise).toBeDefined();
- expect(promise.meta.wasCached).toBeFalsy();
- expect(promise.tagged).toBeUndefined();
-
- promise.tagged = 'I woz here';
-
- promise = ms.fetchGeoMap(id);
- expect(promise).toBeDefined();
- expect(promise.meta.wasCached).toBeTruthy();
- expect(promise.tagged).toEqual('I woz here');
- });
-
- it('should clear the cache when asked', function () {
- var id = 'foo';
- promise = ms.fetchGeoMap(id);
- expect(promise.meta.wasCached).toBeFalsy();
-
- promise = ms.fetchGeoMap(id);
- expect(promise.meta.wasCached).toBeTruthy();
-
- ms.clearCache();
- promise = ms.fetchGeoMap(id);
- expect(promise.meta.wasCached).toBeFalsy();
- });
-
-
- it('should log a warning if data fails to load', function () {
- $httpBackend.expectGET(mapurl).respond(404, 'Not found');
- spyOn($log, 'warn');
-
- promise = ms.fetchGeoMap(mapid);
- $httpBackend.flush();
- expect(promise.mapdata).toBeUndefined();
- expect($log.warn)
- .toHaveBeenCalledWith('Failed to retrieve map data: ' + mapurl,
- 404, 'Not found');
-
- });
-*/
});