blob: 4d3c4a2f38054d234aff34c99d7a5b3e4beffd87 [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
19
20 @author Simon Hunt
21 */
22
23/*
Simon Hunta7b6a6b2015-01-13 19:53:09 -080024 The GeoData Service facilitates the fetching and caching of TopoJSON data
25 from the server, as well as providing a way of creating a path generator
26 for that data, to be used to render the map in an SVG layer.
Simon Huntf8173382015-01-13 14:12:09 -080027
Simon Hunta7b6a6b2015-01-13 19:53:09 -080028 A TopoData object can be fetched by ID. IDs that start with an asterisk
Simon Huntf8173382015-01-13 14:12:09 -080029 identify maps bundled with the GUI. IDs that do not start with an
Simon Hunta7b6a6b2015-01-13 19:53:09 -080030 asterisk are assumed to be URLs to externally provided data.
Simon Huntf8173382015-01-13 14:12:09 -080031
Simon Hunta7b6a6b2015-01-13 19:53:09 -080032 var topodata = GeoDataService.fetchTopoData('*continental-us');
Simon Huntf8173382015-01-13 14:12:09 -080033
Simon Hunta7b6a6b2015-01-13 19:53:09 -080034 The path generator can then be created for that data-set:
35
36 var gen = GeoDataService.createPathGenerator(topodata, opts);
37
38 opts is an optional argument that allows the override of default settings:
39 {
40 objectTag: 'states',
41 projection: d3.geo.mercator(),
42 logicalSize: 1000,
43 mapFillScale: .95
44 };
45
46 The returned object (gen) comprises transformed data (TopoJSON -> GeoJSON),
47 the D3 path generator function, and the settings used ...
48
49 {
50 geodata: { ... },
51 pathgen: function (...) { ... },
52 settings: { ... }
53 }
Simon Huntf8173382015-01-13 14:12:09 -080054 */
55
56(function () {
57 'use strict';
58
59 // injected references
60 var $log, $http, fs;
61
62 // internal state
63 var cache = d3.map(),
64 bundledUrlPrefix = '../data/map/';
65
66 function getUrl(id) {
67 if (id[0] === '*') {
68 return bundledUrlPrefix + id.slice(1) + '.json';
69 }
70 return id + '.json';
71 }
72
73 angular.module('onosSvg')
74 .factory('GeoDataService', ['$log', '$http', 'FnService',
75 function (_$log_, _$http_, _fs_) {
76 $log = _$log_;
77 $http = _$http_;
78 fs = _fs_;
79
80 // start afresh...
81 function clearCache() {
82 cache = d3.map();
83 }
84
85 // returns a promise decorated with:
86 // .meta -- id, url, and whether the data was cached
Simon Hunta7b6a6b2015-01-13 19:53:09 -080087 // .topodata -- TopoJSON data (on response from server)
Simon Huntf8173382015-01-13 14:12:09 -080088
Simon Hunta7b6a6b2015-01-13 19:53:09 -080089 function fetchTopoData(id) {
Simon Huntf8173382015-01-13 14:12:09 -080090 if (!fs.isS(id)) {
91 return null;
92 }
93 var url = getUrl(id),
94 promise = cache.get(id);
95
96 if (!promise) {
97 // need to fetch the data, build the object,
98 // cache it, and return it.
99 promise = $http.get(url);
100
101 promise.meta = {
102 id: id,
103 url: url,
104 wasCached: false
105 };
106
107 promise.then(function (response) {
108 // success
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800109 promise.topodata = response.data;
Simon Huntf8173382015-01-13 14:12:09 -0800110 }, function (response) {
111 // error
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800112 $log.warn('Failed to retrieve map TopoJSON data: ' + url,
Simon Huntf8173382015-01-13 14:12:09 -0800113 response.status, response.data);
114 });
115
116 cache.set(id, promise);
117
118 } else {
119 promise.meta.wasCached = true;
120 }
121
122 return promise;
123 }
124
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800125 var defaultGenSettings = {
126 objectTag: 'states',
127 projection: d3.geo.mercator(),
128 logicalSize: 1000,
129 mapFillScale: .95
130 };
131
132 // converts given TopoJSON-format data into corresponding GeoJSON
133 // data, and creates a path generator for that data.
134 function createPathGenerator(topoData, opts) {
135 var settings = $.extend({}, defaultGenSettings, opts),
136 topoObject = topoData.objects[settings.objectTag],
137 geoData = topojson.feature(topoData, topoObject),
138 proj = settings.projection,
139 dim = settings.logicalSize,
140 mfs = settings.mapFillScale,
141 path = d3.geo.path().projection(proj);
142
143 // adjust projection scale and translation to fill the view
144 // with the map
Simon Huntf8173382015-01-13 14:12:09 -0800145
146 // start with unit scale, no translation..
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800147 proj.scale(1).translate([0, 0]);
Simon Huntf8173382015-01-13 14:12:09 -0800148
149 // figure out dimensions of map data..
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800150 var b = path.bounds(geoData),
Simon Huntf8173382015-01-13 14:12:09 -0800151 x1 = b[0][0],
152 y1 = b[0][1],
153 x2 = b[1][0],
154 y2 = b[1][1],
155 dx = x2 - x1,
156 dy = y2 - y1,
157 x = (x1 + x2) / 2,
158 y = (y1 + y2) / 2;
159
160 // size map to 95% of minimum dimension to fill space..
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800161 var s = mfs / Math.min(dx / dim, dy / dim),
162 t = [dim / 2 - s * x, dim / 2 - s * y];
Simon Huntf8173382015-01-13 14:12:09 -0800163
164 // set new scale, translation on the projection..
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800165 proj.scale(s).translate(t);
Simon Huntf8173382015-01-13 14:12:09 -0800166
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800167 // return the results
168 return {
169 geodata: geoData,
170 pathgen: path,
171 settings: settings
172 };
173 }
Simon Huntf8173382015-01-13 14:12:09 -0800174
175 return {
176 clearCache: clearCache,
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800177 fetchTopoData: fetchTopoData,
178 createPathGenerator: createPathGenerator
Simon Huntf8173382015-01-13 14:12:09 -0800179 };
180 }]);
181}());