GUI -- Split MapService into GeoDataService and MapService. WIP.
Change-Id: Ibfe5b35ecdfaaf39b9d48abd29d0a44327dec130
Showing
5 changed files
with
255 additions
and
82 deletions
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +/* | ||
| 18 | + ONOS GUI -- SVG -- GeoData Service | ||
| 19 | + | ||
| 20 | + @author Simon Hunt | ||
| 21 | + */ | ||
| 22 | + | ||
| 23 | +/* | ||
| 24 | + The GeoData Service caches GeoJSON map data, and provides supporting | ||
| 25 | + projections for mapping into SVG layers. | ||
| 26 | + | ||
| 27 | + A GeoMap object can be fetched by ID. IDs that start with an asterisk | ||
| 28 | + identify maps bundled with the GUI. IDs that do not start with an | ||
| 29 | + asterisk are assumed to be URLs to externally provided data (exact | ||
| 30 | + format to be decided). | ||
| 31 | + | ||
| 32 | + e.g. var geomap = GeoDataService.fetchGeoMap('*continental-us'); | ||
| 33 | + | ||
| 34 | + Note that, since the GeoMap instance is cached / shared, it should | ||
| 35 | + contain no state. | ||
| 36 | + */ | ||
| 37 | + | ||
| 38 | +(function () { | ||
| 39 | + 'use strict'; | ||
| 40 | + | ||
| 41 | + // injected references | ||
| 42 | + var $log, $http, fs; | ||
| 43 | + | ||
| 44 | + // internal state | ||
| 45 | + var cache = d3.map(), | ||
| 46 | + bundledUrlPrefix = '../data/map/'; | ||
| 47 | + | ||
| 48 | + function getUrl(id) { | ||
| 49 | + if (id[0] === '*') { | ||
| 50 | + return bundledUrlPrefix + id.slice(1) + '.json'; | ||
| 51 | + } | ||
| 52 | + return id + '.json'; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + angular.module('onosSvg') | ||
| 56 | + .factory('GeoDataService', ['$log', '$http', 'FnService', | ||
| 57 | + function (_$log_, _$http_, _fs_) { | ||
| 58 | + $log = _$log_; | ||
| 59 | + $http = _$http_; | ||
| 60 | + fs = _fs_; | ||
| 61 | + | ||
| 62 | + // start afresh... | ||
| 63 | + function clearCache() { | ||
| 64 | + cache = d3.map(); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + // returns a promise decorated with: | ||
| 68 | + // .meta -- id, url, and whether the data was cached | ||
| 69 | + // .mapdata -- geojson data (on response from server) | ||
| 70 | + | ||
| 71 | + function fetchGeoMap(id) { | ||
| 72 | + if (!fs.isS(id)) { | ||
| 73 | + return null; | ||
| 74 | + } | ||
| 75 | + var url = getUrl(id), | ||
| 76 | + promise = cache.get(id); | ||
| 77 | + | ||
| 78 | + if (!promise) { | ||
| 79 | + // need to fetch the data, build the object, | ||
| 80 | + // cache it, and return it. | ||
| 81 | + promise = $http.get(url); | ||
| 82 | + | ||
| 83 | + promise.meta = { | ||
| 84 | + id: id, | ||
| 85 | + url: url, | ||
| 86 | + wasCached: false | ||
| 87 | + }; | ||
| 88 | + | ||
| 89 | + promise.then(function (response) { | ||
| 90 | + // success | ||
| 91 | + promise.mapdata = response.data; | ||
| 92 | + }, function (response) { | ||
| 93 | + // error | ||
| 94 | + $log.warn('Failed to retrieve map data: ' + url, | ||
| 95 | + response.status, response.data); | ||
| 96 | + }); | ||
| 97 | + | ||
| 98 | + cache.set(id, promise); | ||
| 99 | + | ||
| 100 | + } else { | ||
| 101 | + promise.meta.wasCached = true; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + return promise; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + // TODO: clean up implementation of projection... | ||
| 108 | + function setProjForView(path, topoData) { | ||
| 109 | + var dim = 1000; | ||
| 110 | + | ||
| 111 | + // start with unit scale, no translation.. | ||
| 112 | + geoMapProj.scale(1).translate([0, 0]); | ||
| 113 | + | ||
| 114 | + // figure out dimensions of map data.. | ||
| 115 | + var b = path.bounds(topoData), | ||
| 116 | + x1 = b[0][0], | ||
| 117 | + y1 = b[0][1], | ||
| 118 | + x2 = b[1][0], | ||
| 119 | + y2 = b[1][1], | ||
| 120 | + dx = x2 - x1, | ||
| 121 | + dy = y2 - y1, | ||
| 122 | + x = (x1 + x2) / 2, | ||
| 123 | + y = (y1 + y2) / 2; | ||
| 124 | + | ||
| 125 | + // size map to 95% of minimum dimension to fill space.. | ||
| 126 | + var s = .95 / Math.min(dx / dim, dy / dim); | ||
| 127 | + var t = [dim / 2 - s * x, dim / 2 - s * y]; | ||
| 128 | + | ||
| 129 | + // set new scale, translation on the projection.. | ||
| 130 | + geoMapProj.scale(s).translate(t); | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + | ||
| 134 | + return { | ||
| 135 | + clearCache: clearCache, | ||
| 136 | + fetchGeoMap: fetchGeoMap | ||
| 137 | + }; | ||
| 138 | + }]); | ||
| 139 | +}()); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -21,22 +21,14 @@ | ... | @@ -21,22 +21,14 @@ |
| 21 | */ | 21 | */ |
| 22 | 22 | ||
| 23 | /* | 23 | /* |
| 24 | - The Map Service caches GeoJSON maps, which can be loaded into the map | 24 | + The Map Service provides a simple API for loading geographical maps into |
| 25 | - layer of the Topology View. | 25 | + an SVG layer. For example, as a background to the Topology View. |
| 26 | 26 | ||
| 27 | - A GeoMap object can be fetched by ID. IDs that start with an asterisk | 27 | + e.g. var ok = MapService.loadMapInto(svgLayer, '*continental-us'); |
| 28 | - identify maps bundled with the GUI. IDs that do not start with an | ||
| 29 | - asterisk are assumed to be URLs to externally provided data (exact | ||
| 30 | - format to be decided). | ||
| 31 | 28 | ||
| 32 | - e.g. var geomap = MapService.fetchGeoMap('*continental-us'); | 29 | + The Map Service makes use of the GeoDataService to load the required data |
| 33 | - | 30 | + from the server. |
| 34 | - The GeoMap object encapsulates topology data (features), and the | 31 | +*/ |
| 35 | - D3 projection object. | ||
| 36 | - | ||
| 37 | - Note that, since the GeoMap instance is cached / shared, it should | ||
| 38 | - contain no state. | ||
| 39 | - */ | ||
| 40 | 32 | ||
| 41 | (function () { | 33 | (function () { |
| 42 | 'use strict'; | 34 | 'use strict'; | ... | ... |
| ... | @@ -44,6 +44,7 @@ | ... | @@ -44,6 +44,7 @@ |
| 44 | <script src="fw/svg/svg.js"></script> | 44 | <script src="fw/svg/svg.js"></script> |
| 45 | <script src="fw/svg/glyph.js"></script> | 45 | <script src="fw/svg/glyph.js"></script> |
| 46 | <script src="fw/svg/icon.js"></script> | 46 | <script src="fw/svg/icon.js"></script> |
| 47 | + <script src="fw/svg/geodata.js"></script> | ||
| 47 | <script src="fw/svg/map.js"></script> | 48 | <script src="fw/svg/map.js"></script> |
| 48 | <script src="fw/svg/zoom.js"></script> | 49 | <script src="fw/svg/zoom.js"></script> |
| 49 | 50 | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +/* | ||
| 18 | + ONOS GUI -- SVG -- GeoData Service - Unit Tests | ||
| 19 | + | ||
| 20 | + @author Simon Hunt | ||
| 21 | + */ | ||
| 22 | +describe('factory: fw/svg/geodata.js', function() { | ||
| 23 | + var $log, $httpBackend, fs, gds, promise; | ||
| 24 | + | ||
| 25 | + beforeEach(module('onosUtil', 'onosSvg')); | ||
| 26 | + | ||
| 27 | + beforeEach(inject(function (_$log_, _$httpBackend_, FnService, GeoDataService) { | ||
| 28 | + $log = _$log_; | ||
| 29 | + $httpBackend = _$httpBackend_; | ||
| 30 | + fs = FnService; | ||
| 31 | + gds = GeoDataService; | ||
| 32 | + gds.clearCache(); | ||
| 33 | + })); | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + it('should define GeoDataService', function () { | ||
| 37 | + expect(gds).toBeDefined(); | ||
| 38 | + }); | ||
| 39 | + | ||
| 40 | + it('should define api functions', function () { | ||
| 41 | + expect(fs.areFunctions(gds, [ | ||
| 42 | + 'clearCache', 'fetchGeoMap' | ||
| 43 | + ])).toBeTruthy(); | ||
| 44 | + }); | ||
| 45 | + | ||
| 46 | + it('should return null when no parameters given', function () { | ||
| 47 | + promise = gds.fetchGeoMap(); | ||
| 48 | + expect(promise).toBeNull(); | ||
| 49 | + }); | ||
| 50 | + | ||
| 51 | + it('should augment the id of a bundled map', function () { | ||
| 52 | + var id = '*foo'; | ||
| 53 | + promise = gds.fetchGeoMap(id); | ||
| 54 | + expect(promise.meta).toBeDefined(); | ||
| 55 | + expect(promise.meta.id).toBe(id); | ||
| 56 | + expect(promise.meta.url).toBe('../data/map/foo.json'); | ||
| 57 | + }); | ||
| 58 | + | ||
| 59 | + it('should treat an external id as the url itself', function () { | ||
| 60 | + var id = 'some/path/to/foo'; | ||
| 61 | + promise = gds.fetchGeoMap(id); | ||
| 62 | + expect(promise.meta).toBeDefined(); | ||
| 63 | + expect(promise.meta.id).toBe(id); | ||
| 64 | + expect(promise.meta.url).toBe(id + '.json'); | ||
| 65 | + }); | ||
| 66 | + | ||
| 67 | + it('should cache the returned objects', function () { | ||
| 68 | + var id = 'foo'; | ||
| 69 | + promise = gds.fetchGeoMap(id); | ||
| 70 | + expect(promise).toBeDefined(); | ||
| 71 | + expect(promise.meta.wasCached).toBeFalsy(); | ||
| 72 | + expect(promise.tagged).toBeUndefined(); | ||
| 73 | + | ||
| 74 | + promise.tagged = 'I woz here'; | ||
| 75 | + | ||
| 76 | + promise = gds.fetchGeoMap(id); | ||
| 77 | + expect(promise).toBeDefined(); | ||
| 78 | + expect(promise.meta.wasCached).toBeTruthy(); | ||
| 79 | + expect(promise.tagged).toEqual('I woz here'); | ||
| 80 | + }); | ||
| 81 | + | ||
| 82 | + it('should clear the cache when asked', function () { | ||
| 83 | + var id = 'foo'; | ||
| 84 | + promise = gds.fetchGeoMap(id); | ||
| 85 | + expect(promise.meta.wasCached).toBeFalsy(); | ||
| 86 | + | ||
| 87 | + promise = gds.fetchGeoMap(id); | ||
| 88 | + expect(promise.meta.wasCached).toBeTruthy(); | ||
| 89 | + | ||
| 90 | + gds.clearCache(); | ||
| 91 | + promise = gds.fetchGeoMap(id); | ||
| 92 | + expect(promise.meta.wasCached).toBeFalsy(); | ||
| 93 | + }); | ||
| 94 | + | ||
| 95 | + | ||
| 96 | + it('should log a warning if data fails to load', function () { | ||
| 97 | + var id = 'foo'; | ||
| 98 | + $httpBackend.expectGET('foo.json').respond(404, 'Not found'); | ||
| 99 | + spyOn($log, 'warn'); | ||
| 100 | + | ||
| 101 | + promise = gds.fetchGeoMap(id); | ||
| 102 | + $httpBackend.flush(); | ||
| 103 | + expect(promise.mapdata).toBeUndefined(); | ||
| 104 | + expect($log.warn) | ||
| 105 | + .toHaveBeenCalledWith('Failed to retrieve map data: foo.json', | ||
| 106 | + 404, 'Not found'); | ||
| 107 | + }); | ||
| 108 | + | ||
| 109 | +}); |
| ... | @@ -82,76 +82,8 @@ describe('factory: fw/svg/map.js', function() { | ... | @@ -82,76 +82,8 @@ describe('factory: fw/svg/map.js', function() { |
| 82 | // TODO: figure out how to test this function as a black box test. | 82 | // TODO: figure out how to test this function as a black box test. |
| 83 | 83 | ||
| 84 | expect(obj).toBeTruthy(); | 84 | expect(obj).toBeTruthy(); |
| 85 | - debugger; | ||
| 86 | 85 | ||
| 87 | // todo: assert that paths are added to map layer element | 86 | // todo: assert that paths are added to map layer element |
| 88 | }); | 87 | }); |
| 89 | 88 | ||
| 90 | -/* | ||
| 91 | - | ||
| 92 | - | ||
| 93 | - | ||
| 94 | - it('should return null when no parameters given', function () { | ||
| 95 | - promise = ms.fetchGeoMap(); | ||
| 96 | - expect(promise).toBeNull(); | ||
| 97 | - }); | ||
| 98 | - | ||
| 99 | - it('should augment the id of a bundled map', function () { | ||
| 100 | - var id = '*foo'; | ||
| 101 | - promise = ms.fetchGeoMap(id); | ||
| 102 | - expect(promise.meta).toBeDefined(); | ||
| 103 | - expect(promise.meta.id).toBe(id); | ||
| 104 | - expect(promise.meta.url).toBe('../data/map/foo.json'); | ||
| 105 | - }); | ||
| 106 | - | ||
| 107 | - it('should treat an external id as the url itself', function () { | ||
| 108 | - var id = 'some/path/to/foo'; | ||
| 109 | - promise = ms.fetchGeoMap(id); | ||
| 110 | - expect(promise.meta).toBeDefined(); | ||
| 111 | - expect(promise.meta.id).toBe(id); | ||
| 112 | - expect(promise.meta.url).toBe(id + '.json'); | ||
| 113 | - }); | ||
| 114 | - | ||
| 115 | - it('should cache the returned objects', function () { | ||
| 116 | - var id = 'foo'; | ||
| 117 | - promise = ms.fetchGeoMap(id); | ||
| 118 | - expect(promise).toBeDefined(); | ||
| 119 | - expect(promise.meta.wasCached).toBeFalsy(); | ||
| 120 | - expect(promise.tagged).toBeUndefined(); | ||
| 121 | - | ||
| 122 | - promise.tagged = 'I woz here'; | ||
| 123 | - | ||
| 124 | - promise = ms.fetchGeoMap(id); | ||
| 125 | - expect(promise).toBeDefined(); | ||
| 126 | - expect(promise.meta.wasCached).toBeTruthy(); | ||
| 127 | - expect(promise.tagged).toEqual('I woz here'); | ||
| 128 | - }); | ||
| 129 | - | ||
| 130 | - it('should clear the cache when asked', function () { | ||
| 131 | - var id = 'foo'; | ||
| 132 | - promise = ms.fetchGeoMap(id); | ||
| 133 | - expect(promise.meta.wasCached).toBeFalsy(); | ||
| 134 | - | ||
| 135 | - promise = ms.fetchGeoMap(id); | ||
| 136 | - expect(promise.meta.wasCached).toBeTruthy(); | ||
| 137 | - | ||
| 138 | - ms.clearCache(); | ||
| 139 | - promise = ms.fetchGeoMap(id); | ||
| 140 | - expect(promise.meta.wasCached).toBeFalsy(); | ||
| 141 | - }); | ||
| 142 | - | ||
| 143 | - | ||
| 144 | - it('should log a warning if data fails to load', function () { | ||
| 145 | - $httpBackend.expectGET(mapurl).respond(404, 'Not found'); | ||
| 146 | - spyOn($log, 'warn'); | ||
| 147 | - | ||
| 148 | - promise = ms.fetchGeoMap(mapid); | ||
| 149 | - $httpBackend.flush(); | ||
| 150 | - expect(promise.mapdata).toBeUndefined(); | ||
| 151 | - expect($log.warn) | ||
| 152 | - .toHaveBeenCalledWith('Failed to retrieve map data: ' + mapurl, | ||
| 153 | - 404, 'Not found'); | ||
| 154 | - | ||
| 155 | - }); | ||
| 156 | -*/ | ||
| 157 | }); | 89 | }); | ... | ... |
-
Please register or login to post a comment