blob: 815937d2c61a8acd023307013d0483743dedbfd4 [file] [log] [blame]
Simon Huntf8173382015-01-13 14:12:09 -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 -- GeoData Service
Simon Huntf8173382015-01-13 14:12:09 -080019 */
20
21/*
Simon Hunta7b6a6b2015-01-13 19:53:09 -080022 The GeoData Service facilitates the fetching and caching of TopoJSON data
23 from the server, as well as providing a way of creating a path generator
24 for that data, to be used to render the map in an SVG layer.
Simon Huntf8173382015-01-13 14:12:09 -080025
Simon Hunta7b6a6b2015-01-13 19:53:09 -080026 A TopoData object can be fetched by ID. IDs that start with an asterisk
Simon Huntf8173382015-01-13 14:12:09 -080027 identify maps bundled with the GUI. IDs that do not start with an
Simon Hunta7b6a6b2015-01-13 19:53:09 -080028 asterisk are assumed to be URLs to externally provided data.
Simon Huntf8173382015-01-13 14:12:09 -080029
Simon Hunta7b6a6b2015-01-13 19:53:09 -080030 var topodata = GeoDataService.fetchTopoData('*continental-us');
Simon Huntf8173382015-01-13 14:12:09 -080031
Simon Hunta7b6a6b2015-01-13 19:53:09 -080032 The path generator can then be created for that data-set:
33
34 var gen = GeoDataService.createPathGenerator(topodata, opts);
35
36 opts is an optional argument that allows the override of default settings:
37 {
38 objectTag: 'states',
39 projection: d3.geo.mercator(),
40 logicalSize: 1000,
41 mapFillScale: .95
42 };
43
44 The returned object (gen) comprises transformed data (TopoJSON -> GeoJSON),
45 the D3 path generator function, and the settings used ...
46
47 {
48 geodata: { ... },
49 pathgen: function (...) { ... },
50 settings: { ... }
51 }
Simon Huntf8173382015-01-13 14:12:09 -080052 */
53
54(function () {
55 'use strict';
56
57 // injected references
58 var $log, $http, fs;
59
60 // internal state
61 var cache = d3.map(),
Thomas Vachuskae95da772015-02-23 15:50:11 -080062 bundledUrlPrefix = 'data/map/';
Simon Huntf8173382015-01-13 14:12:09 -080063
64 function getUrl(id) {
65 if (id[0] === '*') {
Simon Hunt2362b072015-06-11 20:08:22 -070066 return bundledUrlPrefix + id.slice(1) + '.topojson';
Simon Huntf8173382015-01-13 14:12:09 -080067 }
Simon Hunt2362b072015-06-11 20:08:22 -070068 return id + '.topojson';
69 }
70
71
72 // start afresh...
73 function clearCache() {
74 cache = d3.map();
75 }
76
77 // returns a promise decorated with:
78 // .meta -- id, url, and whether the data was cached
79 // .topodata -- TopoJSON data (on response from server)
80
81 function fetchTopoData(id) {
82 if (!fs.isS(id)) {
83 return null;
84 }
85 var url = getUrl(id),
86 promise = cache.get(id);
87
88 if (!promise) {
89 // need to fetch the data, build the object,
90 // cache it, and return it.
91 promise = $http.get(url);
92
93 promise.meta = {
94 id: id,
95 url: url,
96 wasCached: false
97 };
98
99 promise.then(function (response) {
100 // success
101 promise.topodata = response.data;
102 }, function (response) {
103 // error
104 $log.warn('Failed to retrieve map TopoJSON data: ' + url,
105 response.status, response.data);
106 });
107
108 cache.set(id, promise);
109
110 } else {
111 promise.meta.wasCached = true;
112 }
113
114 return promise;
115 }
116
117 var defaultGenSettings = {
118 objectTag: 'states',
119 projection: d3.geo.mercator(),
120 logicalSize: 1000,
121 mapFillScale: .95
122 };
123
124 // converts given TopoJSON-format data into corresponding GeoJSON
125 // data, and creates a path generator for that data.
126 function createPathGenerator(topoData, opts) {
127 var settings = angular.extend({}, defaultGenSettings, opts),
128 topoObject = topoData.objects[settings.objectTag],
129 geoData = topojson.feature(topoData, topoObject),
130 proj = settings.projection,
131 dim = settings.logicalSize,
132 mfs = settings.mapFillScale,
133 path = d3.geo.path().projection(proj);
134
Simon Huntfacad992016-02-25 09:58:33 -0800135 rescaleProjection(proj, mfs, dim, path, geoData, opts.adjustScale);
Simon Hunt2362b072015-06-11 20:08:22 -0700136
137 // return the results
138 return {
139 geodata: geoData,
140 pathgen: path,
141 settings: settings
142 };
143 }
144
Simon Huntfacad992016-02-25 09:58:33 -0800145 function rescaleProjection(proj, mfs, dim, path, geoData, adjustScale) {
146 var adj = adjustScale || 1;
Simon Hunt2362b072015-06-11 20:08:22 -0700147 // adjust projection scale and translation to fill the view
148 // with the map
149
150 // start with unit scale, no translation..
151 proj.scale(1).translate([0, 0]);
152
153 // figure out dimensions of map data..
154 var b = path.bounds(geoData),
155 x1 = b[0][0],
156 y1 = b[0][1],
157 x2 = b[1][0],
158 y2 = b[1][1],
159 dx = x2 - x1,
160 dy = y2 - y1,
161 x = (x1 + x2) / 2,
162 y = (y1 + y2) / 2;
163
164 // size map to 95% of minimum dimension to fill space..
Simon Huntfacad992016-02-25 09:58:33 -0800165 var s = (mfs / Math.min(dx / dim, dy / dim)) * adj,
Simon Hunt2362b072015-06-11 20:08:22 -0700166 t = [dim / 2 - s * x, dim / 2 - s * y];
167
168 // set new scale, translation on the projection..
169 proj.scale(s).translate(t);
Simon Huntf8173382015-01-13 14:12:09 -0800170 }
171
172 angular.module('onosSvg')
173 .factory('GeoDataService', ['$log', '$http', 'FnService',
174 function (_$log_, _$http_, _fs_) {
175 $log = _$log_;
176 $http = _$http_;
177 fs = _fs_;
178
Simon Huntf8173382015-01-13 14:12:09 -0800179
180 return {
181 clearCache: clearCache,
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800182 fetchTopoData: fetchTopoData,
Simon Hunt2362b072015-06-11 20:08:22 -0700183 createPathGenerator: createPathGenerator,
184 rescaleProjection: rescaleProjection
Simon Huntf8173382015-01-13 14:12:09 -0800185 };
186 }]);
187}());