blob: 7a6b981c44b9898c809c99daf1661a3fcd34e706 [file] [log] [blame]
Simon Hunt7ac7be92015-01-06 10:47:56 -08001/*
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 -- Map Service
19
20 @author Simon Hunt
21 */
Simon Huntf044d8a2015-01-07 17:53:04 -080022
23/*
24 The Map Service caches GeoJSON maps, which can be loaded into the map
25 layer of the Topology View.
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 = MapService.fetchGeoMap('*continental-us');
33
34 The GeoMap object encapsulates topology data (features), and the
35 D3 projection object.
36
37 Note that, since the GeoMap instance is cached / shared, it should
38 contain no state.
39 */
40
Simon Hunt7ac7be92015-01-06 10:47:56 -080041(function () {
42 'use strict';
43
Simon Huntf044d8a2015-01-07 17:53:04 -080044 // injected references
Simon Hunt404e54c2015-01-09 11:58:49 -080045 var $log, $http, fs;
Simon Huntf044d8a2015-01-07 17:53:04 -080046
47 // internal state
Simon Hunt404e54c2015-01-09 11:58:49 -080048 var mapCache = d3.map(),
Simon Hunt1e8eff42015-01-08 17:19:02 -080049 bundledUrlPrefix = '../data/map/';
Simon Huntf044d8a2015-01-07 17:53:04 -080050
51 function getUrl(id) {
52 if (id[0] === '*') {
53 return bundledUrlPrefix + id.slice(1) + '.json';
54 }
55 return id + '.json';
56 }
Simon Hunt7ac7be92015-01-06 10:47:56 -080057
58 angular.module('onosSvg')
Simon Hunt404e54c2015-01-09 11:58:49 -080059 .factory('MapService', ['$log', '$http', 'FnService',
60 function (_$log_, _$http_, _fs_) {
Simon Hunt7ac7be92015-01-06 10:47:56 -080061 $log = _$log_;
Simon Hunt1e8eff42015-01-08 17:19:02 -080062 $http = _$http_;
Simon Huntf044d8a2015-01-07 17:53:04 -080063 fs = _fs_;
64
Simon Hunt1e8eff42015-01-08 17:19:02 -080065
Simon Huntf044d8a2015-01-07 17:53:04 -080066 function fetchGeoMap(id) {
67 if (!fs.isS(id)) {
68 return null;
69 }
Simon Hunt404e54c2015-01-09 11:58:49 -080070 var url = getUrl(id),
71 promise = mapCache.get(id);
Simon Huntf044d8a2015-01-07 17:53:04 -080072
Simon Hunt1e8eff42015-01-08 17:19:02 -080073 if (!promise) {
Simon Hunt404e54c2015-01-09 11:58:49 -080074 // need to fetch the data, build the object,
75 // cache it, and return it.
76 promise = $http.get(url);
Simon Hunt1e8eff42015-01-08 17:19:02 -080077
78 promise.meta = {
79 id: id,
80 url: url,
Simon Hunt404e54c2015-01-09 11:58:49 -080081 wasCached: false
Simon Hunt1e8eff42015-01-08 17:19:02 -080082 };
83
Simon Hunt404e54c2015-01-09 11:58:49 -080084 promise.then(function (response) {
85 // success
86 promise.mapdata = response.data;
87 }, function (response) {
88 // error
89 $log.warn('Failed to retrieve map data: ' + url,
90 response.status, response.data);
91 });
92
93 mapCache.set(id, promise);
94
Simon Huntf044d8a2015-01-07 17:53:04 -080095 } else {
Simon Hunt1e8eff42015-01-08 17:19:02 -080096 promise.meta.wasCached = true;
Simon Huntf044d8a2015-01-07 17:53:04 -080097 }
98
Simon Hunt1e8eff42015-01-08 17:19:02 -080099 return promise;
Simon Huntf044d8a2015-01-07 17:53:04 -0800100 }
Simon Hunt7ac7be92015-01-06 10:47:56 -0800101
Simon Hunt404e54c2015-01-09 11:58:49 -0800102 var geoMapProj;
103
104 function setProjForView(path, topoData) {
105 var dim = 1000;
106
107 // start with unit scale, no translation..
108 geoMapProj.scale(1).translate([0, 0]);
109
110 // figure out dimensions of map data..
111 var b = path.bounds(topoData),
112 x1 = b[0][0],
113 y1 = b[0][1],
114 x2 = b[1][0],
115 y2 = b[1][1],
116 dx = x2 - x1,
117 dy = y2 - y1,
118 x = (x1 + x2) / 2,
119 y = (y1 + y2) / 2;
120
121 // size map to 95% of minimum dimension to fill space..
122 var s = .95 / Math.min(dx / dim, dy / dim);
123 var t = [dim / 2 - s * x, dim / 2 - s * y];
124
125 // set new scale, translation on the projection..
126 geoMapProj.scale(s).translate(t);
127 }
128
129
130 function loadMapInto(mapLayer, id) {
131 var mapObject = fetchGeoMap(id);
132 if (!mapObject) {
133 $log.warn('Failed to load map: ' + id);
134 return null;
135 }
136
137 var mapdata = mapObject.mapdata,
138 topoData, path;
139
140 mapObject.then(function () {
141 // extracts the topojson data into geocoordinate-based geometry
142 topoData = topojson.feature(mapdata, mapdata.objects.states);
143
144 // see: http://bl.ocks.org/mbostock/4707858
145 geoMapProj = d3.geo.mercator();
146 path = d3.geo.path().projection(geoMapProj);
147
148 setProjForView(path, topoData);
149
150 mapLayer.selectAll('path')
151 .data(topoData.features)
152 .enter()
153 .append('path')
154 .attr('d', path);
155 });
156 // TODO: review whether we should just return true (not the map object)
157 return mapObject;
158 }
159
Simon Hunt7ac7be92015-01-06 10:47:56 -0800160 return {
Simon Hunt404e54c2015-01-09 11:58:49 -0800161 loadMapInto: loadMapInto
Simon Hunt7ac7be92015-01-06 10:47:56 -0800162 };
163 }]);
164
165}());